### `struct tm_unit_test_runner_i`
defines an interface for *Unit Test Runners* -- objects capable of
running unit tests.
A codebase may have multiple unit test runners. One runner might log the results to the command
line, while another uploads it to a web server, etc.
User data for callback functions.
bool (*test_file)(tm_unit_test_runner_o *inst, const char *name);
Returns `true` if the tests in the file named `name` should run. You can use this to
configure a runner to only run a subset of the tests.
bool (*test_custom)(tm_unit_test_runner_o *inst, const char *name);
Returns `true` if the custom test with the specified `name` should be run. Custom tests
are tests that don't run in every build (typically because they are expensive to run).
bool (*test_disk)(tm_unit_test_runner_o *inst);
Returns `true` if the unit tests are allowed to touch the disk. If it returns `false`, disk
tests should be skipped.
bool (*test_network)(tm_unit_test_runner_o *inst);
Returns `true` if the unit tests are allowed to access the network. If it returns `false`,
network tests should be skipped.
bool (*test_slow_paths)(tm_unit_test_runner_o *inst);
Returns `true` if "slow" tests should run. The entire test suite of The Machinery should
finish in a fraction of a section. If you have complicated tests that take longer, you should
only run them when slow test paths are enabled.
bool (*record)(tm_unit_test_runner_o *inst, bool pass, const char *test_str, const char *file,
Records the result of a unit test with the runner.
* `pass` specifies if the test succeeded or not.
* `test_str` is a string describing the test that will be printed in test reports.
* `file` is the `__FILE__` where the test is located.
* `line` is the `__LINE__` where the test is located.
Returns the value of `pass`.
void (*expect_error)(tm_unit_test_runner_o *inst, const char *err, const char *file,
Tells the test runner to expect the error message `err`.
Normally, a test runner does not expect any errors to occurs, so if a test logs an error
), the test is considered to have failed.
However, sometimes you want to test that the error handling works and that an API produces a
certain error when called in a certain way. To do that, you first call `expect_error()`
the error you expect and then run the specific unit test that should produce the error
If the expected error message is written to `tm_error_i`
before the next call to `record()`
the test is considered to have succeeded (produced the expected error message), otherwise,
the test is considered to have failed (not produced the right error message).
Note that to make this work, the unit test runner has to set up the `tm_error_api->def`
interface to call into the unit test runner code. That way, it can intercept the
call and check if the error matches the expectations or not.
For a sample implementation of how this can work, see `unit_test/unit_test.c`.
### `struct tm_unit_test_i`
Interface for unit tests.
Plugins that want to implement a set of unit tests can register them using this interface. To
find all unit test, query the API registry for `TM_UNIT_TEST_INTERFACE_NAME`
Typically a plugin would register a single `tm_unit_test_i`
, that runs all the unit tests
for the plugin. You don't have to register a separate `tm_unit_test_i`
for each individual
unit test in the plugin.
const char *name;
Name of this unit test. Typically, this is identical to the name of the plugin.
void (*test)(tm_unit_test_runner_i *tr, struct tm_allocator_i *a);
Callback that runs the unit tests, using the specified test runner. The supplied allocator
can be used for any allocations that the unit test needs to make.
#define TM_UNIT_TEST_INTERFACE_NAME "tm_unit_test_i"
A basic unit test runner that logs failed tests using the log system.
extern struct tm_unit_test_runner_i tm_basic_unit_test_runner;
#define TM_UNIT_TEST(tr, assertion)
Unit test macro. Tests the `assertion` using the test runner `tr`. In case of an error, a
stringified version of the `assertion` is logged.
#define TM_UNIT_TESTF(tr, assertion, format, ...)
, but records a formatted string in case of error.
#define TM_EXPECT_ERROR(tr, error)
Macro for calling `expect_error()`
with current `__FILE__` and `__LINE__`.