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