Madelyn Olson 5b1fd222ed
An initial simple unit test framework (#344)
The core idea was to take a lot of the stuff from the C unity framework
and adapt it a bit here. Each file in the `unit` directory that starts
with `test_` is automatically assumed to be a test suite. Within each
file, all functions that start with `test_` are assumed to be a test.

See unit/README.md for details about the implementation.

Instead of compiling basically a net new binary, the way the tests are
compiled is that the main valkey server is compiled as a static archive,
which we then compile the individual test files against to create a new
test executable. This is not all that important now, other than it makes
the compilation simpler, but what it will allow us to do is overwrite
functions in the archive to enable mocking for cross compilation unit
functions. There are also ways to enable mocking from within the same
compilation unit, but I don't know how important this is.

Tests are also written in one of two styles:
1. Including the header file and directly calling functions from the
archive.
2. Importing the original file, and then calling the functions. This
second approach is cool because we can call static functions. It won't
mess up the archive either.

---------

Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
2024-05-02 20:00:04 -07:00
..

Introduction

Valkey uses a very simple C testing framework, built up over time but now based loosely off of Unity.

All test files being test_ in the unit directory. A single test file can have multiple individual tests, and they must be of the form int test_<test_name>(int argc, char *argv[], int flags) {, where test_name is the name of the test. The test name must be globally unique. A test will be marked as successful if returns 0, and will be marked failed in all other cases.

The test framework also parses several flags passed in, and sets them based on the arguments to the tests.

Tests flags:

  • UNIT_TEST_ACCURATE: Corresponds to the --accurate flag. This flag indicates the test should use extra computation to more accurately validate the tests.
  • UNIT_TEST_LARGE_MEMORY: Corresponds to the --large-memory flag. This flag indicates whether or not tests should use more than 100mb of memory.
  • UNIT_TEST_SINGLE: Corresponds to the --single flag. This flag indicates that a single test is being executed.

Tests are allowed to be passed in additional arbitrary argv/argc, which they can access from the argc and argv arguments of the test.

Assertions

There are a few built in assertions that can be used, that will automatically return from the current function and return the correct error code. Assertions are also useful as they will print out the line number that they failed on.

  • TEST_ASSERT(condition): Will evaluate the condition, and if it fails it will return 1 and print out the condition that failed.
  • TEST_ASSERT_MESSAGE(message, condition): Will evaluate the condition, and if it fails it will return 1 and print out the provided message.

Other utilities

If you would like to print out additional data, use the TEST_PRINT_INFO(info, ...) option, which has arguments similar to printf. This macro will also print out the function the code was executed from in addition to the line it was printed from.

Example test

int test_example(int argc, char *argv[], int flags) {
    TEST_ASSERT(5 == 5);
    TEST_ASSERT_MESSAGE("This should pass", 6 == 6);
    return 0;
} 

Running tests

Tests can be run by executing:

make valkey-unit-tests
./valkey-unit-tests

Running a single unit test file

./valkey-unit-tests --single crc64.c

Will just run the crc64.c file.