xref: /dpdk/doc/guides/contributing/unit_test.rst (revision 7917b0d38e92e8b9ec5a870415b791420e10f11a)
1.. SPDX-License-Identifier: BSD-3-Clause
2   Copyright 2021 The DPDK contributors
3
4DPDK Unit Testing Guidelines
5============================
6
7This document outlines the guidelines for running and adding new
8tests to the in-tree DPDK test suites.
9
10The DPDK test suite model is loosely based on the xUnit model,
11where tests are grouped into test suites, and suites are run by runners.
12For a basic overview, see the basic Wikipedia article on `xUnit
13<https://en.wikipedia.org/wiki/XUnit>`_.
14
15
16Background
17----------
18
19The in-tree testing infrastructure for DPDK consists of
20multiple applications and support tools.
21The primary tools are the `dpdk-test` application,
22and the ``meson test`` infrastructure.
23These two are the primary ways through which
24a user will interact with the DPDK testing infrastructure.
25
26There exists a bit of confusion with the test suite and test case separation
27with respect to `dpdk-test` and ``meson test``.
28Both have a concept of test suite and test case.
29In both, the concept is similar.
30A test suite is a group of test cases,
31and a test case represents the steps needed to test a particular set of code.
32Where needed, they will be disambiguated by the word `Meson`
33to denote a Meson test suite / case.
34
35
36Running a test
37--------------
38
39DPDK tests are run via the main test runner, the `dpdk-test` app.
40The `dpdk-test` app is a command-line interface that facilitates
41running various tests or test suites.
42
43There are three modes of operation.
44The first mode is as an interactive command shell
45that allows launching specific test suites.
46This is the default operating mode of `dpdk-test` and can be done by::
47
48   $ ./build/app/test/dpdk-test --dpdk-options-here
49   EAL: Detected 4 lcore(s)
50   EAL: Detected 1 NUMA nodes
51   EAL: Static memory layout is selected, amount of reserved memory...
52   EAL: Multi-process socket /run/user/26934/dpdk/rte/mp_socket
53   EAL: Selected IOVA mode 'VA'
54   EAL: Probing VFIO support...
55   EAL: PCI device 0000:00:1f.6 on NUMA socket -1
56   EAL:   Invalid NUMA socket, default to 0
57   EAL:   probe driver: 8086:15d7 net_e1000_em
58   APP: HPET is not enabled, using TSC as default timer
59   RTE>>
60
61At the prompt, simply type the name of the test suite you wish to run
62and it will execute.
63
64The second form is useful for a scripting environment,
65and is used by the DPDK Meson build system.
66This mode is invoked by
67assigning a specific test suite name to the environment variable ``DPDK_TEST``
68before invoking the `dpdk-test` command, such as::
69
70   $ DPDK_TEST=version_autotest ./build/app/test/dpdk-test --dpdk-options-here
71   EAL: Detected 4 lcore(s)
72   EAL: Detected 1 NUMA nodes
73   EAL: Static memory layout is selected, amount of reserved memory can be...
74   EAL: Multi-process socket /run/user/26934/dpdk/rte/mp_socket
75   EAL: Selected IOVA mode 'VA'
76   EAL: Probing VFIO support...
77   EAL: PCI device 0000:00:1f.6 on NUMA socket -1
78   EAL:   Invalid NUMA socket, default to 0
79   EAL:   probe driver: 8086:15d7 net_e1000_em
80   APP: HPET is not enabled, using TSC as default timer
81   RTE>>version_autotest
82   Version string: 'DPDK 20.02.0-rc0'
83   Test OK
84   RTE>>$
85
86The above shows running a specific test case.
87On success, the return code will be '0',
88otherwise it will be set to some error value (such as '255', or a negative value).
89
90The third form is an alternative
91to providing the test suite name in an environment variable.
92The unit test app can accept test suite names via command line arguments::
93
94   $ ./build/app/test/dpdk-test --dpdk-options-here version_autotest version_autotest
95   EAL: Detected 8 lcore(s)
96   EAL: Detected 1 NUMA nodes
97   EAL: Static memory layout is selected, amount of reserved memory can be...
98   EAL: Detected static linkage of DPDK
99   EAL: Multi-process socket /run/user/26934/dpdk/rte/mp_socket
100   EAL: Selected IOVA mode 'VA'
101   APP: HPET is not enabled, using TSC as default timer
102   RTE>>version_autotest
103   Version string: 'DPDK 21.08.0-rc0'
104   Test OK
105   RTE>>version_autotest
106   Version string: 'DPDK 21.08.0-rc0'
107   Test OK
108   RTE>>
109
110The primary benefit here is specifying multiple test names,
111which is not possible with the ``DPDK_TEST`` environment variable.
112
113Additionally, it is possible to specify additional test parameters
114via the ``DPDK_TEST_PARAMS`` argument,
115in case some tests need additional configuration.
116This isn't currently used in the Meson test suites.
117
118
119Running test cases via Meson
120----------------------------
121
122In order to allow developers to quickly execute all the standard internal tests
123without needing to remember or look up each test suite name,
124the build system includes a standard way of executing the Meson test suites.
125After building via ``ninja``, the ``meson test`` command
126with no arguments will execute the Meson test suites.
127
128There are a number of pre-configured Meson test suites.
129The first is the **fast** test suite, which is the largest group of test cases.
130These are the bulk of the unit tests to validate functional blocks.
131The second is the **perf** tests.
132These test suites can take longer to run and do performance evaluations.
133The third is the **driver** test suite,
134which is mostly for special hardware related testing (such as `cryptodev`).
135The fourth, and currently the last, suite is the **debug** suite.
136These tests mostly are used to dump system information.
137
138The Meson test suites can be selected by adding the ``--suite`` option
139to the ``meson test`` command.
140Ex: ``meson test --suite fast-tests``::
141
142   $ meson test -C build --suite fast-tests
143   ninja: Entering directory `/home/aconole/git/dpdk/build'
144   [2543/2543] Linking target app/test/dpdk-test.
145   1/60 DPDK:fast-tests / acl_autotest          OK       3.17 s
146   2/60 DPDK:fast-tests / bitops_autotest       OK       0.22 s
147   3/60 DPDK:fast-tests / byteorder_autotest    OK       0.22 s
148   4/60 DPDK:fast-tests / cmdline_autotest      OK       0.28 s
149   5/60 DPDK:fast-tests / common_autotest       OK       0.57 s
150   6/60 DPDK:fast-tests / cpuflags_autotest     OK       0.27 s
151   ...
152
153The ``meson test`` command can also execute individual Meson test cases
154via the command line by adding the test names as an argument::
155
156   $ meson test -C build version_autotest
157   ninja: Entering directory `/home/aconole/git/dpdk/build'
158   [2543/2543] Linking target app/test/dpdk-test.
159   1/1 DPDK:fast-tests / version_autotest OK             0.17s
160   ...
161
162Note that these test cases must be known to Meson
163for the ``meson test`` command to run them.
164Simply adding a new test to the `dpdk-test` application isn't enough.
165See the section `Adding a suite or test case to Meson`_ for more details.
166
167
168Adding tests to dpdk-test application
169-------------------------------------
170
171Unit tests should be added to the system
172whenever we introduce new functionality to DPDK,
173as well as whenever a bug is resolved.
174This helps the DPDK project to catch regressions as they are introduced.
175
176The DPDK test application supports two layers of tests:
177   #. *test cases* which are individual tests
178   #. *test suites* which are groups of test cases
179
180To add a new test suite to the DPDK test application,
181create a new test file for that suite
182(ex: see *app/test/test_version.c* for the ``version_autotest`` test suite).
183There are two important functions for interacting with the test harness:
184
185   ``REGISTER_<MESON_SUITE>_TEST(command_name, function_to_execute)``
186      Registers a test command with the name `command_name`
187      and which runs the function `function_to_execute` when `command_name` is invoked.
188      The test is automatically added to the Meson test suite `<MESON_SUITE>` by this macro.
189      Examples would be ``REGISTER_DRIVER_TEST``, or ``REGISTER_PERF_TEST``.
190      **NOTE:** The ``REGISTER_FAST_TEST`` macro is slightly different,
191      in that it takes two additional parameters,
192      specifying whether the test can be run using ``--no-huge``,
193      and whether the test can be run using Address Sanitization (ASAN)
194
195   ``unit_test_suite_runner(struct unit_test_suite *)``
196      Returns a runner for a full test suite object,
197      which contains a test suite name, setup, tear down,
198      a pointer to a list of sub-testsuites,
199      and vector of unit test cases.
200
201Each test suite has a setup and tear down function
202that runs at the beginning and end of the test suite execution.
203Each unit test has a similar function for test case setup and tear down.
204
205Each test suite may use a nested list of sub-testsuites,
206which are iterated by the ``unit_test_suite_runner``.
207This support allows for better granularity when designing test suites.
208The sub-testsuites list can also be used in parallel with the vector of test cases,
209in this case the test cases will be run,
210and then each sub-testsuite is executed.
211To see an example of a test suite using sub-testsuites,
212see *app/test/test_cryptodev.c*.
213
214Test cases are added to the ``.unit_test_cases`` element
215of the appropriate unit test suite structure.
216An example of both a test suite and a case:
217
218.. code-block:: c
219   :linenos:
220
221   #include <time.h>
222
223   #include <rte_common.h>
224   #include <rte_cycles.h>
225   #include <rte_hexdump.h>
226   #include <rte_random.h>
227
228   #include "test.h"
229
230   static int testsuite_setup(void) { return TEST_SUCCESS; }
231   static void testsuite_teardown(void) { }
232
233   static int ut_setup(void) { return TEST_SUCCESS; }
234   static void ut_teardown(void) { }
235
236   static int test_case_first(void) { return TEST_SUCCESS; }
237
238   static struct unit_test_suite example_testsuite = {
239          .suite_name = "EXAMPLE TEST SUITE",
240          .setup = testsuite_setup,
241          .teardown = testsuite_teardown,
242          .unit_test_cases = {
243               TEST_CASE_ST(ut_setup, ut_teardown, test_case_first),
244
245               TEST_CASES_END(), /**< NULL terminate unit test array */
246          },
247   };
248
249   static int example_tests()
250   {
251       return unit_test_suite_runner(&example_testsuite);
252   }
253
254   REGISTER_PERF_TEST(example_autotest, example_tests);
255
256The above code block is a small example
257that can be used to create a complete test suite with test case.
258
259Sub-testsuites can be added to the ``.unit_test_suites`` element
260of the unit test suite structure, for example:
261
262.. code-block:: c
263   :linenos:
264
265   static int testsuite_setup(void) { return TEST_SUCCESS; }
266   static void testsuite_teardown(void) { }
267
268   static int ut_setup(void) { return TEST_SUCCESS; }
269   static void ut_teardown(void) { }
270
271   static int test_case_first(void) { return TEST_SUCCESS; }
272
273   static struct unit_test_suite example_parent_testsuite = {
274          .suite_name = "EXAMPLE PARENT TEST SUITE",
275          .setup = testsuite_setup,
276          .teardown = testsuite_teardown,
277          .unit_test_cases = {TEST_CASES_END()}
278   };
279
280   static int sub_testsuite_setup(void) { return TEST_SUCCESS; }
281   static void sub_testsuite_teardown(void) { }
282
283   static struct unit_test_suite example_sub_testsuite = {
284          .suite_name = "EXAMPLE SUB TEST SUITE",
285          .setup = sub_testsuite_setup,
286          .teardown = sub_testsuite_teardown,
287          .unit_test_cases = {
288               TEST_CASE_ST(ut_setup, ut_teardown, test_case_first),
289
290               TEST_CASES_END(), /**< NULL terminate unit test array */
291          },
292   };
293
294   static struct unit_test_suite end_testsuite = {
295          .suite_name = NULL,
296          .setup = NULL,
297          .teardown = NULL,
298          .unit_test_suites = NULL
299   };
300
301   static int example_tests()
302   {
303       uint8_t ret, i = 0;
304       struct unit_test_suite *sub_suites[] = {
305              &example_sub_testsuite,
306              &end_testsuite /**< NULL test suite to indicate end of list */
307        };
308
309       example_parent_testsuite.unit_test_suites =
310               malloc(sizeof(struct unit_test_suite *) * RTE_DIM(sub_suites));
311
312       for (i = 0; i < RTE_DIM(sub_suites); i++)
313           example_parent_testsuite.unit_test_suites[i] = sub_suites[i];
314
315       ret = unit_test_suite_runner(&example_parent_testsuite);
316       free(example_parent_testsuite.unit_test_suites);
317
318       return ret;
319   }
320
321   REGISTER_FAST_TEST(example_autotest, true /*no-huge*/, false /*ASan*/, example_tests);
322
323
324Designing a test
325----------------
326
327Test cases have multiple ways of indicating an error has occurred,
328in order to reflect failure state back to the runner.
329Using the various methods of indicating errors can assist
330in not only validating the requisite functionality is working,
331but also to help debug when a change in environment or code
332has caused things to go wrong.
333
334The first way to indicate a generic error is
335by returning a test result failure, using the ``TEST_FAILED`` error code.
336This is the most basic way of indicating that an error
337has occurred in a test routine.
338It isn't very informative to the user, so it should really be used in cases
339where the test has catastrophically failed.
340
341The preferred method of indicating an error is
342via the ``RTE_TEST_ASSERT`` family of macros,
343which will immediately return ``TEST_FAILED`` error condition,
344but will also log details about the failure.
345The basic form is:
346
347.. code-block:: c
348
349   RTE_TEST_ASSERT(cond, msg, ...)
350
351In the above macro, *cond* is the condition to evaluate to **true**.
352Any generic condition can go here.
353The *msg* parameter will be a message to display if *cond* evaluates to **false**.
354Some specialized macros already exist.
355See `lib/librte_eal/include/rte_test.h` for a list of defined test assertions.
356
357Sometimes it is important to indicate that a test needs to be skipped,
358either because the environment isn't able to support running the test,
359or because some requisite functionality isn't available.
360The test suite supports returning a result of ``TEST_SKIPPED``
361during test case setup, or during test case execution
362to indicate that the preconditions of the test aren't available.
363Example::
364
365   $ meson test -C build --suite fast-tests
366   ninja: Entering directory `/home/aconole/git/dpdk/build
367   [2543/2543] Linking target app/test/dpdk-test.
368   1/60 DPDK:fast-tests / acl_autotest          OK       3.17 s
369   2/60 DPDK:fast-tests / bitops_autotest       OK       0.22 s
370   3/60 DPDK:fast-tests / byteorder_autotest    OK       0.22 s
371   ...
372   46/60 DPDK:fast-tests / ipsec_autotest       SKIP     0.22 s
373   ...
374
375
376Checking code coverage
377----------------------
378
379The Meson build system supports generating a code coverage report
380via the ``-Db_coverage=true`` option,
381in conjunction with a package like **lcov**,
382to generate an HTML code coverage report.
383Example::
384
385   $ meson setup build -Db_coverage=true
386   $ meson test -C build --suite fast-tests
387   $ ninja coverage-html -C build
388
389The above will generate an HTML report
390in the `build/meson-logs/coveragereport/` directory
391that can be explored for detailed code covered information.
392This can be used to assist in test development.
393
394
395Adding a suite or test case to Meson
396------------------------------------
397
398Adding to one of the Meson test suites involves using the appropriate macro
399to register the test in dpdk-test, as described above.
400For example,
401defining the test command using ``REGISTER_PERF_TEST`` automatically
402adds the test to the perf-test meson suite.
403Once added, the new test will be run
404as part of the appropriate class (fast, perf, driver, etc.).
405
406A user or developer can confirm that a test is known to Meson
407by using the ``--list`` option::
408
409   $ meson test -C build --list
410   DPDK:fast-tests / acl_autotest
411   DPDK:fast-tests / bitops_autotest
412   ...
413
414Some of these test suites are run during continuous integration tests,
415making regression checking automatic for new patches submitted to the project.
416
417.. note::
418
419   The use of the old ``REGISTER_TEST_COMMAND`` macro
420   to add a command without adding it to a meson test suite is deprecated.
421   All new tests must be added to a test suite
422   using the appropriate ``REGISTER_<SUITE>_TEST`` macro.
423
424Running cryptodev tests
425-----------------------
426
427When running cryptodev tests, the user must create any required virtual device
428via EAL arguments, as this is not automatically done by the test::
429
430   $ ./build/app/test/dpdk-test --vdev crypto_aesni_mb
431   $ meson test -C build --suite driver-tests \
432                --test-args="--vdev crypto_aesni_mb"
433
434.. note::
435
436   The ``cryptodev_scheduler_autotest`` is the only exception to this.
437   This vdev will be created automatically by the test app,
438   as it requires a more complex setup than other vdevs.
439