Created
January 31, 2018 22:01
-
-
Save nickrolfe/ffc9b1c02381b9dc17c975b98db42172 to your computer and use it in GitHub Desktop.
C unit test auto-discovery
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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