Attractor is a single function unit test framework written in C11.
Copy attractor.c and attractor.h in your test folder.
Attractor provide a ATT_ASSERT macro that you can use to test your code. The macro outputs a
number different from zero if the test passes:
ATT_ASSERT(the_value_to_test, the_expected_value, "description")The ATT_ASSERT macro accept these types:
charunsigned charchar*const char*shortunsigned shortintunsigned intlongunsigned longlong longunsigned long longfloatdoublelong doublevoid*_Bool
In case of unknown type the input is assumed to be void*.
ATT_ASSERT(value, expected, description)you pass the value to test, the expected one and a description of the test.void att_set_verbose(unsigned int verbose)set verbosity.void att_set_show_error(unsigned int show_error)set to show errors.void att_set_generic_callback(att_generic_callback callback)set a callback to run for generic testsvoid att_set_test_callback(att_test_callback callback)set a callback to call for all tests.unsigned int att_get_valid_tests(void)return the count of valid tests.unsigned int att_get_total_tests(void)return the count of run tests.void att_set_float_epsilon(long double epsilon)set the tolerance for float comparisons.long double att_get_float_epsilon(void)return the current float comparison tolerance.
#include <attractor.h>
int var_to_test_1 = 1;
float var_to_test_2 = 2.0;
ATT_ASSERT(var_to_test_1, 1, "one == one")
ATT_ASSERT(var_to_test_2, 2.0, "2.0 == 2.0")
ATT_ASSERT(3ll, 3ll, "long long 3 == long long 3")
printf("\nTests valid/run: %d/%d\n", att_get_valid_tests(), att_get_total_tests());The code does not output anything by default. But you can change this using:
// By defining:
#define ATT_VERBOSE 1 // 0, 1, 2
// Or by calling:
att_set_verbose(1); // 0, 1, 2With verbose = 1 the code output . for a valid test and F for a failed test:
...
Tests valid/run: 3/3
With verbose = 2 the code output:
OK [int] one == one
OK [float] 2.0 == 2.0
OK [long long] long long 3 == long long 3
Tests valid/run: 3/3
Failures are reported independently of the verbosity level: they are shown whenever att_show_error
is set (the default). Set att_set_show_error(0) if you really want complete silence.
If we change var_to_test_2 in 1.0 this is the output with verbose = 1. Each failure is prefixed
with the source file:line and, at verbose 0 or 1, the test description, so it is self-contained:
.F
test.c:7: 2.0 == 2.0: Expected 2.000000, got 1.000000
.
Tests valid/run: 2/3With verbose = 0 only the failures are printed (no . for the passing tests):
test.c:7: 2.0 == 2.0: Expected 2.000000, got 1.000000
Tests valid/run: 2/3
And with verbose = 2 the description is already on the result line, so it is not repeated:
OK [int] one == one
NO [float] 2.0 == 2.0
tests/char.c:25: Expected 2.000000, got 1.000000
OK [long long] long long 3 == long long 3
Tests valid/run: 2/3
Check tests/test.c for an advanced example.
The valid/total test counters are atomic, so ATT_ASSERT can be used from several threads at once
without losing updates. att_get_valid_tests() and att_get_total_tests() therefore report the
exact number of assertions run, even when tests execute concurrently.
Attractor treats strings as strings. So the content of the string is output to the terminal as it is
in case of error. By defining #define ATT_STRING_AS_POINTERS 1, it will simply output the pointer
address.
float, double and long double are compared for exact equality by default. Because computed
floating point results are rarely bit-exact, you can define a tolerance. Values are then considered
equal when their absolute difference is within ATT_FLOAT_EPSILON:
// 1e-6 is usually enough
#define ATT_FLOAT_EPSILON 1e-6
ATT_ASSERT(0.1 + 0.2, 0.3, "sum within epsilon")The default is 0 (exact equality). The compile-time ATT_FLOAT_EPSILON is just the initial value;
you can also change the tolerance at runtime, the same way you change verbosity:
att_set_float_epsilon(1e-6);
ATT_ASSERT(0.1 + 0.2, 0.3, "sum within epsilon")
att_set_float_epsilon(0); // back to exact equalityYou can register a custom function to be called when a type is not one of the list.
#include <attractor.h>
typedef struct unknown_struct {
int unknown;
} unknown_struct;
int assert_unknown(void* result, void* expected, const char *description) {
if(!result || !expected) {
return 0;
}
return ((unknown_struct*)result)->unknown == ((unknown_struct*)expected)->unknown;
}
void test_unknown_callback() {
unknown_struct sa;
unknown_struct sb;
sa.unknown = 1;
sb.unknown = 1;
att_set_generic_callback(&assert_unknown);
ATT_ASSERT(&sa, &sb, "Unknown struct sa = sb")
att_set_generic_callback(NULL);
}