blob: 627c2bfd0a6a24e919eb813bff5636bb539cfc4d [file] [log] [blame]
#pragma once
/*
* Header file with infrastructure needed for userspace unit tests:
* - Assertion functions.
* - Test structures.
* - Launcher functions for test suites.
*/
#include <malloc.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <parlib/timing.h>
#include <execinfo.h>
__BEGIN_DECLS
/*
* Macros for assertions.
*/
#define UT_ASSERT(test, ...) \
UT_ASSERT_M(#test, test, __VA_ARGS__)
#define UT_ASSERT_M(message, test, ...) \
do { \
if (!(test)) { \
char fmt[] = "Assertion failure in %s() at %s:%d: %s"; \
sprintf(utest_msg, fmt, __FUNCTION__, __FILE__, __LINE__, \
message); \
__VA_ARGS__; \
return false; \
} \
} while (0)
/* If 'test' fails, Sets an assert message, which can be a format string, and
* returns false. */
#define UT_ASSERT_FMT(message, test, ...) \
do { \
if (!(test)) { \
char fmt[] = "Assertion failure in %s() at %s:%d: " #message; \
sprintf(utest_msg, fmt, __func__, __FILE__, __LINE__, \
##__VA_ARGS__); \
return false; \
} \
} while (0)
/*
* Structs and macros for registering test cases.
*/
struct utest {
char name[256]; // Name of the test function.
bool (*func)(void); // Name of the test function, should be = to 'name'.
bool enabled; // Whether or not to run the test.
};
/* Used for defining an userspace test structure entry inline. */
#define UTEST_REG(name) \
{"test_" #name, test_##name, true}
/*
* Creates all the runnable code for a test suite.
*/
#define TEST_SUITE(__suite_name) \
char utest_msg[1024]; \
char suite_name[] = __suite_name;
#define RUN_TEST_SUITE(utests, num_utests, whitelist, whitelist_len) \
do { \
if (whitelist_len > 0) \
apply_whitelist(whitelist, whitelist_len, utests, num_utests); \
run_utests(suite_name, utests, num_utests); \
} while (0)
/* Disables all the tests not passed through a whitelist. */
static void apply_whitelist(char *whitelist[], int whitelist_len,
struct utest tests[], int num_tests)
{
for (int i = 0; i < num_tests; i++) {
struct utest *test = &tests[i];
if (test->enabled) {
for (int j = 0; j < whitelist_len; ++j) {
test->enabled = false;
if (strcmp(test->name, whitelist[j]) == 0) {
test->enabled = true;
break;
}
}
}
}
}
static void run_utests(char *suite_name, struct utest tests[], int num_tests)
{
extern char utest_msg[];
printf("<-- BEGIN_USERSPACE_%s_TESTS -->\n", suite_name);
for (int i=0; i<num_tests; i++) {
struct utest *test = &tests[i];
if (test->enabled) {
uint64_t start = read_tsc();
bool result = test->func();
uint64_t end = read_tsc();
uint64_t et_us = tsc2usec(end - start) % 1000000;
uint64_t et_s = tsc2sec(end - start);
char fmt[] = "\t%s [%s](%llu.%06llus) %s\n";
if (result) {
printf(fmt, "PASSED", test->name, et_s, et_us,
"");
} else {
printf(fmt, "FAILED", test->name, et_s, et_us,
utest_msg);
}
} else {
printf("\tDISABLED [%s]\n", test->name);
}
}
printf("<-- END_USERSPACE_%s_TESTS -->\n", suite_name);
}
__END_DECLS