This repository has been archived by the owner on Mar 22, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
README.tests
226 lines (180 loc) · 10.6 KB
/
README.tests
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
Testing u-nit involves some different approaches, like unit testing and
fuzzy or qemu testing.
Unit test
For inittab, fstab and command line parser, as well as generic lexer.
To generate unit test executable, run `make tests`. It should generate
`inittab_test`, `lexer_test`, `fstab_test` and `cmdline_test` executables
- all must run with no issues.
Fuzzy testing
American Fuzzy Lop (AFL) is run on a single executable, using
`tests/data/parser/inittab` files as input for the fuzzy tool.
To generate AFL ready executable, run `make tests`. It should generate
`afl_inittab_test` executable - that can be run with a command like
`afl-fuzz -i tests/data -o fuzz-results/ ./afl_inittab_test @@`.
See AFL documentation for more details.
QEMU testing
Runs u-nit as PID 1 on a virtual environment using qemu. This virtual
environment needs a kernel image and rootfs. Currently, script that
perform this testing expects a directory `tests/init-qemu`, with files
for the kernel image, an optional initrd and root file system.
Those can be generated with help of `prepare-test-image.sh` script. It
uses `mkosi` [https://github.com/systemd/mkosi] to generate a debian
image, so a `mkosi` installation capable of generating debian images is
necessary. After image generation, script will extract kernel and initrd
files. Their names must be set on `qemu-tests.sh` file, on variables
KERNEL_FILE and INITRD_FILE respectively.
Besides those basic files, `prepare-test-image.sh` also generate
some other files necessary for tests: `gcov.ext4` - where coverage
obtained by gcov is stored and two empty filesystems, `test-fs.ext4`
and `test-fs2.ext4`, used on fstab tests.
Note that one can create their own kernel directly, if desired. Just
change KERNEL_FILE variable on `qemu-tests.sh` so this new kernel is
used. If INITRD_FILE variable is left empty, no initrd file will
be used - useful for custom kernels that don't use initrd/initramfs.
Tests run on qemu focus on two aspects: inittab and fstab files.
Inittab tests for qemu environment are defined using two files:
XXX-inittab and XXX-inspect for each test XXX. These files live in
`tests/data/qemu/inittab` directory. First file defines an inittab file,
with all programs tha should be run. Second one defines inspections to
be done on u-nit log file. In this directory there's also a `default-fstab`
file to be used on all inittab tests.
The inittab file can use some helper executables to perform its tests.
One is the `sleep_test`, which receives as command line arguments an
arbitrary string and a number. The arbitrary string can be used to
inspect u-nit log file. The number is for how long, in seconds, the
program will sleep. After period finishes, program quits. This can help
test if program B starts only after program A runs.
Another helper is the `sleep_crash_test`, that takes the same two
arguments as `sleep_test`, but it ends with a crash after the timer.
Can be helpful to see if a safe program crashes abnormally, for
instance.
Finally, there are two more helpers: `is_running` and `is_not_running`.
They take as argument a regular expression to be run on `ps aux`
command. Helper `is_running` will log failure if a regular expression
is not found on `ps aux` output, `is_not_running` does the opposite.
They should help to see if a process is really running or not on a given
moment. Useful to test if a service is up, for instance. Note that
the regular expression must not match itself on `ps aux` output - or it
would have wrong results, as it appears on it. A good way to achieve
this is to start the regular expression with `[[]`.
The fstab tests live on `tests/data/qemu/fstab`. As with inittab, they
are divided into two categories: fstab files and inspect files, named
XXX-fstab and XXX-inspect. Both are sent to virtual environment, former
will be in `/etc/fstab` and later on `/usr/share/expected_mounts`.
To start shutdown on virtual environment, add a `kill -s USR2 1` on
inittab file, so u-nit will perform shutdown steps.
The inittab inspect file runs some regular expressions on u-nit log file.
Note that log file also contains any output of programs started by u-nit -
except those that define a controlling terminal. For instance, on start,
`sleep_test` prints `START: /usr/bin/sleep_test <string> <timeout>`.
Regular expressions are defined on three bash arrays variables: EXPECT,
EXPECT_IN_ORDER and NOT_EXPECT. The first searches for each expression
on the array on u-nit log file, and expect that they appear on log,
in the same order they defined on the array. The second simply expects
that each expression appears on u-nit log file, and the last doesn't
expect them at all.
The fstab inspect file basically lists expected mountpoints with
expected options. These options should be in the same format and order
returned by findmnt(8). To list options, use EXPECTED bash array
variable. Each entry must be in the format `MNTPOINT:EXPECTED_OPTIONS`,
like `/mnt/a:rw,relatime,data=ordered`. Another bash array variable
available is UNEXPECTED, that checks if no MNTPOINT entry listed is
actually mounted. For instance, entry `/mnt/b` checks if there's no
`/mnt/b` mountpoint. Useful to test if `noauto` mount option is respected
by u-nit.
Expected options are not automatically derived from fstab file because
this would be a rather complex task, as there'snot a 1:1 mapping from
the fstab options to the ones returned, since kernel version plays an
important role on mount options interactions. For instance, `relatime`
options is always defined unless `noatime` is.
In order to mount the rootfs to send files to virtual environment,
this test needs root permissions. To run it, simply run
`make run-qemu-tests`.
If some test fails, check `qemu-tests.log` file to see what happened.
Coverage information
Coverage information of QEMU tests can also be extracted. GCC gcov
is used to get this information. First step is to use `make coverage`
to compile u-nit with coverage instrumentation enabled.
Then, run tests normally (`make run-qemu-test` or `make run-qemu-fault`,
for instance).
Finally, extract coverage information using `make extract-coverage`
(this will need sudo to get info from inside target image). This step
also uses `lcov` (http://ltp.sourceforge.net/coverage/lcov.php) to
make a nice html report of the coverage. To see the report, call
`make show-coverage-report` to open the browser with it.
After using report, to delete coverage information gathered, to run
a new set of tests, use `make clean-coverage`.
Valgrind testing
Runs u-nit as PID 1 inside container environment under Valgrind.
Container is used instead of QEMU because it easily makes `/proc`
available for Valgrind. (On QEMU /proc wouldn't be mounted until
u-nit mount it.)
This tests perform all tests on `tests/data/qemu` directory with u-nit
under Valgrind, checking for memory issues. If any is found, this
check should fail and show Valgrind output on `qemu-tests.log`.
To run these tests, run `make run-valgrind-tests`.
Note that Valgrind doesn't implement `reboot` syscall, so it will
complain about "syscall 169 not implemented". This should not be an
issue. Indeed, as `reboot` call fails, u-nit shall exit with exit code
1 (`EXIT_FAILURE`), due to `reboot` call failure. On container
environment this is not an issue - container will simply exit. This
even allows some cleanup code to run - currently, on normal execution,
`reboot` is called before cleanup, so it never happens.
Naturally, this test expect that Valgrind is available on target image
- rootfs.ext2.
Container being used for tests is systemd-nspawn (minimum
systemd-version: 233)
[https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html].
Note that to run the container, root permissions are necessary.
Also note that fstab tests are skipped on container environment - as
there's no simple way to setup on container the expected device nodes.
ASAN testing
Runs u-nit as PID 1 inside container environment with AdressSanitizer
enabled [https://github.com/google/sanitizers]. Container is used
instead of QEMU because it easily makes `/proc` available from ASAN.
(On QEMU /proc wouldn't be mounted until u-nit mount it.)
This tests perform all tests on `tests/data/qemu` directory with u-nit
with ASAN, checking for memory issues. If any is found, this
check should fail and show ASAN output on `qemu-tests.log`.
To run these tests, first ensure that u-nit is compiled with address
sanitizer support, running `make init-asan`, then, to executed the
tests, run `make run-asan-tests`.
Note that an abrupt end of u-nit - like calling `reboot` - doesn't give
a chance to ASAN output its data. To easily allow that and to keep
consistency with behaviour when running under Valgrind, u-nit doesn't
call `reboot` if compiled with address sanitizer. So it will end up
running all clean up code and exit with exit code 1 (`EXIT_FAILURE`),
just like when u-nit runs under Valgrind.
This test expect that AddressSanitizer libraires are available on
target image - rootfs.ext2.
Container being used for tests is systemd-nspawn (minimum
systemd-version: 233)
[https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html].
Note that to run the container, root permissions are necessary.
Also note that fstab tests are skipped on container environment - as
there's no simple way to setup on container the expected device nodes.
Fault injection
Runs u-nit, injecting faults on some library calls, like `calloc` or
`epoll_wait`. Uses libfiu (https://blitiri.com.ar/p/libfiu/) library
to inject the fails.
Failures are defined on `tests/data/qemu/fault-definitions` file. This
file has the set of functions that will fail, defined in the format
expected by libfiu. Note that several tests are defined there and each
test may define several functions to fail. A test is everything defined
between `""`. To have more than on function failing per test, new
functions need to be on next line, but still inside the same quotes. For
instance:
"enable_random name=linux/io/dup2,probability=0.2
enable_random name=linux/io/timerfd_create,probability=0.2"
Defines a single test, in which both `dup2` and `timerfd_create`
may fail with a probality of 20%.
Each test is run against all tests defined on `tests/data/qemu`. As
tests are non deterministic - since they will fail at random, given
specified probability - each test is run five times. This is so that
it's possible to inject faults on a function that is used in different
parts of u-nit (so we don't always fault on the first `calloc`, for
instance).
To run fault injection tests, simply run `make run-qemu-fault`. Note
that these tests can take very long time. They are a nice way to
expand coverage of tests - since will help test error handling code -
so make sure to run them with coverage enabled and extract coverage.