mirror of
https://github.com/3cky/mbusd
synced 2025-10-12 23:14:13 +08:00
339 lines
10 KiB
Markdown
339 lines
10 KiB
Markdown
# greatest
|
|
|
|
A testing system for C, contained in 1 file.
|
|
|
|
|
|
## Key Features
|
|
|
|
- **Small, Portable, Lightweight**
|
|
|
|
greatest doesn't depend on anything beyond ANSI C89, and the test
|
|
scaffolding should build without warnings when compiled with
|
|
`-Wall -Wextra -pedantic`. It is under 1,000 LOC (SLOCCount),
|
|
and does no dynamic allocation.
|
|
|
|
- **Permissive License**
|
|
|
|
greatest is released under the [ISC License][ISC]. 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) if compiled
|
|
with a C99 or later language standard.
|
|
|
|
I wrote a
|
|
[blog post](http://spin.atomicobject.com/2013/07/31/greatest-c-testing-embedded/)
|
|
with more information.
|
|
|
|
[theft][], a related project, adds [property-based testing][pbt].
|
|
|
|
[1]: http://spin.atomicobject.com/2013/07/31/greatest-c-testing-embedded/
|
|
[theft]: https://github.com/silentbicycle/theft
|
|
[pbt]: https://spin.atomicobject.com/2014/09/17/property-based-testing-c/
|
|
[ISC]: https://opensource.org/licenses/isc-license.txt
|
|
|
|
## Basic Usage
|
|
|
|
```c
|
|
#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 options, 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:
|
|
|
|
```sh
|
|
$ 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`.)
|
|
|
|
|
|
## Filtering By Name
|
|
|
|
greatest runs all tests by default, but can be configured to only run
|
|
suites or tests whose names contain a filter string, and/or exclude
|
|
tests whose name contains a filter string. When test name filtering and
|
|
exclusion are used together, exclusion takes precedence.
|
|
|
|
void greatest_set_suite_filter(const char *name);
|
|
void greatest_set_test_filter(const char *name);
|
|
void greatest_set_test_exclude(const char *name);
|
|
|
|
These correspond to the following command line test runner options:
|
|
|
|
`-s SUITE`: Only run suites whose names contain the string "SUITE"
|
|
`-t TEST`: Only run tests whose names contain the string "TEST"
|
|
`-x EXCLUDE`: Exclude tests whose names contain the string "EXCLUDE"
|
|
|
|
For example, to run any tests with "tree" in the name, in suites with
|
|
"pars" in the name (such as "parser"), but exclude any tests whose names
|
|
also contain "slow":
|
|
|
|
./test_project -s pars -t tree -x slow
|
|
|
|
|
|
## Available Assertions
|
|
|
|
Assertions fail the current test unless some condition holds. All
|
|
assertions have a "message" variant (with an `m` suffix), which takes a
|
|
custom failure message string as their first argument. For example, the
|
|
assertion `ASSERT_EQ(apple, orange);` could instead be used like
|
|
`ASSERT_EQm("these should match", apple, orange)`. Non-message
|
|
assertions create a default message.
|
|
|
|
|
|
### `ASSERT(COND)`
|
|
|
|
Assert that `COND` evaluates to a true value.
|
|
|
|
|
|
### `ASSERT_FALSE(COND)`
|
|
|
|
Assert that `COND` evaluates to a false value.
|
|
|
|
|
|
### `ASSERT_EQ(EXPECTED, ACTUAL)`
|
|
|
|
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_EQ_FMT(EXPECTED, ACTUAL, FORMAT)`
|
|
|
|
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_IN_RANGE(EXPECTED, ACTUAL, TOLERANCE)`
|
|
|
|
Assert that ACTUAL is within EXPECTED +/- TOLERANCE, once the values
|
|
have been converted to a configurable floating point type
|
|
(`GREATEST_FLOAT`).
|
|
|
|
|
|
### `ASSERT_STR_EQ(EXPECTED, ACTUAL)`
|
|
|
|
Assert that the strings are equal
|
|
(i.e., `strcmp(EXPECTED, ACTUAL) == 0`).
|
|
|
|
|
|
### `ASSERT_STRN_EQ(EXPECTED, ACTUAL, SIZE)`
|
|
|
|
Assert that the first SIZE bytes of the strings are equal
|
|
(i.e., `strncmp(EXPECTED, ACTUAL, SIZE) == 0`).
|
|
|
|
|
|
### `ASSERT_MEM_EQ(EXPECTED, ACTUAL, SIZE)`
|
|
|
|
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_ENUM_EQ(EXPECTED, ACTUAL, ENUM_STR_FUN)`
|
|
|
|
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_EQUAL_T(EXPECTED, ACTUAL, TYPE_INFO, UDATA)`
|
|
|
|
Assert that EXPECTED and ACTUAL are equal, using the `greatest_equal_cb`
|
|
function pointed to by `TYPE_INFO->equal` to compare them. The
|
|
assertion'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_OR_LONGJMP(COND)`
|
|
|
|
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`
|
|
defined to 0, then all setjmp/longjmp-related functionality will be
|
|
compiled out.)
|
|
|
|
|
|
## Random Shuffling
|
|
|
|
Groups of suites or tests can be run in random order by using
|
|
`GREATEST_SHUFFLE_SUITES` and `GREATEST_SHUFFLE_TESTS`, respectively.
|
|
This can help find and eliminate coupling between tests.
|
|
|
|
The shuffling depends on the seed and the test/suite count, so a
|
|
consistent seed will only lead to reproducible ordering until the
|
|
group's count changes.
|
|
|
|
Shuffling suites:
|
|
|
|
SHUFFLE_SUITES(seed, {
|
|
RUN_SUITE(suite1);
|
|
RUN_SUITE(suite2);
|
|
RUN_SUITE(suite3);
|
|
RUN_SUITE(suite4);
|
|
RUN_SUITE(suite5);
|
|
});
|
|
|
|
Shuffling tests:
|
|
|
|
SHUFFLE_TESTS(seed, {
|
|
RUN_TEST(test_a);
|
|
RUN_TEST1(test_b, 12345);
|
|
RUN_TEST(test_c);
|
|
RUN_TESTp(test_d, "some_argument");
|
|
RUN_TEST(test_e);
|
|
});
|
|
|
|
Note: Any other code inside the block will be executed several times.
|
|
The shuffling macro expands to a loop with (count + 1) iterations -- the
|
|
first pass counts, and the following passes only execute the next chosen
|
|
suite/test. In particular, avoid running tests directly inside of a
|
|
`SHUFFLE_SUITES` block (without a suite), because the test will run over
|
|
and over.
|
|
|
|
|
|
## Sub-Functions
|
|
|
|
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.
|
|
|
|
|
|
## Command Line Options
|
|
|
|
Test runners build with the following command line options:
|
|
|
|
Usage: (test_runner) [--help] [-hlfv] [-s SUITE] [-t TEST]
|
|
-h, --help print this Help
|
|
-l List suites and tests, then exit (dry run)
|
|
-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
|
|
-t EXCLUDE exclude tests containing string EXCLUDE substring
|
|
|
|
Any arguments after `--` will be ignored.
|
|
|
|
If you want to run multiple test suites in parallel, look at
|
|
[parade](https://github.com/silentbicycle/parade).
|
|
|
|
These command line options are processed by `GREATEST_MAIN_BEGIN();`.
|
|
|
|
|
|
## Aliases
|
|
|
|
Most of the macros have prefixed and unprefixed forms. For example,
|
|
`SUITE` is the same as `GREATEST_SUITE`.
|
|
|
|
Check the source for the list -- search for `#if GREATEST_USE_ABBREVS`.
|
|
|
|
These aliases can be disabled by `#define`-ing `GREATEST_USE_ABBREVS` to 0.
|
|
|
|
|
|
## Color Output
|
|
|
|
If you want color output (`PASS` in green, `FAIL` in red, etc.), you can
|
|
pipe the output through the included `greenest` script in `contrib/`:
|
|
|
|
```sh
|
|
$ ./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.
|