Skip to main content

A test framework for C projects.

Project description

buildstatus coverage codecov

🦁 Nala

A test framework for C projects.

Based on narwhal and narmock.

Features

  • Automatic test discovery

  • Use the same generic assertions everywhere

  • Assertion failures reported as diffs

  • Easy-to-use output capturing utilities

  • 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.

The test program takes an optional single argument to filter out which tests to run.

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.

https://github.com/eerimoq/nala/raw/master/docs/build-and-run.png

Build all tests but only run those whose name contains time.

https://github.com/eerimoq/nala/raw/master/docs/build-and-run-one-test.png

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.

https://github.com/eerimoq/nala/raw/master/docs/build-and-run-assert-eq-fail.png

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

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.

$ 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.

<func>_mock(<params>, <res>)      - check parameters and return
<func>_mock_ignore_in(<res>)      - ignore parameters and return
<func>_mock_none()                - no calls allowed
<func>_mock_implementation(*)     - replace implementation
<func>_mock_real()                - call real implementation
<func>_mock_reset()               - mock reset

Per call control.

<func>_mock_once(<params>, <res>) - check parameters and return once (per call)
<func>_mock_ignore_in_once(<res>) - ignore parameters and return once (per call)
<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.

<func>_mock_set_errno(int)        - errno on return
<func>_mock_set_callback(*)       - additional checks and/or actions

For selected function parameters

<func>_mock_ignore_<param>_in()               - ignore on input
<func>_mock_set_<param>_in(*, size_t)         - check on input
<func>_mock_set_<param>_in_assert(*)          - custom assert function on input
<func>_mock_set_<param>_in_pointer(*, size_t) - check pointer (the address) on input
<func>_mock_set_<param>_out(*, size_t)        - value on return
<func>_mock_set_<param>_out_copy(*)           - custom output copy function

For variadic functions

<func>_mock_ignore_va_arg_in_at(uint)          - ignore on input
<func>_mock_set_va_arg_in_at(uint, *, size_t)  - check on input
<func>_mock_set_va_arg_in_pointer_at(uint, *)  - check pointer on input
<func>_mock_set_va_arg_out_at(uint, *, size_t) - value on return

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

nala-0.87.0.tar.gz (38.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nala-0.87.0-py2.py3-none-any.whl (40.1 kB view details)

Uploaded Python 2Python 3

File details

Details for the file nala-0.87.0.tar.gz.

File metadata

  • Download URL: nala-0.87.0.tar.gz
  • Upload date:
  • Size: 38.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.3

File hashes

Hashes for nala-0.87.0.tar.gz
Algorithm Hash digest
SHA256 5a9defb374d73829612f7c3d05233d0c30fe6f52b70a5c187fdfe21aa9707bd2
MD5 a2d24216f25d0d93df7edfc1d0e8a2ea
BLAKE2b-256 f5081970a843e00baa59774dfb32e5cc19194cb2825c063d8e0645ee5c939d52

See more details on using hashes here.

File details

Details for the file nala-0.87.0-py2.py3-none-any.whl.

File metadata

  • Download URL: nala-0.87.0-py2.py3-none-any.whl
  • Upload date:
  • Size: 40.1 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.3

File hashes

Hashes for nala-0.87.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 158c3b8095125ee0854a2404c8c0f6740160aaf998f5c26e2c4935b6f5ff5e74
MD5 20bee0588a0fab70a518b6e0845d0d40
BLAKE2b-256 86d0b54c17aef7d0f28fd91d00e8e32909268931b2c528c88d5635a81f66f302

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page