mb/lenovo/t530: Convert to ASL 2.0 syntax
[coreboot.git] / Documentation / tutorial / part3.md
blob7ccee877546e9cfa21bf7448b144df7c72968214
1 # Writing unit tests for coreboot
3 ## Introduction
4 General thoughts about unit testing coreboot can be found in
5 [Unit testing coreboot](../technotes/2020-03-unit-testing-coreboot.md).
7 This document aims to guide developers through the process of adding and writing
8 unit tests for coreboot modules.
10 As an example of unit under test, `src/device/i2c.c` (referred hereafter as UUT
11 "Unit Under Test") will be used. This is simple module, thus it should be easy
12 for the reader to focus solely on the testing logic, without the need to spend
13 too much time on digging deeply into the source code details and flow of
14 operations. That being said, a good understanding of what the unit under test is
15 doing is crucial for writing unit tests.
17 This tutorial should also be helpful for developers who want to follow
18 [TDD](https://en.wikipedia.org/wiki/Test-driven_development). Even though TDD
19 has a different work flow of building tests first, followed by the code that
20 satisfies them, the process of writing tests and adding them to the tree is the
21 same.
23 ## Analysis of unit under test
24 First of all, it is necessary to precisely establish what we want to test in a
25 particular module. Usually this will be an externally exposed API, which can be
26 used by other modules.
28 ```eval_rst
29 .. admonition:: i2c-test example
31    In case of our UUT, API consist of two methods:
33    .. code-block:: c
35      int i2c_read_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t *data,
36                  uint8_t mask, uint8_t shift)
37      int i2c_write_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t data,
38                  uint8_t mask, uint8_t shift)
40    For sake of simplicity, let's focus on `i2c_read_field` in this document.
41 ```
43 Once the API is defined, the next question is __what__ this API is doing (or
44 what it will be doing in case of TDD). In other words, what outputs we are
45 expecting from particular functions, when providing particular input parameters.
47 ```eval_rst
48 .. admonition:: i2c-test example
50    .. code-block:: c
52      int i2c_read_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t *data,
53                  uint8_t mask, uint8_t shift)
55    This is a method which means to read content of register `reg` from i2c device
56    on i2c `bus` and slave address `chip`, applying bit `mask` and offset `shift`
57    to it. Returned data should be placed in `data`.
58 ```
60 The next step is to determine all external dependencies of UUT in order to mock
61 them out. Usually we want to isolate the UUT as much as possible, so that the
62 test result depends __only__ on the behavior of UUT and not on the other
63 modules. While some software dependencies may be hard to be mock (for example
64 due to complicated dependencies) and thus should be simply linked into the test
65 binaries, all hardware dependencies need to be mocked out, since in the
66 user-space host environment, targets hardware is not available.
68 ```eval_rst
69 .. admonition:: i2c-test example
71    `i2c_read_field` is calling `i2c_readb`, which eventually invokes
72    `i2c_transfer`. This method simply calls `platform_i2c_transfer`. The last
73    function in the chain is a hardware-touching one, and defined separately for
74    different SOCs. It is responsible for issuing transactions on the i2c bus.
75    For the purpose of writing unit test, we should mock this function.
76 ```
78 ## Adding new tests
79 In order to keep the tree clean, the `tests/` directory should mimic the `src/`
80 directory, so that test harness code is placed in a location corresponding to
81 UUT. Furthermore, the naming convention is to add the suffix `-test` to the UUT
82 name when creating a new test harness file.
84 ```eval_rst
85 .. admonition:: i2c-test example
87    Considering that UUT is `src/device/i2c.c`, test file should be named
88    `tests/device/i2c-test.c`. When adding a new test file, it needs to be
89    registered with the coreboot unit testing infrastructure.
90 ```
92 Every directory under `tests/` should contain a Makefile.inc, similar to what
93 can be seen under the `src/`. Register a new test in Makefile.inc, by
94 __appending__ test name to the `tests-y` variable.
96 ```eval_rst
97 .. admonition:: i2c-test example
99    .. code-block:: c
101      tests-y += i2c-test
104 Next step is to list all source files, which should be linked together in order
105 to create test binary. Usually a tests requires only two files - UUT and test
106 harness code, but sometimes more is needed to provide the test environment.
107 Source files are registered in `<test_name>-srcs` variable.
109 ```eval_rst
110 .. admonition:: i2c-test example
112    .. code-block:: c
114      i2c-test-srcs += tests/device/i2c-test.c
115      i2c-test-srcs += src/device/i2c.c
118 Above minimal configuration is a basis for further work. One can try to build
119 and run test binary either by invoking `make tests/<test_dir>/<test_name>` or by
120 running all unit tests (whole suite) for coreboot `make unit-tests`.
122 ```eval_rst
123 .. admonition:: i2c-test example
125    .. code-block:: c
127      make tests/device/i2c-test
129    or
131    .. code-block:: c
133      make unit-tests
136 When trying to build test binary, one can often see linker complains about
137 `undefined reference` to couple of symbols. This is one of solutions to
138 determine all external dependencies of UUT - iteratively build test and resolve
139 errors one by one. At this step, developer should decide either it's better to
140 add an extra module to provide necessary definitions or rather mock such
141 dependency. Quick guide through adding mocks is provided later in this doc.
143 ## Writing new tests
144 In coreboot, [Cmocka](https://cmocka.org/) is used as unit test framework. The
145 project has exhaustive [API documentation](https://api.cmocka.org/). Let's see
146 how we may incorporate it when writing tests.
148 ### Assertions
149 Testing the UUT consists of calling the functions in the UUT and comparing the
150 returned values to the expected values. Cmocka implements
151 [a set of assert macros](https://api.cmocka.org/group__cmocka__asserts.html) to
152 compare a value with an expected value. If the two values do not match, the test
153 fails with an error message.
155 ```eval_rst
156 .. admonition:: i2c-test example
158    In our example, the simplest test is to call UUT for reading our fake devices
159    registers and do all calculation in the test harness itself. At the end, let's
160    compare integers with `assert_int_equal`.
162    .. code-block:: c
164      #define MASK        0x3
165      #define SHIFT        0x1
167      static void i2c_read_field_test(void **state)
168      {
169              int bus, slave, reg;
170              int i, j;
171              uint8_t buf;
173              mock_expect_params_platform_i2c_transfer();
175              /* Read particular bits in all registers in all devices, then compare
176                 with expected value. */
177              for (i = 0; i < ARRAY_SIZE(i2c_ex_devs); i++)
178                      for (j = 0; j < ARRAY_SIZE(i2c_ex_devs[0].regs); j++) {
179                              i2c_read_field(i2c_ex_devs[i].bus,
180                                      i2c_ex_devs[i].slave,
181                                      i2c_ex_devs[i].regs[j].reg,
182                                      &buf, MASK, SHIFT);
183                              assert_int_equal((i2c_ex_devs[i].regs[j].data &
184                                      (MASK << SHIFT)) >> SHIFT, buf);
185                      };
186      }
189 ### Mocks
191 #### Overview
192 Many coreboot modules are low level software that touch hardware directly.
193 Because of this, one of the most important and challenging part of
194 writing tests is to design and implement mocks. A mock is a software component
195 which implements the API of another component so that the test can verify that
196 certain functions are called (or not called), verify the parameters passed to
197 those functions, and specify the return values from those functions. Mocks are
198 especially useful when the API to be implemented is one that accesses hardware
199 components.
201 When writing a mock, the developer implements the same API as the module being
202 mocked. Such a mock may, for example, register a set of driver methods. Behind
203 this API, there is usually a simulation of real hardware.
205 ```eval_rst
206 .. admonition:: i2c-test example
208    For purpose of our i2c test, we may introduce two i2c devices with set of
209    registers, which simply are structs in memory.
211    .. code-block:: c
213      /* Simulate two i2c devices, both on bus 0, each with three uint8_t regs
214         implemented. */
215      typedef struct {
216              uint8_t reg;
217              uint8_t data;
218      } i2c_ex_regs_t;
220      typedef struct {
221              unsigned int bus;
222              uint8_t slave;
223              i2c_ex_regs_t regs[3];
224      } i2c_ex_devs_t;
226      i2c_ex_devs_t i2c_ex_devs[] = {
227              {.bus = 0, .slave = 0xA, .regs = {
228                      {.reg = 0x0, .data = 0xB},
229                      {.reg = 0x1, .data = 0x6},
230                      {.reg = 0x2, .data = 0xF},
231              } },
232              {.bus = 0, .slave = 0x3, .regs = {
233                      {.reg = 0x0, .data = 0xDE},
234                      {.reg = 0x1, .data = 0xAD},
235                      {.reg = 0x2, .data = 0xBE},
236              } },
237      };
239    These fake devices will be accessed instead of hardware ones:
241    .. code-block:: c
243              reg = tmp->buf[0];
245              /* Find object for requested device */
246              for (i = 0; i < ARRAY_SIZE(i2c_ex_devs); i++, i2c_dev++)
247                      if (i2c_ex_devs[i].slave == tmp->slave) {
248                              i2c_dev = &i2c_ex_devs[i];
249                              break;
250                      }
252              if (i2c_dev == NULL)
253                      return -1;
255              /* Write commands */
256              if (tmp->len > 1) {
257                      i2c_dev->regs[reg].data = tmp->buf[1];
258              };
260              /* Read commands */
261              for (i = 0; i < count; i++, tmp++)
262                      if (tmp->flags & I2C_M_RD) {
263                              *(tmp->buf) = i2c_dev->regs[reg].data;
264                      };
267 Cmocka uses a feature that gcc provides for breaking dependencies at the link
268 time. It is possible to override implementation of some function, with the
269 method from test harness. This allows test harness to take control of execution
270 from binary (during the execution of test), and stimulate UUT as required
271 without changing the source code.
273 coreboot unit test infrastructure supports overriding of functions at link time.
274 This is as simple as adding a `name_of_function` to be mocked into
275 <test_name>-mocks variable in Makefile.inc. The result is that every time the
276 function is called, `wrap_name_of_function` will be called instead.
278 ```eval_rst
279 .. admonition:: i2c-test example
281    .. code-block:: c
283      i2c-test-mocks += platform_i2c_transfer
285    Now, dev can write own implementation of `platform_i2c_transfer` and define it
286    as `wrap_platform_i2c_transfer`. This implementation instead of accessing real
287    i2c bus, will write/read from fake structs.
289    .. code-block:: c
291      int __wrap_platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments,
292              int count)
293      {
294      }
297 #### Checking mock's arguments
298 A test can verify the parameters provided by the UUT to the mock function. The
299 developer may also verify that number of calls to mock is correct and the order
300 of calls to particular mocks is as expected (See
301 [this](https://api.cmocka.org/group__cmocka__call__order.html)). The Cmocka
302 macros for checking parameters are described
303 [here](https://api.cmocka.org/group__cmocka__param.html). In general, in mock
304 function, one makes a call to `check_expected(<param_name>)` and in the
305 corresponding test function, `expect*()` macro, with description which parameter
306 in which mock should have particular value, or be inside a described range.
308 ```eval_rst
309 .. admonition:: i2c-test example
311    In our example, we may want to check that `platform_i2c_transfer` is fed with
312    number of segments bigger than 0, each segment has flags which are in
313    supported range and each segment has buf which is non-NULL. We are expecting
314    such values for _every_ call, thus the last parameter in `expect*` macros is
315    -1.
317    .. code-block:: c
319      static void mock_expect_params_platform_i2c_transfer(void)
320      {
321              unsigned long int expected_flags[] = {0, I2C_M_RD, I2C_M_TEN,
322                      I2C_M_RECV_LEN, I2C_M_NOSTART};
324              /* Flags should always be only within supported range */
325              expect_in_set_count(__wrap_platform_i2c_transfer, segments->flags,
326                      expected_flags, -1);
328              expect_not_value_count(__wrap_platform_i2c_transfer, segments->buf,
329                      NULL, -1);
331              expect_in_range_count(__wrap_platform_i2c_transfer, count, 1, INT_MAX,
332                      -1);
333      }
335    And the checks below should be added to our mock
337    .. code-block:: c
339              check_expected(count);
341              for (i = 0; i < count; i++, segments++) {
342                      check_expected_ptr(segments->buf);
343                      check_expected(segments->flags);
344              }
347 #### Instrument mocks
348 It is possible for the test function to instrument what the mock will return to
349 the UUT. This can be done by using the `will_return*()` and `mock()` macros.
350 These are described in
351 [the Mock Object section](https://api.cmocka.org/group__cmocka__mock.html) of
352 the Cmocka API documentation.
354 ```eval_rst
355 .. admonition:: Example
357    There is an non-coreboot example for using Cmocka available
358    `here <https://lwn.net/Articles/558106/>`_.
361 ### Test runner
362 Finally, the developer needs to implement the test `main()` function. All tests
363 should be registered there and cmocka test runner invoked. All methods for
364 invoking Cmocka test are described
365 [here](https://api.cmocka.org/group__cmocka__exec.html).
367 ```eval_rst
368 .. admonition:: i2c-test example
370    We don't need any extra setup and teardown functions for i2c-test, so let's
371    simply register test for `i2c_read_field` and return from main value which is
372    output of Cmocka's runner (it returns number of tests that failed).
374    .. code-block:: c
376      int main(void)
377      {
378              const struct CMUnitTest tests[] = {
379                      cmocka_unit_test(i2c_read_field_test),
380              };
382              return cmocka_run_group_tests(tests, NULL, NULL);
383      }