mirror of
https://github.com/PerMalmberg/libcron.git
synced 2025-04-22 00:13:01 -05:00
Updated README.
Added tests. Made use of ? mandatory for DoM/DoW.
This commit is contained in:
parent
9d7134c527
commit
d7c17be4d1
60
README.md
60
README.md
@ -1,2 +1,62 @@
|
||||
# libcron
|
||||
A C++ scheduling library using cron formatting.
|
||||
|
||||
# Supported formatting
|
||||
|
||||
This implementation supports cron format, as specified below.
|
||||
|
||||
Each schedule expression conststs of 6 parts, all mandatory. However, if 'day of month' specifies specific days, then 'day of week' is ignored.
|
||||
|
||||
```text
|
||||
┌──────────────seconds (0 - 59)
|
||||
│ ┌───────────── minute (0 - 59)
|
||||
│ │ ┌───────────── hour (0 - 23)
|
||||
│ │ │ ┌───────────── day of month (1 - 31)
|
||||
│ │ │ │ ┌───────────── month (1 - 12)
|
||||
│ │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday)
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
* * * * * *
|
||||
```
|
||||
* Allowed formats:
|
||||
* Special characters: '*', meaning the entire range.
|
||||
* '?' used to ignore day of month/day of week as noted below.
|
||||
|
||||
* Ranges: 1,2,4-6
|
||||
* Result: 1,2,4,5,6
|
||||
* Steps: n/m, where n is the start and m is the step.
|
||||
* `1/2` yields 1,3,5,7...<max>
|
||||
* `5/3` yields 5,8,11,14...<max>
|
||||
* `*/2` yields Result: 1,3,5,7...<max>
|
||||
* Reversed ranges:
|
||||
* `0 0 23-2 * * *`, meaning top of each minute and hour, of hours, 23, 0, 1 and 2, every day.
|
||||
* Compare to `0 0 2-23 * * *` which means top of each minute and hour, of hours, 2,3...21,22,23 every day.
|
||||
|
||||
|
||||
|
||||
For `month`, these (case insensitive) strings can be used instead of numbers: `JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC`.
|
||||
Example: `JAN,MAR,SEP-NOV`
|
||||
|
||||
For `day of week`, these (case insensitive) strings can be used instead of numbers: `SUN, MON, TUE, WED, THU, FRI, SAT`.
|
||||
Example: `MON-THU,SAT`
|
||||
|
||||
Each part is separated by one or more whitespaces. It is thus important to keep whitespaces out of the respective parts.
|
||||
|
||||
* Valid:
|
||||
* 0,3,40-50 * * * * ?
|
||||
|
||||
* Invalid:
|
||||
* 0, 3, 40-50 * * * * ?
|
||||
|
||||
`Day of month` and `day of week` are mutually exclusive so one of them must at always be ignored using
|
||||
the '?'-character unless one field already is something other than '*'.
|
||||
|
||||
# Examples
|
||||
|
||||
|Expression | Meaning
|
||||
| --- | --- |
|
||||
| * * * * * ? | Every second
|
||||
|0 0 12 * * MON-FRI | Every Weekday at noon
|
||||
|0 0 12 1/2 * ? | Every 2 days, starting on the 1st at noon
|
||||
| 0 0 */12 ? * * | Every twelve hours
|
@ -36,6 +36,7 @@ namespace libcron
|
||||
valid &= validate_numeric<DayOfMonth>(match[4], day_of_month);
|
||||
valid &= validate_literal<Months>(match[5], months, month_names);
|
||||
valid &= validate_literal<DayOfWeek>(match[6], day_of_week, day_names);
|
||||
valid &= check_dom_vs_dow(match[4], match[6]);
|
||||
valid &= validate_date_vs_months();
|
||||
}
|
||||
}
|
||||
@ -84,10 +85,10 @@ namespace libcron
|
||||
res = has_any_in_range(day_of_month, 1, 29);
|
||||
}
|
||||
|
||||
if(res)
|
||||
if (res)
|
||||
{
|
||||
// Make sure that if the days contains only 31, at least one month allows that date.
|
||||
if(day_of_month.size() == 1 && day_of_month.find(DayOfMonth::Last) != day_of_month.end())
|
||||
if (day_of_month.size() == 1 && day_of_month.find(DayOfMonth::Last) != day_of_month.end())
|
||||
{
|
||||
std::vector<int32_t> months_with_31;
|
||||
for (int32_t i = 1; i <= 12; ++i)
|
||||
@ -100,7 +101,7 @@ namespace libcron
|
||||
}
|
||||
|
||||
res = false;
|
||||
for(size_t i = 0; !res && i < months_with_31.size(); ++i)
|
||||
for (size_t i = 0; !res && i < months_with_31.size(); ++i)
|
||||
{
|
||||
res = months.find(static_cast<Months>(months_with_31[i])) != months.end();
|
||||
}
|
||||
@ -110,4 +111,23 @@ namespace libcron
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool CronData::check_dom_vs_dow(const std::string& dom, const std::string& dow) const
|
||||
{
|
||||
// Day of month and day of week are mutually exclusive so one of them must at always be ignored using
|
||||
// the '?'-character unless one field already is something other than '*'.
|
||||
//
|
||||
// Since we treat an ignored field as allowing the full range, we're OK with both being flagged
|
||||
// as ignored. To make it explicit to the user of the library, we do however require the use of
|
||||
// '?' as the ignore flag, although it is functionally equivalent to '*'.
|
||||
|
||||
auto check = [](const std::string& l, std::string r)
|
||||
{
|
||||
return l == "*" && (r != "*" || r == "?");
|
||||
};
|
||||
|
||||
return (dom == "?" || dow == "?")
|
||||
|| check(dom, dow)
|
||||
|| check(dow, dom);
|
||||
}
|
||||
}
|
@ -8,51 +8,6 @@
|
||||
|
||||
namespace libcron
|
||||
{
|
||||
/*
|
||||
This class parses strings in the format specified below and holds the resulting allowed values
|
||||
in its internal sets.
|
||||
|
||||
Cron format, 6 parts:
|
||||
|
||||
┌──────────────seconds (0 - 59)
|
||||
│ ┌───────────── minute (0 - 59)
|
||||
│ │ ┌───────────── hour (0 - 23)
|
||||
│ │ │ ┌───────────── day of month (1 - 31)
|
||||
│ │ │ │ ┌───────────── month (1 - 12)
|
||||
│ │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday;
|
||||
│ │ │ │ │ │ 7 is also Sunday on some systems)
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
* * * * * *
|
||||
|
||||
Allowed formats:
|
||||
Special characters: '*', meaning the entire range.
|
||||
|
||||
Ranges: 1,2,4-6
|
||||
Result: 1,2,4,5,6
|
||||
Steps: 1/2
|
||||
Result: 1,3,5,7...<max>
|
||||
|
||||
For day of month, these strings are valid, case insensitive:
|
||||
SUN, MON, TUE, WED, THU, FRI, SAT
|
||||
Example: MON-THU,SAT
|
||||
|
||||
For month, these strings are valid, case insensitive:
|
||||
JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC
|
||||
Example: JAN,MAR,SEP-NOV
|
||||
|
||||
Each part is separated by one or more whitespaces. It is thus important to keep
|
||||
whitespaces out of the respective parts.
|
||||
|
||||
Valid:
|
||||
* * * * * *
|
||||
0,3,40-50 * * * * *
|
||||
|
||||
Invalid:
|
||||
0, 3, 40-50 * * * * *
|
||||
|
||||
*/
|
||||
|
||||
class CronData
|
||||
{
|
||||
public:
|
||||
@ -149,6 +104,8 @@ namespace libcron
|
||||
|
||||
bool validate_date_vs_months() const;
|
||||
|
||||
bool check_dom_vs_dow(const std::string& dom, const std::string& dow) const;
|
||||
|
||||
std::set<Seconds> seconds{};
|
||||
std::set<Minutes> minutes{};
|
||||
std::set<Hours> hours{};
|
||||
@ -213,8 +170,9 @@ namespace libcron
|
||||
|
||||
for (const auto& p : parts)
|
||||
{
|
||||
if (p == "*")
|
||||
if (p == "*" || p == "?")
|
||||
{
|
||||
// We treat the ignore-character '?' the same as the full range being allowed.
|
||||
add_full_range<T>(numbers);
|
||||
}
|
||||
else if (is_number(p))
|
||||
|
@ -27,8 +27,7 @@ namespace libcron
|
||||
curr = s;
|
||||
date_changed = true;
|
||||
}
|
||||
// If all days are allowed, then the 'day of week' takes precedence, which also means that
|
||||
// day of week only is ignored when specific days of months are specified.
|
||||
// If all days are allowed (or the field is ignored via '?'), then the 'day of week' takes precedence.
|
||||
else if (data.get_day_of_month().size() != CronData::value_of(DayOfMonth::Last))
|
||||
{
|
||||
// Add days until one of the allowed days are found, or stay at the current one.
|
||||
@ -56,7 +55,7 @@ namespace libcron
|
||||
}
|
||||
}
|
||||
|
||||
if(!date_changed)
|
||||
if (!date_changed)
|
||||
{
|
||||
auto date_time = to_calendar_time(curr);
|
||||
if (data.get_hours().find(static_cast<Hours>(date_time.hour)) == data.get_hours().end())
|
||||
|
@ -31,7 +31,7 @@ SCENARIO("Numerical inputs")
|
||||
{
|
||||
THEN("All parts are filled")
|
||||
{
|
||||
auto c = CronData::create("* * * * * *");
|
||||
auto c = CronData::create("* * * * * ?");
|
||||
REQUIRE(c.is_valid());
|
||||
REQUIRE(c.get_seconds().size() == 60);
|
||||
REQUIRE(has_value_range(c.get_seconds(), 0, 59));
|
||||
@ -49,7 +49,7 @@ SCENARIO("Numerical inputs")
|
||||
{
|
||||
THEN("Ranges are correct")
|
||||
{
|
||||
auto c = CronData::create("* 0-59 * * * *");
|
||||
auto c = CronData::create("* 0-59 * * * ?");
|
||||
REQUIRE(c.is_valid());
|
||||
REQUIRE(c.get_seconds().size() == 60);
|
||||
REQUIRE(c.get_minutes().size() == 60);
|
||||
@ -63,7 +63,7 @@ SCENARIO("Numerical inputs")
|
||||
{
|
||||
THEN("Ranges are correct")
|
||||
{
|
||||
auto c = CronData::create("* * * 20-30 * *");
|
||||
auto c = CronData::create("* * * 20-30 * ?");
|
||||
REQUIRE(c.is_valid());
|
||||
REQUIRE(c.get_seconds().size() == 60);
|
||||
REQUIRE(c.get_minutes().size() == 60);
|
||||
@ -77,7 +77,7 @@ SCENARIO("Numerical inputs")
|
||||
{
|
||||
THEN("Number of hours are correct")
|
||||
{
|
||||
auto c = CronData::create("* * 20-5 * * *");
|
||||
auto c = CronData::create("* * 20-5 * * ?");
|
||||
REQUIRE(c.is_valid());
|
||||
REQUIRE(c.get_hours().size() == 10);
|
||||
REQUIRE(c.get_hours().find(Hours::First) != c.get_hours().end());
|
||||
@ -87,12 +87,12 @@ SCENARIO("Numerical inputs")
|
||||
{
|
||||
THEN("Validation succeeds")
|
||||
{
|
||||
REQUIRE(CronData::create("0-59 * * * * *").is_valid());
|
||||
REQUIRE(CronData::create("* 0-59 * * * *").is_valid());
|
||||
REQUIRE(CronData::create("* * 0-23 * * *").is_valid());
|
||||
REQUIRE(CronData::create("* * * 1-31 * *").is_valid());
|
||||
REQUIRE(CronData::create("* * * * 1-12 *").is_valid());
|
||||
REQUIRE(CronData::create("* * * * * 0-6").is_valid());
|
||||
REQUIRE(CronData::create("0-59 * * * * ?").is_valid());
|
||||
REQUIRE(CronData::create("* 0-59 * * * ?").is_valid());
|
||||
REQUIRE(CronData::create("* * 0-23 * * ?").is_valid());
|
||||
REQUIRE(CronData::create("* * * 1-31 * ?").is_valid());
|
||||
REQUIRE(CronData::create("* * * * 1-12 ?").is_valid());
|
||||
REQUIRE(CronData::create("* * * ? * 0-6").is_valid());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,19 +105,19 @@ SCENARIO("Numerical inputs")
|
||||
REQUIRE_FALSE(CronData::create("").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("-").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* ").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* 0-60 * * * *").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * 0-25 * * *").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * 1-32 * *").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * * 1-13 *").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* 0-60 * * * ?").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * 0-25 * * ?").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * 1-32 * ?").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * * 1-13 ?").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * * * 0-7").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * 0-31 * *").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * * 0-12 *").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("60 * * * * *").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* 60 * * * *").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * 25 * * *").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * 32 * *").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * * 13 *").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * * * 7").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * 0-31 * ?").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * * 0-12 ?").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("60 * * * * ?").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* 60 * * * ?").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * 25 * * ?").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * 32 * ?").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * * 13 ?").is_valid());
|
||||
REQUIRE_FALSE(CronData::create("* * * ? * 7").is_valid());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -131,13 +131,13 @@ SCENARIO("Literal input")
|
||||
{
|
||||
THEN("Range is valid")
|
||||
{
|
||||
auto c = CronData::create("* * * * JAN-MAR *");
|
||||
auto c = CronData::create("* * * * JAN-MAR ?");
|
||||
REQUIRE(c.is_valid());
|
||||
REQUIRE(has_value_range(c.get_months(), 1, 3));
|
||||
}
|
||||
AND_THEN("Range is valid")
|
||||
{
|
||||
auto c = CronData::create("* * * * * SUN-FRI");
|
||||
auto c = CronData::create("* * * ? * SUN-FRI");
|
||||
REQUIRE(c.is_valid());
|
||||
REQUIRE(has_value_range(c.get_day_of_week(), 0, 5));
|
||||
}
|
||||
@ -146,7 +146,7 @@ SCENARIO("Literal input")
|
||||
{
|
||||
THEN("Range is valid")
|
||||
{
|
||||
auto c = CronData::create("* * * * JAN-MAR,DEC *");
|
||||
auto c = CronData::create("* * * * JAN-MAR,DEC ?");
|
||||
REQUIRE(c.is_valid());
|
||||
REQUIRE(has_value_range(c.get_months(), 1, 3));
|
||||
REQUIRE_FALSE(CronData::has_any_in_range(c.get_months(), 4, 11));
|
||||
@ -154,7 +154,7 @@ SCENARIO("Literal input")
|
||||
}
|
||||
AND_THEN("Range is valid")
|
||||
{
|
||||
auto c = CronData::create("* * * * JAN-MAR,DEC FRI,MON,THU");
|
||||
auto c = CronData::create("* * * ? JAN-MAR,DEC FRI,MON,THU");
|
||||
REQUIRE(c.is_valid());
|
||||
REQUIRE(has_value_range(c.get_months(), 1, 3));
|
||||
REQUIRE_FALSE(CronData::has_any_in_range(c.get_months(), 4, 11));
|
||||
@ -171,7 +171,7 @@ SCENARIO("Literal input")
|
||||
{
|
||||
THEN("Range is valid")
|
||||
{
|
||||
auto c = CronData::create("* * * * APR-JAN *");
|
||||
auto c = CronData::create("* * * ? APR-JAN *");
|
||||
REQUIRE(c.is_valid());
|
||||
REQUIRE(has_value_range(c.get_months(), 4, 12));
|
||||
REQUIRE(has_value_range(c.get_months(), 1, 1));
|
||||
@ -179,7 +179,7 @@ SCENARIO("Literal input")
|
||||
}
|
||||
AND_THEN("Range is valid")
|
||||
{
|
||||
auto c = CronData::create("* * * * * sat-tue,wed");
|
||||
auto c = CronData::create("* * * ? * sat-tue,wed");
|
||||
REQUIRE(c.is_valid());
|
||||
REQUIRE(has_value_range(c.get_day_of_week(), 6, 6)); // Has saturday
|
||||
REQUIRE(has_value_range(c.get_day_of_week(), 0, 3)); // Has sun, mon, tue, wed
|
||||
@ -197,7 +197,7 @@ SCENARIO("Using step syntax")
|
||||
{
|
||||
THEN("Range is valid")
|
||||
{
|
||||
auto c = CronData::create("* * * * JAN/2 *");
|
||||
auto c = CronData::create("* * * * JAN/2 ?");
|
||||
REQUIRE(c.is_valid());
|
||||
REQUIRE(has_value_range(c.get_months(), 1, 1));
|
||||
REQUIRE(has_value_range(c.get_months(), 3, 3));
|
||||
@ -225,5 +225,5 @@ SCENARIO("Dates that does not exist")
|
||||
|
||||
SCENARIO("Date that exist in one of the months")
|
||||
{
|
||||
REQUIRE(CronData::create("0 0 * 31 APR,MAY *").is_valid());
|
||||
}
|
||||
REQUIRE(CronData::create("0 0 * 31 APR,MAY ?").is_valid());
|
||||
}
|
@ -77,34 +77,34 @@ bool test(const std::string& schedule, system_clock::time_point from, system_clo
|
||||
|
||||
SCENARIO("Calculating next runtime")
|
||||
{
|
||||
REQUIRE(test("0 0 * * * *", DT(2010_y / 1 / 1), DT(2010_y / 1 / 1, hours{0})));
|
||||
REQUIRE(test("0 0 * * * *", DT(2010_y / 1 / 1, hours{0}, minutes{0}, seconds{1}), DT(2010_y / 1 / 1, hours{1})));
|
||||
REQUIRE(test("0 0 * * * *", DT(2010_y / 1 / 1, hours{5}), DT(2010_y / 1 / 1, hours{5})));
|
||||
REQUIRE(test("0 0 * * * *", DT(2010_y / 1 / 1, hours{5}, minutes{1}), DT(2010_y / 1 / 1, hours{6})));
|
||||
REQUIRE(test("0 0 * * * *", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
REQUIRE(test("0 0 * * * ?", DT(2010_y / 1 / 1), DT(2010_y / 1 / 1, hours{0})));
|
||||
REQUIRE(test("0 0 * * * ?", DT(2010_y / 1 / 1, hours{0}, minutes{0}, seconds{1}), DT(2010_y / 1 / 1, hours{1})));
|
||||
REQUIRE(test("0 0 * * * ?", DT(2010_y / 1 / 1, hours{5}), DT(2010_y / 1 / 1, hours{5})));
|
||||
REQUIRE(test("0 0 * * * ?", DT(2010_y / 1 / 1, hours{5}, minutes{1}), DT(2010_y / 1 / 1, hours{6})));
|
||||
REQUIRE(test("0 0 * * * ?", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
DT(2018_y / 1 / 1, hours{0})));
|
||||
REQUIRE(test("0 0 10 * * *", DT(2017_y / 12 / 31, hours{9}, minutes{59}, seconds{58}),
|
||||
REQUIRE(test("0 0 10 * * ?", DT(2017_y / 12 / 31, hours{9}, minutes{59}, seconds{58}),
|
||||
DT(2017_y / 12 / 31, hours{10})));
|
||||
REQUIRE(test("0 0 10 * * *", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
REQUIRE(test("0 0 10 * * ?", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
DT(2018_y / 1 / 1, hours{10})));
|
||||
REQUIRE(test("0 0 10 * FEB *", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
REQUIRE(test("0 0 10 ? FEB *", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
DT(2018_y / 2 / 1, hours{10})));
|
||||
REQUIRE(test("0 0 10 25 FEB *", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
REQUIRE(test("0 0 10 25 FEB ?", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
DT(2018_y / 2 / 25, hours{10})));
|
||||
REQUIRE(test("0 0 10 * FEB 1", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
REQUIRE(test("0 0 10 ? FEB 1", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
DT(year_month_day{2018_y / 2 / mon[1]}, hours{10})));
|
||||
REQUIRE(test("0 0 10 * FEB 6", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
REQUIRE(test("0 0 10 ? FEB 6", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||
DT(year_month_day{2018_y / 2 / sat[1]}, hours{10})));
|
||||
REQUIRE(test("* * * 10-12 NOV *", DT(2018_y / 11 / 11, hours{10}, minutes{11}, seconds{12}),
|
||||
REQUIRE(test("* * ? 10-12 NOV ?", DT(2018_y / 11 / 11, hours{10}, minutes{11}, seconds{12}),
|
||||
DT(year_month_day{2018_y / 11 / 11}, hours{10}, minutes{11}, seconds{12})));
|
||||
REQUIRE(test("0 0 * 31 APR,MAY *", DT(2017_y / 6 / 1), DT(2018_y / may / 31)));
|
||||
REQUIRE(test("0 0 * 31 APR,MAY ?", DT(2017_y / 6 / 1), DT(2018_y / may / 31)));
|
||||
}
|
||||
|
||||
SCENARIO("Leap year")
|
||||
{
|
||||
REQUIRE(test("0 0 * 29 FEB *", DT(2015_y / 1 / 1), DT(2016_y / 2 / 29)));
|
||||
REQUIRE(test("0 0 * 29 FEB *", DT(2018_y / 1 / 1), DT(2020_y / 2 / 29)));
|
||||
REQUIRE(test("0 0 * 29 FEB *", DT(2020_y / 2 / 29, hours{15}, minutes{13}, seconds{13}),
|
||||
REQUIRE(test("0 0 * 29 FEB ?", DT(2018_y / 1 / 1), DT(2020_y / 2 / 29)));
|
||||
REQUIRE(test("0 0 * 29 FEB ?", DT(2020_y / 2 / 29, hours{15}, minutes{13}, seconds{13}),
|
||||
DT(2020_y / 2 / 29, hours{16})));
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ SCENARIO("Multiple calculations")
|
||||
{
|
||||
WHEN("Every 15 minutes, every 2nd hour")
|
||||
{
|
||||
REQUIRE(test("0 0/15 0/2 * * *", DT(2018_y / 1 / 1, hours{13}, minutes{14}, seconds{59}),
|
||||
REQUIRE(test("0 0/15 0/2 * * ?", DT(2018_y / 1 / 1, hours{13}, minutes{14}, seconds{59}),
|
||||
{DT(2018_y / 1 / 1, hours{14}, minutes{00}),
|
||||
DT(2018_y / 1 / 1, hours{14}, minutes{15}),
|
||||
DT(2018_y / 1 / 1, hours{14}, minutes{30}),
|
||||
@ -123,7 +123,7 @@ SCENARIO("Multiple calculations")
|
||||
|
||||
WHEN("Every top of the hour, every 12th hour, during 12 and 13:th July")
|
||||
{
|
||||
REQUIRE(test("0 0 0/12 12-13 JUL *", DT(2018_y / 1 / 1),
|
||||
REQUIRE(test("0 0 0/12 12-13 JUL ?", DT(2018_y / 1 / 1),
|
||||
{DT(2018_y / 7 / 12, hours{0}),
|
||||
DT(2018_y / 7 / 12, hours{12}),
|
||||
DT(2018_y / 7 / 13, hours{0}),
|
||||
@ -134,7 +134,7 @@ SCENARIO("Multiple calculations")
|
||||
|
||||
WHEN("Every first of the month, 15h, every second month, 22m")
|
||||
{
|
||||
REQUIRE(test("0 22 15 1 * *", DT(2018_y / 1 / 1),
|
||||
REQUIRE(test("0 22 15 1 * ?", DT(2018_y / 1 / 1),
|
||||
{DT(2018_y / 1 / 1, hours{15}, minutes{22}),
|
||||
DT(2018_y / 2 / 1, hours{15}, minutes{22}),
|
||||
DT(2018_y / 3 / 1, hours{15}, minutes{22}),
|
||||
@ -152,30 +152,64 @@ SCENARIO("Multiple calculations")
|
||||
|
||||
WHEN("“At minute 0 past hour 0 and 12 on day-of-month 1 in every 2nd month")
|
||||
{
|
||||
// Note that day-of-week, 5, is not in effect in this schedule.
|
||||
REQUIRE(test("0 0 0,12 1 */2 5", DT(2018_y / 3 / 10, hours{16}, minutes{51}), DT(2018_y / 5 / 1)));
|
||||
REQUIRE(test("0 0 0,12 1 */2 ?", DT(2018_y / 3 / 10, hours{16}, minutes{51}), DT(2018_y / 5 / 1)));
|
||||
}
|
||||
|
||||
WHEN("“At 00:05 in August")
|
||||
{
|
||||
// Note that day-of-week, 5, is not in effect in this schedule.
|
||||
REQUIRE(test("0 5 0 * 8 *", DT(2018_y / 3 / 10, hours{16}, minutes{51}),
|
||||
DT(2018_y / 8 / 1, hours{0}, minutes{5})));
|
||||
REQUIRE(test("0 5 0 * 8 ?", DT(2018_y / 3 / 10, hours{16}, minutes{51}),
|
||||
{DT(2018_y / 8 / 1, hours{0}, minutes{5}),
|
||||
DT(2018_y / 8 / 2, hours{0}, minutes{5})}));
|
||||
}
|
||||
|
||||
WHEN("At 22:00 on every day-of-week from Monday through Friday")
|
||||
{
|
||||
// Note that day-of-week, 5, is not in effect in this schedule.
|
||||
REQUIRE(test("0 0 22 * * 1-5", DT(2021_y / 12 / 15, hours{16}, minutes{51}),
|
||||
REQUIRE(test("0 0 22 ? * 1-5", DT(2021_y / 12 / 15, hours{16}, minutes{51}),
|
||||
{DT(2021_y / 12 / 15, hours{22}),
|
||||
DT(2021_y / 12 / 16, hours{22}),
|
||||
DT(2021_y / 12 / 17, hours{22}),
|
||||
// 18-19 are weekend
|
||||
// 18-19 are weekend
|
||||
DT(2021_y / 12 / 20, hours{22}),
|
||||
DT(2021_y / 12 / 21, hours{22})}));
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("Examples from README.md")
|
||||
{
|
||||
REQUIRE(test("* * * * * ?", DT(2018_y / 03 / 1, hours{12}, minutes{13}, seconds{45}),
|
||||
{
|
||||
DT(2018_y / 03 / 1, hours{12}, minutes{13}, seconds{45}),
|
||||
DT(2018_y / 03 / 1, hours{12}, minutes{13}, seconds{46}),
|
||||
DT(2018_y / 03 / 1, hours{12}, minutes{13}, seconds{47}),
|
||||
DT(2018_y / 03 / 1, hours{12}, minutes{13}, seconds{48})
|
||||
}));
|
||||
|
||||
REQUIRE(test("0 0 12 * * MON-FRI", DT(2018_y / 03 / 10, hours{12}, minutes{13}, seconds{45}),
|
||||
{
|
||||
DT(2018_y / 03 / 12, hours{12}),
|
||||
DT(2018_y / 03 / 13, hours{12}),
|
||||
DT(2018_y / 03 / 14, hours{12}),
|
||||
DT(2018_y / 03 / 15, hours{12}),
|
||||
DT(2018_y / 03 / 16, hours{12}),
|
||||
DT(2018_y / 03 / 19, hours{12})
|
||||
}));
|
||||
|
||||
REQUIRE(test("0 0 12 1/2 * ?", DT(2018_y / 01 / 2, hours{12}, minutes{13}, seconds{45}),
|
||||
{
|
||||
DT(2018_y / 1 / 3, hours{12}),
|
||||
DT(2018_y / 1 / 5, hours{12}),
|
||||
DT(2018_y / 1 / 7, hours{12})
|
||||
}));
|
||||
|
||||
REQUIRE(test("0 0 */12 ? * *", DT(2018_y / 8 / 15, hours{13}, minutes{13}, seconds{45}),
|
||||
{
|
||||
DT(2018_y / 8 / 16, hours{0}),
|
||||
DT(2018_y / 8 / 16, hours{12}),
|
||||
DT(2018_y / 8 / 17, hours{0})
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
SCENARIO("Unable to calculate time point")
|
||||
{
|
||||
// TODO: Find a schedule that is unsolvable.
|
||||
|
@ -14,7 +14,7 @@ std::string create_schedule_expiring_in(hours h, minutes m, seconds s)
|
||||
std::string res{};
|
||||
res += std::to_string(dt.sec) + " ";
|
||||
res += std::to_string(dt.min) + " ";
|
||||
res += std::to_string(dt.hour) + " * * *";
|
||||
res += std::to_string(dt.hour) + " * * ?";
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -34,7 +34,7 @@ SCENARIO("Adding a task")
|
||||
|
||||
WHEN("Adding a task that runs every second")
|
||||
{
|
||||
REQUIRE(c.add_schedule("A task", "* * * * * *",
|
||||
REQUIRE(c.add_schedule("A task", "* * * * * ?",
|
||||
[&expired]()
|
||||
{
|
||||
expired = true;
|
||||
|
Loading…
x
Reference in New Issue
Block a user