#include #include #include #include #include using namespace libcron; using namespace date; using namespace std::chrono; system_clock::time_point DT(year_month_day ymd, hours h = hours{0}, minutes m = minutes{0}, seconds s = seconds{0}) { sys_days t = ymd; auto sum = t + h + m + s; return sum; } bool test(const std::string& schedule, system_clock::time_point from, const std::vector& expected_next) { auto c = CronData::create(schedule); bool res = c.is_valid(); if (res) { CronSchedule sched(c); auto curr_from = from; for (size_t i = 0; res && i < expected_next.size(); ++i) { auto result = sched.calculate_from(curr_from); auto calculated = std::get<1>(result); res = std::get<0>(result) && calculated == expected_next[i]; if (res) { // Add a second to the time so that we move on to the next expected time // and don't get locked on the current one. curr_from = expected_next[i] + seconds{1}; } else { std::cout << "From: " << curr_from << "\n" << "Expected: " << expected_next[i] << "\n" << "Calculated: " << calculated; } } } return res; } bool test(const std::string& schedule, system_clock::time_point from, system_clock::time_point expected_next) { auto c = CronData::create(schedule); bool res = c.is_valid(); if (res) { CronSchedule sched(c); auto result = sched.calculate_from(from); auto run_time = std::get<1>(result); res &= std::get<0>(result) && expected_next == run_time; if (!res) { std::cout << "From: " << from << "\n" << "Expected: " << expected_next << "\n" << "Calculated: " << run_time; } } return res; } 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}), DT(2018_y / 1 / 1, hours{0}))); 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}), DT(2018_y / 1 / 1, hours{10}))); 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}), 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}), 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}), 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}), 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))); } 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}), DT(2020_y / 2 / 29, hours{16}))); } 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}), {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}), DT(2018_y / 1 / 1, hours{14}, minutes{45}), DT(2018_y / 1 / 1, hours{16}, minutes{00}), DT(2018_y / 1 / 1, hours{16}, minutes{15})})); } 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), {DT(2018_y / 7 / 12, hours{0}), DT(2018_y / 7 / 12, hours{12}), DT(2018_y / 7 / 13, hours{0}), DT(2018_y / 7 / 13, hours{12}), DT(2019_y / 7 / 12, hours{0}), DT(2019_y / 7 / 12, hours{12})})); } WHEN("Every first of the month, 15h, every second month, 22m") { 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}), DT(2018_y / 4 / 1, hours{15}, minutes{22}), DT(2018_y / 5 / 1, hours{15}, minutes{22}), DT(2018_y / 6 / 1, hours{15}, minutes{22}), DT(2018_y / 7 / 1, hours{15}, minutes{22}), DT(2018_y / 8 / 1, hours{15}, minutes{22}), DT(2018_y / 9 / 1, hours{15}, minutes{22}), DT(2018_y / 10 / 1, hours{15}, minutes{22}), DT(2018_y / 11 / 1, hours{15}, minutes{22}), DT(2018_y / 12 / 1, hours{15}, minutes{22}), DT(2019_y / 1 / 1, hours{15}, minutes{22})})); } WHEN("“At minute 0 past hour 0 and 12 on day-of-month 1 in every 2nd month") { 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") { 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") { 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 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. //REQUIRE_FALSE(test("0 0 0 1 1 0", DT(2021_y / 12 / 15), DT(2022_y / 1 / 1))); }