Updating testing to use fixtures

This commit is contained in:
Preston Carman 2024-11-08 11:42:13 -08:00
parent b5d4b576d1
commit 4e7e1f43f5
2 changed files with 148 additions and 129 deletions

View File

@ -19,20 +19,45 @@ extern int yylineno;
UTEST_MAIN(); UTEST_MAIN();
void read_file(const char *filename, char *expected_output) { char* read_file_dynamic(const char* filename) {
// Read the expected output from a file FILE* file = fopen(filename, "r");
FILE *expected_file = fopen(filename, "r"); if (file == NULL) {
if (expected_file == NULL) { perror("fopen");
perror("fopen"); return NULL;
exit(EXIT_FAILURE); }
}
size_t n = // Seek to the end of the file to determine its size
fread(expected_output, 1, sizeof(expected_output) - 1, expected_file); fseek(file, 0, SEEK_END);
expected_output[n] = '\0'; long file_size = ftell(file);
fclose(expected_file); fseek(file, 0, SEEK_SET);
// Allocate a buffer to hold the file contents
char* buffer = (char*)malloc(file_size + 1);
if (buffer == NULL) {
perror("malloc");
fclose(file);
return NULL;
}
// Read the entire file into the buffer
size_t read_size = fread(buffer, 1, file_size, file);
if (read_size != file_size) {
perror("fread");
free(buffer);
fclose(file);
return NULL;
}
// Null-terminate the buffer
buffer[file_size] = '\0';
fclose(file);
return buffer;
} }
void redirect_stdout(const char *filename, int evalutate) { void redirect_stdout(const char *filename, int evalutate) {
fflush(stdout);
// Redirect stdout to a temporary file // Redirect stdout to a temporary file
FILE *temp_file = fopen(filename, "w"); FILE *temp_file = fopen(filename, "w");
if (temp_file == NULL) { if (temp_file == NULL) {
@ -56,42 +81,18 @@ void redirect_stdout(const char *filename, int evalutate) {
close(stdout_fd); close(stdout_fd);
} }
UTEST(interpreter, print) { struct InterpreterTestFile {
const char *evaluated_file;
const char *print_file;
const char *test_file;
};
yyin = fopen("samples/multiple_statements.c", "r"); UTEST_F_SETUP(InterpreterTestFile) {
yyrestart(yyin);
ASSERT_TRUE(yyin);
// yylineno = 1;
yylineno = 1;
int result = yyparse();
if (result == 0) {
// Catch the standard output and compare with expected test result
redirect_stdout("test_print.txt", 0);
redirect_stdout("test_evaluate.txt", 1);
}
// Assert the result to test correctness
ASSERT_EQ(result, 0);
char actual_print[1024];
read_file("test_print.txt", actual_print);
char expected_print[1024];
read_file("samples/multiple_statements_print.txt", expected_print);
ASSERT_STREQ(actual_print, expected_print);
char actual_evaluate[1024];
read_file("test_evaluate.txt", actual_evaluate);
char expected_evaluate[1024];
read_file("samples/multiple_statements_evaluate.txt", expected_evaluate);
ASSERT_STREQ(actual_evaluate, expected_evaluate);
} }
UTEST_F_TEARDOWN(InterpreterTestFile) {
// and also assert and expect in teardown!
UTEST(interpreter, program) { yyin = fopen(utest_fixture->test_file, "r");
yyin = fopen("samples/program.c", "r");
yyrestart(yyin); yyrestart(yyin);
ASSERT_TRUE(yyin); ASSERT_TRUE(yyin);
@ -107,55 +108,23 @@ UTEST(interpreter, program) {
// Assert the result to test correctness // Assert the result to test correctness
ASSERT_EQ(result, 0); ASSERT_EQ(result, 0);
char actual_print[1024]; char *actual_print = read_file_dynamic("test_print.txt");
read_file("test_print.txt", actual_print); char *expected_print = read_file_dynamic(utest_fixture->print_file);
char expected_print[1024];
read_file("samples/program_print.txt", expected_print);
ASSERT_STREQ(actual_print, expected_print); ASSERT_STREQ(actual_print, expected_print);
char actual_evaluate[1024]; char *actual_evaluate = read_file_dynamic("test_evaluate.txt");
read_file("test_evaluate.txt", actual_evaluate); char *expected_evaluate = read_file_dynamic(utest_fixture->evaluated_file);
char expected_evaluate[1024];
read_file("samples/program_evaluate.txt", expected_evaluate);
ASSERT_STREQ(actual_evaluate, expected_evaluate); ASSERT_STREQ(actual_evaluate, expected_evaluate);
} }
// UTEST(parser, missing_new_line) { UTEST_F(InterpreterTestFile, print) {
// // Must include the null character to terminate input utest_fixture->test_file = "samples/multiple_statements.c";
// char string[] = "1+8/4-3\0"; utest_fixture->print_file = "samples/multiple_statements_print.txt";
// YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string)); utest_fixture->evaluated_file = "samples/multiple_statements_evaluate.txt";
}
// yylineno = 1; UTEST_F(InterpreterTestFile, program_file) {
// int result = yyparse(); utest_fixture->test_file = "samples/program.c";
utest_fixture->print_file = "samples/program_print.txt";
// yy_delete_buffer(buffer); utest_fixture->evaluated_file = "samples/program_evaluate.txt";
}
// // Assert the result to test correctness
// ASSERT_EQ(result, 1);
// }
// UTEST(parser, hello_world) {
// // Read sample file as input
// yyin = fopen("samples/hello.py", "r");
// yyrestart(yyin);
// ASSERT_TRUE(yyin);
// yylineno = 1;
// int result = yyparse();
// // Assert the result to test correctness
// ASSERT_EQ(result, 0);
// }
// UTEST(parser, quadratic) {
// // Read sample file as input
// yyin = fopen("samples/quadratic.py", "r");
// yyrestart(yyin);
// ASSERT_TRUE(yyin);
// yylineno = 1;
// int result = yyparse();
// // Assert the result to test correctness
// ASSERT_EQ(result, 0);
// }

View File

@ -255,8 +255,8 @@ UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceFrequency(
static void __cdecl f(void); \ static void __cdecl f(void); \
UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \
__pragma(comment(linker, "/include:" UTEST_SYMBOL_PREFIX #f "_")) \ __pragma(comment(linker, "/include:" UTEST_SYMBOL_PREFIX #f "_")) \
UTEST_C_FUNC __declspec(allocate(".CRT$XCU")) void(__cdecl * \ UTEST_C_FUNC \
f##_)(void) = f; \ __declspec(allocate(".CRT$XCU")) void(__cdecl * f##_)(void) = f; \
UTEST_INITIALIZER_END_DISABLE_WARNINGS \ UTEST_INITIALIZER_END_DISABLE_WARNINGS \
static void __cdecl f(void) static void __cdecl f(void)
#else #else
@ -321,22 +321,32 @@ static UTEST_INLINE void *utest_realloc(void *const pointer, size_t new_size) {
void *const new_pointer = realloc(pointer, new_size); void *const new_pointer = realloc(pointer, new_size);
if (UTEST_NULL == new_pointer) { if (UTEST_NULL == new_pointer) {
free(new_pointer); free(pointer);
} }
return new_pointer; return new_pointer;
} }
// Prevent 64-bit integer overflow when computing a timestamp by using a trick
// from Sokol:
// https://github.com/floooh/sokol/blob/189843bf4f86969ca4cc4b6d94e793a37c5128a7/sokol_time.h#L204
static UTEST_INLINE utest_int64_t utest_mul_div(const utest_int64_t value,
const utest_int64_t numer,
const utest_int64_t denom) {
const utest_int64_t q = value / denom;
const utest_int64_t r = value % denom;
return q * numer + r * numer / denom;
}
static UTEST_INLINE utest_int64_t utest_ns(void) { static UTEST_INLINE utest_int64_t utest_ns(void) {
#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) #if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__)
utest_large_integer counter; utest_large_integer counter;
utest_large_integer frequency; utest_large_integer frequency;
QueryPerformanceCounter(&counter); QueryPerformanceCounter(&counter);
QueryPerformanceFrequency(&frequency); QueryPerformanceFrequency(&frequency);
return UTEST_CAST(utest_int64_t, return utest_mul_div(counter.QuadPart, 1000000000, frequency.QuadPart);
(counter.QuadPart * 1000000000) / frequency.QuadPart);
#elif defined(__linux__) && defined(__STRICT_ANSI__) #elif defined(__linux__) && defined(__STRICT_ANSI__)
return UTEST_CAST(utest_int64_t, clock()) * 1000000000 / CLOCKS_PER_SEC; return utest_mul_div(clock(), 1000000000, CLOCKS_PER_SEC);
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ #elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \
defined(__HAIKU__) defined(__HAIKU__)
@ -447,6 +457,15 @@ struct utest_type_deducer final {
static void _(const T t); static void _(const T t);
}; };
template <> struct utest_type_deducer<char, false> {
static void _(const char c) {
if (std::is_signed<decltype(c)>::value) {
UTEST_PRINTF("%d", static_cast<int>(c));
} else {
UTEST_PRINTF("%u", static_cast<unsigned int>(c));
}
}
};
template <> struct utest_type_deducer<signed char, false> { template <> struct utest_type_deducer<signed char, false> {
static void _(const signed char c) { static void _(const signed char c) {
UTEST_PRINTF("%d", static_cast<int>(c)); UTEST_PRINTF("%d", static_cast<int>(c));
@ -512,6 +531,10 @@ template <> struct utest_type_deducer<unsigned long long, false> {
static void _(const unsigned long long i) { UTEST_PRINTF("%llu", i); } static void _(const unsigned long long i) { UTEST_PRINTF("%llu", i); }
}; };
template <> struct utest_type_deducer<bool, false> {
static void _(const bool i) { UTEST_PRINTF(i ? "true" : "false"); }
};
template <typename T> struct utest_type_deducer<const T *, false> { template <typename T> struct utest_type_deducer<const T *, false> {
static void _(const T *t) { static void _(const T *t) {
UTEST_PRINTF("%p", static_cast<void *>(const_cast<T *>(t))); UTEST_PRINTF("%p", static_cast<void *>(const_cast<T *>(t)));
@ -528,6 +551,12 @@ template <typename T> struct utest_type_deducer<T, true> {
} }
}; };
template <> struct utest_type_deducer<std::nullptr_t, false> {
static void _(std::nullptr_t t) {
UTEST_PRINTF("%p", static_cast<void *>(t));
}
};
template <typename T> template <typename T>
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const T t) { UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const T t) {
utest_type_deducer<T>::_(t); utest_type_deducer<T>::_(t);
@ -626,24 +655,23 @@ utest_type_printer(long long unsigned int i) {
!(defined(__MINGW32__) || defined(__MINGW64__)) || \ !(defined(__MINGW32__) || defined(__MINGW64__)) || \
defined(__TINYC__) defined(__TINYC__)
#define utest_type_printer(val) \ #define utest_type_printer(val) \
UTEST_PRINTF(_Generic((val), signed char \ UTEST_PRINTF( \
: "%d", unsigned char \ _Generic((val), \
: "%u", short \ signed char: "%d", \
: "%d", unsigned short \ unsigned char: "%u", \
: "%u", int \ short: "%d", \
: "%d", long \ unsigned short: "%u", \
: "%ld", long long \ int: "%d", \
: "%lld", unsigned \ long: "%ld", \
: "%u", unsigned long \ long long: "%lld", \
: "%lu", unsigned long long \ unsigned: "%u", \
: "%llu", float \ unsigned long: "%lu", \
: "%f", double \ unsigned long long: "%llu", \
: "%f", long double \ float: "%f", \
: "%Lf", default \ double: "%f", \
: _Generic((val - val), ptrdiff_t \ long double: "%Lf", \
: "%p", default \ default: _Generic((val - val), ptrdiff_t: "%p", default: "undef")), \
: "undef")), \ (val))
(val))
#else #else
/* /*
we don't have the ability to print the values we got, so we create a macro we don't have the ability to print the values we got, so we create a macro
@ -724,10 +752,12 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) {
UTEST_AUTO(x) xEval = (x); \ UTEST_AUTO(x) xEval = (x); \
UTEST_AUTO(y) yEval = (y); \ UTEST_AUTO(y) yEval = (y); \
if (!((xEval)cond(yEval))) { \ if (!((xEval)cond(yEval))) { \
const char *const xAsString = #x; \
const char *const yAsString = #y; \
_Pragma("clang diagnostic pop") \ _Pragma("clang diagnostic pop") \
UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \
UTEST_PRINTF(" Expected : ("); \ UTEST_PRINTF(" Expected : ("); \
UTEST_PRINTF(#x ") " #cond " (" #y); \ UTEST_PRINTF("%s) " #cond " (%s", xAsString, yAsString); \
UTEST_PRINTF(")\n"); \ UTEST_PRINTF(")\n"); \
UTEST_PRINTF(" Actual : "); \ UTEST_PRINTF(" Actual : "); \
utest_type_printer(xEval); \ utest_type_printer(xEval); \
@ -751,9 +781,11 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) {
UTEST_AUTO(x) xEval = (x); \ UTEST_AUTO(x) xEval = (x); \
UTEST_AUTO(y) yEval = (y); \ UTEST_AUTO(y) yEval = (y); \
if (!((xEval)cond(yEval))) { \ if (!((xEval)cond(yEval))) { \
const char *const xAsString = #x; \
const char *const yAsString = #y; \
UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \
UTEST_PRINTF(" Expected : ("); \ UTEST_PRINTF(" Expected : ("); \
UTEST_PRINTF(#x ") " #cond " (" #y); \ UTEST_PRINTF("%s) " #cond " (%s", xAsString, yAsString); \
UTEST_PRINTF(")\n"); \ UTEST_PRINTF(")\n"); \
UTEST_PRINTF(" Actual : "); \ UTEST_PRINTF(" Actual : "); \
utest_type_printer(xEval); \ utest_type_printer(xEval); \
@ -1130,7 +1162,7 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) {
} \ } \
UTEST_INITIALIZER(utest_register_##SET##_##NAME) { \ UTEST_INITIALIZER(utest_register_##SET##_##NAME) { \
const size_t index = utest_state.tests_length++; \ const size_t index = utest_state.tests_length++; \
const char *name_part = #SET "." #NAME; \ const char name_part[] = #SET "." #NAME; \
const size_t name_size = strlen(name_part) + 1; \ const size_t name_size = strlen(name_part) + 1; \
char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \
utest_state.tests = UTEST_PTR_CAST( \ utest_state.tests = UTEST_PTR_CAST( \
@ -1138,13 +1170,19 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) {
utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \
sizeof(struct utest_test_state_s) * \ sizeof(struct utest_test_state_s) * \
utest_state.tests_length)); \ utest_state.tests_length)); \
if (utest_state.tests) { \ if (utest_state.tests && name) { \
utest_state.tests[index].func = &utest_##SET##_##NAME; \ utest_state.tests[index].func = &utest_##SET##_##NAME; \
utest_state.tests[index].name = name; \ utest_state.tests[index].name = name; \
utest_state.tests[index].index = 0; \ utest_state.tests[index].index = 0; \
UTEST_SNPRINTF(name, name_size, "%s", name_part); \ UTEST_SNPRINTF(name, name_size, "%s", name_part); \
} else if (name) { \ } else { \
free(name); \ if (utest_state.tests) { \
free(utest_state.tests); \
utest_state.tests = NULL; \
} \
if (name) { \
free(name); \
} \
} \ } \
} \ } \
UTEST_SURPRESS_WARNINGS_END \ UTEST_SURPRESS_WARNINGS_END \
@ -1178,7 +1216,7 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) {
} \ } \
UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME) { \ UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME) { \
const size_t index = utest_state.tests_length++; \ const size_t index = utest_state.tests_length++; \
const char *name_part = #FIXTURE "." #NAME; \ const char name_part[] = #FIXTURE "." #NAME; \
const size_t name_size = strlen(name_part) + 1; \ const size_t name_size = strlen(name_part) + 1; \
char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \
utest_state.tests = UTEST_PTR_CAST( \ utest_state.tests = UTEST_PTR_CAST( \
@ -1186,12 +1224,18 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) {
utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \
sizeof(struct utest_test_state_s) * \ sizeof(struct utest_test_state_s) * \
utest_state.tests_length)); \ utest_state.tests_length)); \
if (utest_state.tests) { \ if (utest_state.tests && name) { \
utest_state.tests[index].func = &utest_f_##FIXTURE##_##NAME; \ utest_state.tests[index].func = &utest_f_##FIXTURE##_##NAME; \
utest_state.tests[index].name = name; \ utest_state.tests[index].name = name; \
UTEST_SNPRINTF(name, name_size, "%s", name_part); \ UTEST_SNPRINTF(name, name_size, "%s", name_part); \
} else if (name) { \ } else { \
free(name); \ if (utest_state.tests) { \
free(utest_state.tests); \
utest_state.tests = NULL; \
} \
if (name) { \
free(name); \
} \
} \ } \
} \ } \
UTEST_SURPRESS_WARNINGS_END \ UTEST_SURPRESS_WARNINGS_END \
@ -1226,7 +1270,7 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) {
utest_uint64_t iUp; \ utest_uint64_t iUp; \
for (i = 0; i < (INDEX); i++) { \ for (i = 0; i < (INDEX); i++) { \
const size_t index = utest_state.tests_length++; \ const size_t index = utest_state.tests_length++; \
const char *name_part = #FIXTURE "." #NAME; \ const char name_part[] = #FIXTURE "." #NAME; \
const size_t name_size = strlen(name_part) + 32; \ const size_t name_size = strlen(name_part) + 32; \
char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \
utest_state.tests = UTEST_PTR_CAST( \ utest_state.tests = UTEST_PTR_CAST( \
@ -1234,14 +1278,20 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) {
utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \
sizeof(struct utest_test_state_s) * \ sizeof(struct utest_test_state_s) * \
utest_state.tests_length)); \ utest_state.tests_length)); \
if (utest_state.tests) { \ if (utest_state.tests && name) { \
utest_state.tests[index].func = &utest_i_##FIXTURE##_##NAME##_##INDEX; \ utest_state.tests[index].func = &utest_i_##FIXTURE##_##NAME##_##INDEX; \
utest_state.tests[index].index = i; \ utest_state.tests[index].index = i; \
utest_state.tests[index].name = name; \ utest_state.tests[index].name = name; \
iUp = UTEST_CAST(utest_uint64_t, i); \ iUp = UTEST_CAST(utest_uint64_t, i); \
UTEST_SNPRINTF(name, name_size, "%s/%" UTEST_PRIu64, name_part, iUp); \ UTEST_SNPRINTF(name, name_size, "%s/%" UTEST_PRIu64, name_part, iUp); \
} else if (name) { \ } else { \
free(name); \ if (utest_state.tests) { \
free(utest_state.tests); \
utest_state.tests = NULL; \
} \
if (name) { \
free(name); \
} \
} \ } \
} \ } \
} \ } \