A test framework for C projects.
Project description
🦁 Nala
A test framework for C projects.
Features
Automatic test discovery
Use the same generic assertions everywhere
Assertion failures reported as diffs
Easy-to-use output capturing utilities
Traceback(s) on failure
Amalgamated source file and header ready to drop in your project
Mocking support
Works well with errors reported by sanitizers
Test isolation with fork()
JSON test report
Installation
It’s recommended to install Nala with pip.
$ pip install nala
Alternatively, if mocking is not needed, you can download the amalgamated header and source files:
Drop the two files in your project, make sure nala.c is compiled and linked just like the other source files of your test program and you should be good to go.
Example
Use nala init foo to create a test folder called foo.
$ nala init foo
Run 'make -C foo' to build and run all tests!
Two test files are created, foo/test_assertions.c and foo/test_time.c. The first uses all assertions and captures output, and the second mocks the time function.
The assertions tests:
#include "nala.h"
TEST(assertions)
{
ASSERT_EQ(NULL, NULL);
ASSERT_NE(1, 2);
ASSERT_LT(1.0, 2.0);
ASSERT_LE(1, 1);
ASSERT_GT(2L, 1L);
ASSERT_GE(1, 1);
ASSERT_SUBSTRING("12345", "34");
ASSERT_NOT_SUBSTRING("12345", "4567");
ASSERT_MEMORY("abcd", "abcd", 5);
ASSERT(1 == 1);
CAPTURE_OUTPUT(output, errput) {
printf("std!\n");
fprintf(stderr, "err!\n");
}
ASSERT_EQ(output, "std!\n");
ASSERT_EQ(errput, "err!\n");
}
The time tests:
#include <time.h>
#include "nala.h"
#include "nala_mocks.h"
TEST(mock_time)
{
time_mock_once(42);
ASSERT_EQ(time(NULL), 42);
}
Build and run all tests.
Build all tests but only run those whose name contains time.
Now, make the time test fail to see what an error report looks like.
#include <time.h>
#include "nala.h"
#include "nala_mocks.h"
TEST(mock_time)
{
time_mock_once(-1);
ASSERT_EQ(time(NULL), 42);
}
Build and run all tests.
Compiler flags
Pass -no-pie -g -O0 -fsanitize=address to the compiler for better error reporting.
Read more about sanitizers here: https://en.wikipedia.org/wiki/AddressSanitizer
Debugging tips
Nala executes each test in its own process. This means that following the execution of a test with a debugger can be a bit tricky because debuggers like GDB can only follow a single process at a time.
If you’re using GDB, set a breakpoint at <test>_before_fork and then run the program until it stops at the breakpoint. Before continuing the program execution, tell GDB to follow the forked test process by setting follow-fork-mode to child.
All commands are shown below for the assertions test in the example above.
$ gdb ./a.out
(gdb) b assertions_before_fork
(gdb) r
(gdb) set follow-fork-mode child
(gdb) c
The test program takes optional arguments as below, which also can be helpful when debugging, especially –print-all-calls.
$ ./a.out
usage: ./a.out [-h] [-v] [-c] [-a] [<test-pattern>]
Run tests.
positional arguments:
test-pattern Only run tests containing given pattern.
optional arguments:
-h, --help Show this help message and exit.
-v, --version Print version information.
-c, --continue-on-failure Always run all tests.
-a, --print-all-calls Print all calls to ease debugging.
Mocking
Generating mocks
The nala generate_mocks command finds the functions mocked in your code and generates nala_mocks.h, nala_mocks.c and nala_mocks.ld. The first two files declare and define mocks, while the last file contains linker flags.
Use --rename-parameters-file to rename function parameters, often useful when mocking standard library functions. If not given, Nala renames a few function parameters by default.
Use --no-rename-parameters not to rename any function parameters. Overrides --rename-parameters-file.
Use --no-implementation when the implementation of functions starting with given pattern are not available in the binary (and therefore should not be called by the generated code). This option may be given multipe times.
$ gcc -DNALA_GENERATE_MOCKS -E *.c | nala generate_mocks
Nala requires source code to be expanded by the preprocessor. You can directly pipe the output of gcc -DNALA_GENERATE_MOCKS -E to the command-line utility.
Mock API
A function mock will call the real implementation by default. Use the functions below to control mock behaviour.
For all functions
Same behaviour for every call.
void <func>_mock(<params>, <res>) - check parameters and return
void <func>_mock_ignore_in(<res>) - ignore parameters and return
void <func>_mock_none() - no calls allowed
void <func>_mock_implementation(*) - replace implementation
void <func>_mock_real() - call real implementation
Per call control.
int <func>_mock_once(<params>, <res>) - check parameters and return once (per call)
int <func>_mock_ignore_in_once(<res>) - ignore parameters and return once (per call)
void <func>_mock_real_once() - call real implementation once (per call)
Change behaviour of currect mock. Works for both per call and every call functions above.
void <func>_mock_set_errno(int) - errno on return
void <func>_mock_set_callback(*) - additional checks and/or actions
Get per call input parameters.
*<func>_mock_get_params_in(int) - get input parameters for given handle
For selected function parameters
void <func>_mock_ignore_<param>_in() - ignore on input
void <func>_mock_set_<param>_in(*, size_t) - check on input
void <func>_mock_set_<param>_in_assert(*) - custom assert function on input
void <func>_mock_set_<param>_in_pointer(*) - check pointer (the address) on input
void <func>_mock_set_<param>_out(*, size_t) - value on return
void <func>_mock_set_<param>_out_copy(*) - custom output copy function
For variadic functions
Variadic functions mocks are slightly different from the above. They also have a format string and an ellipsis in some “every call” and “per call” functions.
void <func>_mock(<params>, <res>, format, ...)
void <func>_mock_once(<params>, <res>, format, ...)
Not yet implemented:
void <func>_mock_ignore_in(<params>, <res>, format)
void <func>_mock_ignore_in_once(<params>, <res>, format)
The format string supports the following specifiers.
%d - signed integer
%u - unsigned integer
%ld - signed long integer
%lu - unsigned long integer
%p - pointer address
%s - string
The variadic parameters are controlled by index instead of name.
void <func>_mock_ignore_va_arg_in_at(uint) - ignore on input
void <func>_mock_set_va_arg_in_at(uint, *, size_t) - check on input
void <func>_mock_set_va_arg_in_assert_at(uint, *) - custom assert function on input
void <func>_mock_set_va_arg_in_pointer_at(uint, *) - check pointer on input
void <func>_mock_set_va_arg_out_at(uint, *, size_t) - value on return
void <func>_mock_set_va_arg_out_copy_at(uint, *) - custom output copy function
Limitations
Structs and unions passed by value are ignored.
va_list parameters are ignored.
malloc() and free() can’t be mocked if forking and using gcov. They probably can if wrapping __gcov_fork() in an suspend/resume-block.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for nala-0.109.0-py2.py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | eb9f4f9597b35fb086ba51b7b665516468973a48904e4fb1ac46d1e4808d70d2 |
|
MD5 | e59111412d631ca39617adf87eaabdec |
|
BLAKE2b-256 | 463fb3d225df81120d296732caa234aa09b3dd5bc33c3e50513422765754326d |