blob: 2959ff5b83e7ab72a6e0e85bfcd5b8553a14095a [file] [log] [blame]
#ifndef _HF_TEST_H
#define _HF_TEST_H
#include <stdbool.h>
#include <stdint.h>
#include "dlog.h"
/*
* Prefixed to log lines from tests for easy filtering in the console.
*/
#define HF_TEST_LOG_PREFIX "[hf_test] "
/*
* Context for tests.
*/
struct hf_test_context {
uint32_t failures;
};
/*
* This union can store any of the primitive types supported by the assertion
* macros.
*/
union hf_test_any {
bool b;
char c;
signed char sc;
unsigned char uc;
signed short ss;
unsigned short us;
signed int si;
unsigned int ui;
signed long int sli;
unsigned long int uli;
signed long long int slli;
unsigned long long int ulli;
void *p;
};
/* _Generic formatting doesn't seem to be supported so doing this manually. */
/* clang-format off */
/* Select the union member to match the type of the expression. */
#define hf_test_any_get(any, x) \
_Generic((x), \
bool: (any).b, \
char: (any).c, \
signed char: (any).sc, \
unsigned char: (any).uc, \
signed short: (any).ss, \
unsigned short: (any).us, \
signed int: (any).si, \
unsigned int: (any).ui, \
signed long int: (any).sli, \
unsigned long int: (any).uli, \
signed long long int: (any).slli, \
unsigned long long int: (any).ulli, \
void *: (any).p)
/*
* dlog format specifier for types. Note, these aren't the standard specifiers
* for the types.
*/
#define hf_test_dlog_format(x) \
_Generic((x), \
bool: "%u", \
char: "%c", \
signed char: "%d", \
unsigned char: "%u", \
signed short: "%d", \
unsigned short: "%u", \
signed int: "%d", \
unsigned int: "%u", \
signed long int: "%d", \
unsigned long int: "%u", \
signed long long int: "%d", \
unsigned long long int: "%u", \
void *: "%p")
/* clang-format on */
#define ASSERT_OP(lhs, rhs, op, fatal) \
do { \
union hf_test_any lhs_value; \
union hf_test_any rhs_value; \
hf_test_any_get(lhs_value, lhs) = (lhs); \
hf_test_any_get(rhs_value, rhs) = (rhs); \
if (!(hf_test_any_get(lhs_value, lhs) \
op hf_test_any_get(rhs_value, rhs))) { \
++hf_test_ctx->failures; \
dlog(HF_TEST_LOG_PREFIX " %s:%u: Failure\n", \
__FILE__, __LINE__); \
dlog(HF_TEST_LOG_PREFIX " %s %s %s (%s=", #lhs, \
#op, #rhs, #lhs); \
dlog(hf_test_dlog_format(lhs), \
hf_test_any_get(lhs_value, lhs)); \
dlog(", %s=", #rhs); \
dlog(hf_test_dlog_format(rhs), \
hf_test_any_get(rhs_value, rhs)); \
dlog(")\n"); \
if (fatal) { \
return; \
} \
} \
} while (0)
#define ASSERT_EQ(x, y) ASSERT_OP(x, y, ==, true)
#define ASSERT_NE(x, y) ASSERT_OP(x, y, !=, true)
#define ASSERT_LE(x, y) ASSERT_OP(x, y, <=, true)
#define ASSERT_LT(x, y) ASSERT_OP(x, y, <, true)
#define ASSERT_GE(x, y) ASSERT_OP(x, y, >=, true)
#define ASSERT_GT(x, y) ASSERT_OP(x, y, >, true)
#define EXPECT_EQ(x, y) ASSERT_OP(x, y, ==, false)
#define EXPECT_NE(x, y) ASSERT_OP(x, y, !=, false)
#define EXPECT_LE(x, y) ASSERT_OP(x, y, <=, false)
#define EXPECT_LT(x, y) ASSERT_OP(x, y, <, false)
#define EXPECT_GE(x, y) ASSERT_OP(x, y, >=, false)
#define EXPECT_GT(x, y) ASSERT_OP(x, y, >, false)
/*
* Declare a test case.
*/
#define TEST(name) static void name(struct hf_test_context *hf_test_ctx)
/*
* Run a test case.
*/
#define RUN_TEST(test) \
do { \
struct hf_test_context ctx = { \
.failures = 0, \
}; \
dlog(HF_TEST_LOG_PREFIX "RUN %s\n", #test); \
test(&ctx); \
if (ctx.failures) { \
dlog(HF_TEST_LOG_PREFIX "FAILED\n"); \
} else { \
dlog(HF_TEST_LOG_PREFIX "OK\n"); \
} \
} while (0)
#endif /* _HF_TEST_H */