test: started porting cron tests
parent
c018d8d86c
commit
30e086ad6b
|
@ -1,2 +1,3 @@
|
|||
build/
|
||||
compile_commands.json
|
||||
.cache/
|
||||
|
|
4
Makefile
4
Makefile
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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);
|
||||
i++;
|
||||
}
|
||||
|
||||
/* 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}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue