xref: /dpdk/doc/guides/contributing/unit_test.rst (revision 2df20a1d345a5fc0a1b6dc0317d11fc7b1fda7e7)
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   TELEMETRY: No legacy callbacks, legacy socket not created
102   APP: HPET is not enabled, using TSC as default timer
103   RTE>>version_autotest
104   Version string: 'DPDK 21.08.0-rc0'
105   Test OK
106   RTE>>version_autotest
107   Version string: 'DPDK 21.08.0-rc0'
108   Test OK
109   RTE>>
110
111The primary benefit here is specifying multiple test names,
112which is not possible with the ``DPDK_TEST`` environment variable.
113
114Additionally, it is possible to specify additional test parameters
115via the ``DPDK_TEST_PARAMS`` argument,
116in case some tests need additional configuration.
117This isn't currently used in the Meson test suites.
118
119
120Running test cases via Meson
121----------------------------
122
123In order to allow developers to quickly execute all the standard internal tests
124without needing to remember or look up each test suite name,
125the build system includes a standard way of executing the Meson test suites.
126After building via ``ninja``, the ``meson test`` command
127with no arguments will execute the Meson test suites.
128
129There are a number of pre-configured Meson test suites.
130The first is the **fast** test suite, which is the largest group of test cases.
131These are the bulk of the unit tests to validate functional blocks.
132The second is the **perf** tests.
133These test suites can take longer to run and do performance evaluations.
134The third is the **driver** test suite,
135which is mostly for special hardware related testing (such as `cryptodev`).
136The fourth, and currently the last, suite is the **debug** suite.
137These tests mostly are used to dump system information.
138
139The Meson test suites can be selected by adding the ``--suite`` option
140to the ``meson test`` command.
141Ex: ``meson test --suite fast-tests``::
142
143   $ meson test -C build --suite fast-tests
144   ninja: Entering directory `/home/aconole/git/dpdk/build'
145   [2543/2543] Linking target app/test/dpdk-test.
146   1/60 DPDK:fast-tests / acl_autotest          OK       3.17 s
147   2/60 DPDK:fast-tests / bitops_autotest       OK       0.22 s
148   3/60 DPDK:fast-tests / byteorder_autotest    OK       0.22 s
149   4/60 DPDK:fast-tests / cmdline_autotest      OK       0.28 s
150   5/60 DPDK:fast-tests / common_autotest       OK       0.57 s
151   6/60 DPDK:fast-tests / cpuflags_autotest     OK       0.27 s
152   ...
153
154The ``meson test`` command can also execute individual Meson test cases
155via the command line by adding the test names as an argument::
156
157   $ meson test -C build version_autotest
158   ninja: Entering directory `/home/aconole/git/dpdk/build'
159   [2543/2543] Linking target app/test/dpdk-test.
160   1/1 DPDK:fast-tests / version_autotest OK             0.17s
161   ...
162
163Note that these test cases must be known to Meson
164for the ``meson test`` command to run them.
165Simply adding a new test to the `dpdk-test` application isn't enough.
166See the section `Adding a suite or test case to Meson`_ for more details.
167
168
169Adding tests to dpdk-test application
170-------------------------------------
171
172Unit tests should be added to the system
173whenever we introduce new functionality to DPDK,
174as well as whenever a bug is resolved.
175This helps the DPDK project to catch regressions as they are introduced.
176
177The DPDK test application supports two layers of tests:
178   #. *test cases* which are individual tests
179   #. *test suites* which are groups of test cases
180
181To add a new test suite to the DPDK test application,
182create a new test file for that suite
183(ex: see *app/test/test_version.c* for the ``version_autotest`` test suite).
184There are two important functions for interacting with the test harness:
185
186   ``REGISTER_<MESON_SUITE>_TEST(command_name, function_to_execute)``
187      Registers a test command with the name `command_name`
188      and which runs the function `function_to_execute` when `command_name` is invoked.
189      The test is automatically added to the Meson test suite `<MESON_SUITE>` by this macro.
190      Examples would be ``REGISTER_DRIVER_TEST``, or ``REGISTER_PERF_TEST``.
191      **NOTE:** The ``REGISTER_FAST_TEST`` macro is slightly different,
192      in that it takes two additional parameters,
193      specifying whether the test can be run using ``--no-huge``,
194      and whether the test can be run using Address Sanitization (ASAN)
195
196   ``unit_test_suite_runner(struct unit_test_suite *)``
197      Returns a runner for a full test suite object,
198      which contains a test suite name, setup, tear down,
199      a pointer to a list of sub-testsuites,
200      and vector of unit test cases.
201
202Each test suite has a setup and tear down function
203that runs at the beginning and end of the test suite execution.
204Each unit test has a similar function for test case setup and tear down.
205
206Each test suite may use a nested list of sub-testsuites,
207which are iterated by the ``unit_test_suite_runner``.
208This support allows for better granularity when designing test suites.
209The sub-testsuites list can also be used in parallel with the vector of test cases,
210in this case the test cases will be run,
211and then each sub-testsuite is executed.
212To see an example of a test suite using sub-testsuites,
213see *app/test/test_cryptodev.c*.
214
215Test cases are added to the ``.unit_test_cases`` element
216of the appropriate unit test suite structure.
217An example of both a test suite and a case:
218
219.. code-block:: c
220   :linenos:
221
222   #include <time.h>
223
224   #include <rte_common.h>
225   #include <rte_cycles.h>
226   #include <rte_hexdump.h>
227   #include <rte_random.h>
228
229   #include "test.h"
230
231   static int testsuite_setup(void) { return TEST_SUCCESS; }
232   static void testsuite_teardown(void) { }
233
234   static int ut_setup(void) { return TEST_SUCCESS; }
235   static void ut_teardown(void) { }
236
237   static int test_case_first(void) { return TEST_SUCCESS; }
238
239   static struct unit_test_suite example_testsuite = {
240          .suite_name = "EXAMPLE TEST SUITE",
241          .setup = testsuite_setup,
242          .teardown = testsuite_teardown,
243          .unit_test_cases = {
244               TEST_CASE_ST(ut_setup, ut_teardown, test_case_first),
245
246               TEST_CASES_END(), /**< NULL terminate unit test array */
247          },
248   };
249
250   static int example_tests()
251   {
252       return unit_test_suite_runner(&example_testsuite);
253   }
254
255   REGISTER_PERF_TEST(example_autotest, example_tests);
256
257The above code block is a small example
258that can be used to create a complete test suite with test case.
259
260Sub-testsuites can be added to the ``.unit_test_suites`` element
261of the unit test suite structure, for example:
262
263.. code-block:: c
264   :linenos:
265
266   static int testsuite_setup(void) { return TEST_SUCCESS; }
267   static void testsuite_teardown(void) { }
268
269   static int ut_setup(void) { return TEST_SUCCESS; }
270   static void ut_teardown(void) { }
271
272   static int test_case_first(void) { return TEST_SUCCESS; }
273
274   static struct unit_test_suite example_parent_testsuite = {
275          .suite_name = "EXAMPLE PARENT TEST SUITE",
276          .setup = testsuite_setup,
277          .teardown = testsuite_teardown,
278          .unit_test_cases = {TEST_CASES_END()}
279   };
280
281   static int sub_testsuite_setup(void) { return TEST_SUCCESS; }
282   static void sub_testsuite_teardown(void) { }
283
284   static struct unit_test_suite example_sub_testsuite = {
285          .suite_name = "EXAMPLE SUB TEST SUITE",
286          .setup = sub_testsuite_setup,
287          .teardown = sub_testsuite_teardown,
288          .unit_test_cases = {
289               TEST_CASE_ST(ut_setup, ut_teardown, test_case_first),
290
291               TEST_CASES_END(), /**< NULL terminate unit test array */
292          },
293   };
294
295   static struct unit_test_suite end_testsuite = {
296          .suite_name = NULL,
297          .setup = NULL,
298          .teardown = NULL,
299          .unit_test_suites = NULL
300   };
301
302   static int example_tests()
303   {
304       uint8_t ret, i = 0;
305       struct unit_test_suite *sub_suites[] = {
306              &example_sub_testsuite,
307              &end_testsuite /**< NULL test suite to indicate end of list */
308        };
309
310       example_parent_testsuite.unit_test_suites =
311               malloc(sizeof(struct unit_test_suite *) * RTE_DIM(sub_suites));
312
313       for (i = 0; i < RTE_DIM(sub_suites); i++)
314           example_parent_testsuite.unit_test_suites[i] = sub_suites[i];
315
316       ret = unit_test_suite_runner(&example_parent_testsuite);
317       free(example_parent_testsuite.unit_test_suites);
318
319       return ret;
320   }
321
322   REGISTER_FAST_TEST(example_autotest, true /*no-huge*/, false /*ASan*/, example_tests);
323
324
325Designing a test
326----------------
327
328Test cases have multiple ways of indicating an error has occurred,
329in order to reflect failure state back to the runner.
330Using the various methods of indicating errors can assist
331in not only validating the requisite functionality is working,
332but also to help debug when a change in environment or code
333has caused things to go wrong.
334
335The first way to indicate a generic error is
336by returning a test result failure, using the ``TEST_FAILED`` error code.
337This is the most basic way of indicating that an error
338has occurred in a test routine.
339It isn't very informative to the user, so it should really be used in cases
340where the test has catastrophically failed.
341
342The preferred method of indicating an error is
343via the ``RTE_TEST_ASSERT`` family of macros,
344which will immediately return ``TEST_FAILED`` error condition,
345but will also log details about the failure.
346The basic form is:
347
348.. code-block:: c
349
350   RTE_TEST_ASSERT(cond, msg, ...)
351
352In the above macro, *cond* is the condition to evaluate to **true**.
353Any generic condition can go here.
354The *msg* parameter will be a message to display if *cond* evaluates to **false**.
355Some specialized macros already exist.
356See `lib/librte_eal/include/rte_test.h` for a list of defined test assertions.
357
358Sometimes it is important to indicate that a test needs to be skipped,
359either because the environment isn't able to support running the test,
360or because some requisite functionality isn't available.
361The test suite supports returning a result of ``TEST_SKIPPED``
362during test case setup, or during test case execution
363to indicate that the preconditions of the test aren't available.
364Example::
365
366   $ meson test -C build --suite fast-tests
367   ninja: Entering directory `/home/aconole/git/dpdk/build
368   [2543/2543] Linking target app/test/dpdk-test.
369   1/60 DPDK:fast-tests / acl_autotest          OK       3.17 s
370   2/60 DPDK:fast-tests / bitops_autotest       OK       0.22 s
371   3/60 DPDK:fast-tests / byteorder_autotest    OK       0.22 s
372   ...
373   46/60 DPDK:fast-tests / ipsec_autotest       SKIP     0.22 s
374   ...
375
376
377Checking code coverage
378----------------------
379
380The Meson build system supports generating a code coverage report
381via the ``-Db_coverage=true`` option,
382in conjunction with a package like **lcov**,
383to generate an HTML code coverage report.
384Example::
385
386   $ meson setup build -Db_coverage=true
387   $ meson test -C build --suite fast-tests
388   $ ninja coverage-html -C build
389
390The above will generate an HTML report
391in the `build/meson-logs/coveragereport/` directory
392that can be explored for detailed code covered information.
393This can be used to assist in test development.
394
395
396Adding a suite or test case to Meson
397------------------------------------
398
399Adding to one of the Meson test suites involves using the appropriate macro
400to register the test in dpdk-test, as described above.
401For example,
402defining the test command using ``REGISTER_PERF_TEST`` automatically
403adds the test to the perf-test meson suite.
404Once added, the new test will be run
405as part of the appropriate class (fast, perf, driver, etc.).
406
407A user or developer can confirm that a test is known to Meson
408by using the ``--list`` option::
409
410   $ meson test -C build --list
411   DPDK:fast-tests / acl_autotest
412   DPDK:fast-tests / bitops_autotest
413   ...
414
415Some of these test suites are run during continuous integration tests,
416making regression checking automatic for new patches submitted to the project.
417
418.. note::
419
420   The use of the old ``REGISTER_TEST_COMMAND`` macro
421   to add a command without adding it to a meson test suite is deprecated.
422   All new tests must be added to a test suite
423   using the appropriate ``REGISTER_<SUITE>_TEST`` macro.
424
425Running cryptodev tests
426-----------------------
427
428When running cryptodev tests, the user must create any required virtual device
429via EAL arguments, as this is not automatically done by the test::
430
431   $ ./build/app/test/dpdk-test --vdev crypto_aesni_mb
432   $ meson test -C build --suite driver-tests \
433                --test-args="--vdev crypto_aesni_mb"
434
435.. note::
436
437   The ``cryptodev_scheduler_autotest`` is the only exception to this.
438   This vdev will be created automatically by the test app,
439   as it requires a more complex setup than other vdevs.
440