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