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