tests: Use errno value rather than a specific int
[vpp.git] / test / config.py
1 import argparse
2 import os
3 import psutil
4 import time
5 from vpp_qemu_utils import can_create_namespaces
6
7
8 def positive_int_or_default(default):
9     def positive_integer(v):
10         if v is None or v == "" or int(v) == default:
11             return default
12         if int(v) <= 0:
13             raise ValueError("value must be positive")
14         return int(v)
15
16     return positive_integer
17
18
19 def positive_float_or_default(default):
20     def positive_float(v):
21         if v is None or v == "" or float(v) == default:
22             return default
23         if float(v) <= 0:
24             raise ValueError("value must be positive")
25         return float(v)
26
27     return positive_float
28
29
30 def positive_int_or_auto(v):
31     if v is None or v in ("", "auto"):
32         return "auto"
33     if int(v) <= 0:
34         raise ValueError("value must be positive or auto")
35     return int(v)
36
37
38 def int_or_auto(v):
39     if v is None or v in ("", "auto"):
40         return "auto"
41     if int(v) < 0:
42         raise ValueError("value must be positive or auto")
43     return int(v)
44
45
46 def int_choice_or_default(options, default):
47     assert default in options
48
49     def choice(v):
50         if v is None or v == "":
51             return default
52         if int(v) in options:
53             return int(v)
54         raise ValueError("invalid choice")
55
56     return choice
57
58
59 def worker_config(v):
60     if v is None or v == "":
61         return 0
62     if v.startswith("workers "):
63         return int(v.split(" ")[1])
64     return int(v)
65
66
67 def directory(v):
68     if not os.path.isdir(v):
69         raise ValueError(f"provided path '{v}' doesn't exist or is not a directory")
70     return v
71
72
73 def directory_verify_or_create(v):
74     if not os.path.isdir(v):
75         os.mkdir(v)
76     return v
77
78
79 parser = argparse.ArgumentParser(
80     description="VPP unit tests", formatter_class=argparse.RawTextHelpFormatter
81 )
82
83 parser.add_argument(
84     "--failfast", action="store_true", help="stop running tests on first failure"
85 )
86
87 parser.add_argument(
88     "--test-src-dir",
89     action="append",
90     type=directory,
91     help="directory containing test files "
92     "(may be specified multiple times) "
93     "(VPP_WS_DIR/test is added automatically to the set)",
94 )
95
96 default_verbose = 0
97
98 parser.add_argument(
99     "--verbose",
100     action="store",
101     default=default_verbose,
102     type=int_choice_or_default((0, 1, 2), default_verbose),
103     help="verbosity setting - 0 - least verbose, 2 - most verbose (default: 0)",
104 )
105
106 default_test_run_timeout = 600
107
108 parser.add_argument(
109     "--timeout",
110     action="store",
111     type=positive_int_or_default(default_test_run_timeout),
112     default=default_test_run_timeout,
113     metavar="TEST_RUN_TIMEOUT",
114     help="test run timeout in seconds - per test "
115     f"(default: {default_test_run_timeout})",
116 )
117
118 parser.add_argument(
119     "--failed-dir",
120     action="store",
121     type=directory,
122     help="directory containing failed tests (default: --tmp-dir)",
123 )
124
125 filter_help_string = """\
126 expression consists of one or more filters separated by commas (',')
127 filter consists of 3 string selectors separated by dots ('.')
128
129     <file>.<class>.<function>
130
131 - selectors restrict which files/classes/functions are run
132 - selector can be replaced with '*' or omitted entirely if not needed
133 - <file> selector is automatically prepended with 'test_' if required
134 - '.' separators are required only if selector(s) follow(s)
135
136 examples:
137
138 1. all of the following expressions are equivalent and will select
139    all test classes and functions from test_bfd.py:
140    'test_bfd' 'bfd' 'test_bfd..' 'bfd.' 'bfd.*.*' 'test_bfd.*.*'
141 2. 'bfd.BFDAPITestCase' selects all tests from test_bfd.py,
142    which are part of BFDAPITestCase class
143 3. 'bfd.BFDAPITestCase.test_add_bfd' selects a single test named
144    test_add_bfd from test_bfd.py/BFDAPITestCase
145 4. '.*.test_add_bfd' selects all test functions named test_add_bfd
146    from all files/classes
147 5. 'bfd,ip4,..test_icmp_error' selects all test functions in test_bfd.py,
148    test_ip4.py and all test functions named 'test_icmp_error' in all files
149 """
150 parser.add_argument(
151     "--filter", action="store", metavar="FILTER_EXPRESSION", help=filter_help_string
152 )
153
154 default_retries = 0
155
156 parser.add_argument(
157     "--retries",
158     action="store",
159     default=default_retries,
160     type=positive_int_or_default(default_retries),
161     help="retry failed tests RETRIES times",
162 )
163
164 parser.add_argument(
165     "--step", action="store_true", default=False, help="enable stepping through tests"
166 )
167
168 debug_help_string = """\
169 attach     - attach to already running vpp
170 core       - detect coredump and load core in gdb on crash
171 gdb        - print VPP PID and pause allowing attaching gdb
172 gdbserver  - same as above, but run gdb in gdbserver
173 """
174
175 parser.add_argument(
176     "--debug",
177     action="store",
178     choices=["attach", "core", "gdb", "gdbserver"],
179     help=debug_help_string,
180 )
181
182 parser.add_argument(
183     "--debug-framework",
184     action="store_true",
185     help="enable internal test framework debugging",
186 )
187
188 parser.add_argument(
189     "--compress-core",
190     action="store_true",
191     help="compress core files if not debugging them",
192 )
193
194 parser.add_argument("--extended", action="store_true", help="run extended tests")
195 parser.add_argument(
196     "--skip-netns-tests",
197     action="store_true",
198     help="skip tests involving netns operations",
199 )
200
201 parser.add_argument(
202     "--sanity", action="store_true", help="perform sanity vpp run before running tests"
203 )
204 parser.add_argument("--api-preload", action="store_true", help="preload API files")
205
206 parser.add_argument(
207     "--force-foreground",
208     action="store_true",
209     help="force running in foreground - don't fork",
210 )
211
212 parser.add_argument(
213     "--jobs",
214     action="store",
215     type=positive_int_or_auto,
216     default="auto",
217     help="maximum concurrent test jobs",
218 )
219
220 parser.add_argument(
221     "--venv-dir", action="store", type=directory, help="path to virtual environment"
222 )
223
224 default_rnd_seed = time.time()
225 parser.add_argument(
226     "--rnd-seed",
227     action="store",
228     default=default_rnd_seed,
229     type=positive_float_or_default(default_rnd_seed),
230     help="random generator seed (default: current time)",
231 )
232
233 parser.add_argument(
234     "--vpp-worker-count",
235     action="store",
236     type=worker_config,
237     default=0,
238     help="number of vpp workers",
239 )
240
241 parser.add_argument(
242     "--gcov", action="store_true", default=False, help="running gcov tests"
243 )
244
245 parser.add_argument(
246     "--cache-vpp-output",
247     action="store_true",
248     default=False,
249     help="cache VPP stdout/stderr and log as one block after test finishes",
250 )
251
252 parser.add_argument(
253     "--vpp-ws-dir",
254     action="store",
255     required=True,
256     type=directory,
257     help="vpp workspace directory",
258 )
259
260 parser.add_argument(
261     "--vpp-tag",
262     action="store",
263     default="vpp_debug",
264     metavar="VPP_TAG",
265     required=True,
266     help="vpp tag (e.g. vpp, vpp_debug, vpp_gcov)",
267 )
268
269 parser.add_argument(
270     "--vpp",
271     action="store",
272     help="path to vpp binary (default: derive from VPP_WS_DIR and VPP_TAG)",
273 )
274
275 parser.add_argument(
276     "--vpp-install-dir",
277     type=directory,
278     action="store",
279     help="path to vpp install directory"
280     "(default: derive from VPP_WS_DIR and VPP_TAG)",
281 )
282
283 parser.add_argument(
284     "--vpp-build-dir",
285     action="store",
286     type=directory,
287     help="vpp build directory (default: derive from VPP_WS_DIR and VPP_TAG)",
288 )
289
290 parser.add_argument(
291     "--vpp-plugin-dir",
292     action="append",
293     type=directory,
294     help="directory containing vpp plugins"
295     "(default: derive from VPP_WS_DIR and VPP_TAG)",
296 )
297
298 parser.add_argument(
299     "--vpp-test-plugin-dir",
300     action="append",
301     type=directory,
302     help="directory containing vpp api test plugins"
303     "(default: derive from VPP_WS_DIR and VPP_TAG)",
304 )
305
306 parser.add_argument(
307     "--extern-plugin-dir",
308     action="append",
309     type=directory,
310     default=[],
311     help="directory containing external plugins",
312 )
313
314 parser.add_argument(
315     "--extern-apidir",
316     action="append",
317     type=directory,
318     default=[],
319     help="directory to look for API JSON files",
320 )
321
322 parser.add_argument(
323     "--coredump-size",
324     action="store",
325     default="unlimited",
326     help="specify vpp coredump size",
327 )
328
329 parser.add_argument(
330     "--max-vpp-cpus",
331     action="store",
332     type=int_or_auto,
333     default=0,
334     help="max cpus used by vpp",
335 )
336
337 variant_help_string = """\
338 specify which march node variant to unit test
339   e.g. --variant=skx - test the skx march variants
340   e.g. --variant=icl - test the icl march variants
341 """
342
343 parser.add_argument("--variant", action="store", help=variant_help_string)
344
345 parser.add_argument(
346     "--api-fuzz", action="store", default=None, help="specify api fuzzing parameters"
347 )
348
349 parser.add_argument(
350     "--wipe-tmp-dir",
351     action="store_true",
352     default=True,
353     help="remove test tmp directory before running test",
354 )
355
356 parser.add_argument(
357     "--tmp-dir",
358     action="store",
359     default="/tmp",
360     type=directory_verify_or_create,
361     help="directory where to store test temporary directories",
362 )
363
364 parser.add_argument(
365     "--log-dir",
366     action="store",
367     type=directory_verify_or_create,
368     help="directory where to store directories "
369     "containing log files (default: --tmp-dir)",
370 )
371
372 default_keep_pcaps = False
373 parser.add_argument(
374     "--keep-pcaps",
375     action="store_true",
376     default=default_keep_pcaps,
377     help=f"if set, keep all pcap files from a test run (default: {default_keep_pcaps})",
378 )
379
380 parser.add_argument(
381     "-r",
382     "--use-running-vpp",
383     dest="running_vpp",
384     required=False,
385     action="store_true",
386     default=False,
387     help="Runs tests against a running VPP.",
388 )
389
390 parser.add_argument(
391     "--excluded-plugin",
392     dest="excluded_plugins",
393     required=False,
394     action="append",
395     default=[],
396     help="Exclude the tests that indicate they require this plugin(s)",
397 )
398
399 parser.add_argument(
400     "-d",
401     "--socket-dir",
402     dest="socket_dir",
403     required=False,
404     action="store",
405     default="",
406     help="Relative or absolute path to running VPP's socket directory.\n"
407     "The directory must contain VPP's socket files:api.sock & stats.sock.\n"
408     "Default: /var/run/vpp if VPP is started as the root user, else "
409     "/var/run/user/${uid}/vpp.",
410 )
411
412 default_decode_pcaps = False
413 parser.add_argument(
414     "--decode-pcaps",
415     action="store_true",
416     default=default_decode_pcaps,
417     help=f"if set, decode all pcap files from a test run (default: {default_decode_pcaps})",
418 )
419
420 config = parser.parse_args()
421
422 ws = config.vpp_ws_dir
423 br = f"{ws}/build-root"
424 tag = config.vpp_tag
425
426 if config.vpp_install_dir is None:
427     config.vpp_install_dir = f"{br}/install-{tag}-native"
428
429 if config.vpp is None:
430     config.vpp = f"{config.vpp_install_dir}/vpp/bin/vpp"
431
432 if config.vpp_build_dir is None:
433     config.vpp_build_dir = f"{br}/build-{tag}-native"
434
435 libs = ["lib", "lib64"]
436
437 if config.vpp_plugin_dir is None:
438     config.vpp_plugin_dir = [
439         f"{config.vpp_install_dir}/vpp/{lib}/vpp_plugins" for lib in libs
440     ]
441
442 if config.vpp_test_plugin_dir is None:
443     config.vpp_test_plugin_dir = [
444         f"{config.vpp_install_dir}/vpp/{lib}/vpp_api_test_plugins" for lib in libs
445     ]
446
447 test_dirs = [f"{ws}/test"]
448
449 if config.test_src_dir is not None:
450     test_dirs.extend(config.test_src_dir)
451
452 config.test_src_dir = test_dirs
453
454
455 if config.venv_dir is None:
456     config.venv_dir = f"{ws}/build-root/test/venv"
457
458 if config.failed_dir is None:
459     config.failed_dir = f"{config.tmp_dir}"
460
461 available_cpus = psutil.Process().cpu_affinity()
462 num_cpus = len(available_cpus)
463
464 if config.max_vpp_cpus == "auto":
465     max_vpp_cpus = num_cpus
466 elif config.max_vpp_cpus > 0:
467     max_vpp_cpus = min(config.max_vpp_cpus, num_cpus)
468 else:
469     max_vpp_cpus = num_cpus
470
471 if not config.skip_netns_tests:
472     if not can_create_namespaces():
473         config.skip_netns_tests = True
474
475 if __name__ == "__main__":
476     print("Provided arguments:")
477     for i in config.__dict__:
478         print(f"  {i} is {config.__dict__[i]}")