A testing system for C, contained in 1 file.
-
Small, Portable, Lightweight
greatest doesn't depend on anything beyond ANSI C89, and the test scaffolding should build without warnings when compiled with
-Wall -Weverything -pedantic
. It is under 1,000 LOC (SLOCCount), and does no dynamic allocation. -
Permissive License
greatest is released under the ISC License. You can use it freely, even for commercial purposes.
-
Easy To Set Up
To use, just
#include "greatest.h"
in your project. There is very little boilerplate. Most features are optional. -
Un-Opinionated
When a command-line test runner is useful, greatest can provide one, but it can also run as part of other programs. It doesn't depend on a particular build system or other tooling, and should accommodate a variety of testing approaches. It actively avoids imposing architectural choices on code under test. While greatest was designed with C in mind, it attempts to be usable from C++.
-
Modular
Tests can be run individually, or grouped into suites. Suites can share common setup, and can be in distinct compilation units.
-
Low Friction
Specific tests or suites can be run by name, for focused and rapid iteration during development. greatest adds very little startup latency.
There are some compile-time options, and slightly nicer syntax for
parametric testing (running tests with arguments) is available if
compiled with -std=c99
.
I wrote a blog post with more information.
theft, a related project, adds property-based testing.
#include "greatest.h"
/* A test runs various assertions, then calls PASS(), FAIL(), or SKIP(). */
TEST x_should_equal_1(void) {
int x = 1;
ASSERT_EQ(1, x); /* default message */
ASSERT_EQm("yikes, x doesn't equal 1", 1, x); /* custom message */
/* printf expected and actual values as "%d" if they differ */
ASSERT_EQ_FMT(1, x, "%d");
PASS();
}
/* Suites can group multiple tests with common setup. */
SUITE(the_suite) {
RUN_TEST(x_should_equal_1);
}
/* Add definitions that need to be in the test runner's main file. */
GREATEST_MAIN_DEFS();
int main(int argc, char **argv) {
GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */
/* Individual tests can be run directly. */
/* RUN_TEST(x_should_equal_1); */
/* Tests can also be gathered into test suites. */
RUN_SUITE(the_suite);
GREATEST_MAIN_END(); /* display results */
}
Output:
$ make simple && ./simple
cc -g -Wall -Werror -pedantic simple.c -o simple
* Suite the_suite:
.
1 test - 1 passed, 0 failed, 0 skipped (5 ticks, 0.000 sec)
Total: 1 test (47 ticks, 0.000 sec), 3 assertions
Pass: 1, fail: 0, skip: 0.
Test cases should call assertions and then end in PASS(), SKIP(),
FAIL(), or one of their message variants (e.g. SKIPm("TODO");
).
If there are any test failures, the test runner will return 1,
otherwise it will return 0. (Skips do not cause a test runner to
report failure.)
Tests and suites are just functions, so normal C scoping rules apply. For example, a test or suite named "main" will have a name collision.
(For more examples, look at example.c and example-suite.c.)
Note that all assertions have a "message" form, which takes an
additional first argument, a custom string to include in the test
failure message. This form adds an 'm' suffix to the ASSERT name. For
example, ASSERT_EQ(foo, bar);
could also be used as
ASSERT_EQm("theese should match", foo, bar)
. If the "message" form is
not used, greatest will attempt to create a reasonable default message.
Assert that COND
evaluates to a true value.
Assert that COND
evaluates to a false value.
Assert that EXPECTED == ACTUAL
. To compare with a custom equality test
function, use ASSERT_EQUAL_T
instead. To print the values if they
differ, use ASSERT_EQ_FMT
.
Assert that EXPECTED == ACTUAL
. If they are not equal, print their
values using FORMAT as the printf
format string.
For example: ASSERT_EQ_FMT(123, result, "%d");
Note: EXPECTED
and ACTUAL
will be evaluated more than once on
failure, so they should not be a function call with side effects.
(Since their type is not known by the macro, they cannot be
captured in a local variable.)
Assert that ACTUAL is within EXPECTED +/- TOLERANCE, once the values
have been converted to a configurable floating point type
(GREATEST_FLOAT
).
Assert that the strings are equal
(i.e., strcmp(EXPECTED, ACTUAL) == 0
).
Assert that the first SIZE bytes of the strings are equal
(i.e., strncmp(EXPECTED, ACTUAL, SIZE) == 0
).
Assert that the first SIZE bytes of memory pointed to by EXPECTED and ACTUAL are equal. If the memory differs, print a hexdump and highlight the lines and individual bytes which do not match.
Assert that the enum value EXPECTED is equal to ACTUAL. If not, convert
each enum value to a string using ENUM_STR_FUN
before printing them.
ENUM_STR_FUN
should have a type like:
const char *some_enum_str(enum some_enum x);
Assert that EXPECTED and ACTUAL are equal, using the greatest_equal_cb
function pointed to by TYPE_INFO->equal
to compare them. The
function's UDATA argument can be used to pass in arbitrary user data (or
NULL). If the values are not equal and the TYPE_INFO->print
function
is defined, it will be used to print an "Expected: X, Got: Y" message.
Assert that COND
evaluates to a true value. If not, then use
longjmp(3) to immediately return from the test case and any intermediate
function calls. (If built with GREATEST_USE_LONGJMP
set to 0, then all
setjmp/longjmp-related functionality will be compiled out.)
Because of how PASS()
, ASSERT()
, FAIL()
, etc. are implemented
(returning a test result enum value), calls to functions that use them
directly from test functions must be wrapped in CHECK_CALL
:
TEST example_using_subfunctions(void) {
CHECK_CALL(less_than_three(5));
PASS();
}
This is only necessary if the called function can cause test failures.
Test runners build with the following command line options:
Usage: (test_runner) [-hlfv] [-s SUITE] [-t TEST]
-h print this Help
-l List suites and their tests, then exit
-f Stop runner after first failure
-v Verbose output
-s SUITE only run suite w/ name containing SUITE substring
-t TEST only run test w/ name containing TEST substring
If you want to run multiple test suites in parallel, look at parade.
These command line options are processed by GREATEST_MAIN_BEGIN();
.
All the macros have unprefixed and prefixed forms. For example, SUITE
is the same as GREATEST_SUITE
.
Checkout the source for the entire list.
These aliases can be disabled by #define
-ing GREATEST_USE_ABBREVS
to 0.
If you want color output (PASS
in green, FAIL
in red, etc.), you can
pipe the output through the included greenest
script in contrib/
:
$ ./example -v | greenest
(Note that greenest
depends on a Unix-like environment.)
greatest itself doesn't have built-in coloring to stay small and portable.