From 311b6689fe97d7d2bbb68a4bd51ed94c8d3050b4 Mon Sep 17 00:00:00 2001 From: vincent <vincent@groupe-sa.fr> Date: Wed, 23 Dec 2015 10:03:39 +0100 Subject: [PATCH] QCron::next returns the next date matching the cron pattern. --- src/qcron.cpp | 181 ++++++++++++++++++++++- src/qcron.hpp | 6 +- src/qcronfield.cpp | 165 +++++++++++++-------- src/qcronfield.hpp | 30 +++- src/qcronnode.cpp | 228 +++++++++++++++++++++++++++-- src/qcronnode.hpp | 47 +++++- test/qcron_test.cpp | 337 +++++++++++++++++++++++++++++++++++++++++-- test/qcron_test.hpp | 6 +- test/syntax_test.cpp | 39 ++--- 9 files changed, 921 insertions(+), 118 deletions(-) diff --git a/src/qcron.cpp b/src/qcron.cpp index 734db92..d06bc92 100644 --- a/src/qcron.cpp +++ b/src/qcron.cpp @@ -1,4 +1,5 @@ #include "qcron.hpp" +#include "qcronnode.hpp" #include <cmath> @@ -75,11 +76,11 @@ _parsePattern(QString & pattern) } try { - for (int i = 0; i < 6; ++i) - { - _fields[i].parse(fields[i]); - _is_valid &= _fields[i].isValid(); - } + for (int i = 0; i < 6; ++i) + { + _fields[i].parse(fields[i]); + _is_valid &= _fields[i].isValid(); + } } catch (QCronFieldException & e) { @@ -87,15 +88,167 @@ _parsePattern(QString & pattern) } } +/******************************************************************************/ + +QList<EField> +getPreviousFields(EField field) +{ + QList<EField> fields; + + switch (field) + { + case YEAR: + fields << MONTH; + case MONTH: + fields << DOM; + case DOW: + case DOM: + fields << HOUR; + case HOUR: + fields << MINUTE; + case MINUTE: + break; + default: + qFatal("Should not be in getPreviousTimeUnit"); + } + + return fields; +} + +/******************************************************************************/ + +void +QCron:: +add(QDateTime & dt, EField field, int value) +{ + switch (field) + { + case YEAR: + dt = dt.addYears(value); + break; + case MONTH: + dt = dt.addMonths(value); + break; + case DOW: + case DOM: + dt = dt.addDays(value); + break; + case HOUR: + dt = dt.addSecs(3600 * value); + break; + case MINUTE: + dt = dt.addSecs(60 * value); + break; + default: + qFatal("Unknown value in add"); + } + QList<EField> previous_fields = getPreviousFields(field); + foreach (EField field, previous_fields) + { + _fields[field].reset(dt); + } +} + +/******************************************************************************/ + +void +set(QDateTime & dt, EField field, int value) +{ + QDate date = dt.date(); + QTime time = dt.time(); + + switch (field) + { + case YEAR: + dt.setDate(QDate(value, date.month(), date.day())); + break; + case MONTH: + dt.setDate(QDate(date.year(), value, date.day())); + break; + case DOW: + case DOM: + dt.setDate(QDate(date.year(), date.month(), value)); + break; + case HOUR: + dt.setTime(QTime(value, time.minute(), 0)); + break; + case MINUTE: + dt.setTime(QTime(time.hour(), value, 0)); + break; + default: + qFatal("Unknown value in add"); + } +} + +/******************************************************************************/ + +void +QCron:: +catchUp(QDateTime & dt, EField field, int value) +{ + int current_time_unit = _fields[field].getDateTimeSection(dt); + if (current_time_unit < value) + { + add(dt, field, value - current_time_unit); + } + else if (current_time_unit == value) + { + // Nothing + } + else if (current_time_unit > value) + { + if (YEAR == field) + { + dt = QDateTime(); + } + else if (MINUTE != field) + { + int max = _fields[field].getMax(); + add(dt, field, (max - current_time_unit + value)); + } + } +} + +/******************************************************************************/ + +void +QCron:: +chiche(QDateTime & dt, EField field) +{ + QCronNode * node = _fields[field].getRoot(); + if (NULL == node) + { + qFatal("Problem"); + } + node->process(this, dt, field); +} + + /******************************************************************************/ QDateTime QCron:: next(QDateTime dt) { - for (int i = 0; i < 6; ++i) + dt = dt.addSecs(60); + while (!match(dt)) { - _fields[i].next(dt); + //qDebug() << dt << "doesn't match"; + for (int i = YEAR; i >= 0; --i) + { + chiche(dt, (EField)i); + //qDebug() << dt << "after Chcihe"; + if (!dt.isValid()) + { + return dt; + } + while (!_fields[(EField)i].match(dt)) + { + //qDebug() << dt << (EField)i << "doesn't match"; + int dummy = 1; + _fields[(EField)i].applyOffset(dt, dummy); + } + } } return dt; } @@ -111,3 +264,17 @@ next(int n) } /******************************************************************************/ + +bool +QCron:: +match(const QDateTime & dt) const +{ + bool does_match = true; + for (int i = 0; i < 6; ++i) + { + does_match &= _fields[i].match(dt); + } + return does_match; +} + +/******************************************************************************/ diff --git a/src/qcron.hpp b/src/qcron.hpp index 0296ee1..e0cba5c 100644 --- a/src/qcron.hpp +++ b/src/qcron.hpp @@ -28,6 +28,10 @@ public: QDateTime next(int n = 1); QDateTime next(QDateTime dt); + void catchUp(QDateTime & dt, EField field, int value); + bool match(const QDateTime & dt) const; + void add(QDateTime & dt, EField field, int value); + signals: void activated(); @@ -45,7 +49,7 @@ private: void _parseField(QString & field_str, EField field); QString _validCharacters(EField field); - + void chiche(QDateTime & dt, EField field); }; #endif diff --git a/src/qcronfield.cpp b/src/qcronfield.cpp index 93d84bc..ae10e8c 100644 --- a/src/qcronfield.cpp +++ b/src/qcronfield.cpp @@ -1,4 +1,5 @@ #include "qcronfield.hpp" +#include "qcronnode.hpp" #include <QDebug> @@ -29,7 +30,6 @@ _parseInt(QString & str) .arg(str)); } str.remove(0, char_idx); - //qDebug() << "Parsing int" << value; if (value < _min || _max < value) { throw QCronFieldException(QString("Value %1 out of range [%2;%3]") @@ -40,11 +40,10 @@ _parseInt(QString & str) /******************************************************************************/ -QCronRangeNode* +QCronRangeNode * QCronField:: _parseRange(QString & str) { - // qDebug() << "Parsing a Range"; if (_last_node == NULL) { throw QCronFieldException(QString("Syntax error at %1: range has no beginning") @@ -73,28 +72,29 @@ _parseRange(QString & str) /******************************************************************************/ -QCronEveryNode* +QCronEveryNode * QCronField:: _parseEvery(QString & str) { -// qDebug() << "Parsing an Every"; str.remove(0, 1); return new QCronEveryNode(_last_node, _parseInt(str)); } /******************************************************************************/ -QCronListNode* +QCronListNode * QCronField:: _parseList(QString & str) { -// qDebug() << "Parsing a List"; QCronListNode * list = new QCronListNode(); list->nodes() << _last_node; _last_node = list; - while (str[0] == ',') + while (!str.isEmpty()) { - str.remove(0, 1); + if (str[0] == ',') + { + str.remove(0, 1); + } QCronNode * node = _parseNode(str); list->nodes() << node; _last_node = node; @@ -108,29 +108,35 @@ QCronNode * QCronField:: _parseNode(QString & str) { - //qDebug() << "Parsing a node"; + QCronNode * node = NULL; QChar c = str[0]; if (c.isDigit()) { - return _parseInt(str); + node = _parseInt(str); } else if ("-" == c) { - return _parseRange(str); + node = _parseRange(str); } else if ("/" == c) { - return _parseEvery(str); + node = _parseEvery(str); } else if ("*" == c) { - return new QCronAllNode; + str.remove(0, 1); + node = new QCronAllNode; } else if ("," == c) { - return _parseList(str); + node = _parseList(str); } - throw QCronFieldException(QString("Unexpected character %1").arg(c)); + if (NULL == node) + { + throw QCronFieldException(QString("Unexpected character %1").arg(c)); + } + node->setField(this); + return node; } /******************************************************************************/ @@ -139,38 +145,38 @@ void QCronField:: parse(QString & str) { - try + _last_node = NULL; + _root = _parseNode(str); + while (!str.isEmpty()) { - _last_node = NULL; + _last_node = _root; _root = _parseNode(str); - if (!str.isEmpty()) - { - _last_node = _root; - _root = _parseNode(str); - } - _is_valid = true; - } - catch (int) - { - _is_valid = false; } + _is_valid = true; } /******************************************************************************/ int QCronField:: -getDateTimeSection(QDateTime & dt) const +getDateTimeSection(const QDateTime & dt) const { switch (_field) { - case MINUTE: return dt.time().minute(); - case HOUR: return dt.time().hour(); - case DOM: return dt.date().day(); - case MONTH: return dt.date().month(); - case DOW: return dt.date().dayOfWeek(); - case YEAR: return dt.date().year(); - default: qFatal("Shouldn't be here"); + case MINUTE: + return dt.time().minute(); + case HOUR: + return dt.time().hour(); + case DOM: + return dt.date().day(); + case MONTH: + return dt.date().month(); + case DOW: + return dt.date().dayOfWeek(); + case YEAR: + return dt.date().year(); + default: + qFatal("Shouldn't be here"); } } @@ -178,43 +184,37 @@ getDateTimeSection(QDateTime & dt) const void QCronField:: -applyOffset(QDateTime & dt, int offset) const +applyOffset(QDateTime & dt, int & offset) const { - bool overflow = offset < 0 || (offset == 0 && _field == MINUTE); - - offset += overflow ? 1 : 0; - switch (_field) { - case MINUTE: - { - offset -= overflow ? 1 : 0; - offset += offset <= 0 ? 60 : 0; - dt = dt.addSecs(60 * offset); - break; - } - case HOUR: + case MINUTE: { - dt.addSecs(3600 * offset); + dt = dt.addSecs(60 * offset); break; } - case DOM: - case DOW: + case HOUR: { - dt.addDays(offset); + dt = dt.addSecs(3600 * offset); break; } - case MONTH: + case DOM: + case DOW: { - dt.addMonths(offset); + dt = dt.addDays(offset); break; } - case YEAR: + case MONTH: { - dt.addYears(offset); + dt = dt.addMonths(offset); break; } - default: + case YEAR: + { + dt = dt.addYears(offset); + break; + } + default: { qFatal("Shouldn't be here"); } @@ -223,11 +223,54 @@ applyOffset(QDateTime & dt, int offset) const /******************************************************************************/ -void +int QCronField:: next(QDateTime & dt) { int time_section = getDateTimeSection(dt); - int offset = _root->next(time_section); - applyOffset(dt, offset); + return _root->next(time_section); } + +/******************************************************************************/ + +void +QCronField:: +reset(QDateTime & dt) +{ + int value = _min; + QDate date = dt.date(); + QTime time = dt.time(); + + switch (_field) + { + case YEAR: + dt.setDate(QDate(value, date.month(), date.day())); + break; + case MONTH: + dt.setDate(QDate(date.year(), value, date.day())); + break; + case DOW: + case DOM: + dt.setDate(QDate(date.year(), date.month(), value)); + break; + case HOUR: + dt.setTime(QTime(value, time.minute(), 0)); + break; + case MINUTE: + dt.setTime(QTime(time.hour(), value, 0)); + break; + default: + qFatal("Unknown value in add"); + } +} + +/******************************************************************************/ + +bool +QCronField:: +match(const QDateTime & dt) const +{ + return _root->match(getDateTimeSection(dt)); +} + +/******************************************************************************/ diff --git a/src/qcronfield.hpp b/src/qcronfield.hpp index 4aa60ce..577bf0f 100644 --- a/src/qcronfield.hpp +++ b/src/qcronfield.hpp @@ -4,7 +4,6 @@ #include <QDateTime> #include <QList> #include <QString> -#include "qcronnode.hpp" enum EField { @@ -16,6 +15,12 @@ enum EField YEAR }; +class QCronIntNode; +class QCronRangeNode; +class QCronNode; +class QCronEveryNode; +class QCronListNode; + class QCronFieldException { public: @@ -47,21 +52,36 @@ public: case DOM: _min = 1; _max = 31; break; case MONTH: _min = 1; _max = 12; break; case DOW: _min = 1; _max = 7 ; break; - case YEAR: _min = 2016; _max = 2099; break; + case YEAR: _min = 1; _max = 2099; break; default: throw 42; } } + EField getField() const + { return _field; } + + int getMax() const + { return _max; } + + int getMin() const + { return _min; } // Features. void parse(QString & str); bool isValid() const { return _is_valid; } + bool match(const QDateTime & dt) const; - void next(QDateTime & dt); + QCronNode * getRoot() const + { return _root; } + + int next(QDateTime & dt); + + int getDateTimeSection(const QDateTime & dt) const; + void applyOffset(QDateTime & dt, int & offset) const; + + void reset(QDateTime & dt); - int getDateTimeSection(QDateTime & dt) const; - void applyOffset(QDateTime & dt, int offset) const; private: int _min; diff --git a/src/qcronnode.cpp b/src/qcronnode.cpp index d4b89fc..965ea09 100644 --- a/src/qcronnode.cpp +++ b/src/qcronnode.cpp @@ -1,4 +1,10 @@ #include "qcronnode.hpp" +#include "qcronfield.hpp" +#include "qcron.hpp" + +#include <QDebug> + +#include <QDebug> /******************************************************************************/ @@ -9,6 +15,16 @@ QCronNode:: /******************************************************************************/ +void +QCronNode:: +setField(QCronField * field) +{ + _field = field; +} + +/******************************************************************************/ +/******************************************************************************/ + QCronIntNode:: QCronIntNode(int v) : _value(v) @@ -35,25 +51,91 @@ next(int t) const /******************************************************************************/ +void +QCronIntNode:: +process(QCron * cron, + QDateTime & dt, + EField field) +{ + cron->catchUp(dt, field, _value); +} + +/******************************************************************************/ + +bool +QCronIntNode:: +match(int tu) const +{ + return tu == _value; +} + +/******************************************************************************/ +/******************************************************************************/ + int QCronStrNode:: next(int t) const - { - return t - 1; - } +{ + return t - 1; +} /******************************************************************************/ +void +QCronStrNode:: +process(QCron *, + QDateTime &, + EField ) +{ + // TODO +} + +/******************************************************************************/ + +bool +QCronStrNode:: +match(int tu) const +{ + return tu; +} + +/******************************************************************************/ +/******************************************************************************/ + int QCronAllNode:: next(int t) const { Q_UNUSED(t); - return 1; + return 0; } /******************************************************************************/ +void +QCronAllNode:: +process(QCron * cron, + QDateTime & dt, + EField field) +{ + Q_UNUSED(cron); + Q_UNUSED(dt); + Q_UNUSED(field); +} + +/******************************************************************************/ + +bool +QCronAllNode:: +match(int tu) const +{ + Q_UNUSED(tu); + return true; +} + +/******************************************************************************/ +/******************************************************************************/ + QCronRangeNode:: QCronRangeNode(const QCronIntNode * begin, const QCronIntNode * end) @@ -64,19 +146,71 @@ QCronRangeNode(const QCronIntNode * begin, /******************************************************************************/ +void +QCronRangeNode:: +process(QCron * cron, + QDateTime & dt, + EField field) +{ + int begin = beginValue(); + int end = endValue(); + int current_time_unit = _field->getDateTimeSection(dt); + if (current_time_unit < begin || end < current_time_unit) + { + cron->catchUp(dt, field, begin); + } + else + { + if (field == MINUTE) + { + cron->add(dt, field, 1); + } + } +} + +/******************************************************************************/ + +int +QCronRangeNode:: +beginValue() const +{ + return _begin->value(); +} + +/******************************************************************************/ + +int +QCronRangeNode:: +endValue() const +{ + return _end->value(); +} + +/******************************************************************************/ + int QCronRangeNode:: next(int t) const { - if (_begin->value() <= t && t < _end->value()) + if (_begin->value() <= t && t <= _end->value()) { - return 0; + return 1; } return _begin->value() - t; } /******************************************************************************/ +bool +QCronRangeNode:: +match(int tu) const +{ + return _begin->value() <= tu && tu <= _end->value(); +} + +/******************************************************************************/ +/******************************************************************************/ + QCronEveryNode:: QCronEveryNode(QCronNode * what, QCronIntNode * freq) @@ -85,27 +219,82 @@ QCronEveryNode(QCronNode * what, { } +/******************************************************************************/ + +void +QCronEveryNode:: +process(QCron * cron , + QDateTime & dt, + EField field) +{ + int freq = _freq->value(); + _what->process(cron, dt, field); + while (_field->getDateTimeSection(dt) % freq) + { + cron->add(dt, field, 1); + _what->process(cron, dt, field); + } +} + + /******************************************************************************/ int QCronEveryNode:: next(int t) const { - int next = _what->next(t) % _freq->value(); - return _freq->value() - next; + int what_next = _what->next(t); + if (what_next == 1) + { + /* what_next == 1 if we're in the right range AND if we're one + * time unit ahead of it */ + int next = t % _freq->value(); + return _freq->value() - next; + } + return what_next; } /******************************************************************************/ -QList<QCronNode *> +bool +QCronEveryNode:: +match(int tu) const +{ + return _what->match(tu) && tu % _freq->value() == 0; +} + +/******************************************************************************/ +/******************************************************************************/ + +QList<QCronNode *> & QCronListNode:: -nodes() const +nodes() { return _nodes; } /******************************************************************************/ +void +QCronListNode:: +process(QCron * cron, + QDateTime & dt, + EField field) +{ + foreach (QCronNode * node, _nodes) + { + int node_next = node->next(_field->getDateTimeSection(dt)); + if (node_next >= 0) + { + node->process(cron, dt, field); + return; + } + } + _nodes[0]->process(cron, dt, field); +} + +/******************************************************************************/ + int QCronListNode:: next(int t) const @@ -113,7 +302,7 @@ next(int t) const foreach (const QCronNode * node, _nodes) { int node_next = node->next(t); - if (t > 0) + if (node_next > 0 && node_next != _field->getMax() + 1) { return node_next; } @@ -122,3 +311,20 @@ next(int t) const } /******************************************************************************/ + +bool +QCronListNode:: +match(int tu) const +{ + foreach (const QCronNode * node, _nodes) + { + if (node->match(tu)) + { + return true; + } + } + return false; +} + +/******************************************************************************/ +/******************************************************************************/ diff --git a/src/qcronnode.hpp b/src/qcronnode.hpp index bd28803..6a8ed4b 100644 --- a/src/qcronnode.hpp +++ b/src/qcronnode.hpp @@ -4,12 +4,27 @@ #include <QList> #include <QtGlobal> +#include "qcronfield.hpp" + +class QCron; +class QCronField; + class QCronNode { public: virtual ~QCronNode(); + void setField(QCronField * field); + virtual int next(int t) const = 0; + + virtual bool match(int tu) const = 0; + virtual void process(QCron * cron, + QDateTime & dt, + EField field) = 0; + +protected: + QCronField * _field; }; class QCronValueNode : public QCronNode @@ -24,6 +39,11 @@ public: int value() const; virtual int next(int t) const Q_DECL_OVERRIDE; + virtual bool match(int tu) const Q_DECL_OVERRIDE; + + virtual void process(QCron * cron, + QDateTime & dt, + EField field) Q_DECL_OVERRIDE; private: int _value; @@ -33,12 +53,20 @@ class QCronStrNode : public QCronValueNode { public: virtual int next(int t) const Q_DECL_OVERRIDE; + virtual bool match(int tu) const Q_DECL_OVERRIDE; + virtual void process(QCron * cron, + QDateTime & dt, + EField field) Q_DECL_OVERRIDE; }; class QCronAllNode : public QCronValueNode { public: virtual int next(int t) const Q_DECL_OVERRIDE; + virtual bool match(int tu) const Q_DECL_OVERRIDE; + virtual void process(QCron * cron, + QDateTime & dt, + EField field) Q_DECL_OVERRIDE; }; class QCronRangeNode : public QCronNode @@ -47,7 +75,15 @@ public: QCronRangeNode(const QCronIntNode * begin, const QCronIntNode * end); + int beginValue() const; + int endValue() const; + virtual int next(int t) const Q_DECL_OVERRIDE; + virtual bool match(int tu) const Q_DECL_OVERRIDE; + + virtual void process(QCron * cron, + QDateTime & dt, + EField field) Q_DECL_OVERRIDE; private: const QCronIntNode * _begin; @@ -60,6 +96,11 @@ public: QCronEveryNode(QCronNode *, QCronIntNode *); virtual int next(int t) const Q_DECL_OVERRIDE; + virtual bool match(int tu) const Q_DECL_OVERRIDE; + + virtual void process(QCron * cron, + QDateTime & dt, + EField field) Q_DECL_OVERRIDE; private: QCronNode * _what; @@ -69,9 +110,13 @@ private: class QCronListNode : public QCronNode { public: - QList<QCronNode *> nodes() const; + QList<QCronNode *> & nodes(); virtual int next(int t) const Q_DECL_OVERRIDE; + virtual bool match(int tu) const Q_DECL_OVERRIDE; + virtual void process(QCron * cron, + QDateTime & dt, + EField field) Q_DECL_OVERRIDE; private: QList<QCronNode*> _nodes; diff --git a/test/qcron_test.cpp b/test/qcron_test.cpp index 10023eb..76956d1 100644 --- a/test/qcron_test.cpp +++ b/test/qcron_test.cpp @@ -11,8 +11,8 @@ void QCronTest:: init() { - _dnow = QDate::currentDate(); - _tnow = QTime(0,0,0); + _dnow = QDate(1, 1, 1); + _tnow = QTime(0, 0, 0); } /******************************************************************************/ @@ -28,29 +28,338 @@ actual(QString & pattern) /******************************************************************************/ +void +QCronTest:: +minutes() +{ + // Star + QString pattern = "* * * * * *"; + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + + // Int + pattern = "30 * * * * *"; + QCOMPARE(actual(pattern), now().addSecs(60 * 30)); + _tnow.setHMS(0, 31, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 59)); + _tnow.setHMS(0, 30, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 60)); + + pattern = "0 * * * * *"; + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 60)); + _tnow.setHMS(0, 31, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 29)); + _tnow.setHMS(0, 59, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + + pattern = "59 * * * * *"; + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 59)); + _tnow.setHMS(0, 58, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 59, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 60)); + + // Range + pattern = "10-20 * * * * *"; + _tnow.setHMS(0, 5, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 5)); + _tnow.setHMS(0, 9, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 10, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 15, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 20, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 50)); + _tnow.setHMS(0, 30, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 40)); + + // Every - easy + pattern = "*/5 * * * * *"; + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 5)); + _tnow.setHMS(0, 4, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 5, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 5)); + _tnow.setHMS(0, 16, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 4)); + _tnow.setHMS(0, 54, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 57, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 3)); + + // Every - medium + pattern = "14-28/2 * * * * *"; + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 14)); + _tnow.setHMS(0, 13, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 14, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 2)); + _tnow.setHMS(0, 15, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 16, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 2)); + _tnow.setHMS(0, 28, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 46)); + + // List - easy + pattern = "10,15,16 * * * * *"; + _tnow.setHMS(0, 5, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 5)); + _tnow.setHMS(0, 9, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 10, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 5)); + _tnow.setHMS(0, 14, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 15, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 16, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 54)); + _tnow.setHMS(0, 17, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 53)); + + // List - medium + pattern = "10,15-20,30 * * * * *"; + _tnow.setHMS(0, 5, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 5)); + _tnow.setHMS(0, 9, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 10, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 5)); + _tnow.setHMS(0, 15, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 19, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(0, 20, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 10)); + _tnow.setHMS(0, 30, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 40)); +} + +/******************************************************************************/ + QDateTime QCronTest:: -expected(int offset) +now() { - QDateTime exp(_dnow, _tnow); - return exp.addSecs(offset * 60); + return QDateTime(_dnow, _tnow); +} + +/******************************************************************************/ + + +void +QCronTest:: +years() +{ + // Int + QString pattern = "* * * * * 30"; + _dnow.setDate(15, 1, 2); + QCOMPARE(actual(pattern), now().addYears(15).addDays(-1)); + _dnow.setDate(31, 1, 1); + QCOMPARE(actual(pattern), QDateTime()); + _dnow.setDate(30, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(30, 12, 31); + _tnow.setHMS(23, 59, 0); + QCOMPARE(actual(pattern), QDateTime()); + + pattern = "* * * 6 * 30"; + _dnow.setDate(15, 1, 1); + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), QDateTime(QDate(30, 6, 1), _tnow)); + _dnow.setDate(31, 1, 1); + QCOMPARE(actual(pattern), QDateTime()); + _dnow.setDate(30, 6, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + + // Range + pattern = "* * * * * 10-20"; + _dnow.setDate(5, 1, 1); + QCOMPARE(actual(pattern), now().addYears(5)); + _dnow.setDate(9, 1, 1); + QCOMPARE(actual(pattern), now().addYears(1)); + _dnow.setDate(10, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(15, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(20, 12, 31); + _tnow.setHMS(23, 59, 0); + QCOMPARE(actual(pattern), QDateTime()); + _dnow.setDate(20, 12, 31); + QCOMPARE(actual(pattern), QDateTime()); + + // Every - easy + pattern = "* * * * * */5"; + _dnow.setDate(1, 1, 1); + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addYears(4)); + _dnow.setDate(4, 1, 1); + QCOMPARE(actual(pattern), now().addYears(1)); + _dnow.setDate(5, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(16, 1, 1); + QCOMPARE(actual(pattern), now().addYears(4)); + _dnow.setDate(54, 1, 1); + QCOMPARE(actual(pattern), now().addYears(1)); + _dnow.setDate(57, 1, 1); + QCOMPARE(actual(pattern), now().addYears(3)); + + // Every - medium + pattern = "* * * * * 14-28/2"; + _dnow.setDate(1, 1, 1); + QCOMPARE(actual(pattern), now().addYears(13)); + _dnow.setDate(13, 1, 1); + QCOMPARE(actual(pattern), now().addYears(1)); + _dnow.setDate(14, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(15, 1, 1); + QCOMPARE(actual(pattern), now().addYears(1)); + _dnow.setDate(16, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(28, 12, 31); + QCOMPARE(actual(pattern), now().addSecs(60)); + _tnow.setHMS(23, 59, 0); + QCOMPARE(actual(pattern), QDateTime()); + + // List - easy + pattern = "* * * * * 10,15,16"; + _dnow.setDate(5, 1, 1); + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addYears(5)); + _dnow.setDate(9, 1, 1); + QCOMPARE(actual(pattern), now().addYears(1)); + _dnow.setDate(10, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(14, 1, 1); + QCOMPARE(actual(pattern), now().addYears(1)); + _dnow.setDate(15, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(16, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(16, 12, 31); + _tnow.setHMS(23, 59, 00); + QCOMPARE(actual(pattern), QDateTime()); + + // List - medium + pattern = "* * * * * 10,15-20,30"; + _dnow.setDate(5, 1, 1); + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addYears(5)); + _dnow.setDate(9, 1, 1); + QCOMPARE(actual(pattern), now().addYears(1)); + _dnow.setDate(10, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(15, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(20, 1, 1); + QCOMPARE(actual(pattern), now().addSecs(60)); + _dnow.setDate(20, 12, 31); + _tnow.setHMS(23, 59, 0); + QCOMPARE(actual(pattern), now().addSecs(60).addYears(9)); + + // Star + pattern = "* * * * * *"; + _dnow.setDate(1, 1, 1); + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addSecs(60)); } /******************************************************************************/ void QCronTest:: -minutes() +hours() { - QString pattern = "* * * * * *"; - QCOMPARE(actual(pattern), expected(1)); + // Int + QString pattern = "* 1 * * * *"; + _dnow.setDate(2016, 1, 1); + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 60)); + _tnow.setHMS(0, 59, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(1, 0, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(1, 58, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 1)); + _tnow.setHMS(1, 59, 0); + QCOMPARE(actual(pattern), now().addDays(1).addSecs(-60 * 59)); + + pattern = "30 1,3 * * * *"; + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 90)); + _tnow.setHMS(1, 30, 0); + QCOMPARE(actual(pattern), now().addSecs(60 * 120)); + _tnow.setHMS(3, 30, 0); + QCOMPARE(actual(pattern), now().addDays(1).addSecs(-120 * 60)); + + pattern = "* * * * * 2017"; + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addYears(1)); + + pattern = "* 2 * * * 2017"; + QCOMPARE(actual(pattern), now().addYears(1).addSecs(60 * 120)); +} + +/******************************************************************************/ + +void +QCronTest:: +dow() +{ + // 1 = Mon; 7 = Sun + QString pattern = "* * * * 1 *"; // Mon + + _dnow.setDate(2016, 1, 1); // Fri + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addDays(3)); + + pattern = "* * * * 5 *"; // Fri + QCOMPARE(actual(pattern), now().addSecs(60)); + _tnow.setHMS(23, 59, 59); + QCOMPARE(actual(pattern), now().addSecs(1).addDays(6)); + + pattern = "* * * * 3 *"; // Wed + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(pattern), now().addDays(5)); +} + +/******************************************************************************/ + +void +QCronTest:: +realLife() +{ + /* From monday to friday, from 8:00 to 11:59 and from 14:00 to 17:59. */ + QString working_hours = "* 8-11,14-17 * * 1-5 *"; + _dnow.setDate(2016, 1, 4); // Mon + _tnow.setHMS(0, 0, 0); + QCOMPARE(actual(working_hours), now().addSecs(8 * 3600)); + _tnow.setHMS(7, 59, 59); + QCOMPARE(actual(working_hours), now().addSecs(60)); + _tnow.setHMS(8, 0, 0); + QCOMPARE(actual(working_hours), now().addSecs(60)); + _tnow.setHMS(11, 58, 0); + QCOMPARE(actual(working_hours), now().addSecs(60)); + _tnow.setHMS(11, 59, 0); + QCOMPARE(actual(working_hours), now().addSecs(60 + 3600 * 2)); + _tnow.setHMS(13, 59, 0); + QCOMPARE(actual(working_hours), now().addSecs(60)); + _tnow.setHMS(14, 0, 0); + QCOMPARE(actual(working_hours), now().addSecs(60)); + _tnow.setHMS(17, 58, 0); + QCOMPARE(actual(working_hours), now().addSecs(60)); + _tnow.setHMS(17, 59, 0); + QCOMPARE(actual(working_hours), now().addSecs(60 + 14 * 3600)); + + _dnow.setDate(2016, 1, 8); // Fri + QCOMPARE(actual(working_hours), now().addSecs(60 + 14 * 3600).addDays(2)); - pattern = "30 * * * * *"; - QCOMPARE(actual(pattern), expected(30)); - _tnow.setHMS(0, 31, 0); - QCOMPARE(actual(pattern), expected(59)); - _tnow.setHMS(0, 30, 0); - QCOMPARE(actual(pattern), expected(60)); } /******************************************************************************/ diff --git a/test/qcron_test.hpp b/test/qcron_test.hpp index 2337dcd..8d2e85e 100644 --- a/test/qcron_test.hpp +++ b/test/qcron_test.hpp @@ -10,14 +10,18 @@ class QCronTest : public QObject private slots: void init(); + void years(); + void dow(); + void hours(); void minutes(); + void realLife(); private: QDate _dnow; QTime _tnow; QDateTime actual(QString & pattern); - QDateTime expected(int offset); + QDateTime now(); }; diff --git a/test/syntax_test.cpp b/test/syntax_test.cpp index 10570da..a0eb695 100644 --- a/test/syntax_test.cpp +++ b/test/syntax_test.cpp @@ -44,17 +44,22 @@ buildTests(QStringList & good, buildPattern(pattern_idx, "2-a") << buildPattern(pattern_idx, QString("%1-%2").arg(min).arg(max + 1)) << buildPattern(pattern_idx, QString("%1-%2").arg(min - 1).arg(max)) << - buildPattern(pattern_idx, QString("%1,%2,").arg(min).arg(min +1)) << + buildPattern(pattern_idx, QString("%1,%2,").arg(min).arg(min + 1)) << buildPattern(pattern_idx, "1,a") << buildPattern(pattern_idx, QString("%1,%2").arg(min).arg(max + 1)) << - buildPattern(pattern_idx, QString("%1,%2").arg(min).arg(min - 1)); + buildPattern(pattern_idx, QString("%1,%2").arg(min).arg(min - 1)) << + buildPattern(pattern_idx, "1/2/3 * * * * *") << + buildPattern(pattern_idx, "1-2-3 * * * * *") << // Bad range + buildPattern(pattern_idx, "** * * * * *") << // Bad star + buildPattern(pattern_idx, "1,,2 * * * * *") // Bad list + ; good << buildPattern(pattern_idx, QString::number(min)) << - buildPattern(pattern_idx, QString("000000000%1").arg(min)) << - buildPattern(pattern_idx, QString::number(max)) << - buildPattern(pattern_idx, QString("%1-%2").arg(min).arg(max)) << - buildPattern(pattern_idx, QString("%1-%1").arg(min)) << - buildPattern(pattern_idx, QString("%1,%2,%3").arg(min).arg(min + 1).arg(max -1)) << - buildPattern(pattern_idx, QString("%1,%1").arg(min)); + buildPattern(pattern_idx, QString("000000000%1").arg(min)) << + buildPattern(pattern_idx, QString::number(max)) << + buildPattern(pattern_idx, QString("%1-%2").arg(min).arg(max)) << + buildPattern(pattern_idx, QString("%1-%1").arg(min)) << + buildPattern(pattern_idx, QString("%1,%2,%3").arg(min).arg(min + 1).arg(max - 1)) << + buildPattern(pattern_idx, QString("%1,%1").arg(min)); } /******************************************************************************/ @@ -64,13 +69,13 @@ SyntaxTest:: global() { QStringList good = QStringList() << - "* * * * * *" << - "* * * * * *" << - "*\t* *\t\t * * *"; + "* * * * * *" << + "* * * * * *" << + "*\t* *\t\t * * *"; QStringList bad = QStringList() << - "* * * * *" << // Not enough fields - "* * * * * * *" << // To many fields - "* * *\n* * *"; // Bad separator + "* * * * *" << // Not enough fields + "* * * * * * *" << // To many fields + "* * *\n* * *"; // Bad separator doTest(good, bad); } @@ -142,7 +147,7 @@ years() { QStringList good; QStringList bad; - buildTests(good, bad, 5, 2016, 2099); + buildTests(good, bad, 5, 1, 2099); doTest(good, bad); } @@ -154,13 +159,13 @@ doTest(QStringList good, QStringList bad) { foreach (QString s, good) { -// qDebug() << s; + // qDebug() << s; QCron cron(s); QVERIFY(cron.isValid()); } foreach (QString s, bad) { -// qDebug() << s; + qDebug() << s; QCron cron(s); QVERIFY(!cron.isValid()); }