Unit Tests for C, in a single header.
#ifndef TEST_H_ | |
#define TEST_H_ | |
/* Unit testing framework. | |
* | |
* Copyright (c) 2014, Jim Ingram <ingramj@gmail.com> | |
* | |
* Permission is hereby granted, free of charge, to any person | |
* obtaining a copy of this software and associated documentation | |
* files (the "Software"), to deal in the Software without | |
* restriction, including without limitation the rights to use, | |
* copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following | |
* conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
* OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* http://jingram.sdf.org/2014/11/29/unit-tests-for-c-in-two-macros.html | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
/* | |
* Begin a new test definition, with the given name. | |
*/ | |
#define BEGIN_TEST(name) \ | |
static int test_##name(void) \ | |
{ \ | |
pid_t pid = fork(); \ | |
if (pid < 0) { \ | |
return -1; \ | |
} \ | |
if (pid == 0) { \ | |
int failed = 0; | |
/* | |
* End a test definition and set its expected exit status. | |
*/ | |
#define END_TEST(expected) \ | |
exit(failed ? !expected : expected); \ | |
} else { \ | |
int status; \ | |
waitpid(pid, &status, 0); \ | |
if (WIFEXITED(status)) { \ | |
return WEXITSTATUS(status) == expected ? 0 : 1; \ | |
} \ | |
return -1; \ | |
} \ | |
} | |
/* | |
* Set the `failed` flag to 1 for this test, if `cond` evaluates to true. | |
*/ | |
#define FAIL_IF(cond) failed = (cond) ? 1 : failed | |
/* | |
* Set the `failed` flag to 1 for this test, unless `cond` evaluates to true. | |
*/ | |
#define FAIL_UNLESS(cond) failed = (cond) ? failed : 1 | |
/* | |
* Container for summary data about a series of tests. | |
*/ | |
typedef struct { | |
int passed; | |
int failed; | |
int errors; | |
} tests_summary; | |
/* | |
* Reset passed, failed, and error counts in a `tests_summary` struct. | |
*/ | |
static inline void reset_summary(tests_summary *summary) | |
{ | |
summary->passed = 0; | |
summary->failed = 0; | |
summary->errors = 0; | |
} | |
/* | |
* Print passed, failed, errors, and total tests run from a | |
* `tests_summary` struct. | |
*/ | |
static inline void print_summary(tests_summary *summary) | |
{ | |
printf("passed : %d\nfailed : %d\nerrors : %d\ntotal : %d\n", | |
summary->passed, summary->failed, summary->errors, | |
summary->passed + summary->failed + summary->errors); | |
} | |
/* | |
* Run the named test, and update `summary` with the result. | |
*/ | |
#define RUN_TEST(name, summary) \ | |
do { \ | |
int ret; \ | |
printf("%s: ", #name); \ | |
fflush(stdout); \ | |
ret = test_##name(); \ | |
if (ret == 0) { \ | |
printf("passed\n"); \ | |
(summary).passed++; \ | |
} else if (ret == 1) { \ | |
printf("failed\n"); \ | |
(summary).failed++; \ | |
} else { \ | |
printf("failed to to an error\n"); \ | |
(summary).errors++; \ | |
} \ | |
} while (0) | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment