Updated README.

Added tests.
Made use of ? mandatory for DoM/DoW.
This commit is contained in:
Per Malmberg 2018-03-11 18:45:27 +01:00
parent 9d7134c527
commit d7c17be4d1
7 changed files with 182 additions and 111 deletions

View File

@ -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

View File

@ -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);
}
}

View File

@ -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))

View File

@ -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())

View File

@ -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());
}

View File

@ -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.

View File

@ -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;