libcron/test/CronScheduleTest.cpp

215 lines
8.8 KiB
C++

#include <catch.hpp>
#include <chrono>
#include <date/date.h>
#include <libcron/include/libcron/Cron.h>
#include <iostream>
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<system_clock::time_point>& 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")
{
REQUIRE_FALSE(test( "0 0 * 31 FEB *", DT(2021_y / 1 / 1), DT(2022_y / 1 / 1)));
}