mirror of
				https://github.com/PerMalmberg/libcron.git
				synced 2025-10-30 19:48:38 -05:00 
			
		
		
		
	More tests.
Fix for february. Limit iterations in calculate_from.
This commit is contained in:
		| @@ -78,9 +78,9 @@ namespace libcron | ||||
|         bool res = true; | ||||
|  | ||||
|         // 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); | ||||
|         } | ||||
|  | ||||
| @@ -102,8 +102,7 @@ namespace libcron | ||||
|                 res = false; | ||||
|                 for(size_t i = 0; !res && i < months_with_31.size(); ++i) | ||||
|                 { | ||||
|                     auto month = months_with_31[i]; | ||||
|                     res = months.find(static_cast<Months>(month)) != months.end(); | ||||
|                     res = months.find(static_cast<Months>(months_with_31[i])) != months.end(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -295,7 +295,7 @@ namespace libcron | ||||
|     { | ||||
|         bool res = false; | ||||
|  | ||||
|         auto value_range = R"#((\d+)/(\d+))#"; | ||||
|         auto value_range = R"#((\d+|\*)/(\d+))#"; | ||||
|  | ||||
|         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)) | ||||
|         { | ||||
|             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()); | ||||
|  | ||||
|             if (is_within_limits<T>(raw_start, raw_start) && raw_step > 0) | ||||
|   | ||||
| @@ -6,17 +6,17 @@ using namespace date; | ||||
| namespace libcron | ||||
| { | ||||
|  | ||||
|     std::chrono::system_clock::time_point | ||||
|     CronSchedule::calculate_from(const std::chrono::system_clock::time_point& from) | ||||
|     std::tuple<bool, std::chrono::system_clock::time_point> | ||||
|     CronSchedule::calculate_from(const std::chrono::system_clock::time_point& from) const | ||||
|     { | ||||
|         //auto time_part = from - date::floor<days>(from); | ||||
|         auto curr = from;// - time_part; | ||||
|         //auto dt = to_calendar_time(curr); | ||||
|         auto curr = from; | ||||
|  | ||||
|         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); | ||||
|  | ||||
|             // 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}; | ||||
|                 sys_days s = next_month.year() / next_month.month() / 1; | ||||
|                 curr = s; | ||||
|                 continue; | ||||
|                 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. | ||||
| @@ -38,7 +38,7 @@ namespace libcron | ||||
|                     sys_days s = ymd; | ||||
|                     curr = s; | ||||
|                     curr += days{1}; | ||||
|                     continue; | ||||
|                     date_changed = true; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
| @@ -52,33 +52,35 @@ namespace libcron | ||||
|                     sys_days s = ymd; | ||||
|                     curr = s; | ||||
|                     curr += days{1}; | ||||
|                     continue; | ||||
|                     date_changed = true; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             //curr += time_part; | ||||
|             auto date_time = to_calendar_time(curr); | ||||
|             if (data.get_hours().find(static_cast<Hours>(date_time.hour)) == data.get_hours().end()) | ||||
|             if(!date_changed) | ||||
|             { | ||||
|                 curr += hours{1}; | ||||
|                 curr -= minutes{date_time.min}; | ||||
|                 curr -= seconds{date_time.sec}; | ||||
|             } | ||||
|             else if (data.get_minutes().find(static_cast<Minutes >(date_time.min)) == data.get_minutes().end()) | ||||
|             { | ||||
|                 curr += minutes{1}; | ||||
|                 curr -= seconds{date_time.sec}; | ||||
|             } | ||||
|             else if (data.get_seconds().find(static_cast<Seconds>(date_time.sec)) == data.get_seconds().end()) | ||||
|             { | ||||
|                 curr += seconds{1}; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 done = true; | ||||
|                 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}; | ||||
|                     curr -= minutes{date_time.min}; | ||||
|                     curr -= seconds{date_time.sec}; | ||||
|                 } | ||||
|                 else if (data.get_minutes().find(static_cast<Minutes >(date_time.min)) == data.get_minutes().end()) | ||||
|                 { | ||||
|                     curr += minutes{1}; | ||||
|                     curr -= seconds{date_time.sec}; | ||||
|                 } | ||||
|                 else if (data.get_seconds().find(static_cast<Seconds>(date_time.sec)) == data.get_seconds().end()) | ||||
|                 { | ||||
|                     curr += seconds{1}; | ||||
|                 } | ||||
|                 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 | ||||
|             static DateTime to_calendar_time(std::chrono::system_clock::time_point time) | ||||
|             { | ||||
|                 auto daypoint = date::floor<date::days>(time); | ||||
|                 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 | ||||
|                 DateTime dt{ | ||||
|                         int(ymd.year()), | ||||
|                         unsigned(ymd.month()), | ||||
|                         unsigned(ymd.day()), | ||||
|                         static_cast<uint8_t>(tod.hours().count()), | ||||
|                         static_cast<uint8_t>(tod.minutes().count()), | ||||
|                         static_cast<uint8_t>(tod.seconds().count())}; | ||||
|                         static_cast<uint8_t>(time_of_day.hours().count()), | ||||
|                         static_cast<uint8_t>(time_of_day.minutes().count()), | ||||
|                         static_cast<uint8_t>(time_of_day.seconds().count())}; | ||||
|  | ||||
|                 return dt; | ||||
|             } | ||||
|   | ||||
| @@ -15,6 +15,43 @@ system_clock::time_point DT(year_month_day ymd, hours h = hours{0}, minutes m = | ||||
|     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) | ||||
| { | ||||
|     auto c = CronData::create(schedule); | ||||
| @@ -22,8 +59,9 @@ bool test(const std::string& schedule, system_clock::time_point from, system_clo | ||||
|     if (res) | ||||
|     { | ||||
|         CronSchedule sched(c); | ||||
|         auto run_time = sched.calculate_from(from); | ||||
|         res &= expected_next == run_time; | ||||
|         auto result = sched.calculate_from(from); | ||||
|         auto run_time = std::get<1>(result); | ||||
|         res &= std::get<0>(result) && expected_next == run_time; | ||||
|  | ||||
|         if (!res) | ||||
|         { | ||||
| @@ -31,32 +69,115 @@ bool test(const std::string& schedule, system_clock::time_point from, system_clo | ||||
|                     << "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 * * * *", 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") | ||||
|     { | ||||
|         // 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))); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user