#pragma once #include #include template void print_value(const T& value); template <> void print_value(const int8_t& value) { printf("%d", value); } template <> void print_value(const int32_t& value) { printf("%d", value); } template <> void print_value(const uint8_t& value) { printf("%u", value); } template <> void print_value(const uint32_t& value) { printf("%u", value); } template <> void print_value(const float& value) { printf("%f", value); } template <> void print_value(const double& value) { printf("%f", value); } void __begin_test(const char* function_name, const char* file, int line) { printf("######### Running %s @ %s:%d\n", function_name, file, line); } #define BEGIN_TEST() __begin_test(__FUNCTION__, __FILE__, __LINE__) void test_fail() { printf("[!] Test failed. Exiting with status code 1.\n"); exit(1); } template void debug_print_array(int len, const T* as) { printf("["); for (int i = 0; i < len; i++) { if (i != 0) printf(", "); print_value(as[i]); } printf("]"); } void print_assertion_passed(const char* file, int line) { printf("[*] Assertion passed on %s:%d\n", file, line); } void print_assertion_failed(const char* file, int line) { printf("[!] Assertion failed on %s:%d\n", file, line); } void __assert_true(const char* file, int line, bool cond) { if (cond) { print_assertion_passed(file, line); } else { print_assertion_failed(file, line); test_fail(); } } #define assert_true(cond) __assert_true(__FILE__, __LINE__, cond) template void __assert_arrays_match(const char* file, int line, int len, const T* expected, const T* got) { if (arrays_match(len, expected, got)) { print_assertion_passed(file, line); } else { print_assertion_failed(file, line); printf("Expect = "); debug_print_array(len, expected); printf("\n"); printf(" Got = "); debug_print_array(len, got); printf("\n"); test_fail(); } } #define assert_arrays_match(len, expected, got) \ __assert_arrays_match(__FILE__, __LINE__, len, expected, got) template void __assert_values_match(const char* file, int line, T expected, T got) { if (expected == got) { print_assertion_passed(file, line); } else { print_assertion_failed(file, line); printf("Expect = "); print_value(expected); printf("\n"); printf(" Got = "); print_value(got); printf("\n"); test_fail(); } } #define assert_values_match(expected, got) \ __assert_values_match(__FILE__, __LINE__, expected, got) // A fake set of ErrorIds for testing only const ErrorIds TEST_ERROR_IDS = { .index_error = 0, .value_error = 1, .assertion_error = 2, .runtime_error = 3, .type_error = 4, }; ErrorContext create_testing_errctx() { // Everything is global so it is fine to directly return a struct // ErrorContext ErrorContext errctx; errctx.initialize(&TEST_ERROR_IDS); return errctx; } void print_errctx_content(ErrorContext* errctx) { if (errctx->has_error()) { printf( "(Error ID %d): %s ... where param1 = %ld, param2 = %ld, param3 = " "%ld\n", errctx->error_id, errctx->message_template, errctx->param1, errctx->param2, errctx->param3); } else { printf("\n"); } } void __assert_errctx_no_error(const char* file, int line, ErrorContext* errctx) { if (errctx->has_error()) { print_assertion_failed(file, line); printf("Expecting no error but caught the following:\n\n"); print_errctx_content(errctx); test_fail(); } } #define assert_errctx_no_error(errctx) \ __assert_errctx_no_error(__FILE__, __LINE__, errctx) void __assert_errctx_has_error(const char* file, int line, ErrorContext* errctx, ExceptionId expected_error_id) { if (errctx->has_error()) { if (errctx->error_id != expected_error_id) { print_assertion_failed(file, line); printf( "Expecting error id %d but got error id %d. Error caught:\n\n", expected_error_id, errctx->error_id); print_errctx_content(errctx); test_fail(); } } else { print_assertion_failed(file, line); printf("Expecting an error, but there is none."); test_fail(); } } #define assert_errctx_has_error(errctx, expected_error_id) \ __assert_errctx_has_error(__FILE__, __LINE__, errctx, expected_error_id)