summaryrefslogtreecommitdiffstats
path: root/doc/userguide/devguide/codebase/unittests-c.rst
blob: 0ae7bdf92e0c30f99e9c70fe61b559ce50cb637e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
**************
Unit Tests - C
**************

Unit tests are a great way to create tests that can check the internal state
of parsers, structures and other objects.

Tests should:

- use ``FAIL``/``PASS`` macros
- be deterministic
- not leak memory on ``PASS``
- not use conditions

Unit tests are used by developers of Suricata and advanced users who would like to contribute by debugging and testing the engine.
Unit tests are small pieces (units) of code which check certain code functionalities in Suricata. If Suricata's code is modified, developers can run unit tests to see if there are any unforeseen effects on other parts of the engine's code.
Unit tests will not be compiled with Suricata by default.
If you would like to compile Suricata with unit tests, enter the following during the configure-stage::

   ./configure --enable-unittests

The unit tests specific command line options can be found at `Command Line Options <https://docs.suricata.io/en/suricata-6.0.3/command-line-options.html#unit-tests>`_.

Example:
You can run tests specifically on flowbits. This is how you should do that::

   suricata -u -U flowbit

It is highly appreciated if you would run unit tests and report failing tests in our `issue tracker
<https://redmine.openinfosecfoundation.org/projects/suricata/issues>`_.

If you want more info about the unittests, regular debug mode can help. This is enabled by adding the configure option::

    --enable-debug

Then, set the debug level from the command-line::

    SC_LOG_LEVEL=Debug suricata -u

This will be very verbose. You can also add the ``SC_LOG_OP_FILTER`` to limit the output, it is grep-like::

    SC_LOG_LEVEL=Debug SC_LOG_OP_FILTER="(something|somethingelse)" suricata -u

This example will show all lines (debug, info, and all other levels) that contain either something or something else.
Keep in mind the `log level <https://docs.suricata.io/en/latest/manpages/suricata.html#id1>`_  precedence: if you choose *Info* level, for instance, Suricata won't show messages from the other levels.

Writing Unit Tests - C codebase
===============================

Suricata unit tests are somewhat different in C and in Rust. In C, they are comprised of a function with no arguments and returning 0 for failure or 1 for success. Instead of explicitly returning a value, FAIL_* and PASS macros should be used. For example:

.. code-block:: c

    void MyUnitTest(void)
    {
        int n = 1;
        void *p = NULL;

        FAIL_IF(n != 1);
        FAIL_IF_NOT(n == 1);
        FAIL_IF_NOT_NULL(p);
        FAIL_IF_NULL(p);

        PASS;
    }

Each unit test needs to be registered with ``UtRegisterTest()``. Example::

    UtRegisterTest("MyUnitTest", MyUnitTest);

where the first argument is the name of the test, and the second argument is the function. Existing modules should already have a function that registers its unit tests. Otherwise the unit tests will need to be registered. Look for a module similar to your new module to see how best to register the unit tests or ask the development team for help.

Examples
--------

From ``conf-yaml-loader.c``:

.. code-block:: c

    /**
     * Test that a configuration section is overridden but subsequent
     * occurrences.
     */
    static int
    ConfYamlOverrideTest(void)
    {
        char config[] =
            "%YAML 1.1\n"
            "---\n"
            "some-log-dir: /var/log\n"
            "some-log-dir: /tmp\n"
            "\n"
            "parent:\n"
            "  child0:\n"
            "    key: value\n"
            "parent:\n"
            "  child1:\n"
            "    key: value\n"
            ;
        const char *value;

        ConfCreateContextBackup();
        ConfInit();

        FAIL_IF(ConfYamlLoadString(config, strlen(config)) != 0);
        FAIL_IF_NOT(ConfGet("some-log-dir", &value));
        FAIL_IF(strcmp(value, "/tmp") != 0);

        /* Test that parent.child0 does not exist, but child1 does. */
        FAIL_IF_NOT_NULL(ConfGetNode("parent.child0"));
        FAIL_IF_NOT(ConfGet("parent.child1.key", &value));
        FAIL_IF(strcmp(value, "value") != 0);

        ConfDeInit();
        ConfRestoreContextBackup();

        PASS;
    }

In ``detect-ike-chosen-sa.c``, it is possible to see the freeing of resources (``DetectIkeChosenSaFree``) and the
function that should group all the ``UtRegisterTest`` calls:

.. code-block:: c

    #ifdef UNITTESTS
    .
    .
    .
    static int IKEChosenSaParserTest(void)
    {
        DetectIkeChosenSaData *de = NULL;
        de = DetectIkeChosenSaParse("alg_hash=2");

        FAIL_IF_NULL(de);
        FAIL_IF(de->sa_value != 2);
        FAIL_IF(strcmp(de->sa_type, "alg_hash") != 0);

        DetectIkeChosenSaFree(NULL, de);
        PASS;
    }

    #endif /* UNITTESTS */

    void IKEChosenSaRegisterTests(void)
    {
    #ifdef UNITTESTS
        UtRegisterTest("IKEChosenSaParserTest", IKEChosenSaParserTest);
    #endif /* UNITTESTS */