/** * @file testutil.h * @brief Shared assertions for the akbasic CTest programs. * * A test passes by exiting zero and fails by exiting non-zero with something * informative on stderr. * * TEST_ASSERT and friends expand to a `break`, so they belong directly inside an * ATTEMPT block, never inside a loop nested in one -- the same rule that governs * CATCH and the FAIL_*_BREAK macros. TEST_REQUIRE is the return-based form for * use inside loops and outside ATTEMPT blocks. */ #ifndef _AKBASIC_TESTUTIL_H_ #define _AKBASIC_TESTUTIL_H_ #include #include #include #include #include /** Count of failures recorded by TEST_REQUIRE; main() returns it. */ static int akbasic_test_failures = 0; /* * Mark an error handled and hand it back to the pool. Assigning the result is * what satisfies AKERR_NOIGNORE -- a (void) cast does not, and libakerror's * IGNORE() would log to stderr and pollute the test output. */ static void test_discard_error(akerr_ErrorContext *e) { akerr_ErrorContext *released = NULL; if ( e == NULL ) { return; } e->handled = true; released = akerr_release_error(e); (void)released; } #define TEST_REQUIRE(__cond, ...) \ do { \ if ( !(__cond) ) { \ fprintf(stderr, "%s:%d: FAIL: ", __FILE__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n"); \ akbasic_test_failures += 1; \ } \ } while ( 0 ) #define TEST_REQUIRE_STR(__got, __want) \ TEST_REQUIRE(strcmp((__got), (__want)) == 0, \ "expected \"%s\", got \"%s\"", (__want), (__got)) #define TEST_REQUIRE_INT(__got, __want) \ TEST_REQUIRE((long long)(__got) == (long long)(__want), \ "expected %lld, got %lld", \ (long long)(__want), (long long)(__got)) #define TEST_REQUIRE_FEQ(__got, __want) \ TEST_REQUIRE(fabs((double)(__got) - (double)(__want)) < 1e-9, \ "expected %f, got %f", (double)(__want), (double)(__got)) /* * Assert that a call succeeded. Releases the context on failure so the pool does * not drain across a long test. */ #define TEST_REQUIRE_OK(__call) \ do { \ akerr_ErrorContext *__e = (__call); \ if ( __e != NULL && __e->status != 0 ) { \ fprintf(stderr, "%s:%d: FAIL: %s: unexpected error %d (%s): %s\n", \ __FILE__, __LINE__, #__call, __e->status, \ akerr_name_for_status(__e->status, NULL), __e->message); \ akbasic_test_failures += 1; \ } \ test_discard_error(__e); \ } while ( 0 ) /** Assert that a call failed with a specific status. */ #define TEST_REQUIRE_STATUS(__call, __status) \ do { \ akerr_ErrorContext *__e = (__call); \ if ( __e == NULL || __e->status != (__status) ) { \ fprintf(stderr, "%s:%d: FAIL: %s: expected status %d (%s), got %d\n", \ __FILE__, __LINE__, #__call, (int)(__status), \ akerr_name_for_status((__status), NULL), \ (__e == NULL ? 0 : __e->status)); \ akbasic_test_failures += 1; \ } \ test_discard_error(__e); \ } while ( 0 ) /** Assert that a call failed, without caring which status. */ #define TEST_REQUIRE_ANY_ERROR(__call) \ do { \ akerr_ErrorContext *__e = (__call); \ if ( __e == NULL || __e->status == 0 ) { \ fprintf(stderr, "%s:%d: FAIL: %s: expected an error, got success\n", \ __FILE__, __LINE__, #__call); \ akbasic_test_failures += 1; \ } \ test_discard_error(__e); \ } while ( 0 ) #endif // _AKBASIC_TESTUTIL_H_