1 # Unit testing coreboot
4 First part of this document, Introduction, comprises disambiguation for what
5 unit testing is and what is not. This definition will be a basis for the whole
8 Next, Rationale, explains why to use unit testing and how coreboot specifically
11 This is followed by evaluation of different available free C unit test
12 frameworks. Firstly, collection of requirements is provided. Secondly, there is
13 a description of a few selected candidates. Finally, requirements are applied to
14 candidates to see if they might be a good fit.
16 Fourth part is a summary of evaluation, with proposal of unit test framework
17 for coreboot to be used.
19 Finally, Implementation proposal paragraph touches how build system and coreboot
20 codebase in general should be organized, in order to support unit testing. This
21 comprises couple of design considerations which need to be addressed.
24 A unit test is supposed to test a single unit of code in isolation. In C
25 language (in contrary to OOP) unit usually means a function. One may also
26 consider unit under test to be a single compilation unit which exposes some
27 API (set of functions). A function, talking to some external component can be
28 tested if this component can be mocked out.
30 In other words (looking from C compilation angle), there should be no extra
31 dependencies (executables) required beside unit under test and test harness in
32 order to compile unit test binary. Test harness, beside code examining a
33 routines, may comprise test framework implementation.
35 It is hard to apply this strict definition of unit test to firmware code in
36 practice, mostly due to constraints on speed of execution and size of final
37 executable. coreboot codebase often cannot be adjusted to be testable. Because
38 of this, coreboot unit testing subsystem should allow to include some additional
39 source object files beside unit under test. That being said, the default and
40 goal wherever possible, should be to isolate unit under test from other parts.
42 Unit testing is not an integration testing and it doesn't replace it. First of
43 all, integration tests cover larger set of components and interactions between
44 them. Positive integration test result gives more confidence than a positive
45 unit test does. Furthermore, unit tests are running on the build machine, while
46 integration tests usually are executed on the target (or simulator).
49 Considering above, what is the benefit of unit testing, especially keeping in
50 mind that coreboot is low-level firmware? Unit tests should be quick, thus may
51 be executed frequently during development process. It is much easier to build
52 and run a unit test on a build machine, than any integration test. This in turn
53 may be used by dev to gather extra confidence early during code development
54 process. Actually developer may even write unit tests earlier than the code -
55 see [TDD](https://en.wikipedia.org/wiki/Test-driven_development) concept.
57 That being said, unit testing embedded C code is a difficult task, due to
58 significant amount of dependencies on underlying hardware. Mocking can handle
59 some hardware dependencies. However, complex mocks make the unit test
60 susceptible to failing and can require significant development effort.
62 Writing unit tests for a code (both new and currently existing) may be favorable
63 for the code quality. It is not only about finding bugs, but in general - easily
64 testable code is a good code.
66 coreboot benefits the most from testing common libraries (lib/, commonlib/,
67 payloads/libpayload) and coreboot infrastructure (console/, device/, security/).
69 ## Evaluation of unit testing frameworks
72 Requirements for unit testing frameworks:
77 Standard C library is all we should need
79 * Isolation between tests
81 * Support for some machine parsable output
84 Compiler for the host _must_ support the same language standards as the target
85 compiler. Ideally the same toolchain should be used for building firmware
86 executables and test binaries, however the host compiler will be used to build
87 unit tests, whereas the coreboot toolchain will be used for building the
88 firmware executables. For some targets, the host compiler and the target
89 compiler could be the same, but this is not a requirement.
91 * Same language for tests and code
93 Unit tests will be written in C, because coreboot code is also written in C
97 * Easy to integrate with build system/build tools
99 Ideally JUnit-like XML output format for Jenkins
101 * Popularity is a plus
103 We want a larger community for a couple of reasons. Firstly, easier access to
104 people with knowledge and tutorials. Secondly, bug fixes for the top of tree
105 are more frequent and known issues are usually shorter in the pending state.
106 Last but not least, larger reviewer pool means better and easier upstream
107 improvements that we would like to submit.
109 * Extra features may be a plus
112 This should not be a blocker, since test binaries are not distributed.
113 However ideally compatible with GPL.
118 There is a lot of frameworks which allow unit testing C code
119 ([list](https://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C) from
120 Wikipedia). While not all of them were evaluated, because that would take an
121 excessive amount of time, couple of them were selected based on the good
122 opinions among C devs, popularity and fitting above criteria.
124 * [SputUnit](https://www.use-strict.de/sput-unit-testing/)
125 * [GoogleTest](https://github.com/google/googletest)
126 * [Cmocka](https://cmocka.org/)
127 * [Unity](http://www.throwtheswitch.org/unity) (CMock, Ceedling)
129 We looked at several other test frameworks, but decided not to do a full evaluation
130 for various reasons such as functionality, size of the developer community, or
134 * [SputUnit](https://www.use-strict.de/sput-unit-testing/)
136 * No dependencies, one header file to include - that’s all
141 * Main repo doesn’t have support for generating JUnit XML reports for
142 Jenkins to consume - this feature is available only on the fork from
143 SputUnit called “Sput_report”. It makes it niche in a niche, so there are
144 some reservations whether support for this will be satisfactory
145 * No support for mocks
147 * No automatic test registration
148 * [GoogleTest](https://github.com/google/googletest)
150 * Automatic test registration
151 * Support for different output formats (including XML for Jenkins)
152 * Good support, widely used, the biggest and the most active community out
153 of all frameworks that were investigated
154 * Available as a package in the most common distributions
155 * Test fixtures easily available
157 * Easy to integrate with an IDE
160 * Requires C++11 compiler
161 * To make most out of it (use GMock) C++ knowledge is required
162 * [Cmocka](https://cmocka.org/)
164 * Self-contained, autonomous framework
166 * API is well documented
167 * Multiple output formats (including XML for Jenkins)
168 * Available as a package in the most common distributions
169 * Used in some popular open source projects (libssh, OpenVPN, Samba)
170 * Test fixtures available
171 * Support for exception handling
173 * No automatic test registration
174 * It will require some effort to make it work from within an IDE
175 * Apache 2.0 license (not compatible with GPLv2)
176 * [Unity](http://www.throwtheswitch.org/unity) (CMock, Ceedling)
178 * Pure C (Unity testing framework itself, not test runner)
179 * Support for different output formats (including XML for Jenkins)
180 * There are some (rather easy) hints how to use this from an IDE (e.g. Eclipse)
183 * Test runner (Ceedling) is not written in C - uses Ruby
184 * Mocking/Exception handling functionalities are actually separate tools
185 * No automatic test registration
188 ### Summary & framework proposal
189 After research, we propose using the Cmocka unit test framework. Cmocka fulfills
190 all stated evaluation criteria. It is rather easy to use, doesn’t have extra
191 dependencies, written fully in C, allows for tests fixtures and some popular
192 open source projects already are using it. Cmocka also includes support for
195 Cmocka's limitations, such as the lack of automatic test registration, are
196 considered minor issues that will require only minimal additional work from a
197 developer. At the same time, it may be worth to propose improvement to Cmocka
198 community or simply apply some extra wrapper with demanded functionality.
202 ### Framework as a submodule or external package
203 Unit test frameworks may be either compiled from source (from a git submodule
204 under 3rdparty/) or pre-compiled as a package. The second option seems to be
205 easier to maintain, while at the same time may bring some unwanted consequences
206 (different version across distributions, frequent changes in API). It makes sense
207 to initially experiment with packages and check how it works. If this will
208 cause any issues, then it is always possible to switch to submodule approach.
210 ### Integration with build system
211 To get the most out of unit testing framework, it should be integrated with
212 Jenkins automation server. Verification of all unit tests for new changes may
213 improve code reliability to some extent.
215 ### Build configuration (Kconfig)
216 While building unit under test object file, it is necessary to apply some
217 configuration (config) just like when building usual firmware. For simplicity,
218 there will be one default tests .config `qemu_x86_i440fx` for all unit tests. At
219 the same time, some tests may require running with different values of particular
220 config. This should be handled by adding extra header, included after config.h.
221 This header will comprise #undef of old CONFIG values and #define of the
222 required value. When unit testing will be integrated with Jenkins, it may be
223 preferred to use every available config for periodic builds.
225 ### Directory structure
226 Tests should be kept separate from the code, while at the same time it must be
227 easy to match code with test harness.
229 We create new directory for test files ($(toplevel)/tests/) and mimic the
230 structure of src/ directory.
232 Test object files (test harness, unit under tests and any additional executables
233 are stored under build/tests/<test_name> directory.
235 Below example shows how directory structure is organized for the two test cases:
236 tests/lib/string-test and tests/device/i2c-test:
241 │ │ ├── string.c <- unit under test
248 │ │ ├── mocks <- mock headers, which replace original headers
250 │ ├── Makefile.mk <- top Makefile for unit tests subsystem
253 │ │ ├── string-test.c <- test code for src/lib/string.c
260 │ ├── tests <-all test-related executables
261 ├── config.h <- default config used for tests builds
263 │ ├── string-test <- all string-test executables
264 │ │ ├── run <- final test binary
265 │ │ ├── tests <- all test harness executables
267 │ │ ├── string-test.o <-test harness executable
268 │ │ ├── src <- unit under test and other src executables
270 │ │ ├── string.o <- unit under test executable
282 ### Writing new tests
283 Our tutorial series has [detailed guidelines](../tutorial/part3.md) for writing