|
| 1 | +================================ |
| 2 | +NuttX Unit Test Selection (NUTS) |
| 3 | +================================ |
| 4 | + |
| 5 | +NUTS is a selection of unit test suites that can be compiled and run on NuttX |
| 6 | +devices to verify correct operation of the OS APIs. NUTS depends on the |
| 7 | +:doc:`cmocka </applications/testing/cmocka/index>` testing framework in order to |
| 8 | +run the unit tests. |
| 9 | + |
| 10 | +Building & Deploying |
| 11 | +-------------------- |
| 12 | + |
| 13 | +In order to build and deploy NUTS on your target device, you will need to enable |
| 14 | +the cmocka library and ``CONFIG_TESTING_NUTS``. Once NUTS is enabled, you will |
| 15 | +see a list of available test categories in Kconfig. You may enable the test |
| 16 | +categories that you want to run tests for. |
| 17 | + |
| 18 | +Once a test category is enabled, you will have access to toggles for each test |
| 19 | +group within the category. For example, if you enable the 'devices' category, |
| 20 | +you will then be able to choose if you want to include the tests for |
| 21 | +``/dev/zero`` or ``/dev/null``, etc. By default, all test groups within a |
| 22 | +category are enabled and must be opted-out of by disabling their toggle. |
| 23 | + |
| 24 | +.. note:: |
| 25 | + |
| 26 | + Some individual tests may have specific dependencies which need to be enabled |
| 27 | + first, so do not assume that the visible list of tests in Kconfig is the |
| 28 | + complete selection (unless you have enabled showing hidden options in |
| 29 | + Kconfig). For example, ``/dev/urandom`` depends on `CONFIG_DEV_URANDOM` to be |
| 30 | + enabled, and will not show in the menu unless the dependency is selected. |
| 31 | + |
| 32 | +Once built and deployed to the target, you can use the following command to |
| 33 | +see your test results (example of running ``/dev/zero`` tests only): |
| 34 | + |
| 35 | +.. code:: console |
| 36 | +
|
| 37 | + nsh> nuts |
| 38 | + [==========] /dev/zero: Running 8 test(s). |
| 39 | + [ RUN ] open_rdonly |
| 40 | + [ OK ] open_rdonly |
| 41 | + [ RUN ] open_rdwr |
| 42 | + [ OK ] open_rdwr |
| 43 | + [ RUN ] open_wronly |
| 44 | + [ OK ] open_wronly |
| 45 | + [ RUN ] readzero |
| 46 | + [ OK ] readzero |
| 47 | + [ RUN ] readlarge |
| 48 | + [ OK ] readlarge |
| 49 | + [ RUN ] writezero |
| 50 | + [ OK ] writezero |
| 51 | + [ RUN ] writelarge |
| 52 | + [ OK ] writelarge |
| 53 | + [ RUN ] wrrd |
| 54 | + [ OK ] wrrd |
| 55 | + [==========] /dev/zero: 8 test(s) run. |
| 56 | + [ PASSED ] 8 test(s). |
| 57 | +
|
| 58 | +Included Tests |
| 59 | +-------------- |
| 60 | + |
| 61 | +NUTS tests are split into categories. Within each category there are test |
| 62 | +groups/suites which contain all of the individual test cases related to a |
| 63 | +specific API. |
| 64 | + |
| 65 | +.. toctree:: |
| 66 | + :caption: Test categories |
| 67 | + |
| 68 | + devices.rst |
| 69 | + dstructs.rst |
| 70 | + |
| 71 | +Test Cases |
| 72 | +---------- |
| 73 | + |
| 74 | +The output of NUTS displays the pass/fail/skip results of each test case that |
| 75 | +has been included in the build. Although the names of the test cases are |
| 76 | +intended to describe what the test case is verifying, sometimes more information |
| 77 | +is needed. Each test case in NUTS has a corresponding source-code |
| 78 | +comment/docstring which includes a description of what the test is verifying. |
| 79 | +You can also determine more information by looking at the assertion statements |
| 80 | +within the test itself. |
| 81 | + |
| 82 | +Adding New Tests |
| 83 | +---------------- |
| 84 | + |
| 85 | +In order to add new tests, first take a look at the :doc:`cmocka |
| 86 | +</applications/testing/cmocka/index>` API documentation. |
| 87 | + |
| 88 | +Then, you should add a new ``.c`` file under the category that you wish to |
| 89 | +extend. All of the test cases are static functions that take a ``void **state`` |
| 90 | +in order to pass in context from a setup function. Each test case function |
| 91 | +should have a docstring comment which describes what the particular test case is |
| 92 | +testing (and the name should be as descriptive as possible). Setup and teardown |
| 93 | +functions must start with ``setup_`` and ``teardown_`` respectively, where the |
| 94 | +suffix is a descriptive name about what is being set up/torn down. |
| 95 | + |
| 96 | +An example of a test case taken from the circular buffer test suite is: |
| 97 | + |
| 98 | +.. code:: c |
| 99 | +
|
| 100 | + static int setup_empty_cbuf(void **state) |
| 101 | + { |
| 102 | + *state = &g_cbuf; |
| 103 | + return circbuf_init(&g_cbuf, g_buf, sizeof(g_buf)); |
| 104 | + } |
| 105 | +
|
| 106 | + static int teardown_cbuf(void **state) |
| 107 | + { |
| 108 | + circbuf_uninit(*state); |
| 109 | + if (circbuf_is_init(*state)) return -1; |
| 110 | + return 0; |
| 111 | + } |
| 112 | +
|
| 113 | + /**************************************************************************** |
| 114 | + * Name: empty_postinit |
| 115 | + * |
| 116 | + * Description: |
| 117 | + * Tests that a circular buffer is empty right after being initialized. |
| 118 | + ****************************************************************************/ |
| 119 | +
|
| 120 | + static void empty_postinit(void **state) |
| 121 | + { |
| 122 | + struct circbuf_s *cbuf = *state; |
| 123 | +
|
| 124 | + assert_true(circbuf_is_empty(cbuf)); |
| 125 | + assert_false(circbuf_is_full(cbuf)); |
| 126 | + assert_uint_equal(0, circbuf_used(cbuf)); |
| 127 | + assert_uint_equal(CBUF_SIZE, circbuf_space(cbuf)); |
| 128 | + } |
| 129 | +
|
| 130 | +Each test group must implement a public function for running all of the test |
| 131 | +cases, following the convention: |
| 132 | + |
| 133 | +.. code:: c |
| 134 | +
|
| 135 | + int nuts_category_group(void) |
| 136 | + { |
| 137 | + static const struct CMUnitTests tests[] = |
| 138 | + { |
| 139 | + cmocka_unit_test(this_is_test_one), |
| 140 | + cmocka_unit_test_setup_teardown(this_is_test_two, setup_func, |
| 141 | + teardown_func), |
| 142 | + }; |
| 143 | +
|
| 144 | + return cmocka_run_group_tests_name("group name", tests, NULL, NULL); |
| 145 | + } |
| 146 | +
|
| 147 | +where 'category' is the name of the test category, and 'group' is the name of |
| 148 | +the test group (i.e. category of 'devices' and group of 'devnull'). |
| 149 | + |
| 150 | +Inside the ``tests.h`` header file for the category, include the following under |
| 151 | +the public function prototypes: |
| 152 | + |
| 153 | +.. code:: c |
| 154 | +
|
| 155 | + #ifdef CONFIG_TESTING_NUTS_CATEGORY_GROUP |
| 156 | + int nuts_category_group(void); |
| 157 | + #else |
| 158 | + #define nuts_category_group() |
| 159 | + #endif /* CONFIG_TESTING_NUTS_CATEGORY_GROUP */ |
| 160 | +
|
| 161 | +where ``CONFIG_TESTING_NUTS_CATEGORY_GROUP`` is the Kconfig option to toggle |
| 162 | +on/off the test group. You will have to add this to the NUTS Kconfig file. |
| 163 | + |
| 164 | +In order to reduce the amount of special switches in the |
| 165 | +``Makefile``/``CMakeLists.txt`` files, all source files in each category |
| 166 | +directory are included with a wild card. Thus, to avoid compiling unselected |
| 167 | +test groups, each test group's C file should be surrounded in the gaurd: |
| 168 | + |
| 169 | +.. code:: c |
| 170 | +
|
| 171 | + #ifdef CONFIG_TESTING_NUTS_CATEGORY_GROUP |
| 172 | +
|
| 173 | + /* ... your test cases and public test runner function ... */ |
| 174 | +
|
| 175 | + #endif /* CONFIG_TESTING_NUTS_CATEGORY_GROUP */ |
0 commit comments