Unit tests are tests that allow you to check the correctness and efficiency of your functions.

To start with we need the librairy criterion.

Criterion is a unit-testing tool that will allow you to test your code efficiently. Criterion lets you create a set of tests that will be executed on your code.

Install Criterion

$ sudo dnf install criterion

If you want your tests to work, you must compile your code with Criterion’s library using the -lcriterion flag.

To see the coverage you need to install gcovr

$ sudo dnf install gcovr

Basic of Unit testing

Assert

Asserts are Criterion’s way of defining tests to run and all asserts are located in the header <criterion/criterion.h>.

Basic asserts ./ The asserts below allows to test a function while returning a sentence if the condition is not verified.

Test(suite_name, test_name) {
    int i = 2;
    cr_assert(i * 2 == 4, "The result was %d. Expected %d", i * 2, 4);
}

In this example if the condition i * 2 == j is not verified the message is displayed.

cr_assert(condition)
cr_assert_not(condition)

If you want to compare arrays, please refer to cr_assert_str_eq() or cr_assert_arr_eq().

Redirections

To use the following assertions, you must include <criterion/redirect.h> along with <criterion/criterion.h>.

cr_assert_stdout_eq_str(Value)

These asserts allow to compare the content of stdout with Value

 cr_assert_stderr_eq_str(Value)

These asserts allow to compare the content of stderr with value.

Here is a sample usage of this assert.

#include <criterion/criterion.h>
#include <criterion/redirect.h>

void redirect_all_stdout(void)
{
        cr_redirect_stdout();
        cr_redirect_stderr();
}

int error(void)
{
        write(2, "error", 5);
        return(0);
}

Test(errors, exit_code, .init=redirect_all_stdout)
{
        error();
        cr_assert_stderr_eq_str("error", "");
}

NB: You must include criterion.h and redirect.h in this order, otherwise your tests won’t work.

Test options

.init

This parameter takes a function pointer as an argument. Criterion will execute the function just before running the test. The function pointer should be of type void(*)(void).

void my_func(void)
{
        my_putstr("My test\n");
}

Test(suite_name, test_name, .init = my_func)
{
        //tests
}

.fini

This parameter takes a function pointer to a function that will be executed after the tests is finished.

It takes the same pointer type as the .init parameter, and also has the same usage.

.exit_code

If you want to test your error handling, you can use the .exit_code parameter so the test will be marked as passed if the given exit code is found.

int error(void)
{
        write(2, "error", 5);
        exit(0);
}

Test(errors, exit_code, .exit_code = 84)
{
        error();
        cr_assert_stderr_eq("error", "");
}

.signal

If a test receives a signal, it will by default be marked as a failure. However, you can expect a test to pass if a special kind of signal is received.

.disabled

If it's true, the test will be skipped.

.description

This parameter must be used in order to give extra definition of the test’s purpose, which can be quite helpful if your suite_name and test_name aren’t as explicit as you would like.

.timeout

This parameter takes a double, representing a duration. If your test takes longer than this duration to run, the test will be marked as failed.

Makefile rule for Unit Test

$ gcc -o binary_name file.c tests/test_project.c -- coverage -lcriterion
$ gcovr --exclude tests
$ gcovr --exclude tests --branches

NB: The command to execute the test units must be

$ make tests_run

Criterion doc epitech