mirror of
https://github.com/PerMalmberg/libcron.git
synced 2025-04-22 08:23:04 -05:00
More tests.
Fix for february. Limit iterations in calculate_from.
This commit is contained in:
parent
cb6a7958e8
commit
20667ae3c6
@ -78,9 +78,9 @@ namespace libcron
|
|||||||
bool res = true;
|
bool res = true;
|
||||||
|
|
||||||
// Verify that the available dates are possible based on the given months
|
// Verify that the available dates are possible based on the given months
|
||||||
if (months.find(static_cast<Months>(2)) != months.end())
|
if (months.size() == 1 && months.find(static_cast<Months>(2)) != months.end())
|
||||||
{
|
{
|
||||||
// February allowed, make sure that the allowed date(s) includes 29 and below.
|
// Only february allowed, make sure that the allowed date(s) includes 29 and below.
|
||||||
res = has_any_in_range(day_of_month, 1, 29);
|
res = has_any_in_range(day_of_month, 1, 29);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,8 +102,7 @@ namespace libcron
|
|||||||
res = false;
|
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)
|
||||||
{
|
{
|
||||||
auto month = months_with_31[i];
|
res = months.find(static_cast<Months>(months_with_31[i])) != months.end();
|
||||||
res = months.find(static_cast<Months>(month)) != months.end();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,7 +295,7 @@ namespace libcron
|
|||||||
{
|
{
|
||||||
bool res = false;
|
bool res = false;
|
||||||
|
|
||||||
auto value_range = R"#((\d+)/(\d+))#";
|
auto value_range = R"#((\d+|\*)/(\d+))#";
|
||||||
|
|
||||||
std::regex range(value_range, std::regex_constants::ECMAScript);
|
std::regex range(value_range, std::regex_constants::ECMAScript);
|
||||||
|
|
||||||
@ -303,7 +303,16 @@ namespace libcron
|
|||||||
|
|
||||||
if (std::regex_match(s.begin(), s.end(), match, range))
|
if (std::regex_match(s.begin(), s.end(), match, range))
|
||||||
{
|
{
|
||||||
auto raw_start = std::stoi(match[1].str().c_str());
|
int raw_start;
|
||||||
|
if(match[1].str() == "*")
|
||||||
|
{
|
||||||
|
raw_start = value_of(T::First);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
raw_start = std::stoi(match[1].str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
auto raw_step = std::stoi(match[2].str().c_str());
|
auto raw_step = std::stoi(match[2].str().c_str());
|
||||||
|
|
||||||
if (is_within_limits<T>(raw_start, raw_start) && raw_step > 0)
|
if (is_within_limits<T>(raw_start, raw_start) && raw_step > 0)
|
||||||
|
@ -6,17 +6,17 @@ using namespace date;
|
|||||||
namespace libcron
|
namespace libcron
|
||||||
{
|
{
|
||||||
|
|
||||||
std::chrono::system_clock::time_point
|
std::tuple<bool, std::chrono::system_clock::time_point>
|
||||||
CronSchedule::calculate_from(const std::chrono::system_clock::time_point& from)
|
CronSchedule::calculate_from(const std::chrono::system_clock::time_point& from) const
|
||||||
{
|
{
|
||||||
//auto time_part = from - date::floor<days>(from);
|
auto curr = from;
|
||||||
auto curr = from;// - time_part;
|
|
||||||
//auto dt = to_calendar_time(curr);
|
|
||||||
|
|
||||||
bool done = false;
|
bool done = false;
|
||||||
|
auto max_iterations = std::numeric_limits<uint16_t>::max();
|
||||||
|
|
||||||
while (!done)
|
while (!done && --max_iterations > 0)
|
||||||
{
|
{
|
||||||
|
bool date_changed = false;
|
||||||
year_month_day ymd = date::floor<days>(curr);
|
year_month_day ymd = date::floor<days>(curr);
|
||||||
|
|
||||||
// Add months until one of the allowed days are found, or stay at the current one.
|
// Add months until one of the allowed days are found, or stay at the current one.
|
||||||
@ -25,7 +25,7 @@ namespace libcron
|
|||||||
auto next_month = ymd + months{1};
|
auto next_month = ymd + months{1};
|
||||||
sys_days s = next_month.year() / next_month.month() / 1;
|
sys_days s = next_month.year() / next_month.month() / 1;
|
||||||
curr = s;
|
curr = s;
|
||||||
continue;
|
date_changed = true;
|
||||||
}
|
}
|
||||||
// If all days are allowed, then the 'day of week' takes precedence, which also means that
|
// 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.
|
// day of week only is ignored when specific days of months are specified.
|
||||||
@ -38,7 +38,7 @@ namespace libcron
|
|||||||
sys_days s = ymd;
|
sys_days s = ymd;
|
||||||
curr = s;
|
curr = s;
|
||||||
curr += days{1};
|
curr += days{1};
|
||||||
continue;
|
date_changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -52,33 +52,35 @@ namespace libcron
|
|||||||
sys_days s = ymd;
|
sys_days s = ymd;
|
||||||
curr = s;
|
curr = s;
|
||||||
curr += days{1};
|
curr += days{1};
|
||||||
continue;
|
date_changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//curr += time_part;
|
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())
|
|
||||||
{
|
{
|
||||||
curr += hours{1};
|
auto date_time = to_calendar_time(curr);
|
||||||
curr -= minutes{date_time.min};
|
if (data.get_hours().find(static_cast<Hours>(date_time.hour)) == data.get_hours().end())
|
||||||
curr -= seconds{date_time.sec};
|
{
|
||||||
}
|
curr += hours{1};
|
||||||
else if (data.get_minutes().find(static_cast<Minutes >(date_time.min)) == data.get_minutes().end())
|
curr -= minutes{date_time.min};
|
||||||
{
|
curr -= seconds{date_time.sec};
|
||||||
curr += minutes{1};
|
}
|
||||||
curr -= seconds{date_time.sec};
|
else if (data.get_minutes().find(static_cast<Minutes >(date_time.min)) == data.get_minutes().end())
|
||||||
}
|
{
|
||||||
else if (data.get_seconds().find(static_cast<Seconds>(date_time.sec)) == data.get_seconds().end())
|
curr += minutes{1};
|
||||||
{
|
curr -= seconds{date_time.sec};
|
||||||
curr += seconds{1};
|
}
|
||||||
}
|
else if (data.get_seconds().find(static_cast<Seconds>(date_time.sec)) == data.get_seconds().end())
|
||||||
else
|
{
|
||||||
{
|
curr += seconds{1};
|
||||||
done = true;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return curr;
|
return std::make_tuple(max_iterations > 0, max_iterations > 0 ? curr : system_clock::now());
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,23 +15,24 @@ namespace libcron
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::system_clock::time_point calculate_from(const std::chrono::system_clock::time_point& from);
|
std::tuple<bool, std::chrono::system_clock::time_point>
|
||||||
|
calculate_from(const std::chrono::system_clock::time_point& from) const;
|
||||||
|
|
||||||
// https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes#obtaining-ymd-hms-components-from-a-time_point
|
// https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes#obtaining-ymd-hms-components-from-a-time_point
|
||||||
static DateTime to_calendar_time(std::chrono::system_clock::time_point time)
|
static DateTime to_calendar_time(std::chrono::system_clock::time_point time)
|
||||||
{
|
{
|
||||||
auto daypoint = date::floor<date::days>(time);
|
auto daypoint = date::floor<date::days>(time);
|
||||||
auto ymd = date::year_month_day(daypoint); // calendar date
|
auto ymd = date::year_month_day(daypoint); // calendar date
|
||||||
auto tod = date::make_time(time - daypoint); // Yields time_of_day type
|
auto time_of_day = date::make_time(time - daypoint); // Yields time_of_day type
|
||||||
|
|
||||||
// Obtain individual components as integers
|
// Obtain individual components as integers
|
||||||
DateTime dt{
|
DateTime dt{
|
||||||
int(ymd.year()),
|
int(ymd.year()),
|
||||||
unsigned(ymd.month()),
|
unsigned(ymd.month()),
|
||||||
unsigned(ymd.day()),
|
unsigned(ymd.day()),
|
||||||
static_cast<uint8_t>(tod.hours().count()),
|
static_cast<uint8_t>(time_of_day.hours().count()),
|
||||||
static_cast<uint8_t>(tod.minutes().count()),
|
static_cast<uint8_t>(time_of_day.minutes().count()),
|
||||||
static_cast<uint8_t>(tod.seconds().count())};
|
static_cast<uint8_t>(time_of_day.seconds().count())};
|
||||||
|
|
||||||
return dt;
|
return dt;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,43 @@ system_clock::time_point DT(year_month_day ymd, hours h = hours{0}, minutes m =
|
|||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool test(const std::string& schedule, system_clock::time_point from,
|
||||||
|
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)
|
bool test(const std::string& schedule, system_clock::time_point from, system_clock::time_point expected_next)
|
||||||
{
|
{
|
||||||
auto c = CronData::create(schedule);
|
auto c = CronData::create(schedule);
|
||||||
@ -22,8 +59,9 @@ bool test(const std::string& schedule, system_clock::time_point from, system_clo
|
|||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
CronSchedule sched(c);
|
CronSchedule sched(c);
|
||||||
auto run_time = sched.calculate_from(from);
|
auto result = sched.calculate_from(from);
|
||||||
res &= expected_next == run_time;
|
auto run_time = std::get<1>(result);
|
||||||
|
res &= std::get<0>(result) && expected_next == run_time;
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
@ -31,32 +69,115 @@ bool test(const std::string& schedule, system_clock::time_point from, system_clo
|
|||||||
<< "From: " << from << "\n"
|
<< "From: " << from << "\n"
|
||||||
<< "Expected: " << expected_next << "\n"
|
<< "Expected: " << expected_next << "\n"
|
||||||
<< "Calculated: " << run_time;
|
<< "Calculated: " << run_time;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SCENARIO("Calculating next runtime")
|
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), 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{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}), 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(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 * * * *", DT(2017_y / 12 / 31, hours{23}, 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})));
|
DT(2018_y / 1 / 1, hours{0})));
|
||||||
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 * * *", DT(2017_y / 12 / 31, hours{9}, 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})));
|
DT(2017_y / 12 / 31, 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 * * *", 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})));
|
DT(2018_y / 1 / 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("0 0 10 * FEB *", DT(2017_y / 12 / 31, hours{23}, minutes{59}, seconds{58}),
|
||||||
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})));
|
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)));
|
REQUIRE(test("0 0 * 31 APR,MAY *", DT(2017_y / 6 / 1), DT(2018_y / may / 31)));
|
||||||
}
|
}
|
||||||
|
|
||||||
SCENARIO("Leap year")
|
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(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")
|
||||||
|
{
|
||||||
|
// 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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
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})));
|
||||||
|
}
|
||||||
|
|
||||||
|
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}),
|
||||||
|
{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("Unable to calculate time point")
|
||||||
|
{
|
||||||
|
// TODO: Find a
|
||||||
|
//REQUIRE_FALSE(test("0 0 0 1 1 0", DT(2021_y / 12 / 15), DT(2022_y / 1 / 1)));
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user