From 4e7e1f43f5e17ad4425b3bd907505419d37d093d Mon Sep 17 00:00:00 2001 From: Preston Carman Date: Fri, 8 Nov 2024 11:42:13 -0800 Subject: [PATCH] Updating testing to use fixtures --- lab-5/main_test.c | 149 ++++++++++++++++++---------------------------- lab-5/utest.h | 128 +++++++++++++++++++++++++++------------ 2 files changed, 148 insertions(+), 129 deletions(-) diff --git a/lab-5/main_test.c b/lab-5/main_test.c index d40e918..4b1ec9f 100644 --- a/lab-5/main_test.c +++ b/lab-5/main_test.c @@ -19,20 +19,45 @@ extern int yylineno; UTEST_MAIN(); -void read_file(const char *filename, char *expected_output) { - // Read the expected output from a file - FILE *expected_file = fopen(filename, "r"); - if (expected_file == NULL) { - perror("fopen"); - exit(EXIT_FAILURE); - } - size_t n = - fread(expected_output, 1, sizeof(expected_output) - 1, expected_file); - expected_output[n] = '\0'; - fclose(expected_file); +char* read_file_dynamic(const char* filename) { + FILE* file = fopen(filename, "r"); + if (file == NULL) { + perror("fopen"); + return NULL; + } + + // Seek to the end of the file to determine its size + fseek(file, 0, SEEK_END); + long file_size = ftell(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) { + fflush(stdout); + // Redirect stdout to a temporary file FILE *temp_file = fopen(filename, "w"); if (temp_file == NULL) { @@ -56,42 +81,18 @@ void redirect_stdout(const char *filename, int evalutate) { 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"); - 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_SETUP(InterpreterTestFile) { } - - -UTEST(interpreter, program) { - yyin = fopen("samples/program.c", "r"); +UTEST_F_TEARDOWN(InterpreterTestFile) { + // and also assert and expect in teardown! + yyin = fopen(utest_fixture->test_file, "r"); yyrestart(yyin); ASSERT_TRUE(yyin); @@ -107,55 +108,23 @@ UTEST(interpreter, program) { // 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/program_print.txt", expected_print); + char *actual_print = read_file_dynamic("test_print.txt"); + char *expected_print = read_file_dynamic(utest_fixture->print_file); ASSERT_STREQ(actual_print, expected_print); - char actual_evaluate[1024]; - read_file("test_evaluate.txt", actual_evaluate); - char expected_evaluate[1024]; - read_file("samples/program_evaluate.txt", expected_evaluate); + char *actual_evaluate = read_file_dynamic("test_evaluate.txt"); + char *expected_evaluate = read_file_dynamic(utest_fixture->evaluated_file); ASSERT_STREQ(actual_evaluate, expected_evaluate); } -// UTEST(parser, missing_new_line) { -// // Must include the null character to terminate input -// char string[] = "1+8/4-3\0"; -// YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string)); +UTEST_F(InterpreterTestFile, print) { + utest_fixture->test_file = "samples/multiple_statements.c"; + utest_fixture->print_file = "samples/multiple_statements_print.txt"; + utest_fixture->evaluated_file = "samples/multiple_statements_evaluate.txt"; +} -// yylineno = 1; -// int result = yyparse(); - -// yy_delete_buffer(buffer); - -// // 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); -// } +UTEST_F(InterpreterTestFile, program_file) { + utest_fixture->test_file = "samples/program.c"; + utest_fixture->print_file = "samples/program_print.txt"; + utest_fixture->evaluated_file = "samples/program_evaluate.txt"; +} diff --git a/lab-5/utest.h b/lab-5/utest.h index 8767600..82de631 100644 --- a/lab-5/utest.h +++ b/lab-5/utest.h @@ -255,8 +255,8 @@ UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceFrequency( static void __cdecl f(void); \ UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ __pragma(comment(linker, "/include:" UTEST_SYMBOL_PREFIX #f "_")) \ - UTEST_C_FUNC __declspec(allocate(".CRT$XCU")) void(__cdecl * \ - f##_)(void) = f; \ + UTEST_C_FUNC \ + __declspec(allocate(".CRT$XCU")) void(__cdecl * f##_)(void) = f; \ UTEST_INITIALIZER_END_DISABLE_WARNINGS \ static void __cdecl f(void) #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); if (UTEST_NULL == new_pointer) { - free(new_pointer); + free(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) { #if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) utest_large_integer counter; utest_large_integer frequency; QueryPerformanceCounter(&counter); QueryPerformanceFrequency(&frequency); - return UTEST_CAST(utest_int64_t, - (counter.QuadPart * 1000000000) / frequency.QuadPart); + return utest_mul_div(counter.QuadPart, 1000000000, frequency.QuadPart); #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__) || \ defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ defined(__HAIKU__) @@ -447,6 +457,15 @@ struct utest_type_deducer final { static void _(const T t); }; +template <> struct utest_type_deducer { + static void _(const char c) { + if (std::is_signed::value) { + UTEST_PRINTF("%d", static_cast(c)); + } else { + UTEST_PRINTF("%u", static_cast(c)); + } + } +}; template <> struct utest_type_deducer { static void _(const signed char c) { UTEST_PRINTF("%d", static_cast(c)); @@ -512,6 +531,10 @@ template <> struct utest_type_deducer { static void _(const unsigned long long i) { UTEST_PRINTF("%llu", i); } }; +template <> struct utest_type_deducer { + static void _(const bool i) { UTEST_PRINTF(i ? "true" : "false"); } +}; + template struct utest_type_deducer { static void _(const T *t) { UTEST_PRINTF("%p", static_cast(const_cast(t))); @@ -528,6 +551,12 @@ template struct utest_type_deducer { } }; +template <> struct utest_type_deducer { + static void _(std::nullptr_t t) { + UTEST_PRINTF("%p", static_cast(t)); + } +}; + template UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const T t) { utest_type_deducer::_(t); @@ -626,24 +655,23 @@ utest_type_printer(long long unsigned int i) { !(defined(__MINGW32__) || defined(__MINGW64__)) || \ defined(__TINYC__) #define utest_type_printer(val) \ - UTEST_PRINTF(_Generic((val), signed char \ - : "%d", unsigned char \ - : "%u", short \ - : "%d", unsigned short \ - : "%u", int \ - : "%d", long \ - : "%ld", long long \ - : "%lld", unsigned \ - : "%u", unsigned long \ - : "%lu", unsigned long long \ - : "%llu", float \ - : "%f", double \ - : "%f", long double \ - : "%Lf", default \ - : _Generic((val - val), ptrdiff_t \ - : "%p", default \ - : "undef")), \ - (val)) + UTEST_PRINTF( \ + _Generic((val), \ + signed char: "%d", \ + unsigned char: "%u", \ + short: "%d", \ + unsigned short: "%u", \ + int: "%d", \ + long: "%ld", \ + long long: "%lld", \ + unsigned: "%u", \ + unsigned long: "%lu", \ + unsigned long long: "%llu", \ + float: "%f", \ + double: "%f", \ + long double: "%Lf", \ + default: _Generic((val - val), ptrdiff_t: "%p", default: "undef")), \ + (val)) #else /* 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(y) yEval = (y); \ if (!((xEval)cond(yEval))) { \ + const char *const xAsString = #x; \ + const char *const yAsString = #y; \ _Pragma("clang diagnostic pop") \ UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ UTEST_PRINTF(" Expected : ("); \ - UTEST_PRINTF(#x ") " #cond " (" #y); \ + UTEST_PRINTF("%s) " #cond " (%s", xAsString, yAsString); \ UTEST_PRINTF(")\n"); \ UTEST_PRINTF(" Actual : "); \ 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(y) yEval = (y); \ if (!((xEval)cond(yEval))) { \ + const char *const xAsString = #x; \ + const char *const yAsString = #y; \ UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ UTEST_PRINTF(" Expected : ("); \ - UTEST_PRINTF(#x ") " #cond " (" #y); \ + UTEST_PRINTF("%s) " #cond " (%s", xAsString, yAsString); \ UTEST_PRINTF(")\n"); \ UTEST_PRINTF(" Actual : "); \ 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) { \ 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; \ char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 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), \ sizeof(struct utest_test_state_s) * \ 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].name = name; \ utest_state.tests[index].index = 0; \ UTEST_SNPRINTF(name, name_size, "%s", name_part); \ - } else if (name) { \ - free(name); \ + } else { \ + if (utest_state.tests) { \ + free(utest_state.tests); \ + utest_state.tests = NULL; \ + } \ + if (name) { \ + free(name); \ + } \ } \ } \ 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) { \ 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; \ char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 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), \ sizeof(struct utest_test_state_s) * \ 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].name = name; \ UTEST_SNPRINTF(name, name_size, "%s", name_part); \ - } else if (name) { \ - free(name); \ + } else { \ + if (utest_state.tests) { \ + free(utest_state.tests); \ + utest_state.tests = NULL; \ + } \ + if (name) { \ + free(name); \ + } \ } \ } \ 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; \ for (i = 0; i < (INDEX); i++) { \ 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; \ char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 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), \ sizeof(struct utest_test_state_s) * \ 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].index = i; \ utest_state.tests[index].name = name; \ iUp = UTEST_CAST(utest_uint64_t, i); \ UTEST_SNPRINTF(name, name_size, "%s/%" UTEST_PRIu64, name_part, iUp); \ - } else if (name) { \ - free(name); \ + } else { \ + if (utest_state.tests) { \ + free(utest_state.tests); \ + utest_state.tests = NULL; \ + } \ + if (name) { \ + free(name); \ + } \ } \ } \ } \ @@ -1665,4 +1715,4 @@ cleanup: return utest_main(argc, argv); \ } -#endif /* SHEREDOM_UTEST_H_INCLUDED */ +#endif /* SHEREDOM_UTEST_H_INCLUDED */ \ No newline at end of file