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