Skip to content

Instantly share code, notes, and snippets.

@nickrolfe
Created January 31, 2018 22:01
Show Gist options
  • Save nickrolfe/ffc9b1c02381b9dc17c975b98db42172 to your computer and use it in GitHub Desktop.
Save nickrolfe/ffc9b1c02381b9dc17c975b98db42172 to your computer and use it in GitHub Desktop.
C unit test auto-discovery
#include <stdbool.h>
#include <stdio.h>
//------------------------------------------------------------------------------
// The code we're testing
int add_one(int x) {
return x + 1;
}
int multiply(int x, int y) {
return x * y;
}
//------------------------------------------------------------------------------
// Our little test framework
typedef void (*TestFunctionPtr)(bool *test_passed);
typedef struct TestInfo {
const char *name;
TestFunctionPtr function;
} TestInfo;
typedef const TestInfo* TestInfoPtr;
#define TEST_ASSERT(condition) if (!(condition)) { *test_passed = false; }
#if defined _WIN32
//------------------------------------------------------------------------------
// Windows-specific code
#pragma section("tests$a", read) // for the start symbol
#pragma section("tests$b", read) // for the real TestInfo structures
#pragma section("tests$c", read) // for the stop symbol
__declspec(allocate("tests$a"))
TestInfo __start_test_section;
__declspec(allocate("tests$c"))
TestInfo __stop_test_section;
#define TEST_FUNCTION(name) \
void test_ ## name(bool *test_passed); /* declaration */ \
__declspec(allocate("tests$b")) const TestInfo test_info_ ## name = { \
#name, test_ ## name \
}; \
void test_ ## name(bool *test_passed) /* definition */
#define FIRST_TEST_INFO (&__start_test_section + 1)
#define ONE_PAST_LAST_TEST_INFO &__stop_test_section
#elif defined __APPLE__
//------------------------------------------------------------------------------
// macOS-specific code
extern TestInfo __start_test_section __asm("section$start$__DATA$test_section");
extern TestInfo __stop_test_section __asm("section$end$__DATA$test_section");
#define FIRST_TEST_INFO &__start_test_section
#define ONE_PAST_LAST_TEST_INFO &__stop_test_section
#define TEST_FUNCTION(name) \
void test_ ## name(bool *test_passed); /* declaration */ \
const TestInfo test_info_ ## name __attribute__((section ("__DATA,test_section"))) = { \
#name, test_ ## name \
}; \
void test_ ## name(bool *test_passed) /* definition */
#elif defined __linux__
//------------------------------------------------------------------------------
// Linux-specific code
extern TestInfo __start_test_section;
extern TestInfo __stop_test_section;
#define FIRST_TEST_INFO &__start_test_section
#define ONE_PAST_LAST_TEST_INFO &__stop_test_section
#define TEST_FUNCTION(name) \
void test_ ## name(bool *test_passed); /* declaration */ \
const TestInfo test_info_ ## name __attribute__((section ("test_section"))) = { \
#name, test_ ## name \
}; \
void test_ ## name(bool *test_passed) /* definition */
#else
#error Unknown platform!
#endif // End of platform-specific code
//------------------------------------------------------------------------------
// Test functions
TEST_FUNCTION(add_one) {
TEST_ASSERT(add_one(2) == 3);
TEST_ASSERT(add_one(-2) == -1);
}
TEST_FUNCTION(multiply) {
TEST_ASSERT(multiply(3, 5) == 15);
TEST_ASSERT(multiply(0, 5) == 0);
TEST_ASSERT(multiply(3, 0) == 0);
TEST_ASSERT(multiply(12, 12) == 144);
}
//------------------------------------------------------------------------------
// Test runner
void run_all_tests(void) {
int num_passes = 0;
int num_failures = 0;
for (TestInfo *cur_test = FIRST_TEST_INFO;
cur_test < ONE_PAST_LAST_TEST_INFO;
cur_test++) {
printf("Calling test '%s'... ", cur_test->name);
bool test_passed = true;
cur_test->function(&test_passed);
if (test_passed) {
num_passes++;
printf("passed\n");
} else {
num_failures++;
printf("FAILED :(\n");
}
}
printf("%d failures, %d passes\n", num_failures, num_passes);
}
int main(void) {
run_all_tests();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment