1 <!--
2 Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
4 SPDX-License-Identifier: MPL-2.0
5
6 This Source Code Form is subject to the terms of the Mozilla Public
7 License, v. 2.0. If a copy of the MPL was not distributed with this
8 file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
10 See the COPYRIGHT file distributed with this work for additional
11 information regarding copyright ownership.
12 -->
13
14 # BIND9 System Test Framework
15
16 This directory holds test environments for running bind9 system tests involving
17 multiple name servers.
18
19 Each system test directory holds a set of scripts and configuration files to
20 test different parts of BIND. The directories are named for the aspect of BIND
21 they test, for example:
22
23 dnssec/ DNSSEC tests
24 forward/ Forwarding tests
25 glue/ Glue handling tests
26
27 etc.
28
29 A system test directory must start with an alphabetic character and may not
30 contain any special characters. Only hyphen may be used as a word separator.
31
32 Typically each set of tests sets up 2-5 name servers and then performs one or
33 more tests against them. Within the test subdirectory, each name server has a
34 separate subdirectory containing its configuration data. These subdirectories
35 are named "nsN" or "ansN" (where N is a number between 1 and 8, e.g. ns1, ans2
36 etc.)
37
38 The tests are completely self-contained and do not require access to the real
39 DNS. Generally, one of the test servers (usually ns1) is set up as a root
40 nameserver and is listed in the hints file of the others.
41
42
43 ## Running the Tests
44
45 ### Prerequisites
46
47 To run system tests, make sure you have the following dependencies installed:
48
49 - python3 (3.10 and newer)
50 - perl
51
52 List of required python packages and their versions can be found in
53 requirements.txt (can be installed with `pip3 install -r requirements.txt`).
54
55 ### Network Setup
56
57 To enable all servers to run on the same machine, they bind to separate virtual
58 IP addresses on the loopback interface. ns1 runs on 10.53.0.1, ns2 on
59 10.53.0.2, etc. Before running any tests, you must set up these addresses by
60 running the command
61
62 sh ifconfig.sh up
63
64 as root. The interfaces can be removed by executing the command:
65
66 sh ifconfig.sh down
67
68 ... also as root.
69
70 The servers use unprivileged ports (above 1024) instead of the usual port 53,
71 so they can be run without root privileges once the interfaces have been set
72 up.
73
74 **Note for MacOS Users**
75
76 If you wish to make the interfaces survive across reboots, copy
77 org.isc.bind.system and org.isc.bind.system.plist to /Library/LaunchDaemons
78 then run
79
80 launchctl load /Library/LaunchDaemons/org.isc.bind.system.plist
81
82 ... as root.
83
84 ### Running a Single Test
85
86 The recommended way is to use pytest and its test selection facilities:
87
88 pytest -k <test-name-or-pattern>
89
90 Using `-k` to specify a pattern allows to run a single pytest test case within
91 a system test. E.g. you can use `-k test_sslyze_dot` to execute just the
92 `test_sslyze_dot()` function from `doth/tests_sslyze.py`.
93
94 However, using the `-k` pattern might pick up more tests than intended. You can
95 use the `--collect-only` option to check the list of tests which match you `-k`
96 pattern. If you just want to execute all system tests within a single test
97 directory, you can also use the utility script:
98
99 ./run.sh system_test_dir_name
100
101 ### Running All the System Tests
102
103 Issuing plain `pytest` command without any argument will execute all tests
104 sequentially. To execute them in parallel, ensure you have pytest-xdist
105 installed and run:
106
107 pytest [-n <number-of-workers>]
108
109 Alternately, using the make command is also supported:
110
111 make [-j numproc] test
112
113 ### rr
114
115 When running system tests, named can be run under the rr tool. rr records a
116 trace to the $system_test/nsX/named-Y/ directory, which can be later used to
117 replay named. To enable this, execute start.pl with the USE_RR environment
118 variable set.
119
120 ### Test Artifacts
121
122 Each test module is executed inside a unique temporary directory which contains
123 all the artifacts from the test run. If the tests succeed, they are deleted by
124 default. To override this behaviour, pass `--noclean` to pytest.
125
126 The directory name starts with the system test name, followed by `-tmp-XXXXXX`,
127 i.e. `dns64-tmp-r07vei9s` for `dns64` test run. Since this name changes each
128 run, a convenience symlink that has a stable name is also created. It points to
129 the latest test artifacts directory and has a form of `dns64-sh_dns64`
130 (depending on the particular test module).
131
132 To clean up the temporary directories and symlinks, run `make clean-local` in
133 the system test directory.
134
135 The following test artifacts are typically available:
136
137 - pytest.log.txt: main log file with test output
138 - files generated by the test itself, e.g. output from "dig" and "rndc"
139 - files produced by named, other tools or helper scripts
140
141
142 ## Writing System Tests
143
144 ### File Overview
145
146 Tests are organized into system test directories which may hold one or more
147 test modules (python files). Each module may have multiple test cases. The
148 system test directories may contain the following standard files:
149
150 - `tests_*.py`: These python files are picked up by pytest as modules. If they
151 contain any test functions, they're added to the test suite.
152
153 - `*.j2`: These jinja2 templates can be used for configuration files or any
154 other files which require certain variables filled in, e.g. ports from the
155 environment variables. During test setup, the pytest runner will automatically
156 fill those in and strip the filename extension .j2, e.g. `ns1/named.conf.j2`
157 becomes `ns1/named.conf`. When using advanced templating to conditionally
158 include/omit entire sections or when filling in custom variables used for the
159 test, ensure the templates always include the defaults. If you don't need the
160 file to be auto-templated during test setup, use `.j2.manual` instead and then
161 no defaults are needed.
162
163 - `setup.sh`: This sets up the preconditions for the tests.
164
165 - `tests.sh`: Any shell-based tests are located within this file. Runs the
166 actual tests.
167
168 - `tests_sh_*.py`: A glue file for the pytest runner for executing shell tests.
169
170 - `ns<N>`: These subdirectories contain test name servers that can be queried
171 or can interact with each other. The value of N indicates the address the
172 server listens on: for example, ns2 listens on 10.53.0.2, and ns4 on
173 10.53.0.4. All test servers use an unprivileged port, so they don't need to
174 run as root. These servers log at the highest debug level and the log is
175 captured in the file "named.run".
176
177 - `ans<N>`: Like ns[X], but these are simple mock name servers implemented in
178 Perl or Python. They are generally programmed to misbehave in ways named
179 would not so as to exercise named's ability to interoperate with badly
180 behaved name servers.
181
182 ### Module Scope
183
184 A module is a python file which contains test functions. Every system
185 test directory may contain multiple modules (i.e. tests_*.py files).
186
187 The server setup/teardown is performed for each module. Bundling test cases
188 together inside a single module may save some resources. However, test cases
189 inside a single module can't be executed in parallel.
190
191 It is possible to execute different modules defined within a single system test
192 directory in parallel. This is possible thanks to executing the tests inside a
193 temporary directory and proper port assignment to ensure there won't be any
194 conflicts.
195
196 ### Port Usage
197
198 In order for the tests to run in parallel, each test requires a unique set of
199 ports. This is ensured by the pytest runner, which assigns a unique set of
200 ports to each test module.
201
202 Inside the python tests, it is possible to use the `ports` fixture to get the
203 assigned port numbers. They're also set as environment variables. These include:
204
205 - `PORT`: used as the basic dns port
206 - `TLSPORT`: used as the port for DNS-over-TLS
207 - `HTTPPORT`, `HTTPSPORT`: used as the ports for DNS-over-HTTP
208 - `CONTROLPORT`: used as the RNDC control port
209 - `EXTRAPORT1` through `EXTRAPORT8`: additional ports that can be used as needed
210
211 ### Logging
212
213 Each module has a separate log which will be saved as pytest.log.txt in the
214 temporary directory in which the test is executed. This log includes messages
215 for this module setup/teardown as well as any logging from the tests. Logging
216 level DEBUG and above will be present in this log.
217
218 In general, any log messages using INFO or above will also be printed out
219 during pytest execution. In CI, the pytest output is also saved to
220 pytest.out.txt in the bin/tests/system directory.
221
222 ### Adding a Test to the System Test Suite
223
224 Once a test has been created it will be automatically picked up by the pytest
225 runner if it upholds the convention expected by pytest (especially when it comes
226 to naming files and test functions).
227
228 However, if a new system test directory is created, it also needs to be added to
229 `TESTS` in `Makefile.am`, in order to work with `make check`.
230
231
232 ## Test Files
233
234 ### setup.sh
235
236 This script is responsible for setting up the configuration files used in the
237 test. It is used by both the python and shell tests. It is interpreted just
238 before the servers are started up for each test module.
239
240 ### tests_*.py
241
242 These are test modules containing tests written in python. Every test is a
243 function which begins with the name `test_` (according to pytest convention). It
244 is possible to pass fixtures to the test function by specifying their name as
245 function arguments. Fixtures are used to provide context to the tests, e.g.:
246
247 - `ports` is a dictionary with assigned port numbers
248
249 ### tests_sh_*.py
250
251 These are glue files that are required to execute shell based tests (see below).
252 These modules shouldn't contain any python tests (use a separate file instead).
253
254 ### tests.sh
255
256 This is the test file for shell based tests.
257
258
259 ## Nameservers
260
261 As noted earlier, a system test will involve a number of nameservers. These
262 will be either instances of named, or special servers written in a language
263 such as Perl or Python.
264
265 For the former, the version of "named" being run is that in the "bin/named"
266 directory in the tree holding the tests (i.e. if "make test" is being run
267 immediately after "make", the version of "named" used is that just built). The
268 configuration files, zone files etc. for these servers are located in
269 subdirectories of the test directory named "nsN", where N is a small integer.
270 The latter are special nameservers, mostly used for generating deliberately bad
271 responses, located in subdirectories named "ansN" (again, N is an integer).
272 In addition to configuration files, these directories should hold the
273 appropriate script files as well.
274
275 Note that the "N" for a particular test forms a single number space, e.g. if
276 there is an "ns2" directory, there cannot be an "ans2" directory as well.
277 Ideally, the directory numbers should start at 1 and work upwards.
278
279 When tests are executed, pytest takes care of the test setup and teardown. It
280 looks for any `nsN` and `ansN` directories in the system test directory and
281 starts those servers.
282
283 ### `named` Command-Line Options
284
285 By default, `named` server is started with the following options:
286
287 -c named.conf Specifies the configuration file to use (so by implication,
288 each "nsN" nameserver's configuration file must be called
289 named.conf).
290
291 -d 99 Sets the maximum debugging level.
292
293 -D <name> The "-D" option sets a string used to identify the
294 nameserver in a process listing. In this case, the string
295 is the name of the subdirectory.
296
297 -g Runs the server in the foreground and logs everything to
298 stderr.
299
300 -m record
301 Turns on these memory usage debugging flags.
302
303 All output is sent to a file called `named.run` in the nameserver directory.
304
305 The options used to start named can be altered. There are a couple ways of
306 doing this. `start.pl` checks the methods in a specific order: if a check
307 succeeds, the options are set and any other specification is ignored. In order,
308 these are:
309
310 1. Specifying options to `start.pl` or `start_server` shell utility function
311 after the name of the test directory, e.g.
312
313 start_server --noclean --restart --port ${PORT} ns1 -- "-D xfer-ns1 -T transferinsecs -T transferslowly"
314
315 2. Including a file called "named.args" in the "nsN" directory. If present,
316 the contents of the first non-commented, non-blank line of the file are used as
317 the named command-line arguments. The rest of the file is ignored.
318
319 3. Tweaking the default command line arguments with "-T" options. This flag is
320 used to alter the behavior of BIND for testing and is not documented in the
321 ARM. The presence of certain files in the "nsN" directory adds flags to
322 the default command line (the content of the files is irrelevant - it
323 is only the presence that counts):
324
325 named.noaa Appends "-T noaa" to the command line, which causes
326 "named" to never set the AA bit in an answer.
327
328 named.dropedns Adds "-T dropedns" to the command line, which causes
329 "named" to recognise EDNS options in messages, but drop
330 messages containing them.
331
332 named.maxudp1460 Adds "-T maxudp1460" to the command line, setting the
333 maximum UDP size handled by named to 1460.
334
335 named.maxudp512 Adds "-T maxudp512" to the command line, setting the
336 maximum UDP size handled by named to 512.
337
338 named.noedns Appends "-T noedns" to the command line, which disables
339 recognition of EDNS options in messages.
340
341 named.notcp Adds "-T notcp", which disables TCP in "named".
342
343 named.soa Appends "-T nosoa" to the command line, which disables
344 the addition of SOA records to negative responses (or to
345 the additional section if the response is triggered by RPZ
346 rewriting).
347
348 ### Running Nameservers Interactively
349
350 In order to debug the nameservers, you can let pytest perform the nameserver
351 setup and interact with the servers before the test starts, or even at specific
352 points during the test, using the `--trace` option to drop you into pdb debugger
353 which pauses the execution of the tests, while keeping the server state intact:
354
355 pytest -k dns64 --trace
356
357
358 ## Developer Notes
359
360 ### Test discovery and collection
361
362 There are two distinct types of system tests. The first is a shell script
363 tests.sh containing individual test cases executed sequentially and the
364 success/failure is determined by return code. The second type is a regular
365 pytest file which contains test functions.
366
367 Dealing with the regular pytest files doesn't require any special consideration
368 as long as the naming conventions are met. Discovering the tests.sh tests is
369 more complicated.
370
371 The chosen solution is to add a bit of glue for each system test. For every
372 tests.sh, there is an accompanying tests_sh_*.py file that contains a test
373 function which utilizes a custom run_tests_sh fixture to call the tests.sh
374 script. Other solutions were tried and eventually rejected. While this
375 introduces a bit of extra glue, it is the most portable, compatible and least
376 complex solution.
377
378 ### Compatibility with older pytest version
379
380 Keep in mind that the pytest runner must work with ancient versions of pytest.
381 When implementing new features, it is advisable to check feature support in
382 pytest and pytest-xdist in older distributions first.
383
384 As a general rule, any changes to the pytest runner need to keep working on all
385 platforms in CI that use the pytest runner. As of 2023-11-14, the oldest
386 supported version is whatever is available in EL8.
387
388 We may need to add more compat code eventually to handle breaking upstream
389 changes. For example, using request.fspath attribute is already deprecated in
390 latest pytest.
391
392 ### Format of Shell Test Output
393
394 Shell-based tests have the following format of output:
395
396 <letter>:<test-name>:<message> [(<number>)]
397
398 e.g.
399
400 I:catz:checking that dom1.example is not served by primary (1)
401
402 The meanings of the fields are as follows:
403
404 <letter>
405 This indicates the type of message. This is one of:
406
407 S Start of the test
408 A Start of test (retained for backwards compatibility)
409 T Start of test (retained for backwards compatibility)
410 E End of the test
411 I Information. A test will typically output many of these messages
412 during its run, indicating test progress. Note that such a message may
413 be of the form "I:testname:failed", indicating that a sub-test has
414 failed.
415 R Result. Each test will result in one such message, which is of the
416 form:
417
418 R:<test-tmpdir>:<result>
419
420 where <result> is one of:
421
422 PASS The test passed
423 FAIL The test failed
424 SKIPPED The test was not run, usually because some
425 prerequisites required to run the test are missing.
426
427 <test-tmpdir>
428 This is the name of the temporary test directory from which the message
429 emanated, which is also the name of the subdirectory holding the test files.
430
431 <message>
432 This is text output by the test during its execution.
433
434 (<number>)
435 If present, this will correlate with a file created by the test. The tests
436 execute commands and route the output of each command to a file. The name of
437 this file depends on the command and the test, but will usually be of the form:
438
439 <command>.out.<suffix><number>
440
441 e.g. nsupdate.out.test28, dig.out.q3. This aids diagnosis of problems by
442 allowing the output that caused the problem message to be identified.
443
444