Created
August 9, 2020 06:30
-
-
Save MichaelSnowden/b75899180ec5c2e1387a2ae8b4e705c4 to your computer and use it in GitHub Desktop.
C Test Runner
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 <stdlib.h> | |
#include <zconf.h> | |
#include <stdio.h> | |
#include <fcntl.h> | |
#include <memory.h> | |
#include <errno.h> | |
#include <assert.h> | |
#include "test_runner.h" | |
int runTests(Test *tests, int numTests) { | |
assert(numTests > 0); | |
int maxNameLength = 0; | |
for (int i = 0; i < numTests; ++i) { | |
int length = (int) strlen(tests[i].name); | |
if (length > maxNameLength) { | |
maxNameLength = length; | |
} | |
} | |
for (int i = 0; i < numTests; ++i) { | |
Test *test = tests + i; | |
int stderrPipe[2]; | |
assert(pipe(stderrPipe) == 0); | |
int stdoutPipe[2]; | |
assert(pipe(stdoutPipe) == 0); | |
pid_t pid = fork(); | |
if (pid == 0) { | |
assert(dup2(stderrPipe[1], STDERR_FILENO) != -1); | |
assert(dup2(stdoutPipe[1], STDOUT_FILENO) != -1); | |
// this might exit early, but if, instead, it returns, just use that as the exit code | |
int testStatus = test->run(); | |
exit(testStatus); | |
} | |
const char *name = test->name; | |
printf("%*s: ", maxNameLength, name); | |
fflush(stdout); | |
int status; | |
wait: | |
while (waitpid(pid, &status, 0) != pid) { | |
if (errno == EINTR) { | |
goto wait; | |
} | |
perror("waitpid"); | |
return -1; | |
} | |
if (status == 0) { | |
printf("\033[1;32m" "pass\n" "\033[0m"); | |
continue; | |
} | |
ssize_t r; | |
char buffer[1024]; | |
while ((r = read(stderrPipe[0], buffer, sizeof(buffer) - 1)) < 0 && errno == EINTR) {} | |
if (r < 0) { | |
perror("read"); | |
return -1; | |
} | |
if (r == 0) { | |
printf("\033[1;34m" "[no output from test]\n" "\033[0m"); | |
continue; | |
} | |
buffer[r] = '\0'; | |
// remove trailing newlines | |
while (r > 0 && buffer[r - 1] == '\n') { | |
buffer[r - 1] = '\0'; | |
} | |
printf("\033[1:31m" "%s\n" "\033[0m", buffer); | |
} | |
return 0; | |
} |
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
#ifndef TEST_RUNNER_H | |
#define TEST_RUNNER_H | |
/* | |
* A lot of tests in C have the same signature: int (*run)(). | |
* This test runner will iterate through a list of such functions, executing each one. | |
* It runs each test in a subprocess, so that the test is allowed to fuck up in any way imaginable, | |
* but the test runner continues chugging along. | |
* The test runner also redirects stdout and stderr in the tests so that they are ignored. | |
* However, when a test fails, a snippet of stderr from the failed test will be displayed. | |
*/ | |
typedef struct { | |
const char *name; | |
int (*run)(); | |
} Test; | |
int runTests(Test *test, int numTests); | |
#endif |
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 <printf.h> | |
#include "test_runner_test.h" | |
#include "test_runner.h" | |
int test1() { | |
printf("log1\n"); | |
fprintf(stderr, "err1\n"); | |
return -1; | |
} | |
int test2() { | |
printf("log2\n"); | |
fprintf(stderr, "err2\n"); | |
return 0; | |
} | |
int test3() { | |
printf("log3\n"); | |
fprintf(stderr, "err3\n"); | |
return -1; | |
} | |
int testTestRunner() { | |
Test tests[] = { | |
{"test1_jfw0", test1}, | |
{"test2_fjsj29v[zh33gf9sd8", test2}, | |
{"test3_hba03hfaz", test3}, | |
}; | |
int numTests = sizeof(tests) / sizeof(*tests); | |
return runTests(tests, numTests); | |
} |
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
#ifndef TEST_RUNNER_TEST_H | |
#define TEST_RUNNER_TEST_H | |
int testTestRunner(); | |
#endif |
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 "test_runner_test.h" | |
int main() { | |
return testTestRunner(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment