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