Skip to content

Instantly share code, notes, and snippets.

@w-vi
Last active February 28, 2017 08:33
Show Gist options
  • Save w-vi/1fa9cf80b449f58909fba706b2e815cf to your computer and use it in GitHub Desktop.
Save w-vi/1fa9cf80b449f58909fba706b2e815cf to your computer and use it in GitHub Desktop.
C unit tests
#include "testing.h"
TEST_IMPL(fail) {
return TEST_FAIL;
}
#include "testing.h"
TEST_IMPL(pass) {
return TEST_OK;
}
#include "testing.h"
#include "tests.h"
#ifdef HAVE_GETOPT_LONG
# include <getopt.h>
#else
# include "my_getopt.h"
#endif
int quiet = TRUE;
int tap = TRUE;
static void
tap_progress(int current,
int total,
int passed,
int failed,
int xfailed,
int skipped,
test_status_t result,
test_instance_t *test)
{
switch (result) {
case TEST_OK: printf("ok %d %s\n", current, test->test_name); break;
case TEST_SKIP:
printf("ok %d %s # SKIP %s\n", current, test->test_name, test->comment);
break;
case TEST_XFAIL:
printf("not ok %d %s # TODO %s\n",
current,
test->test_name,
test->comment);
break;
default: printf("not ok %d %s\n", current, test->test_name);
}
}
static void
no_tap_progress(int current,
int total,
int passed,
int failed,
int xfailed,
int skipped,
test_status_t result,
test_instance_t *test)
{
int progress = 0;
char *status = NULL;
switch (result) {
case TEST_OK: status = "PASSED"; break;
case TEST_SKIP: status = "SKIPPED"; break;
case TEST_XFAIL: status = "X FAILED"; break;
default: status = "FAILED";
}
if (total == 0) total = 1;
progress = 100 * (passed + failed + xfailed + skipped) / total;
LOGF("[%% %3d|P %3d|F %3d|X %3d|S %3d]: %s (%s)\n",
progress,
passed,
failed,
xfailed,
skipped,
test->test_name,
status);
}
int
run_test(test_instance_t *t, int curr)
{
return t->main();
}
void
print_usage()
{
printf("run-tests: "PACKAGE" unit tests\n");
printf("Usage: run-tests [hVn]\n");
printf("\
-n, --no-tap Do not output TAP protocol messages but use internal\n\
reporting\n");
printf("\
-h, --help print this help\n");
printf("\
-V, --version print version information\n");
exit(EXIT_SUCCESS);
}
void
print_version()
{
printf("run-tests "PACKAGE" unit tests version: "VERSION"\n");
printf("send bugs to: "PACKAGE_BUGREPORT"\n");
#ifdef CONFIG_ARGS
printf("\n");
printf(PACKAGE" was built with "CONFIG_ARGS"\n");
#endif
exit(EXIT_SUCCESS);
}
int
main(int argc, char *argv[])
{
int opt;
int long_index = 0;
extern char *optarg;
int total = 0;
int passed = 0;
int failed = 0;
int xfailed = 0;
int skipped = 0;
int current = 1;
test_status_t test_result = TEST_FAIL;
test_instance_t* t = NULL;
void (*progress)(int, int, int, int, int, int,
test_status_t, test_instance_t *);
/*COMMAND LINE ARGS*/
static const struct option long_opts[] = {
{ "no-tap", no_argument, NULL, 'n' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V'},
{ NULL, 0, NULL, 0 }
};
while ((opt = getopt_long(argc, argv,"hVn",
long_opts, &long_index )) != -1) {
switch (opt) {
case 'n': tap = FALSE; break;
case 'h': print_usage(); break;
case 'V': print_version(); break;
case '?':
default: print_usage();
}
}
/* How many tests we have? */
for (t = test_instances; t->main; t++) {
total++;
}
if (tap) {
printf("1..%d\n", total);
progress = tap_progress;
} else {
LOGF("Runing tests 1 ... %d\n", total);
progress = no_tap_progress;
}
/* Run all tests. */
for (t = test_instances; t->main; t++) {
test_result = run_test(t, current);
switch (test_result) {
case TEST_OK: passed++; break;
case TEST_SKIP: skipped++; break;
case TEST_XFAIL: xfailed++; break;
default: failed++;
}
progress(current,
total,
passed,
failed,
xfailed,
skipped,
test_result,
t);
current++;
}
if (!tap) {
LOGF("\nTest run summary:\n");
LOGF("=================\n");
LOGF("Total: %3d\nSuccess: %3d\nFailed: %3d\nX Failed: %3d\nSkipped: %3d\n",
total,
passed,
failed,
xfailed,
skipped);
}
exit(0);
}
#ifndef TESTING_H_
#define TESTING_H_
#include <stddef.h> /* size_t and family */
#include <stdint.h> /* std int types */
#include <inttypes.h>
#include <stdio.h> /* FILE etc */
#include <stdlib.h> /* malloc */
#include <string.h> /* memset */
#include <errno.h>
#if !defined(_WIN32)
# include <unistd.h> /* Posix file and dir */
# include <sys/types.h> /* *nix types*/
#endif
typedef enum test_status {
TEST_OK = 0,
TEST_FAIL,
TEST_XFAIL,
TEST_SKIP
} test_status_t;
/* Check test case and possibly fail */
#define CHECK(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, \
"Check failed in %s on line %d: %s\n", \
__FILE__, \
__LINE__, \
#expr); \
return TEST_FAIL; \
} \
} while (0)
/* Check test case and possibly fail */
#define THR_CHECK(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, \
"Check failed in %s on line %d: %s\n", \
__FILE__, \
__LINE__, \
#expr); \
return NULL; \
} \
} while (0)
/* Test logging to stderr. */
#define LOGF(...) \
do { \
fprintf(stderr, __VA_ARGS__); \
fflush(stderr); \
} while (0)
/* Die with fatal error. */
#define DIE(msg) \
do { \
fprintf(stderr, \
"Fatal error in %s on line %d: %s\n", \
__FILE__, \
__LINE__, \
msg); \
fflush(stderr); \
abort(); \
} while (0)
#define TEST_INSTANCE(name) \
{ #name, #name, &test_##name}
#define TEST_INSTANCE_CMT(name, comment) \
{ #name, #comment, &test_##name}
/* Declare test */
#define TEST_DECL(name) \
test_status_t test_##name(void)
/* Easy way to implement new test */
#define TEST_IMPL(name) \
test_status_t test_##name(void)
#endif /* TESTING_H_ */
#ifndef TESTS_H_
#define TESTS_H_
#include "testing.h"
extern "C" {
/* Test instance definiton */
typedef struct {
char *test_name;
char *comment;
test_status_t (*main)(void);
} test_instance_t;
/* First declare new test function here, using the TEST_DECL macro */
TEST_DECL(pass);
TEST_DECL(fail);
/* Add test instances to this list.
* If they are not here they are ignored.
*/
test_instance_t test_instances[] = {
#ifdef TEST_RUNNER /* define if you want to test the test runner */
TEST_INSTANCE(pass),
TEST_INSTANCE(fail),
#endif
{0, 0, 0} /* Last item in the list, do not put anything after this line*/
};
}
#endif /* TESTS_H_ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment