Skip to content

Instantly share code, notes, and snippets.

@justinbowes
Last active October 31, 2015 03:23
Show Gist options
  • Save justinbowes/c4490b73d06e2427a130 to your computer and use it in GitHub Desktop.
Save justinbowes/c4490b73d06e2427a130 to your computer and use it in GitHub Desktop.
RUI test harness. C99, MSVC/GCC/Clang, standalone tests.
//
// rui_test.h
// Quanta
//
// Created by Justin Bowes on 2014-11-06.
// Copyright (c) 2014 Informi Software Inc. All rights reserved.
//
#ifndef rui_test__B2BD1D7_F65A_4026_B67F_437E3C640A18_h
#define rui_test__B2BD1D7_F65A_4026_B67F_437E3C640A18_h
#ifdef RUI_TEST_INSTRUCTIONS
/*
# RUI Test
-------------------------------------------------------------------------------------------
## How do I...
```
#include "rui_test.h"
rt_test(sanity) {
rt_must(true);
rt_must_not(false);
}
```
That's it.
## No, how do I really...
```
#include "rui_test.h"
#include "myobject.h"
#define RT_BREAK_ON_FAILURE
static myobject_t *obj;
static const char *test_str = "gom jabbar"
static size_t test_data_size;
rt_suite_begin() {
obj = myobject_new();
test_data_size = sizeof(test_str);
}
rt_suite_end() {
myobject_free(obj);
}
rt_test_begin() {
myobject_add(test_str);
}
rt_test_end() {
myobject_clear(obj);
}
rt_test(strings) {
rt_must_be_equal(myobject_size(obj), test_data_size + 1);
rt_strings_must_be_same(myobject_tostr(obj), test_str);
rt_numbers_must_be_within(myobject_size(obj) * 0.1f,
test_data_size * 0.1f,
0.001f)
}
rt_test(floats) {
rt_include(strings);
rt_must_be_null(myobject_tofloat(obj));
}
#include <math.h>
#include <complex.h>
rt_test(sanity) {
float complex euler_val = cpowf(M_E, I * M_PI);
bool eulers_identity = (creal(euler_val) == -1.f &&
cimag(euler_val) == 0.f);
rt_assert(eulers_identity,
"Universe error", "e^(i*π) == -1 should be true, "
"but was [%f, %f]. Check local value of
true.",
creal(euler_val), cimag(euler_val));
}
*/
#endif
#define RUI_TEST
#define rt_must(v) rt_assert(v, "must")
#define rt_must_not_be_null(v) rt_assert((v) != NULL, "must not be null")
#define rt_must_be_null(v) rt_assert((v) == NULL, "must not be null")
#define rt_must_not(v) rt_assert(!(v), "must not")
#define rt_must_be_equal(a, b) rt_must((a) == (b), "must be equal")
#define rt_must_not_be_equal(a, b) rt_assert((a) != (b), "must not be equal")
#define rt_strings_must_be_same(a, b) \
rt_assert_fmt(strcmp(a, b) == 0, "strings must be same", "%s == %s", a, b)
#define rt_numbers_must_be_within(a, b, within) \
rt_assert(fabs(a - b) < within, "numbers must be within", \
"%f ~= %f [+/- %f]", (double)a, (double)b, (double)within)
#define rt_test_begin() \
static void rt_test_setup_(void); \
RT_STATIC_INIT(rt_test_setup_init) { \
rt_test_setup_fn = rt_test_setup_; \
} \
static void rt_test_setup_(void)
#define rt_test_end() \
static void rt_test_teardown_(void); \
RT_STATIC_INIT(rt_test_teardown_init) { \
rt_test_teardown_fn = rt_test_teardown_; \
} \
static void rt_test_teardown_(void)
#define rt_suite_begin() \
static void rt_suite_setup_(void); \
RT_STATIC_INIT(rt_suite_setup_init) { \
rt_suite_setup_fn = rt_suite_setup_; \
} \
static void rt_suite_setup_(void)
#define rt_suite_end() \
static void rt_suite_teardown_(void); \
RT_STATIC_INIT(rt_suite_teardown_init) { \
rt_suite_teardown_fn = rt_suite_teardown_; \
} \
static void rt_suite_teardown_(void)
#define rt_test(named) \
static void test_##named(void); \
RT_STATIC_INIT(add_test_##named) { \
rt_tests[rt_test_count] = test_##named; \
rt_test_names[rt_test_count] = "test_" #named; \
++rt_test_count; \
} \
static void test_##named(void)
#define rt_include(included) test_##included()
#define XXrt_test(named) static void disabled_test_##named(void)
#define RT_DIVMAJ \
"====================================================================" \
"====================================================================" \
"\n"
#if _MSC_VER
// CRT static initializer
#define RTCCALL __cdecl
#pragma section(".CRT$XCU", read)
#define RT_STATIC_INIT(f) \
static void RTCCALL f(void); \
__declspec(allocate(".CRT$XCU")) void(RTCCALL * f##_)(void) = f; \
static void RTCCALL f(void)
#elif __GNUC__ || __clang__
// GCC + Clang static initializer
#define RT_STATIC_INIT(f) \
static void f(void) __attribute__((constructor)); \
static void f(void)
#endif
#define RT_STRINGIFY(x) #x
#define RT_TOSTRING(x) RT_STRINGIFY(x)
#ifndef RT_PRINT_WIDTH
#define RT_PRINT_WIDTH 80
#endif
#define RT_TEST_CWIDTH 30
#define RT_LINE_CWIDTH 5
#define RT_FILE_CWIDTH (RT_PRINT_WIDTH - RT_LINE_CWIDTH - RT_TEST_CWIDTH - 5)
#define RT_FILE_CTRUNC (RT_FILE_CWIDTH - 3)
#define rt_print(f, t, x) \
fprintf(f, "[%" RT_TOSTRING(RT_TEST_CWIDTH) "s %s%s:%-" RT_TOSTRING(RT_LINE_CWIDTH) "d] --> %s\n", \
t, strlen(__FILE__) > RT_FILE_CTRUNC ? "..." : "", \
__FILE__ + (strlen(__FILE__) > RT_FILE_CWIDTH ? strlen(__FILE__) - RT_FILE_CTRUNC : 0), \
__LINE__, x)
#define rt_info(x) rt_print(stdout, rt_test_current_name, x)
#define rt_squawk(x) rt_print(stderr, rt_test_current_name, x)
#ifdef RT_BREAK_ON_FAILURE
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#define rt_breakpoint_or_not() DebugBreak()
#else
#include <signal.h>
#define rt_breakpoint_or_not() raise(SIGTRAP)
#endif
#else
#define rt_breakpoint_or_not()
#endif
#define rt_fail(why) \
do { \
char rt_failbuf[1024]; \
snprintf(rt_failbuf, 1024, "fail < %s", why); \
rt_failures[rt_failed++] = rt_test_index; \
rt_set_result = true; \
rt_squawk(rt_failbuf); \
rt_breakpoint_or_not(); \
} while (0)
#define rt_pass \
do { \
++rt_passed; \
rt_set_result = true; \
return; \
} while (0)
#define rt_assert(v, desc, ...) \
if (!(v)) rt_fail(desc " < " #v)
#define rt_assert_fmt(v, desc, fmt, ...) \
if (!(v)) { \
char assert_buf[1024]; \
snprintf(assert_buf, 1024, "%s < %s " fmt, #v, desc, __VA_ARGS__); \
rt_fail(assert_buf); \
}
#define RT_DIVSEP \
"--------------------------------------------------------------------" \
"-------------------------------------------------------------------" \
"-"
#define RT_TRUNC(x) "%." RT_TOSTRING(RT_PRINT_WIDTH) "s\n", x
#define RT_SUMMARY_FMT "\t%10s: %10zu %s\n"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#define rt_insert \
typedef void (*rt_test_fn)(void); \
static rt_test_fn rt_tests[1024]; \
static const char *rt_test_names[1024]; \
\
static rt_test_fn rt_suite_setup_fn; \
static rt_test_fn rt_test_setup_fn; \
static rt_test_fn rt_test_teardown_fn; \
static rt_test_fn rt_suite_teardown_fn; \
\
static size_t rt_test_index; \
static const char *rt_test_current_name; \
static rt_test_fn rt_test_current; \
\
static size_t rt_test_count; \
static bool rt_set_result; \
static size_t rt_failed, rt_passed; \
static size_t rt_failures[1024]; \
\
int main(int argc, const char *argv[]) { \
if (rt_suite_setup_fn) rt_suite_setup_fn(); \
for (rt_test_index = 0; rt_test_index < rt_test_count; ++rt_test_index) { \
if (rt_test_setup_fn) rt_test_setup_fn(); \
rt_test_current = rt_tests[rt_test_index]; \
rt_test_current_name = rt_test_names[rt_test_index]; \
rt_set_result = false; \
rt_info("starting"); \
rt_test_current(); \
if (!rt_set_result) ++rt_passed; \
rt_failed > 0 && rt_failures[rt_failed - 1] == rt_test_index ? \
rt_squawk("failed") : \
rt_info("passed"); \
if (rt_test_teardown_fn) rt_test_teardown_fn(); \
} \
if (rt_suite_teardown_fn) rt_suite_teardown_fn(); \
FILE *of = rt_failed ? stderr : stdout; \
fprintf(of, RT_TRUNC(RT_DIVMAJ)); \
fprintf(of, "Summary\n"); \
fprintf(of, RT_SUMMARY_FMT, "Run", rt_test_count, "="); \
fprintf(of, RT_SUMMARY_FMT, "Passed", rt_passed, "+"); \
fprintf(of, RT_SUMMARY_FMT, "Failed", rt_failed, rt_failed ? "!" : ""); \
fprintf(of, RT_TRUNC(RT_DIVSEP)); \
if (rt_failed) { \
fprintf(of, "\nFailing tests:\n"); \
for (size_t i = 0; i < rt_failed; ++i) { \
fprintf(of, "\t%s\n", rt_test_names[rt_failures[i]]); \
} \
} \
fprintf(of, RT_TRUNC(RT_DIVSEP)); \
}
#ifndef RT_NO_MAIN
rt_insert
#endif
#endif
@justinbowes
Copy link
Author

RUI Test


How do I...

 #include "rui_test.h"

 rt_test(sanity) {
    rt_must(true);
    rt_must_not(false);
 }

That's it.

No, how do I really...

 #include "rui_test.h"
 #include "myobject.h"

 #define RT_BREAK_ON_FAILURE

 static myobject_t *obj;
 static const char *test_str = "gom jabbar"
 static size_t test_data_size;

 rt_suite_begin() {
    obj = myobject_new();
    test_data_size = sizeof(test_str);
 }
 rt_suite_end() {
    myobject_free(obj);
 }
 rt_test_begin() {
    myobject_add(test_str);
 }
 rt_test_end() {
    myobject_clear(obj);
 }

 rt_test(strings) {
    rt_must_be_equal(myobject_size(obj), test_data_size + 1);
    rt_strings_must_be_same(myobject_tostr(obj), test_str);
    rt_numbers_must_be_within(myobject_size(obj) * 0.1f, 
                  test_data_size * 0.1f, 
                  0.001f)
 }

 rt_test(floats) {
    rt_include(strings);
    rt_must_be_null(myobject_tofloat(obj));
 }

 #include <math.h>
 #include <complex.h>
 rt_test(sanity) {
    float complex euler_val = cpowf(M_E, I * M_PI);
    bool eulers_identity = (creal(euler_val) == -1.f && 
                cimag(euler_val) == 0.f);
    rt_assert(eulers_identity,
          "Universe error", "e^(i*π) == -1 should be true, "
                    "but was [%f, %f]. Check local value of true.",
                    creal(euler_val), cimag(euler_val));
 }


================================================================================
[                   test_strings ...quanta/src/common/rui/test/rui_test.h:287  ] --> starting
[                   test_strings ...quanta/src/common/rui/test/rui_test.h:287  ] --> passed
[                    test_floats ...quanta/src/common/rui/test/rui_test.h:287  ] --> starting
[                    test_floats ...quanta/src/common/rui/test/rui_test.h:287  ] --> passed
[                   test_sanity ...quanta/src/common/rui/test/rui_test.h:287  ] --> starting
[                   test_sanity ...common/rui/test/rui_properties_test.c:84   ] --> fail < e^(i*π) == -1 should be true, but was [%f, %f]. Check local value of true. < eulers_identity == true
[                   test_sanity ...quanta/src/common/rui/test/rui_test.h:287  ] --> failed
================================================================================
Summary
           Run:          3 =
        Passed:          2 +
        Failed:          1 !
--------------------------------------------------------------------------------

Failing tests:
    test_sanity
--------------------------------------------------------------------------------
Program ended with exit code: 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment