diff --git a/CMakeLists.txt b/CMakeLists.txt index b747b5f..5d9e4f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.28) project(ccronexpr C) set(CMAKE_C_STANDARD 11) +add_compile_definitions(CRON_TEST_MALLOC=1) include_directories(.) diff --git a/ccronexpr.c b/ccronexpr.c index 1369332..149bff0 100644 --- a/ccronexpr.c +++ b/ccronexpr.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "ccronexpr.h" @@ -883,7 +884,7 @@ static char* str_replace(char *orig, const char *rep, const char *with) { static unsigned int parse_uint(const char* str, int* errcode) { char* endptr; errno = 0; - long int l = strtol(str, &endptr, 0); + long int l = strtol(str, &endptr, 10); if (errno == ERANGE || *endptr != '\0' || l < 0 || l > INT_MAX) { *errcode = 1; return 0; @@ -1017,7 +1018,9 @@ void cron_init_custom_hash_fn(cron_custom_hash_fn func) } /** - * Replace H parameter with integer in proper range. If using an iterator fielo, min/max have to be set to proper values before! + * Replace H parameter with integer in proper range. If using an iterator field, min/max have to be set to proper values before! + * The input field will always be freed, the returned char* should be used instead. + * * @param field CRON field which needs a value for its 'H' (in string form) * @param n Position of the field in the CRON string, from 0 - 5 * @param min Minimum value allowed in field/for replacement @@ -1254,77 +1257,147 @@ void set_number_hits(const char* value, uint8_t* target, unsigned int min, unsig } -static char* check_and_replace_h(char* field, unsigned int pos, unsigned int min, const char** error) -{ +static char *replace_h_entry(char *field, unsigned int pos, unsigned int min, const char **error) { + char* has_h = strchr(field, 'H'); + if (has_h == NULL) { + return field; + } + unsigned int fieldMax = 0, customMax = 0; // minBuf is 0xFF to see if it has been altered/read successfully, since 0 is a valid value for it unsigned int minBuf = 0xFF, maxBuf = 0; - char* has_h = strchr(field, 'H'); - if (has_h) { - if ( *(has_h+1) == '/') { /* H before an iterator */ - sscanf(has_h, "H/%2u", &customMax); // get value of iterator, so it will be used as maximum instead of standard maximum for field - if (!customMax) { /* iterator might have been specified as an ordinal instead... */ - *error = "Hashed: Iterator error"; - return field; - } - } - if ( (has_h != field) && (*(has_h-1) == '/') ) { /* H not allowed as iterator */ - *error = "Hashed: 'H' not allowed as iterator"; - return field; - } - if ( *(has_h+1) =='-' || \ - ( has_h != field && *(has_h-1) == '-') ) { // 'H' not starting field, so may be the end of a range - *error = "'H' is not allowed for use in ranges"; + + if(*(has_h + 1) == '/') { /* H before an iterator */ + sscanf(has_h, "H/%2u", &customMax); // get value of iterator, so it will be used as maximum instead of standard maximum for field + if (!customMax) { /* iterator might have been specified as an ordinal instead... */ + *error = "Hashed: Iterator error"; return field; } - // Test if custom Range is specified - if ( *(has_h+1) == '(' ) { - sscanf(has_h, "H(%2u-%2u)", &minBuf, &maxBuf); - if ( !maxBuf || \ + } + if ((has_h != field) && (*(has_h - 1) == '/') ) { /* H not allowed as iterator */ + *error = "Hashed: 'H' not allowed as iterator"; + return field; + } + if (*(has_h + 1) == '-' || \ + (has_h != field && *(has_h - 1) == '-') ) { // 'H' not starting field, so may be the end of a range + *error = "'H' is not allowed for use in ranges"; + return field; + } + // Test if custom Range is specified + if (*(has_h + 1) == '(' ) { + sscanf(has_h, "H(%2u-%2u)", &minBuf, &maxBuf); + if ( !maxBuf || \ (minBuf == 0xFF) || \ (minBuf > maxBuf) || \ (minBuf < min) || \ // if a customMax is present: Is read maximum bigger than it? (which it shouldn't be) - (customMax ? maxBuf > customMax : 0) - ) { - *error = "'H' custom range error"; - return field; - } - min = minBuf; - // maxBuf needs to be incremented by 1 to include it - customMax = maxBuf + 1; - } - switch (pos) { - case CRON_FIELD_SECOND: - fieldMax = CRON_MAX_SECONDS; - break; - case CRON_FIELD_MINUTE: - fieldMax = CRON_MAX_MINUTES; - break; - case CRON_FIELD_HOUR: - fieldMax = CRON_MAX_HOURS; - break; - case CRON_FIELD_DAY_OF_MONTH: - // limited to 28th so the hashed cron will be executed every month - fieldMax = 28; - break; - case CRON_FIELD_MONTH: - fieldMax = CRON_MAX_MONTHS; - break; - case CRON_FIELD_DAY_OF_WEEK: - fieldMax = CRON_MAX_DAYS_OF_WEEK; - break; - default: - *error = "Unknown field!"; - return field; + (customMax ? maxBuf > customMax : 0) + ) { + *error = "'H' custom range error"; + return field; } - if (!customMax) { - customMax = fieldMax; - } else if (customMax > fieldMax) { - *error = "'H' range maximum error"; + min = minBuf; + // maxBuf needs to be incremented by 1 to include it + customMax = maxBuf + 1; + } + switch (pos) { + case CRON_FIELD_SECOND: + fieldMax = CRON_MAX_SECONDS; + break; + case CRON_FIELD_MINUTE: + fieldMax = CRON_MAX_MINUTES; + break; + case CRON_FIELD_HOUR: + fieldMax = CRON_MAX_HOURS; + break; + case CRON_FIELD_DAY_OF_MONTH: + // limited to 28th so the hashed cron will be executed every month + fieldMax = 28; + break; + case CRON_FIELD_MONTH: + fieldMax = CRON_MAX_MONTHS; + break; + case CRON_FIELD_DAY_OF_WEEK: + fieldMax = CRON_MAX_DAYS_OF_WEEK; + break; + default: + *error = "Unknown field!"; return field; + } + if (!customMax) { + customMax = fieldMax; + } else if (customMax > fieldMax) { + *error = "'H' range maximum error"; + return field; + } + field = replace_hashed(field, pos, min, customMax, fn, error); + + return field; +} + +static char* check_and_replace_h(char* field, unsigned int pos, unsigned int min, const char** error) +{ + char* has_h = strchr(field, 'H'); + if (has_h) { + char *accum_field = NULL; + char **subfields = NULL; + size_t subfields_len = 0; + // Check if Field contains ',', if so, split into multiple subfields, and replace in each (with same position no) + char *has_comma = strchr(field, ','); + if (has_comma) { + // Iterate over split sub-fields, check for 'H' and replace if present + subfields = split_str(field, ',', &subfields_len); + if (subfields == NULL) { + *error = "Failed to split 'H' string in list"; + goto return_error; + } + size_t res_len = 0; + size_t res_lens[subfields_len]; + for (size_t i = 0; i < subfields_len; i++) { + has_h = strchr(subfields[i], 'H'); + if (has_h) { + subfields[i] = replace_h_entry(subfields[i], pos, min, error); + } + if (*error != NULL) { + goto return_error; + } + res_lens[i] = strnlen(subfields[i], CRON_MAX_STR_LEN_TO_SPLIT); + res_len += res_lens[i]; + } + // Allocate space for the full string: Result lengths + (result count - 1) for the commas + 1 for '\0' + accum_field = (char *) cronMalloc(res_len + subfields_len ); + if (accum_field == NULL) { + *error = "Failed to merge 'H' in list"; + goto return_error; + } + memset(accum_field, 0, res_len + subfields_len); + char *tracking = accum_field; + for (size_t i = 0; i < subfields_len; i++) { + // Sanity check: Is "tracking" still in the allocated memory boundaries? + if ((tracking - accum_field) > (res_len + subfields_len)) { + *error = "Failed to insert subfields to merged fields: String went oob"; + goto return_error; + } + strncpy(tracking, subfields[i], res_lens[i]); + tracking += res_lens[i]; + // Don't append comma to last list entry + if (i < subfields_len-1) { + strncpy(tracking, ",", 2); // using 2 to ensure the string ends in '\0', tracking will be set to that char + tracking += 1; + } + } + free_splitted(subfields, subfields_len); + cronFree(field); + return accum_field; } - field = replace_hashed(field, pos, min, customMax, fn, error); + // only one H to find and replace, then return + field = replace_h_entry(field, pos, min, error); + return field; + + return_error: + if (subfields) free_splitted(subfields, subfields_len); + if (accum_field) cronFree(accum_field); + return field; } return field; } @@ -1468,6 +1541,7 @@ static char* w_check(char* field, cron_expr* target, const char** error) goto return_error; } memset(newField, 0, sizeof(char) * strlen(field)); + char *tracking = newField; // Ensure only 1 day is specified, and W day is not the last in a range or list or iterator of days if ( has_char(field, '/') || has_char(field, '-')) { *error = "W not allowed in iterators or ranges in 'day of month' field"; @@ -1502,7 +1576,14 @@ static char* w_check(char* field, cron_expr* target, const char** error) cron_setBit(target->w_flags, w_day); } } else { - strcat(newField, splitField[i]); + if (tracking != newField) { + // A field was already added. Add a comma first + strncpy(tracking, ",", 2); // ensure string ends in '\0', tracking will be set to it + tracking += 1; + } + size_t field_len = strnlen(splitField[i], CRON_MAX_STR_LEN_TO_SPLIT); + strncpy(tracking, splitField[i], field_len); + tracking += field_len; } } free_splitted(splitField, len_out); diff --git a/ccronexpr_test.c b/ccronexpr_test.c index a98e40e..54b9f1e 100644 --- a/ccronexpr_test.c +++ b/ccronexpr_test.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include "ccronexpr.h" @@ -169,21 +169,21 @@ struct tm* poors_mans_strptime(const char* str) { return cal; } -void check_next(const char* pattern, const char* initial, const char* expected) { +bool check_next(const char* pattern, const char* initial, const char* expected) { const char* err = NULL; cron_expr parsed; cron_parse_expr(pattern, &parsed, &err); if (err) { printf("Error: %s\nPattern: %s\n", err, pattern); - assert(0); + return false; } struct tm* calinit = poors_mans_strptime(initial); time_t dateinit = timegm(calinit); - assert(-1 != dateinit); + if(-1 == dateinit) return false; time_t datenext = cron_next(&parsed, dateinit); struct tm* calnext = gmtime(&datenext); - assert(calnext); + if (calnext == NULL) return false; char* buffer = (char*) malloc(21); memset(buffer, 0, 21); strftime(buffer, 20, DATE_FORMAT, calnext); @@ -192,13 +192,14 @@ void check_next(const char* pattern, const char* initial, const char* expected) printf("Initial: %s\n", initial); printf("Expected: %s\n", expected); printf("Actual: %s\n", buffer); - assert(0); + return false; } free(buffer); free(calinit); + return true; } -void check_same(const char* expr1, const char* expr2) { +bool check_same(const char* expr1, const char* expr2) { cron_expr parsed1; cron_parse_expr(expr1, &parsed1, NULL); cron_expr parsed2; @@ -206,37 +207,43 @@ void check_same(const char* expr1, const char* expr2) { if (!crons_equal(&parsed1, &parsed2)) { printf("\nThe following CRONs aren't equal, although they should be:\n"); printf("%s\t%s\n", expr1, expr2); - assert(crons_equal(&parsed1, &parsed2)); + return false; + //assert(crons_equal(&parsed1, &parsed2)); } + return true; } -void check_calc_invalid() { +bool check_calc_invalid() { cron_expr parsed; cron_parse_expr("0 0 0 31 6 *", &parsed, NULL); struct tm * calinit = poors_mans_strptime("2012-07-01_09:53:50"); time_t dateinit = timegm(calinit); time_t res = cron_next(&parsed, dateinit); - assert(INVALID_INSTANT == res); free(calinit); + if (INVALID_INSTANT != res) return false; + return true; } -void check_expr_invalid(const char* expr) { +bool check_expr_invalid(const char* expr) { const char* err = NULL; cron_expr test; cron_parse_expr(expr, &test, &err); if (!err) { printf("Error: '%s' parsed without an error (but it should)\n", expr); - assert(err); + return false; } + return true; } -void check_expr_valid(const char* expr) { +bool check_expr_valid(const char* expr) { const char* err = NULL; cron_expr test; cron_parse_expr(expr, &test, &err); if (err) { printf("Error: '%s' parsed with an error: %s\n", expr, err); + return false; } + return true; } int testing_hash_function(int seed, uint8_t idx) { @@ -249,324 +256,355 @@ int fake_custom_hash_function(int seed, uint8_t idx) } void test_expr() { - check_next("*/15 * 1-4 * * *", "2012-07-01_09:53:50", "2012-07-02_01:00:00"); - check_next("*/15 * 1-4 * * *", "2012-07-01_09:53:00", "2012-07-02_01:00:00"); - check_next("0 */2 1-4 * * *", "2012-07-01_09:00:00", "2012-07-02_01:00:00"); - check_next("* * * * * *", "2012-07-01_09:00:00", "2012-07-01_09:00:01"); - check_next("* * * * * *", "2012-12-01_09:00:58", "2012-12-01_09:00:59"); - check_next("10 * * * * *", "2012-12-01_09:42:09", "2012-12-01_09:42:10"); - check_next("11 * * * * *", "2012-12-01_09:42:10", "2012-12-01_09:42:11"); - check_next("10 * * * * *", "2012-12-01_09:42:10", "2012-12-01_09:43:10"); - check_next("10-15 * * * * *", "2012-12-01_09:42:09", "2012-12-01_09:42:10"); - check_next("10-15 * * * * *", "2012-12-01_21:42:14", "2012-12-01_21:42:15"); - check_next("0 * * * * *", "2012-12-01_21:10:42", "2012-12-01_21:11:00"); - check_next("0 * * * * *", "2012-12-01_21:11:00", "2012-12-01_21:12:00"); - check_next("0 11 * * * *", "2012-12-01_21:10:42", "2012-12-01_21:11:00"); - check_next("0 10 * * * *", "2012-12-01_21:11:00", "2012-12-01_22:10:00"); - check_next("0 0 * * * *", "2012-09-30_11:01:00", "2012-09-30_12:00:00"); - check_next("0 0 * * * *", "2012-09-30_12:00:00", "2012-09-30_13:00:00"); - check_next("0 0 * * * *", "2012-09-10_23:01:00", "2012-09-11_00:00:00"); - check_next("0 0 * * * *", "2012-09-11_00:00:00", "2012-09-11_01:00:00"); - check_next("0 0 0 * * *", "2012-09-01_14:42:43", "2012-09-02_00:00:00"); - check_next("0 0 0 * * *", "2012-09-02_00:00:00", "2012-09-03_00:00:00"); - check_next("* * * 10 * *", "2012-10-09_15:12:42", "2012-10-10_00:00:00"); - check_next("* * * 10 * *", "2012-10-11_15:12:42", "2012-11-10_00:00:00"); - check_next("0 0 0 * * *", "2012-09-30_15:12:42", "2012-10-01_00:00:00"); - check_next("0 0 0 * * *", "2012-10-01_00:00:00", "2012-10-02_00:00:00"); - check_next("0 0 0 * * *", "2012-08-30_15:12:42", "2012-08-31_00:00:00"); - check_next("0 0 0 * * *", "2012-08-31_00:00:00", "2012-09-01_00:00:00"); - check_next("0 0 0 * * *", "2012-10-30_15:12:42", "2012-10-31_00:00:00"); - check_next("0 0 0 * * *", "2012-10-31_00:00:00", "2012-11-01_00:00:00"); - check_next("0 0 0 1 * *", "2012-10-30_15:12:42", "2012-11-01_00:00:00"); - check_next("0 0 0 1 * *", "2012-11-01_00:00:00", "2012-12-01_00:00:00"); - check_next("0 0 0 1 * *", "2010-12-31_15:12:42", "2011-01-01_00:00:00"); - check_next("0 0 0 1 * *", "2011-01-01_00:00:00", "2011-02-01_00:00:00"); - check_next("0 0 0 31 * *", "2011-10-30_15:12:42", "2011-10-31_00:00:00"); - check_next("0 0 0 1 * *", "2011-10-30_15:12:42", "2011-11-01_00:00:00"); - check_next("* * * * * 2", "2010-10-25_15:12:42", "2010-10-26_00:00:00"); - check_next("* * * * * 2", "2010-10-20_15:12:42", "2010-10-26_00:00:00"); - check_next("* * * * * 2", "2010-10-27_15:12:42", "2010-11-02_00:00:00"); - check_next("55 5 * * * *", "2010-10-27_15:04:54", "2010-10-27_15:05:55"); - check_next("55 5 * * * *", "2010-10-27_15:05:55", "2010-10-27_16:05:55"); - check_next("20,40 5 * * * *", "2010-10-27_15:06:30", "2010-10-27_16:05:20"); - check_next("20 6 * * * *", "2010-10-27_15:06:30", "2010-10-27_16:06:20"); - check_next("20 5,7 16 * * *", "2010-10-27_15:06:30", "2010-10-27_16:05:20"); - check_next("20,40 5 16 * * *", "2010-10-27_15:06:30", "2010-10-27_16:05:20"); - check_next("20 5 15,17 28 * *", "2010-10-27_15:06:30", "2010-10-28_15:05:20"); - check_next("20,40 5 15,17 28 * *","2010-10-27_15:06:30", "2010-10-28_15:05:20"); - check_next("55 * 10 * * *", "2010-10-27_09:04:54", "2010-10-27_10:00:55"); - check_next("55 * 10 * * *", "2010-10-27_10:00:55", "2010-10-27_10:01:55"); - check_next("* 5 10 * * *", "2010-10-27_09:04:55", "2010-10-27_10:05:00"); - check_next("* 5 10 * * *", "2010-10-27_10:05:00", "2010-10-27_10:05:01"); - check_next("55 * * 3 * *", "2010-10-02_10:05:54", "2010-10-03_00:00:55"); - check_next("55 * * 3 * *", "2010-10-03_00:00:55", "2010-10-03_00:01:55"); - check_next("* * * 3 11 *", "2010-10-02_14:42:55", "2010-11-03_00:00:00"); - check_next("* * * 3 11 *", "2010-11-03_00:00:00", "2010-11-03_00:00:01"); - check_next("0 0 0 29 2 *", "2007-02-10_14:42:55", "2008-02-29_00:00:00"); - check_next("0 0 0 29 2 *", "2008-02-29_00:00:00", "2012-02-29_00:00:00"); - check_next("0 0 7 ? * MON-FRI", "2009-09-26_00:42:55", "2009-09-28_07:00:00"); - check_next("0 0 7 ? * MON-FRI", "2009-09-28_07:00:00", "2009-09-29_07:00:00"); - check_next("0 30 23 30 1/3 ?", "2010-12-30_00:00:00", "2011-01-30_23:30:00"); - check_next("0 30 23 30 1/3 ?", "2011-01-30_23:30:00", "2011-04-30_23:30:00"); - check_next("0 30 23 30 1/3 ?", "2011-04-30_23:30:00", "2011-07-30_23:30:00"); - check_next("0 0 1 28 * ?", "2022-02-28_02:00:00", "2022-03-28_01:00:00"); - check_next("0 0 0 * 12 ?", "2022-01-01_00:00:00", "2022-12-01_00:00:00"); + assert(check_next("*/15 * 1-4 * * *", "2012-07-01_09:53:50", "2012-07-02_01:00:00")); + assert(check_next("*/15 * 1-4 * * *", "2012-07-01_09:53:00", "2012-07-02_01:00:00")); + assert(check_next("0 */2 1-4 * * *", "2012-07-01_09:00:00", "2012-07-02_01:00:00")); + assert(check_next("* * * * * *", "2012-07-01_09:00:00", "2012-07-01_09:00:01")); + assert(check_next("* * * * * *", "2012-12-01_09:00:58", "2012-12-01_09:00:59")); + assert(check_next("10 * * * * *", "2012-12-01_09:42:09", "2012-12-01_09:42:10")); + assert(check_next("11 * * * * *", "2012-12-01_09:42:10", "2012-12-01_09:42:11")); + assert(check_next("10 * * * * *", "2012-12-01_09:42:10", "2012-12-01_09:43:10")); + assert(check_next("10-15 * * * * *", "2012-12-01_09:42:09", "2012-12-01_09:42:10")); + assert(check_next("10-15 * * * * *", "2012-12-01_21:42:14", "2012-12-01_21:42:15")); + assert(check_next("0 * * * * *", "2012-12-01_21:10:42", "2012-12-01_21:11:00")); + assert(check_next("0 * * * * *", "2012-12-01_21:11:00", "2012-12-01_21:12:00")); + assert(check_next("0 11 * * * *", "2012-12-01_21:10:42", "2012-12-01_21:11:00")); + assert(check_next("0 10 * * * *", "2012-12-01_21:11:00", "2012-12-01_22:10:00")); + assert(check_next("0 0 * * * *", "2012-09-30_11:01:00", "2012-09-30_12:00:00")); + assert(check_next("0 0 * * * *", "2012-09-30_12:00:00", "2012-09-30_13:00:00")); + assert(check_next("0 0 * * * *", "2012-09-10_23:01:00", "2012-09-11_00:00:00")); + assert(check_next("0 0 * * * *", "2012-09-11_00:00:00", "2012-09-11_01:00:00")); + assert(check_next("0 0 0 * * *", "2012-09-01_14:42:43", "2012-09-02_00:00:00")); + assert(check_next("0 0 0 * * *", "2012-09-02_00:00:00", "2012-09-03_00:00:00")); + assert(check_next("* * * 10 * *", "2012-10-09_15:12:42", "2012-10-10_00:00:00")); + assert(check_next("* * * 10 * *", "2012-10-11_15:12:42", "2012-11-10_00:00:00")); + assert(check_next("0 0 0 * * *", "2012-09-30_15:12:42", "2012-10-01_00:00:00")); + assert(check_next("0 0 0 * * *", "2012-10-01_00:00:00", "2012-10-02_00:00:00")); + assert(check_next("0 0 0 * * *", "2012-08-30_15:12:42", "2012-08-31_00:00:00")); + assert(check_next("0 0 0 * * *", "2012-08-31_00:00:00", "2012-09-01_00:00:00")); + assert(check_next("0 0 0 * * *", "2012-10-30_15:12:42", "2012-10-31_00:00:00")); + assert(check_next("0 0 0 * * *", "2012-10-31_00:00:00", "2012-11-01_00:00:00")); + assert(check_next("0 0 0 1 * *", "2012-10-30_15:12:42", "2012-11-01_00:00:00")); + assert(check_next("0 0 0 1 * *", "2012-11-01_00:00:00", "2012-12-01_00:00:00")); + assert(check_next("0 0 0 1 * *", "2010-12-31_15:12:42", "2011-01-01_00:00:00")); + assert(check_next("0 0 0 1 * *", "2011-01-01_00:00:00", "2011-02-01_00:00:00")); + assert(check_next("0 0 0 31 * *", "2011-10-30_15:12:42", "2011-10-31_00:00:00")); + assert(check_next("0 0 0 1 * *", "2011-10-30_15:12:42", "2011-11-01_00:00:00")); + assert(check_next("* * * * * 2", "2010-10-25_15:12:42", "2010-10-26_00:00:00")); + assert(check_next("* * * * * 2", "2010-10-20_15:12:42", "2010-10-26_00:00:00")); + assert(check_next("* * * * * 2", "2010-10-27_15:12:42", "2010-11-02_00:00:00")); + assert(check_next("55 5 * * * *", "2010-10-27_15:04:54", "2010-10-27_15:05:55")); + assert(check_next("55 5 * * * *", "2010-10-27_15:05:55", "2010-10-27_16:05:55")); + assert(check_next("20,40 5 * * * *", "2010-10-27_15:06:30", "2010-10-27_16:05:20")); + assert(check_next("20 6 * * * *", "2010-10-27_15:06:30", "2010-10-27_16:06:20")); + assert(check_next("20 5,7 16 * * *", "2010-10-27_15:06:30", "2010-10-27_16:05:20")); + assert(check_next("20,40 5 16 * * *", "2010-10-27_15:06:30", "2010-10-27_16:05:20")); + assert(check_next("20 5 15,17 28 * *", "2010-10-27_15:06:30", "2010-10-28_15:05:20")); + assert(check_next("20,40 5 15,17 28 * *","2010-10-27_15:06:30", "2010-10-28_15:05:20")); + assert(check_next("55 * 10 * * *", "2010-10-27_09:04:54", "2010-10-27_10:00:55")); + assert(check_next("55 * 10 * * *", "2010-10-27_10:00:55", "2010-10-27_10:01:55")); + assert(check_next("* 5 10 * * *", "2010-10-27_09:04:55", "2010-10-27_10:05:00")); + assert(check_next("* 5 10 * * *", "2010-10-27_10:05:00", "2010-10-27_10:05:01")); + assert(check_next("55 * * 3 * *", "2010-10-02_10:05:54", "2010-10-03_00:00:55")); + assert(check_next("55 * * 3 * *", "2010-10-03_00:00:55", "2010-10-03_00:01:55")); + assert(check_next("* * * 3 11 *", "2010-10-02_14:42:55", "2010-11-03_00:00:00")); + assert(check_next("* * * 3 11 *", "2010-11-03_00:00:00", "2010-11-03_00:00:01")); + assert(check_next("0 0 0 29 2 *", "2007-02-10_14:42:55", "2008-02-29_00:00:00")); + assert(check_next("0 0 0 29 2 *", "2008-02-29_00:00:00", "2012-02-29_00:00:00")); + assert(check_next("0 0 7 ? * MON-FRI", "2009-09-26_00:42:55", "2009-09-28_07:00:00")); + assert(check_next("0 0 7 ? * MON-FRI", "2009-09-28_07:00:00", "2009-09-29_07:00:00")); + assert(check_next("0 30 23 30 1/3 ?", "2010-12-30_00:00:00", "2011-01-30_23:30:00")); + assert(check_next("0 30 23 30 1/3 ?", "2011-01-30_23:30:00", "2011-04-30_23:30:00")); + assert(check_next("0 30 23 30 1/3 ?", "2011-04-30_23:30:00", "2011-07-30_23:30:00")); + assert(check_next("0 0 1 28 * ?", "2022-02-28_02:00:00", "2022-03-28_01:00:00")); + assert(check_next("0 0 0 * 12 ?", "2022-01-01_00:00:00", "2022-12-01_00:00:00")); // H Tests cron_init_custom_hash_fn(testing_hash_function); cron_init_hash(7); - check_next("H H H H H ?", "2022-05-12_00:00:00", "2022-05-22_14:07:00"); // 0 7 14 22 5 (1) - check_next("H H H H H ?", "2022-06-12_00:00:00", "2023-05-22_14:07:00"); - check_next("H H H ? H H", "2022-05-12_00:00:00", "2022-05-16_14:07:00"); - check_next("H 0 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:00:00"); - check_next("H 0,12 1 * * ?", "2022-05-12_01:01:00", "2022-05-12_01:12:00"); - check_next("H 0,H 1 * * ?", "2022-05-12_01:01:00", "2022-05-12_01:07:00"); - check_next("H 0 1/4 * * ?", "2022-05-12_01:01:00", "2022-05-12_05:00:00"); - check_next("H H 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:07:00"); + assert(check_next("H H H H H ?", "2022-05-12_00:00:00", "2022-05-22_14:07:00")); // 0 7 14 22 5 (1) + assert(check_next("H H H H H ?", "2022-06-12_00:00:00", "2023-05-22_14:07:00")); + assert(check_next("H H H ? H H", "2022-05-12_00:00:00", "2022-05-16_14:07:00")); + assert(check_next("H 0 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:00:00")); + assert(check_next("H 0,12 1 * * ?", "2022-05-12_01:01:00", "2022-05-12_01:12:00")); + assert(check_next("H 0,H 1 * * ?", "2022-05-12_01:01:00", "2022-05-12_01:07:00")); + assert(check_next("H 0 1/4 * * ?", "2022-05-12_01:01:00", "2022-05-12_05:00:00")); + assert(check_next("H H 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:07:00")); // H,H is same as H - check_next("H H,H 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:07:00"); - check_next("0 H/5 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:02:00"); - check_next("0 0 1 1 H/MAY ?", "2022-05-12_00:00:00", "2022-06-01_01:00:00"); - check_next("0 0 1 1 H/MAY ?", "2022-06-12_00:00:00", "2022-11-01_01:00:00"); + assert(check_next("H H,H 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:07:00")); + assert(check_next("0 H/5 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:02:00")); + assert(check_next("0 0 1 1 H/MAY ?", "2022-05-12_00:00:00", "2022-06-01_01:00:00")); + assert(check_next("0 0 1 1 H/MAY ?", "2022-06-12_00:00:00", "2022-11-01_01:00:00")); // Tests for H in custom range - check_next("0 H(0-5) 1 1 * ?", "2022-06-12_00:00:00", "2022-07-01_01:01:00"); // 0 1 1 1 * * - //check_next("0 H,H(0-5) 1 1 * ?", "2022-06-12_00:00:00", "2022-07-01_01:01:00"); // 0 1 1 1 * * - //check_next("0 H(0-5),H(2-9) 1 1 * ?", "2022-06-12_02:00:00", "2022-07-01_01:01:00"); // 0 1 1 1 * * - //check_next("0 H(0-5),H(2-7) 1 1 * ?", "2022-06-12_02:00:00", "2022-07-03_01:01:00"); // 0 1 1 1 * * - check_next("0 0 1 H(1-9)W * ?","2022-06-12_00:00:00", "2022-07-04_01:00:00"); // Day is 4 - check_next("0 0 1 H(1-9)W * ?","2022-06-01_00:00:00", "2022-06-03_01:00:00"); - check_next("0 0 1 ? * HL", "2022-06-12_00:00:00", "2022-06-27_01:00:00"); - check_next("0 0 1 ? * H(1-6)L", "2022-06-12_00:00:00", "2022-06-25_01:00:00"); + assert(check_next("0 H(0-5) 1 1 * ?", "2022-06-12_00:00:00", "2022-07-01_01:01:00")); // 0 1 1 1 * * + assert(check_next("0 H,H(0-5) 1 1 * ?", "2022-06-12_00:00:00", "2022-07-01_01:01:00")); // 0 1,1 1 1 * * + assert(check_next("0 H(0-5),H(2-9) 1 1 * ?", "2022-06-12_02:00:00", "2022-07-01_01:01:00")); // 0 1,9 1 1 * * + assert(check_next("0 H(0-5),H(2-9) 1 1 * ?", "2022-07-01_01:01:01", "2022-07-01_01:09:00")); // 0 1,9 1 1 * * + assert(check_next("0 H(0-5),H(2-7) 1 1 * ?", "2022-06-12_02:00:00", "2022-07-01_01:01:00")); // 0 1,3 1 1 * * + assert(check_next("0 H(0-5),H(2-7) 1 1 * ?", "2022-07-01_01:01:01", "2022-07-01_01:03:00")); // 0 1,3 1 1 * * + assert(check_next("0 0 0 H(1-5),H(1-2) * ?", "2022-07-01_01:01:01", "2022-07-02_00:00:00")); // 0 0 0 2,3 * * + assert(check_next("0 0 0 H(1-5),H(1-2) * ?", "2022-07-02_01:01:01", "2022-08-02_00:00:00")); // 0 0 0 2,3 * * + assert(check_next("0 0 1 H(1-9)W * ?","2022-06-12_00:00:00", "2022-07-04_01:00:00")); // Day is 4 + assert(check_next("0 0 1 H(1-9)W * ?","2022-06-01_00:00:00", "2022-06-03_01:00:00")); + assert(check_next("0 0 1 ? * HL", "2022-06-12_00:00:00", "2022-06-27_01:00:00")); + assert(check_next("0 0 1 ? * H(1-6)L", "2022-06-12_00:00:00", "2022-06-25_01:00:00")); cron_init_hash(42); - check_next("H H H H H ?", "2022-05-12_00:00:00", "2023-01-19_12:42:00"); // 0 42 12(84) 19(126) 1(168) 1(210) - check_next("H H H ? H H", "2022-05-12_00:00:00", "2023-01-02_12:42:00"); - check_next("H 0 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:00:00"); - check_next("0 H/10 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:02:00"); - check_next("0 0 1 1 H/MAY ?", "2022-05-12_00:00:00", "2022-06-01_01:00:00"); + assert(check_next("H H H H H ?", "2022-05-12_00:00:00", "2023-01-19_12:42:00")); // 0 42 12(84) 19(126) 1(168) 1(210) + assert(check_next("H H H ? H H", "2022-05-12_00:00:00", "2023-01-02_12:42:00")); + assert(check_next("H 0 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:00:00")); + assert(check_next("0 H/10 1 * * ?", "2022-05-12_00:00:00", "2022-05-12_01:02:00")); + assert(check_next("0 0 1 1 H/MAY ?", "2022-05-12_00:00:00", "2022-06-01_01:00:00")); cron_init_hash(12); - check_next("H H H H H ?", "2022-05-12_00:00:00", "2023-01-10_00:12:00"); // 0 12 0 10 1 5 - check_next("H H H ? H H", "2022-05-12_00:00:00", "2023-01-06_00:12:00"); + assert(check_next("H H H H H ?", "2022-05-12_00:00:00", "2023-01-10_00:12:00")); // 0 12 0 10 1 5 + assert(check_next("H H H ? H H", "2022-05-12_00:00:00", "2023-01-06_00:12:00")); // Tests for a custom hash function cron_custom_hash_fn custom_fn = fake_custom_hash_function; cron_init_custom_hash_fn(custom_fn); - check_next("H H H H H ?", "2022-05-12_00:00:00", "2023-01-22_12:24:12"); // 12 24 12 22 1 3 - check_next("H H H ? H H", "2022-05-12_00:00:00", "2023-01-04_12:24:12"); // 12 24 12 22 1 3 - check_next("0 0 1 ? * H/TUE", "2022-05-12_00:00:00", "2022-05-13_01:00:00"); // 1/TUE + assert(check_next("H H H H H ?", "2022-05-12_00:00:00", "2023-01-22_12:24:12")); // 12 24 12 22 1 3 + assert(check_next("H H H ? H H", "2022-05-12_00:00:00", "2023-01-04_12:24:12")); // 12 24 12 22 1 3 + assert(check_next("0 0 1 ? * H/TUE", "2022-05-12_00:00:00", "2022-05-13_01:00:00")); // 1/TUE cron_init_custom_hash_fn(testing_hash_function); // W Tests - check_next("0 0 1 4W * ?", "2022-04-12_00:00:00", "2022-05-04_01:00:00"); - check_next("0 0 1 4W * ?", "2022-05-12_00:00:00", "2022-06-03_01:00:00"); - check_next("0 0 1 1W * ?", "2022-10-01_00:00:00", "2022-10-03_01:00:00"); - check_next("0 0 1 1W * ?", "2022-10-03_00:00:00", "2022-10-03_01:00:00"); - check_next("0 0 1 16W * ?", "2022-07-16_00:00:00", "2022-08-16_01:00:00"); - check_next("0 0 1 20W * ?", "2022-08-20_00:00:00", "2022-09-20_01:00:00"); - check_next("0 0 1 1W * ?", "2022-10-03_02:00:00", "2022-11-01_01:00:00"); - check_next("0 0 1 1W * ?", "2022-05-01_02:00:00", "2022-05-02_01:00:00"); - check_next("0 0 1 1W * ?", "2022-09-01_00:00:00", "2022-09-01_01:00:00"); - check_next("0 0 1 1,3W * ?", "2022-09-01_00:00:00", "2022-09-01_01:00:00"); - check_next("0 0 1 1,3W * ?", "2022-09-02_00:00:00", "2022-09-02_01:00:00"); - check_next("0 0 1 1,3W * ?", "2022-09-03_00:00:00", "2022-10-01_01:00:00"); - check_next("0 0 1 1,3W * ?", "2022-10-02_00:00:00", "2022-10-03_01:00:00"); - check_next("0 0 1 1W,4W * ?", "2022-09-01_00:00:00", "2022-09-01_01:00:00"); - check_next("0 0 1 1W,4W * ?", "2022-09-02_00:00:00", "2022-09-05_01:00:00"); - check_next("0 0 1 1W,4W * ?", "2022-06-03_00:00:00", "2022-06-03_01:00:00"); - check_next("0 0 1 1W,4W * ?", "2022-09-03_00:00:00", "2022-09-05_01:00:00"); - check_next("0 0 1 1W,4W * ?", "2022-10-01_00:00:00", "2022-10-03_01:00:00"); - check_next("0 0 1 1W,15W * ?", "2022-09-01_00:00:00", "2022-09-01_01:00:00"); - check_next("0 0 1 1W,15W * ?", "2022-10-01_00:00:00", "2022-10-03_01:00:00"); - check_next("0 0 1 1W,15W * ?", "2022-09-02_00:00:00", "2022-09-15_01:00:00"); - check_next("0 0 1 1W,15W * ?", "2022-01-01_00:00:00", "2022-01-03_01:00:00"); - check_next("0 0 1 1W,15W * ?", "2022-01-04_00:00:00", "2022-01-14_01:00:00"); - check_next("0 0 1 1W,15W * ?", "2022-01-15_00:00:00", "2022-02-01_01:00:00"); - check_next("0 0 1 8W,26W * ?", "2022-01-06_00:00:00", "2022-01-07_01:00:00"); - check_next("0 0 1 8W,26W * ?", "2022-01-26_00:00:00", "2022-01-26_01:00:00"); - check_next("0 0 1 8W,26W * ?", "2022-02-26_00:00:00", "2022-03-08_01:00:00"); - check_next("0 0 1 8W,26W * ?", "2022-03-09_00:00:00", "2022-03-25_01:00:00"); - check_next("0 0 1 29W * ?", "2022-02-28_00:00:00", "2022-03-29_01:00:00"); - check_next("0 0 1 29W * ?", "2024-02-28_00:00:00", "2024-02-29_01:00:00"); - check_next("0 0 1 31W * ?", "2022-02-28_00:00:00", "2022-03-31_01:00:00"); - check_next("0 0 1 31W * ?", "2022-06-17_00:00:00", "2022-07-29_01:00:00"); - check_next("0 0 1 31W * ?", "2022-07-30_00:00:00", "2022-08-31_01:00:00"); - check_next("0 0 1 26W * ?", "2022-06-27_00:00:00", "2022-06-27_01:00:00"); - check_next("H 0 1 26W * ?", "2022-06-27_00:00:00", "2022-06-27_01:00:00"); - check_next("H 0 1 26W * ?", "2022-06-27_02:00:00", "2022-07-26_01:00:00"); - check_next("H 0 1 HW * ?", "2022-06-27_02:00:00", "2022-07-11_01:00:00"); // 10W - check_next("H 0 1 HW * ?", "2022-05-27_02:00:00", "2022-06-10_01:00:00"); // 10W + assert(check_next("0 0 1 4W * ?", "2022-04-12_00:00:00", "2022-05-04_01:00:00")); + assert(check_next("0 0 1 4W * ?", "2022-05-12_00:00:00", "2022-06-03_01:00:00")); + assert(check_next("0 0 1 1W * ?", "2022-10-01_00:00:00", "2022-10-03_01:00:00")); + assert(check_next("0 0 1 1W * ?", "2022-10-03_00:00:00", "2022-10-03_01:00:00")); + assert(check_next("0 0 1 16W * ?", "2022-07-16_00:00:00", "2022-08-16_01:00:00")); + assert(check_next("0 0 1 20W * ?", "2022-08-20_00:00:00", "2022-09-20_01:00:00")); + assert(check_next("0 0 1 1W * ?", "2022-10-03_02:00:00", "2022-11-01_01:00:00")); + assert(check_next("0 0 1 1W * ?", "2022-05-01_02:00:00", "2022-05-02_01:00:00")); + assert(check_next("0 0 1 1W * ?", "2022-09-01_00:00:00", "2022-09-01_01:00:00")); + assert(check_next("0 0 1 1,3W * ?", "2022-09-01_00:00:00", "2022-09-01_01:00:00")); + assert(check_next("0 0 1 1,3W * ?", "2022-09-02_00:00:00", "2022-09-02_01:00:00")); + assert(check_next("0 0 1 1,3W * ?", "2022-09-03_00:00:00", "2022-10-01_01:00:00")); + assert(check_next("0 0 1 1,3W * ?", "2022-10-02_00:00:00", "2022-10-03_01:00:00")); + // Check behaviour with more days mixed with W fields + assert(check_next("0 0 1 1,3W,15 * ?", "2022-09-01_00:00:00", "2022-09-01_01:00:00")); + assert(check_next("0 0 1 1,3W,15 * ?", "2022-09-02_00:00:00", "2022-09-02_01:00:00")); + assert(check_next("0 0 1 1,3W,15 * ?", "2022-09-03_00:00:00", "2022-09-15_01:00:00")); + assert(check_next("0 0 1 1,3W,15 * ?", "2022-09-16_00:00:00", "2022-10-01_01:00:00")); + assert(check_next("0 0 1 1,3W,15 * ?", "2022-10-02_00:00:00", "2022-10-03_01:00:00")); + assert(check_next("0 0 1 1,3W,15,16W * ?", "2022-09-01_00:00:00", "2022-09-01_01:00:00")); + assert(check_next("0 0 1 1,3W,15,16W * ?", "2022-09-02_00:00:00", "2022-09-02_01:00:00")); + assert(check_next("0 0 1 1,3W,15,16W * ?", "2022-09-03_00:00:00", "2022-09-15_01:00:00")); + assert(check_next("0 0 1 1,3W,15,16W * ?", "2022-09-16_00:00:00", "2022-09-16_01:00:00")); + assert(check_next("0 0 1 1,3W,15,16W * ?", "2022-09-17_00:00:00", "2022-10-01_01:00:00")); + assert(check_next("0 0 1 1,3W,15,16W * ?", "2022-10-02_00:00:00", "2022-10-03_01:00:00")); + assert(check_next("0 0 1 1,3W,15,16W * ?", "2025-02-16_00:00:00", "2025-02-17_01:00:00")); + assert(check_next("0 0 1 1W,4W * ?", "2022-09-01_00:00:00", "2022-09-01_01:00:00")); + assert(check_next("0 0 1 1W,4W * ?", "2022-09-02_00:00:00", "2022-09-05_01:00:00")); + assert(check_next("0 0 1 1W,4W * ?", "2022-06-03_00:00:00", "2022-06-03_01:00:00")); + assert(check_next("0 0 1 1W,4W * ?", "2022-09-03_00:00:00", "2022-09-05_01:00:00")); + assert(check_next("0 0 1 1W,4W * ?", "2022-10-01_00:00:00", "2022-10-03_01:00:00")); + assert(check_next("0 0 1 1W,15W * ?", "2022-09-01_00:00:00", "2022-09-01_01:00:00")); + assert(check_next("0 0 1 1W,15W * ?", "2022-10-01_00:00:00", "2022-10-03_01:00:00")); + assert(check_next("0 0 1 1W,15W * ?", "2022-09-02_00:00:00", "2022-09-15_01:00:00")); + assert(check_next("0 0 1 1W,15W * ?", "2022-01-01_00:00:00", "2022-01-03_01:00:00")); + assert(check_next("0 0 1 1W,15W * ?", "2022-01-04_00:00:00", "2022-01-14_01:00:00")); + assert(check_next("0 0 1 1W,15W * ?", "2022-01-15_00:00:00", "2022-02-01_01:00:00")); + assert(check_next("0 0 1 8W,26W * ?", "2022-01-06_00:00:00", "2022-01-07_01:00:00")); + assert(check_next("0 0 1 8W,26W * ?", "2022-01-26_00:00:00", "2022-01-26_01:00:00")); + assert(check_next("0 0 1 8W,26W * ?", "2022-02-26_00:00:00", "2022-03-08_01:00:00")); + assert(check_next("0 0 1 8W,26W * ?", "2022-03-09_00:00:00", "2022-03-25_01:00:00")); + assert(check_next("0 0 1 29W * ?", "2022-02-28_00:00:00", "2022-03-29_01:00:00")); + assert(check_next("0 0 1 29W * ?", "2024-02-28_00:00:00", "2024-02-29_01:00:00")); + assert(check_next("0 0 1 31W * ?", "2022-02-28_00:00:00", "2022-03-31_01:00:00")); + assert(check_next("0 0 1 31W * ?", "2022-06-17_00:00:00", "2022-07-29_01:00:00")); + assert(check_next("0 0 1 31W * ?", "2022-07-30_00:00:00", "2022-08-31_01:00:00")); + assert(check_next("0 0 1 26W * ?", "2022-06-27_00:00:00", "2022-06-27_01:00:00")); + assert(check_next("H 0 1 26W * ?", "2022-06-27_00:00:00", "2022-06-27_01:00:00")); + assert(check_next("H 0 1 26W * ?", "2022-06-27_02:00:00", "2022-07-26_01:00:00")); + assert(check_next("H 0 1 HW * ?", "2022-06-27_02:00:00", "2022-07-11_01:00:00")); // 10W + assert(check_next("H 0 1 HW * ?", "2022-05-27_02:00:00", "2022-06-10_01:00:00")); // 10W // L Tests - check_next("0 0 1 LW * ?", "2022-06-22_00:00:00", "2022-06-30_01:00:00"); - check_next("0 0 1 LW * ?", "2022-07-01_00:00:00", "2022-07-29_01:00:00"); - check_next("0 0 1 LW * ?", "2022-07-29_02:00:00", "2022-08-31_01:00:00"); - check_next("0 0 1 LW * ?", "2022-10-01_00:00:00", "2022-10-31_01:00:00"); - check_next("0 0 1 LW * ?", "2022-07-31_00:00:00", "2022-08-31_01:00:00"); - check_next("0 0 1 LW * ?", "2022-07-30_00:00:00", "2022-08-31_01:00:00"); + assert(check_next("0 0 1 LW * ?", "2022-06-22_00:00:00", "2022-06-30_01:00:00")); + assert(check_next("0 0 1 LW * ?", "2022-07-01_00:00:00", "2022-07-29_01:00:00")); + assert(check_next("0 0 1 LW * ?", "2022-07-29_02:00:00", "2022-08-31_01:00:00")); + assert(check_next("0 0 1 LW * ?", "2022-10-01_00:00:00", "2022-10-31_01:00:00")); + assert(check_next("0 0 1 LW * ?", "2022-07-31_00:00:00", "2022-08-31_01:00:00")); + assert(check_next("0 0 1 LW * ?", "2022-07-30_00:00:00", "2022-08-31_01:00:00")); cron_init_hash(7); - check_next("H 0 H LW * ?", "2022-10-01_00:00:00", "2022-10-31_14:00:00"); - check_next("0 0 1 L * ?", "2022-05-12_00:00:00", "2022-05-31_01:00:00"); - check_next("0 0 1 L * ?", "2022-02-12_00:00:00", "2022-02-28_01:00:00"); - check_next("0 0 1 L * ?", "2020-02-12_00:00:00", "2020-02-29_01:00:00"); - check_next("0 0 1 L * ?", "2021-02-12_00:00:00", "2021-02-28_01:00:00"); - check_next("0 0 1 ? * L", "2022-05-12_00:00:00", "2022-05-15_01:00:00"); - check_next("0 0 1 ? * 4L", "2022-05-12_00:00:00", "2022-05-26_01:00:00"); - check_next("0 0 1 ? * 1L", "2022-03-29_00:00:00", "2022-04-25_01:00:00"); - check_next("0 0 1 ? * 5L", "2022-06-25_00:00:00", "2022-07-29_01:00:00"); - check_next("0 0 1 L-2 * ?", "2022-05-12_00:00:00", "2022-05-29_01:00:00"); - check_next("0 0 1 L-3 * ?", "2020-02-12_00:00:00", "2020-02-26_01:00:00"); - check_next("0 0 1 L-30 * ?", "2022-03-01_00:00:00", "2022-03-01_01:00:00"); - check_next("0 0 1 L-30 * ?", "2022-01-02_00:00:00", "2022-02-01_01:00:00"); - check_next("0 0 1 L-31 * ?", "2022-05-12_00:00:00", "2022-06-01_01:00:00"); - check_next("0 0 1 L-32 * ?", "2022-05-12_00:00:00", "2022-06-01_01:00:00"); - check_next("0 0 1 L-31 2 ?", "2022-01-01_00:00:00", "2022-02-01_01:00:00"); + assert(check_next("H 0 H LW * ?", "2022-10-01_00:00:00", "2022-10-31_14:00:00")); + assert(check_next("0 0 1 L * ?", "2022-05-12_00:00:00", "2022-05-31_01:00:00")); + assert(check_next("0 0 1 L * ?", "2022-02-12_00:00:00", "2022-02-28_01:00:00")); + assert(check_next("0 0 1 L * ?", "2020-02-12_00:00:00", "2020-02-29_01:00:00")); + assert(check_next("0 0 1 L * ?", "2021-02-12_00:00:00", "2021-02-28_01:00:00")); + assert(check_next("0 0 1 ? * L", "2022-05-12_00:00:00", "2022-05-15_01:00:00")); + assert(check_next("0 0 1 ? * 4L", "2022-05-12_00:00:00", "2022-05-26_01:00:00")); + assert(check_next("0 0 1 ? * 1L", "2022-03-29_00:00:00", "2022-04-25_01:00:00")); + assert(check_next("0 0 1 ? * 5L", "2022-06-25_00:00:00", "2022-07-29_01:00:00")); + assert(check_next("0 0 1 L-2 * ?", "2022-05-12_00:00:00", "2022-05-29_01:00:00")); + assert(check_next("0 0 1 L-3 * ?", "2020-02-12_00:00:00", "2020-02-26_01:00:00")); + assert(check_next("0 0 1 L-30 * ?", "2022-03-01_00:00:00", "2022-03-01_01:00:00")); + assert(check_next("0 0 1 L-30 * ?", "2022-01-02_00:00:00", "2022-02-01_01:00:00")); + assert(check_next("0 0 1 L-31 * ?", "2022-05-12_00:00:00", "2022-06-01_01:00:00")); + assert(check_next("0 0 1 L-32 * ?", "2022-05-12_00:00:00", "2022-06-01_01:00:00")); + assert(check_next("0 0 1 L-31 2 ?", "2022-01-01_00:00:00", "2022-02-01_01:00:00")); // Tests for unintended month rollovers when going from 31st, see https://github.com/staticlibs/ccronexpr/issues/35 - check_next("0 0 0 ? 11-12 *", "2022-05-31_00:00:00", "2022-11-01_00:00:00"); - check_next("0 0 0 ? 11-12 *", "2022-07-31_00:00:00", "2022-11-01_00:00:00"); - check_next("0 0 0 ? 11-12 *", "2022-08-31_00:00:00", "2022-11-01_00:00:00"); - check_next("0 0 0 ? 11-12 *", "2022-10-31_00:00:00", "2022-11-01_00:00:00"); - check_next("0 0 0 ? 6-7 *", "2022-05-31_00:00:00", "2022-06-01_00:00:00"); - check_next("0 0 0 ? 8-9 *", "2022-07-31_00:00:00", "2022-08-01_00:00:00"); - check_next("0 0 0 ? 9-10 *", "2022-08-31_00:00:00", "2022-09-01_00:00:00"); - - check_next("0 0 0 ? 2-3 *", "2022-01-31_00:00:00", "2022-02-01_00:00:00"); - check_next("0 0 0 ? 4-5 *", "2022-03-31_00:00:00", "2022-04-01_00:00:00"); + assert(check_next("0 0 0 ? 11-12 *", "2022-05-31_00:00:00", "2022-11-01_00:00:00")); + assert(check_next("0 0 0 ? 11-12 *", "2022-07-31_00:00:00", "2022-11-01_00:00:00")); + assert(check_next("0 0 0 ? 11-12 *", "2022-08-31_00:00:00", "2022-11-01_00:00:00")); + assert(check_next("0 0 0 ? 11-12 *", "2022-10-31_00:00:00", "2022-11-01_00:00:00")); + assert(check_next("0 0 0 ? 6-7 *", "2022-05-31_00:00:00", "2022-06-01_00:00:00")); + assert(check_next("0 0 0 ? 8-9 *", "2022-07-31_00:00:00", "2022-08-01_00:00:00")); + assert(check_next("0 0 0 ? 9-10 *", "2022-08-31_00:00:00", "2022-09-01_00:00:00")); + + assert(check_next("0 0 0 ? 2-3 *", "2022-01-31_00:00:00", "2022-02-01_00:00:00")); + assert(check_next("0 0 0 ? 4-5 *", "2022-03-31_00:00:00", "2022-04-01_00:00:00")); // Multiple consecutive days with 'W' flag - check_next("0 0 0 24W * *", "2022-09-22_01:02:03", "2022-09-23_00:00:00"); - check_next("0 0 0 25W * *", "2022-09-24_01:02:03", "2022-09-26_00:00:00"); - check_next("0 0 0 30W * *", "2023-04-24_01:02:03", "2023-04-28_00:00:00"); - check_next("0 0 0 24W,25W * *", "2022-09-22_01:02:03", "2022-09-23_00:00:00"); - check_next("0 0 0 24W,25W * *", "2022-09-24_01:02:03", "2022-09-26_00:00:00"); - check_next("0 0 0 29W,30W * *", "2022-10-24_01:02:03", "2022-10-28_00:00:00"); - check_next("0 0 0 29W,30W * *", "2022-10-28_01:02:03", "2022-10-31_00:00:00"); - check_next("0 0 0 29W,30W * *", "2022-10-29_01:02:03", "2022-10-31_00:00:00"); - check_next("0 0 0 29W,30W * *", "2023-04-27_01:02:03", "2023-04-28_00:00:00"); - check_next("0 0 0 29W,30W * *", "2023-04-29_01:02:03", "2023-05-29_00:00:00"); - check_next("0 0 0 1W,2W * *", "2023-04-01_01:02:03", "2023-04-03_00:00:00"); - check_next("0 0 0 1W,2W * *", "2023-04-02_01:02:03", "2023-04-03_00:00:00"); - check_next("0 0 0 1W,2W * *", "2023-04-03_01:02:03", "2023-05-01_00:00:00"); - check_next("0 0 0 1W,15W,30W * *","2023-04-01_01:02:03", "2023-04-03_00:00:00"); - check_next("0 0 0 1W,15W,30W * *","2023-04-03_01:02:03", "2023-04-14_00:00:00"); - check_next("0 0 0 1W,15,30W * *","2023-04-03_01:02:03", "2023-04-15_00:00:00"); - check_next("0 0 0 1W,15W,30W * *","2023-04-14_01:02:03", "2023-04-28_00:00:00"); - check_next("0 0 0 1W,15,30W * *","2023-04-14_01:02:03", "2023-04-15_00:00:00"); - check_next("0 0 0 1W,15,30W * *","2023-04-15_01:02:03", "2023-04-28_00:00:00"); - check_next("0 0 0 1W,15W,30W * *","2023-04-28_01:02:03", "2023-05-01_00:00:00"); - check_next("0 0 0 1W,8W,15W,30W * *","2023-04-01_01:02:03", "2023-04-03_00:00:00"); - check_next("0 0 0 1W,8W,15W,30W * *","2023-04-03_01:02:03", "2023-04-07_00:00:00"); - check_next("0 0 0 1W,8W,15W,30W * *","2023-04-07_01:02:03", "2023-04-14_00:00:00"); - check_next("0 0 0 1W,8W,15W,30W * *","2023-04-14_01:02:03", "2023-04-28_00:00:00"); - check_next("0 0 0 1W,8W,15W,30W * *","2023-04-28_01:02:03", "2023-05-01_00:00:00"); - check_next("0 0 0 1W,15W,LW * *","2023-04-01_01:02:03", "2023-04-03_00:00:00"); - check_next("0 0 0 1W,15W,LW * *","2023-04-03_01:02:03", "2023-04-14_00:00:00"); - check_next("0 0 0 1W,15W,LW * *","2023-04-14_01:02:03", "2023-04-28_00:00:00"); - check_next("0 0 0 1W,15W,LW * *","2023-04-28_01:02:03", "2023-05-01_00:00:00"); - check_next("0 0 0 1W,15W,LW * *","2023-06-15_01:02:03", "2023-06-30_00:00:00"); - check_next("0 0 0 1W,15W,LW * *","2023-09-15_01:02:03", "2023-09-29_00:00:00"); - check_next("0 0 0 1,1W,15W * *","2021-12-31_01:02:03", "2022-01-01_00:00:00"); - check_next("0 0 0 1,1W,15W * *","2022-01-01_01:02:03", "2022-01-03_00:00:00"); - check_next("0 0 0 1,1W,15W * *","2022-01-03_01:02:03", "2022-01-14_00:00:00"); + assert(check_next("0 0 0 24W * *", "2022-09-22_01:02:03", "2022-09-23_00:00:00")); + assert(check_next("0 0 0 25W * *", "2022-09-24_01:02:03", "2022-09-26_00:00:00")); + assert(check_next("0 0 0 30W * *", "2023-04-24_01:02:03", "2023-04-28_00:00:00")); + assert(check_next("0 0 0 24W,25W * *", "2022-09-22_01:02:03", "2022-09-23_00:00:00")); + assert(check_next("0 0 0 24W,25W * *", "2022-09-24_01:02:03", "2022-09-26_00:00:00")); + assert(check_next("0 0 0 29W,30W * *", "2022-10-24_01:02:03", "2022-10-28_00:00:00")); + assert(check_next("0 0 0 29W,30W * *", "2022-10-28_01:02:03", "2022-10-31_00:00:00")); + assert(check_next("0 0 0 29W,30W * *", "2022-10-29_01:02:03", "2022-10-31_00:00:00")); + assert(check_next("0 0 0 29W,30W * *", "2023-04-27_01:02:03", "2023-04-28_00:00:00")); + assert(check_next("0 0 0 29W,30W * *", "2023-04-29_01:02:03", "2023-05-29_00:00:00")); + assert(check_next("0 0 0 1W,2W * *", "2023-04-01_01:02:03", "2023-04-03_00:00:00")); + assert(check_next("0 0 0 1W,2W * *", "2023-04-02_01:02:03", "2023-04-03_00:00:00")); + assert(check_next("0 0 0 1W,2W * *", "2023-04-03_01:02:03", "2023-05-01_00:00:00")); + assert(check_next("0 0 0 1W,15W,30W * *","2023-04-01_01:02:03", "2023-04-03_00:00:00")); + assert(check_next("0 0 0 1W,15W,30W * *","2023-04-03_01:02:03", "2023-04-14_00:00:00")); + assert(check_next("0 0 0 1W,15,30W * *","2023-04-03_01:02:03", "2023-04-15_00:00:00")); + assert(check_next("0 0 0 1W,15W,30W * *","2023-04-14_01:02:03", "2023-04-28_00:00:00")); + assert(check_next("0 0 0 1W,15,30W * *","2023-04-14_01:02:03", "2023-04-15_00:00:00")); + assert(check_next("0 0 0 1W,15,30W * *","2023-04-15_01:02:03", "2023-04-28_00:00:00")); + assert(check_next("0 0 0 1W,15W,30W * *","2023-04-28_01:02:03", "2023-05-01_00:00:00")); + assert(check_next("0 0 0 1W,8W,15W,30W * *","2023-04-01_01:02:03", "2023-04-03_00:00:00")); + assert(check_next("0 0 0 1W,8W,15W,30W * *","2023-04-03_01:02:03", "2023-04-07_00:00:00")); + assert(check_next("0 0 0 1W,8W,15W,30W * *","2023-04-07_01:02:03", "2023-04-14_00:00:00")); + assert(check_next("0 0 0 1W,8W,15W,30W * *","2023-04-14_01:02:03", "2023-04-28_00:00:00")); + assert(check_next("0 0 0 1W,8W,15W,30W * *","2023-04-28_01:02:03", "2023-05-01_00:00:00")); + assert(check_next("0 0 0 1W,15W,LW * *","2023-04-01_01:02:03", "2023-04-03_00:00:00")); + assert(check_next("0 0 0 1W,15W,LW * *","2023-04-03_01:02:03", "2023-04-14_00:00:00")); + assert(check_next("0 0 0 1W,15W,LW * *","2023-04-14_01:02:03", "2023-04-28_00:00:00")); + assert(check_next("0 0 0 1W,15W,LW * *","2023-04-28_01:02:03", "2023-05-01_00:00:00")); + assert(check_next("0 0 0 1W,15W,LW * *","2023-06-15_01:02:03", "2023-06-30_00:00:00")); + assert(check_next("0 0 0 1W,15W,LW * *","2023-09-15_01:02:03", "2023-09-29_00:00:00")); + assert(check_next("0 0 0 1,1W,15W * *","2021-12-31_01:02:03", "2022-01-01_00:00:00")); + assert(check_next("0 0 0 1,1W,15W * *","2022-01-01_01:02:03", "2022-01-03_00:00:00")); + assert(check_next("0 0 0 1,1W,15W * *","2022-01-03_01:02:03", "2022-01-14_00:00:00")); } void test_parse() { - check_same("* * * 2 * *", "* * * 2 * ?"); - check_same("57,59 * * * * *", "57/2 * * * * *"); - check_same("1,3,5 * * * * *", "1-6/2 * * * * *"); - check_same("* * 4,8,12,16,20 * * *", "* * 4/4 * * *"); - check_same("* * * * * 0-6", "* * * * * TUE,WED,THU,FRI,SAT,SUN,MON"); - check_same("* * * * * 0", "* * * * * SUN"); - check_same("* * * * * 0", "* * * * * 7"); - check_same("* * * * 1-12 *", "* * * * FEB,JAN,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC *"); - check_same("* * * * 2 *", "* * * * Feb *"); - check_same("* * * * 1 *", "* * * * 1 *"); - check_same("* * * * 1 L", "* * * * 1 SUN"); - check_same("* * * * * *", "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19-59,H 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18-59,H 0,1,2,3,4,5,6,7,8,9,10,11-23,H 1,2,3,4,5,6,7,8,9,10,11,12,13,14-31,H jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec,H mon,tue,wed,thu,fri,sat,sun,H"); + assert(check_same("* * * 2 * *", "* * * 2 * ?")); + assert(check_same("57,59 * * * * *", "57/2 * * * * *")); + assert(check_same("1,3,5 * * * * *", "1-6/2 * * * * *")); + assert(check_same("* * 4,8,12,16,20 * * *", "* * 4/4 * * *")); + assert(check_same("* * * * * 0-6", "* * * * * TUE,WED,THU,FRI,SAT,SUN,MON")); + assert(check_same("* * * * * 0", "* * * * * SUN")); + assert(check_same("* * * * * 0", "* * * * * 7")); + assert(check_same("* * * * 1-12 *", "* * * * FEB,JAN,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC *")); + assert(check_same("* * * * 2 *", "* * * * Feb *")); + assert(check_same("* * * * 1 *", "* * * * 1 *")); + assert(check_same("* * * * 1 L", "* * * * 1 SUN")); + assert(check_same("* * * * * *", "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19-59,H 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18-59,H 0,1,2,3,4,5,6,7,8,9,10,11-23,H 1,2,3,4,5,6,7,8,9,10,11,12,13,14-31,H jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec,H mon,tue,wed,thu,fri,sat,sun,H")); + //assert(check_same("0 0 15 1,16,L * *", "0 0 15 1,L,16 * *")); + //assert(check_expr_valid("0 0 15 1,16,L * *")); + //assert(check_expr_valid("0 0 15 1,L,16 * *")); // check default hash func has valid output cron_init_custom_hash_fn(NULL); - check_expr_valid("0 0 1 * * ?"); - check_expr_valid("H H H H H ?"); - check_expr_valid("H(0-59) H H H H ?"); - check_expr_valid("H H H ? H H"); - check_expr_valid("H H H,H ? H H"); - check_expr_valid("H H H/2 ? H H"); - check_expr_valid("H H H(0-12) ? H H"); - check_expr_valid("H H H H(1-17) H ?"); + assert(check_expr_valid("0 0 1 * * ?")); + assert(check_expr_valid("H H H H H ?")); + assert(check_expr_valid("H(0-59) H H H H ?")); + assert(check_expr_valid("H H H ? H H")); + assert(check_expr_valid("H H H,H ? H H")); + assert(check_expr_valid("H H H/2 ? H H")); + assert(check_expr_valid("H H H(0-12) ? H H")); + assert(check_expr_valid("H H H H(1-17) H ?")); + assert(check_expr_valid("H H H H(1-3),H(2-12) H *")); + assert(check_expr_valid("H H H(1-3),H(0-12) H H *")); + assert(check_expr_valid("H,H(0-59) H H H H *")); cron_init_custom_hash_fn(testing_hash_function); - - check_expr_invalid("77 * * * * *"); - check_expr_invalid("44-77 * * * * *"); - check_expr_invalid("* 77 * * * *"); - check_expr_invalid("* 44-77 * * * *"); - check_expr_invalid("* * 27 * * *"); - check_expr_invalid("* * 23-28 * * *"); - check_expr_invalid("* * * 45 * *"); - check_expr_invalid("* * * L-0 * *"); - check_expr_invalid("* * * 28-45 * *"); - check_expr_invalid("0 0 0 25 13 ?"); - check_expr_invalid("0 0 0 25 0 ?"); - check_expr_invalid("0 0 0 32 12 ?"); - check_expr_invalid("* * * * 11-13 *"); - check_expr_invalid("0 0 1 1-3W * ?"); - check_expr_invalid("0 0 1 1/3W * ?"); - check_expr_invalid("0 0 1 1W/3 * ?"); - check_expr_invalid("0 0 1 16WL * ?"); - check_expr_invalid("0 0 1 16LW * ?"); - check_expr_invalid("0 0 1 W3 * ?"); - check_expr_invalid("0 0 1 WL * ?"); - check_expr_invalid("0 0 1 10L * ?"); - check_expr_invalid("0 0 1 L * 3"); - check_expr_invalid("0 0 1 LW * 3"); - check_expr_invalid("0 0 1 9W * 3"); - check_expr_invalid("0 0 1 L-10 * 3"); - check_expr_invalid("0 0 1 L/7 * ?"); - check_expr_invalid("0 0 1 HLW * ?"); - check_expr_invalid("0 0 1 HL/H * ?"); - check_expr_invalid("0 0 1 HL/HW * ?"); - check_expr_invalid("0 0 1 ? * 5L,SUN"); - check_expr_invalid("0 0 1 ? * H/L"); - check_expr_invalid("0 0 1 ? * 19L"); - check_expr_invalid("0 0 1 17 * 5L"); - check_expr_invalid("0 0 1 ? * L-7"); - check_expr_invalid("0 0 1 ? * 5L-7"); - check_expr_invalid("0 0 1 5L-7 * ?"); - check_expr_invalid("0 0 1 L12 * ?"); - check_expr_invalid("0 0 1 L12- * ?"); - check_expr_invalid("0 0 1 L1-4 * ?"); + // Check H can be used in lists + assert(check_expr_valid("H(30-32),H(0-59) H H,H(1-2),H(3-5) H H *")); + assert(check_expr_valid("2,6,H,H,H H(0-9),H(7-15) H(1-3),H(1-3) H H *")); + assert(check_expr_valid("2,6,H,H,H,7 H(0-9),H(7-15) H(1-3),H(1-3) 1,H(2-27),15 H *")); + assert(check_expr_valid("2,6,H,H,H,7 H(0-9),H(7-15) H(1-3),H(1-3) 1,H(2-27),15W H *")); + assert(check_expr_valid("2,6,H,H,H,7 H(0-9),H(7-15) H(1-3),H(1-3) 1,H(2-27),15W,LW H *")); + assert(check_expr_valid("H,H H 12,H,7 ? H H,1,H(3-6)")); + assert(check_expr_valid("H,H H 12,H,7 ? H H,1,H(3-6),THU")); + + assert(check_expr_invalid("77 * * * * *")); + assert(check_expr_invalid("44-77 * * * * *")); + assert(check_expr_invalid("* 77 * * * *")); + assert(check_expr_invalid("* 44-77 * * * *")); + assert(check_expr_invalid("* * 27 * * *")); + assert(check_expr_invalid("* * 23-28 * * *")); + assert(check_expr_invalid("* * * 45 * *")); + assert(check_expr_invalid("* * * L-0 * *")); + assert(check_expr_invalid("* * * 28-45 * *")); + assert(check_expr_invalid("0 0 0 25 13 ?")); + assert(check_expr_invalid("0 0 0 25 0 ?")); + assert(check_expr_invalid("0 0 0 32 12 ?")); + assert(check_expr_invalid("* * * * 11-13 *")); + assert(check_expr_invalid("0 0 1 1-3W * ?")); + assert(check_expr_invalid("0 0 1 1/3W * ?")); + assert(check_expr_invalid("0 0 1 1W/3 * ?")); + assert(check_expr_invalid("0 0 1 16WL * ?")); + assert(check_expr_invalid("0 0 1 16LW * ?")); + assert(check_expr_invalid("0 0 1 W3 * ?")); + assert(check_expr_invalid("0 0 1 WL * ?")); + assert(check_expr_invalid("0 0 1 10L * ?")); + assert(check_expr_invalid("0 0 1 L * 3")); + assert(check_expr_invalid("0 0 1 LW * 3")); + assert(check_expr_invalid("0 0 1 9W * 3")); + assert(check_expr_invalid("0 0 1 L-10 * 3")); + assert(check_expr_invalid("0 0 1 L/7 * ?")); + assert(check_expr_invalid("0 0 1 HLW * ?")); + assert(check_expr_invalid("0 0 1 HL/H * ?")); + assert(check_expr_invalid("0 0 1 HL/HW * ?")); + assert(check_expr_invalid("0 0 1 ? * 5L,SUN")); + assert(check_expr_invalid("0 0 1 ? * H/L")); + assert(check_expr_invalid("0 0 1 ? * 19L")); + assert(check_expr_invalid("0 0 1 17 * 5L")); + assert(check_expr_invalid("0 0 1 ? * L-7")); + assert(check_expr_invalid("0 0 1 ? * 5L-7")); + assert(check_expr_invalid("0 0 1 5L-7 * ?")); + assert(check_expr_invalid("0 0 1 L12 * ?")); + assert(check_expr_invalid("0 0 1 L12- * ?")); + assert(check_expr_invalid("0 0 1 L1-4 * ?")); // H can not be used in ranges - check_expr_invalid("H H-H 1 * * ?" ); // H-H Must be error, H can not be used in ranges - check_expr_invalid("H H-H 1 * * ?"); // H-60 Must be error, H can not be used in ranges - check_expr_invalid("H H-60 1 * * ?"); // H-60 Must be error, H can not be used in ranges - check_expr_invalid("H 1-H 1 * * ?"); // H can not be used in ranges - check_expr_invalid("1-H 0 1 * * ?"); // H can not be used in ranges - check_expr_invalid("1-H 0 1 * * ?"); // H can not be used in ranges - check_expr_invalid("0 0 1-H * * ?"); // H can not be used in ranges - check_expr_invalid("0 0 1 1-H * ?"); // H can not be used in ranges - check_expr_invalid("0 0 1 * 1-H ?"); // H can not be used in ranges - check_expr_invalid("0 0 1 ? * 1-H"); // H can not be used in ranges + assert(check_expr_invalid("H H-H 1 * * ?" )); // H-H Must be error, H can not be used in ranges + assert(check_expr_invalid("H H-H 1 * * ?")); // H-60 Must be error, H can not be used in ranges + assert(check_expr_invalid("H H-60 1 * * ?")); // H-60 Must be error, H can not be used in ranges + assert(check_expr_invalid("H 1-H 1 * * ?")); // H can not be used in ranges + assert(check_expr_invalid("1-H 0 1 * * ?")); // H can not be used in ranges + assert(check_expr_invalid("1-H 0 1 * * ?")); // H can not be used in ranges + assert(check_expr_invalid("0 0 1-H * * ?")); // H can not be used in ranges + assert(check_expr_invalid("0 0 1 1-H * ?")); // H can not be used in ranges + assert(check_expr_invalid("0 0 1 * 1-H ?")); // H can not be used in ranges + assert(check_expr_invalid("0 0 1 ? * 1-H")); // H can not be used in ranges // Invalid iterator values - check_expr_invalid("0/60 * * * * *"); - check_expr_invalid("/12 * * * * *"); - check_expr_invalid("12/ * * * * *"); - check_expr_invalid("12- * * * * *"); - check_expr_invalid("* 0/60 * * * *"); - check_expr_invalid("* * 0/24 * * *"); - check_expr_invalid("* * * 1/32 * *"); - check_expr_invalid("* * * * 1/13 *"); - check_expr_invalid("* * * * * 1/8"); - check_expr_invalid("* * * * * 1/-1"); - check_expr_invalid("H H H */H H *"); - check_expr_invalid("H H H H(0-39) H *"); - check_expr_invalid("H(0-60) H H H H *"); - check_expr_invalid("H,H(0-60) H H H H *"); - check_expr_invalid("H(0-30 H H H H *"); - check_expr_invalid("H(5-69) H H H H *"); - check_expr_invalid("H(11-6) H H H H *"); - check_expr_invalid("H H(17-93) H H H *"); - check_expr_invalid("H H H(0-25) H H *"); - check_expr_invalid("H H H H(0-12) H *"); - check_expr_invalid("H H H H(1-3),H(0-12) H *"); - check_expr_invalid("H H H H H(0-2) *"); - check_expr_invalid("H H H * H H(0-9)"); - check_expr_invalid("H(5-o) H H H H *"); - check_expr_invalid("H(o-10) H H H H *"); - check_expr_invalid("H H H * H(0-8) *"); - check_expr_invalid("H H H * H(-1-8) *"); + assert(check_expr_invalid("0/60 * * * * *")); + assert(check_expr_invalid("/12 * * * * *")); + assert(check_expr_invalid("12/ * * * * *")); + assert(check_expr_invalid("12- * * * * *")); + assert(check_expr_invalid("* 0/60 * * * *")); + assert(check_expr_invalid("* * 0/24 * * *")); + assert(check_expr_invalid("* * * 1/32 * *")); + assert(check_expr_invalid("* * * * 1/13 *")); + assert(check_expr_invalid("* * * * * 1/8")); + assert(check_expr_invalid("* * * * * 1/-1")); + assert(check_expr_invalid("H H H */H H *")); + assert(check_expr_invalid("H H H H(0-39) H *")); + assert(check_expr_invalid("H(0-60) H H H H *")); + assert(check_expr_invalid("H(0-30 H H H H *")); + assert(check_expr_invalid("H(5-69) H H H H *")); + assert(check_expr_invalid("H(11-6) H H H H *")); + assert(check_expr_invalid("H H(17-93) H H H *")); + assert(check_expr_invalid("H H H(0-25) H H *")); + assert(check_expr_invalid("H H H H(0-12) H *")); + assert(check_expr_invalid("H H H H H(0-2) *")); + assert(check_expr_invalid("H H H * H H(0-9)")); + assert(check_expr_invalid("H(5-o) H H H H *")); + assert(check_expr_invalid("H(o-10) H H H H *")); + assert(check_expr_invalid("H H H * H(0-8) *")); + assert(check_expr_invalid("H H H * H(-1-8) *")); + assert(check_expr_invalid("0 0\\ 0 * * *")); // no "escaping" + assert(check_expr_invalid("0 0 \\ 0 * * *")); // no "escaping" } void test_bits() {