ds/ihct/ihct.h
2025-01-06 19:39:32 +01:00

151 lines
No EOL
6.1 KiB
C

#ifndef IHCT_H
#define IHCT_H
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
// Structure for a testunits return value. Contains state, the code (assert) which
// failed the test, and a reference to where the code is.
typedef struct {
enum {PASS, FAIL, FAIL_FORCE, ERR, TIMEOUT} status;
char *code;
char *file;
unsigned long line;
} ihct_test_result;
// Short for a function returning a test_result pointer, with no arguments.
typedef void (*ihct_test_proc)(ihct_test_result *);
// Called within a test.
bool ihct_assert_impl(bool eval, ihct_test_result *result, char *code, char *file,
unsigned long line);
void ihct_pass_impl(ihct_test_result *result, char *file, unsigned long line);
void ihct_fail_impl(ihct_test_result *result, char *file, unsigned long line);
// Called on test unit construction.
void ihct_construct_test_impl(char *s, ihct_test_proc proc);
// Runs all tests.
int ihct_run(int argc, char **argv);
// Initializes the unitlist (Has to be done before all testing units are created).
// Using priority to ensure that the unit list is constructed before it gets populated.
void ihct_init(void) __attribute__((constructor(101)));
// Assertions
/// @defgroup assertions Assertions
/// @brief Wraps all assertions.
///
/// Decides the success of a test. Right now, tests can only result in
/// PASS or FAIL. Assertions are only given for expressions.
/// @brief Asserts a statement inside a test unit. If the expression is false,
/// the unit will fail the test.
/// @ingroup assertions
/// @param expr the expression to evaluate.
///
/// Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_ASSERT(expr) \
if(!ihct_assert_impl(expr, result, #expr, __FILE__, __LINE__)) return
/// @brief Asserts a statement inside a test unit. If the expression is true,
/// the unit will fail the test.
/// @ingroup assertions
/// @param expr the expression to evaluate.
///
/// Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_NASSERT(expr) \
if(!ihct_assert_impl(!expr, result, "!(" #expr ")", __FILE__, __LINE__)) return
/// @brief Asserts two strings inside a test unit to be equal. If there is any difference
/// in the strings, the unit will fail the test.
/// @ingroup assertions
/// @param s1 first string to compare
/// @param s2 second string to compare
///
/// Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_ASSERT_STR(s1, s2) \
if(!ihct_assert_impl(!strcmp(s1, s2), result, #s1 " == " #s2, __FILE__, \
__LINE__)) return
/// @brief Asserts two strings inside a test unit not to be equal. If there is any
/// difference in the strings, the unit will fail the test.
/// @ingroup assertions
/// @param s1 first string to compare
/// @param s2 second string to compare
///
/// Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_NASSERT_STR(s1, s2) \
if(!ihct_assert_impl(strcmp(s1, s2), result, #s1 " != " #s2, __FILE__, \
__LINE__)) return
/// @brief Set the test as passed and return.
/// @ingroup assertions
///
/// Used for more complex tests where the PASS/FAIL status is more complex
/// than an assert. Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_PASS() \
do { ihct_pass_impl(result, __FILE__, __LINE__); return; } while(0)
/// @brief Set the test as failed and return.
/// @ingroup assertions
///
/// Used for more complex tests where the PASS/FAIL status is more complex
/// than an assert. Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_FAIL() \
do { ihct_fail_impl(result, __FILE__, __LINE__); return; } while(0)
// Function macros
/// @defgroup funcs Testing functions
/// @brief More general macros for function.
/// @brief Runs all tests. Is to be called once in the main entrypoint.
/// @ingroup funcs
/// @code
/// int main(int argc, char **argv) {
/// return IHCT_RUN(argc, argv);
/// }
/// @endcode
/// @param argc argument count, directly passed from main.
/// @param argv argument array, directly passed from main.
#define IHCT_RUN(argc, argv) \
ihct_run(argc, argv)
// Create a new test unit, and adds it using 'ihct_add_test'.
/// @brief Create a new test unit, which can take any number of asserts.
/// @ingroup funcs
/// @code
/// IHCT_TEST(basic_test) {
/// IHCT_ASSERT(1 == 1);
/// }
/// @endcode
/// @param name the name of the test.
///
/// Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_TEST(name) \
static void test_##name(ihct_test_result *result); \
static void __attribute__((constructor(102))) __construct_test_##name(void) { \
ihct_construct_test_impl(#name, &test_##name); \
} \
static void test_##name(ihct_test_result *result)
/// @brief Defines a fixture with data to be preloaded before a test.
/// A ficture is included by a IHCT_REQUIRE inside a test.
#define IHCT_FIXTURE(name) _Static_assert(0, "Fixtures not implemented.")
/// @brief Make the test require the given fixtures.
/// @param ... one or more fixture names.
#define IHCT_REQUIRE(...) _Static_assert(0, "Fixture requirements not implemented.")
#ifdef IHCT_SHORT
#define TEST(name) IHCT_TEST(name)
#define ASSERT(expr) IHCT_ASSERT(expr)
#define NASSERT(expr) IHCT_NASSERT(expr)
#define ASSERT_STR(s1, s2) IHCT_ASSERT_STR(s1, s2)
#define NASSERT_STR(s1, s2) IHCT_NASSERT_STR(s1, s2)
#define PASS() IHCT_PASS()
#define FAIL() IHCT_FAIL()
#endif
#endif