summaryrefslogtreecommitdiff
path: root/docs/ztest.md
blob: 1dd4c91a0c08dc5cc9746ce0ca2da437e538b834 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# Porting EC unit tests to Ztest

[TOC]

This HOWTO shows the process for porting the EC's `base32` unit test to
Zephyr's Ztest framework. All of the work is done in `src/platform/ec`.

See [Test Framework - Zephyr Project Documentation](https://docs.zephyrproject.org/1.12.0/subsystems/test/ztest.html#quick-start-unit-testing) for details about Zephyr's Ztest framework.


For examples of porting an EC unit test to the Ztest API, see:
* [base32](https://crrev.com/c/2492527) and [improvements](https://crrev.com/c/2634401)
* [accel_cal](https://crrev.com/c/2645198)

## Porting Considerations

Not every EC unit test can be ported to Ztest. This section describes cases
that are not supported and cases where caveats apply.

### EC Mocks Are Not Supported

If a test has a `$TEST.mocklist` file associated with the unit test, it is
using the EC mocking framework, which is unsupported in the Ztest framework.
Ztest has its own mocking framework which the EC does not support.

### Multiple Task Caveats

The EC unit test framework starts a single task to call `run_test`, and this
task will then call the functions for the various test cases.  Some unit tests
have multiple threads of execution, which is enabled by a `$TEST.tasklist`
file associated with the unit test. The test runner task has a task ID of
`TASK_ID_TEST_RUNNER`, which can be used as an argument to any of the task
functions. See for example the [`charge_ramp` unit test](https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/master/test/charge_ramp.c#81)
and the [`host_command` unit test](https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/master/test/host_command.c#32)

When a unit test is ported to Ztest, `test_main` doesn't have a thread ID, so
`TASK_ID_TEST_RUNNER` is undefined. `charge_ramp` and `host_command` cannot
be ported at this time. `test_main` also cannot call any of the task functions
that must be called from a task, such as `task_wake`; these functions can pend
the calling task, but since `test_main` doesn't have a thread ID, the pend
will fail. See the [`mutex` unit test](https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/master/test/mutex.c#116)
for an example.

## Determine source files being tested

Determine which C files the unit test requires by finding the test in
`test/test_config.h`:
```
#ifdef TEST_BASE32
#define CONFIG_BASE32
#endif
```
Locate the `CONFIG` item(s) in `common/build.mk`:
```
common-$(CONFIG_BASE32)+=base32.o
```
So for the `base32` test, we only need to shim `common/base32.c`.

Add the C files to `zephyr/shim/CMakeLists.txt`, in the "Shimmed modules"
section:

```
# Shimmed modules
zephyr_sources_ifdef(CONFIG_PLATFORM_EC "${PLATFORM_EC}/common/base32.c")
```

Refer to [zephyr: shim in base32.c](https://crrev.com/c/2468631).

## Create test directory

Create a new directory for the unit test in `zephyr/test/base32`.

Create `zephyr/test/base32/prj.conf` with these contents:
```
CONFIG_ZTEST=y
CONFIG_PLATFORM_EC=y
```

Create `zephyr/test/base32/CMakeLists.txt` with these contents:
```
cmake_minimum_required(VERSION 3.13.1)
project(base32)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

target_sources(app PRIVATE ${PLATFORM_EC}/test/base32.c)
```

## Modify test source code

### Test cases
In the unit test, replace `run_test` with `TEST_MAIN()`. This will allow both
platform/ec tests and Ztests to share the same entry point.

Change `RUN_TEST` to `ztest_unit_test` and add the `ztest_test_suite` wrapper
plus the call to `ztest_run_test_suite`.

```
/*
 * Define the test cases to run. We need to do this twice, once in the format
 * that Ztest uses, and again in the format the the EC test framework uses.
 * If you add a test to one of them, make sure to add it to the other.
 */
TEST_MAIN()
{
	ztest_test_suite(test_base32_lib,
			 ztest_unit_test(test_crc5),
			 ztest_unit_test(test_encode),
			 ztest_unit_test(test_decode));
	ztest_run_test_suite(test_base32_lib);
}
```

Each function that is called by `ztest_unit_test` needs to be declared using
`DECLARE_EC_TEST`. Keep the `return EC_SUCCESS;` at the end
of the test function. Note that for the EC build, `TEST_MAIN` will call
`test_reset` before running the test cases, and `test_print_result` after.

### Assert macros
Change the `TEST_ASSERT` macros to `zassert` macros. There are plans to
automate this process, but for now, it's a manual process involving some
intelligent find-and-replace.

* `TEST_ASSERT(n)` to `zassert_true(n, NULL)`
* `TEST_EQ(a, b, fmt)` to `zassert_equal(a, b, fmt ## ", " ## fmt, a, b)`
  * e.g. `TEST_EQ(a, b, "%d")` becomes `zassert_equal(a, b, "%d, %d", a, b)`
* `TEST_NE(a, b, fmt)` to `zassert_not_equal(a, b, fmt ## ", " ## fmt, a, b)`
* `TEST_LT(a, b, fmt)` to `zassert_true(a < b, fmt ## ", " ## fmt, a, b)`
* `TEST_LE(a, b, fmt)` to `zassert_true(a <= b, fmt ## ", " ## fmt, a, b)`
* `TEST_GT(a, b, fmt)` to `zassert_true(a > b, fmt ## ", " ## fmt, a, b)`
* `TEST_GE(a, b, fmt)` tp `zassert_true(a >= b, fmt ## ", " ## fmt, a, b)`
* `TEST_BITS_SET(a, bits)` to `zassert_true(a & (int)bits == (int)bits, "%u, %u", a & (int)bits, (int)bits)`
* `TEST_BITS_CLEARED(a, bits)` to `zassert_true(a & (int)bits == 0, "%u, 0", a & (int)bits)`
* `TEST_ASSERT_ARRAY_EQ(s, d, n)` to `zassert_mem_equal(s, d, b, NULL)`
* `TEST_CHECK(n)` to `zassert_true(n, NULL)`
* `TEST_NEAR(a, b, epsilon, fmt)` to `zassert_within(a, b, epsilon, fmt, a)`
  * Currently, every usage of `TEST_NEAR` involves floating point values
* `TEST_ASSERT_ABS_LESS(n, t)` to `zassert_true(abs(n) < t, "%d, %d", n, t)`
  * Currently, every usage of `TEST_ASSERT_ANS_LESS` involves signed integers.

There isn't a good replacement for `TEST_ASSERT_MEMSET(d, c, n)`, but it is
only used in two tests, `printf.c` and `utils.c`. If you need this test,
you'll need to code up a loop over the `n` bytes starting at `d`, and
`zassert_equal` that each byte is equal to `c`.

Also note that some tests use constructs like `TEST_ASSERT(var == const)`,
which would have been better write as `TEST_EQ(var, const)`. These should be
rewritten to use `zassert_equal`.

Refer to
[test: Allow EC unit test to use Ztest API](https://crrev.com/c/2492527) for
the changes to the base32.c source code.

### Tasklist

For any test that has a corresponding `${TESTNAME}.tasklist`, add the file
`shimmed_test_tasks.h` in the zephyr test directory, and in that file,
`#include` the tasklist file. See [accel_cal](https://crrev.com/c/2645198)
for an example.

Add `CONFIG_HAS_TEST_TASKS=y` to the `prj.conf` file, as well as the appropriate
`CONFIG_PLATFORM_EC` defines to include or exclude code that the unit under
test uses.

## Build and run

Use `zmake` to build and run the test:
```
(cr) $ zmake -l DEBUG configure --test -B build/ztest/base32 zephyr/test/base32
...
UART_0 connected to pseudotty: /dev/pts/1
*** Booting Zephyr OS build zephyr-v2.4.0-1-g63b2330a85cd  ***
Running test suite test_base32_lib
===================================================================
START - test_crc5
 PASS - test_crc5
===================================================================
START - test_encode
 PASS - test_encode
===================================================================
START - test_decode
 PASS - test_decode
===================================================================
Test suite test_base32_lib succeeded
===================================================================
PROJECT EXECUTION SUCCESSFUL
(cr) $
```