test: started porting cron tests

main
Jef Roosens 2023-01-18 13:53:50 +01:00
parent c018d8d86c
commit 30e086ad6b
6 changed files with 97 additions and 119 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
build/
compile_commands.json
.cache/

View File

@ -60,11 +60,11 @@ $(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c
# =====MAINTENANCE=====
.PHONY: lint
lint:
clang-format -n --Werror $(SRCS) $(SRCS_H) $(SRCS_TEST)
clang-format -n --Werror $(SRCS) $(SRCS_H)
.PHONY: fmt
fmt:
clang-format -i $(SRCS) $(SRCS_H) $(SRCS_TEST)
clang-format -i $(SRCS) $(SRCS_H)
.PHONY: clean
clean:

View File

@ -7,16 +7,16 @@
#include <string.h>
#include <time.h>
typedef enum cron_parse_error {
cron_parse_ok = 0,
cron_parse_invalid_expression = 1,
cron_parse_invalid_number = 2,
cron_parse_out_of_range = 3,
cron_parse_too_many_parts = 4,
cron_parse_not_enough_parts = 5
} cron_parse_error;
typedef enum vieter_cron_parse_error {
vieter_cron_parse_ok = 0,
vieter_cron_parse_invalid_expression = 1,
vieter_cron_parse_invalid_number = 2,
vieter_cron_parse_out_of_range = 3,
vieter_cron_parse_too_many_parts = 4,
vieter_cron_parse_not_enough_parts = 5
} vieter_cron_parse_error;
typedef struct cron_expression {
typedef struct vieter_cron_expression {
uint8_t *minutes;
uint8_t *hours;
uint8_t *days;
@ -25,26 +25,29 @@ typedef struct cron_expression {
uint8_t hour_count;
uint8_t day_count;
uint8_t month_count;
} cron_expression;
} vieter_cron_expression;
typedef struct cron_simple_time {
typedef struct vieter_cron_simple_time {
int year;
int month;
int day;
int hour;
int minute;
} cron_simple_time;
} vieter_cron_simple_time;
cron_expression *ce_init();
vieter_cron_expression *ce_init();
void cron_ce_free(cron_expression *ce);
void vieter_cron_ce_free(vieter_cron_expression *ce);
void cron_ce_next(cron_simple_time *out, cron_expression *ce,
cron_simple_time *ref);
void vieter_cron_ce_next(vieter_cron_simple_time *out,
vieter_cron_expression *ce,
vieter_cron_simple_time *ref);
void cron_ce_next_from_now(cron_simple_time *out, cron_expression *ce);
void vieter_cron_ce_next_from_now(vieter_cron_simple_time *out,
vieter_cron_expression *ce);
enum cron_parse_error cron_ce_parse_expression(cron_expression *out,
enum vieter_cron_parse_error
vieter_cron_parse_expression(vieter_cron_expression *out,
const char *expression);
#endif

View File

@ -3,9 +3,11 @@
const uint8_t month_days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
cron_expression *ce_init() { return malloc(sizeof(cron_expression)); }
vieter_cron_expression *vieter_cron_expression_init() {
return malloc(sizeof(vieter_cron_expression));
}
void ce_free(cron_expression *ce) {
void ce_free(vieter_cron_expression *ce) {
free(ce->months);
free(ce->days);
free(ce->hours);
@ -13,8 +15,8 @@ void ce_free(cron_expression *ce) {
free(ce);
}
void ce_next(cron_simple_time *out, cron_expression *ce,
cron_simple_time *ref) {
void vieter_cron_next(vieter_cron_simple_time *out, vieter_cron_expression *ce,
vieter_cron_simple_time *ref) {
// For all of these values, the rule is the following: if their value is
// the length of their respective array in the CronExpression object, that
// means we've looped back around. This means that the "bigger" value has
@ -98,12 +100,13 @@ void ce_next(cron_simple_time *out, cron_expression *ce,
}
}
void ce_next_from_now(cron_simple_time *out, cron_expression *ce) {
void vieter_cron_next_from_now(vieter_cron_simple_time *out,
vieter_cron_expression *ce) {
time_t t = time(NULL);
struct tm gm;
gmtime_r(&t, &gm);
cron_simple_time ref = {// tm_year contains years since 1900
vieter_cron_simple_time ref = {// tm_year contains years since 1900
.year = 1900 + gm.tm_year,
// tm_mon goes from 0 to 11
.month = gm.tm_mon + 1,
@ -111,5 +114,5 @@ void ce_next_from_now(cron_simple_time *out, cron_expression *ce) {
.hour = gm.tm_hour,
.minute = gm.tm_min};
ce_next(out, ce, &ref);
vieter_cron_next(out, ce, &ref);
}

View File

@ -16,10 +16,10 @@ const uint8_t max_parts = 4;
#define SAFE_ATOI(v, s, min, max) \
int _##v = atoi(s); \
if ((_##v) == 0 && strcmp((s), "0") != 0) { \
return cron_parse_invalid_number; \
return vieter_cron_parse_invalid_number; \
} \
if (((_##v) < (min)) || ((_##v) > (max))) { \
return cron_parse_out_of_range; \
return vieter_cron_parse_out_of_range; \
} \
v = (uint8_t)(_##v);
@ -41,7 +41,8 @@ const uint8_t max_parts = 4;
* - a/c
* - a-b/c
*/
cron_parse_error ce_parse_range(uint64_t *out, char *s, uint8_t min,
vieter_cron_parse_error vieter_cron_expression_parse_range(uint64_t *out,
char *s, uint8_t min,
uint8_t max) {
size_t slash_index = 0, dash_index = 0;
size_t s_index = 0;
@ -54,12 +55,12 @@ cron_parse_error ce_parse_range(uint64_t *out, char *s, uint8_t min,
(cur_char >= '0' && cur_char <= '9');
if (!is_valid_character) {
return cron_parse_invalid_expression;
return vieter_cron_parse_invalid_expression;
}
if (cur_char == '/') {
if (s_index == 0 || slash_index != 0) {
return cron_parse_invalid_expression;
return vieter_cron_parse_invalid_expression;
}
slash_index = s_index;
@ -68,7 +69,7 @@ cron_parse_error ce_parse_range(uint64_t *out, char *s, uint8_t min,
} else if (cur_char == '-') {
// At most one dash is allowed, and it must be before the slash
if (s_index == 0 || dash_index != 0 || slash_index != 0) {
return cron_parse_invalid_expression;
return vieter_cron_parse_invalid_expression;
}
dash_index = s_index;
@ -85,7 +86,7 @@ cron_parse_error ce_parse_range(uint64_t *out, char *s, uint8_t min,
if (s[0] == '*') {
if (s[1] != '\0' || dash_index != 0) {
return cron_parse_invalid_expression;
return vieter_cron_parse_invalid_expression;
}
start = min;
@ -112,7 +113,7 @@ cron_parse_error ce_parse_range(uint64_t *out, char *s, uint8_t min,
}
}
return cron_parse_ok;
return vieter_cron_parse_ok;
}
/*
@ -120,19 +121,19 @@ cron_parse_error ce_parse_range(uint64_t *out, char *s, uint8_t min,
* min-max range the part represents. A part consists of one or more range
* expressions, separated by commas.
*/
cron_parse_error ce_parse_part(uint64_t *out, char *s, uint8_t min,
vieter_cron_parse_error ce_parse_part(uint64_t *out, char *s, uint8_t min,
uint8_t max) {
*out = 0;
char *next;
cron_parse_error res;
vieter_cron_parse_error res;
while ((next = strchr(s, ',')) != NULL) {
next[0] = '\0';
res = ce_parse_range(out, s, min, max);
res = vieter_cron_expression_parse_range(out, s, min, max);
if (res != cron_parse_ok) {
if (res != vieter_cron_parse_ok) {
return res;
}
@ -140,7 +141,7 @@ cron_parse_error ce_parse_part(uint64_t *out, char *s, uint8_t min,
}
// Make sure to parse the final range as well
return ce_parse_range(out, s, min, max);
return vieter_cron_expression_parse_range(out, s, min, max);
}
/*
@ -196,13 +197,14 @@ uint8_t bf_to_nums(uint8_t **out, uint64_t bf, uint8_t min, uint8_t max) {
/*
* Parse a cron expression string into a cron_expression struct.
*/
cron_parse_error ce_parse_expression(cron_expression *out,
vieter_cron_parse_error
vieter_cron_parse_expression(vieter_cron_expression *out,
const char *expression) {
// The parsing functions modify the input string in-place
char *s = strdup(expression);
char *orig_s = s;
cron_parse_error res = cron_parse_ok;
vieter_cron_parse_error res = vieter_cron_parse_ok;
// First we divide the input string into its parts, divided by spaces.
// Each part is delimited by a NULL byte.
@ -237,7 +239,7 @@ cron_parse_error ce_parse_expression(cron_expression *out,
// s[0] isn't '\0', there's still another part before the end of the string.
if (s[0] != '\0') {
if (part_count == max_parts) {
res = cron_parse_too_many_parts;
res = vieter_cron_parse_too_many_parts;
goto end;
}
@ -246,7 +248,7 @@ cron_parse_error ce_parse_expression(cron_expression *out,
}
if (part_count < min_parts) {
res = cron_parse_not_enough_parts;
res = vieter_cron_parse_not_enough_parts;
goto end;
}
@ -259,7 +261,7 @@ cron_parse_error ce_parse_expression(cron_expression *out,
if (part_count >= 4) {
res = ce_parse_part(&bit_field, parts[3], min[3], max[3]);
if (res != cron_parse_ok) {
if (res != vieter_cron_parse_ok) {
goto end;
}
@ -283,7 +285,7 @@ cron_parse_error ce_parse_expression(cron_expression *out,
res = ce_parse_part(&bit_field, parts[2], min[2], max_day_value);
if (res != cron_parse_ok) {
if (res != vieter_cron_parse_ok) {
free(out->months);
goto end;
@ -301,7 +303,7 @@ cron_parse_error ce_parse_expression(cron_expression *out,
res = ce_parse_part(&bit_field, parts[1], min[1], max[1]);
if (res != cron_parse_ok) {
if (res != vieter_cron_parse_ok) {
free(out->months);
free(out->days);
@ -315,7 +317,7 @@ cron_parse_error ce_parse_expression(cron_expression *out,
res = ce_parse_part(&bit_field, parts[0], min[0], max[0]);
if (res != cron_parse_ok) {
if (res != vieter_cron_parse_ok) {
free(out->months);
free(out->days);
free(out->hours);

View File

@ -1,69 +1,38 @@
#include "acutest.h"
#include "vieter_cron.h"
void test_tutorial(void) {
void *mem;
void test_not_allowed() {
char *expressions[] = {
"4 *-7",
"4 *-7/4",
"4 7/*",
"0 0 30 2",
"0 /5",
"0 ",
"0",
" 0",
" 0 ",
"1 2 3 4~9",
"1 1-3-5",
"0 5/2-5",
"",
"1 1/2/3",
"*5 8",
"x 8",
NULL
};
mem = malloc(10);
TEST_CHECK(mem != NULL);
int i = 0;
mem = realloc(mem, 20);
TEST_CHECK(mem != NULL);
while (expressions[i] != NULL) {
vieter_cron_expression out;
TEST_CHECK_(vieter_cron_parse_expression(&out, expressions[i]) != vieter_cron_parse_ok, "%s should error", expressions[i]);
free(mem);
i++;
}
}
void test_fail(void) {
int a, b;
/* This condition is designed to fail so you can see what the failed test
* output looks like. */
a = 1;
b = 2;
TEST_CHECK(a + b == 5);
/* Here is TEST_CHECK_ in action. */
TEST_CHECK_(a + b == 5, "%d + %d == 5", a, b);
/* We may also show more information about the failure. */
if (!TEST_CHECK(a + b == 5)) {
TEST_MSG("a: %d", a);
TEST_MSG("b: %d", b);
}
/* The macro TEST_MSG() only outputs something when the preceding
* condition fails, so we can avoid the 'if' statement. */
TEST_CHECK(a + b == 3);
TEST_MSG("a: %d", a);
TEST_MSG("b: %d", b);
}
static void helper(void) {
/* Kill the current test with a condition which is never true. */
TEST_ASSERT(1 == 2);
/* This never happens because the test is aborted above. */
TEST_CHECK(1 + 2 == 2 + 1);
}
void test_abort(void) {
helper();
/* This test never happens because the test is aborted inside the helper()
* function. */
TEST_CHECK(1 * 2 == 2 * 1);
}
void test_crash(void) {
int *invalid = ((int *)NULL) + 0xdeadbeef;
*invalid = 42;
TEST_CHECK_(1 == 1, "This should never execute, due to a write into "
"an invalid address.");
}
TEST_LIST = {{"tutorial", test_tutorial},
{"fail", test_fail},
{"abort", test_abort},
{"crash", test_crash},
{NULL, NULL}};
TEST_LIST = {
{"not_allowed", test_not_allowed},
{NULL, NULL}
};