tests: replace pycodestyle with black
[vpp.git] / test / config.py
1 import argparse
2 import os
3 import psutil
4 import textwrap
5 import time
6
7
8 def positive_int_or_default(default):
9     def positive_integer(v):
10         if v is None or v == "":
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 == "":
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",
123 )
124
125 filter_help_string = """\
126 expression consists of 3 string selectors separated by '.' separators:
127
128     <file>.<class>.<function>
129
130 - selectors restrict which files/classes/functions are run
131 - selector can be replaced with '*' or omitted entirely if not needed
132 - <file> selector is automatically prepended with 'test_' if required
133 - '.' separators are required only if selector(s) follow(s)
134
135 examples:
136
137 1. all of the following expressions are equivalent and will select
138    all test classes and functions from test_bfd.py:
139    'test_bfd' 'bfd' 'test_bfd..' 'bfd.' 'bfd.*.*' 'test_bfd.*.*'
140 2. 'bfd.BFDAPITestCase' selects all tests from test_bfd.py,
141    which are part of BFDAPITestCase class
142 3. 'bfd.BFDAPITestCase.test_add_bfd' selects a single test named
143    test_add_bfd from test_bfd.py/BFDAPITestCase
144 4. '.*.test_add_bfd' selects all test functions named test_add_bfd
145    from all files/classes
146 """
147 parser.add_argument(
148     "--filter", action="store", metavar="FILTER_EXPRESSION", help=filter_help_string
149 )
150
151 default_retries = 0
152
153 parser.add_argument(
154     "--retries",
155     action="store",
156     default=default_retries,
157     type=positive_int_or_default(default_retries),
158     help="retry failed tests RETRIES times",
159 )
160
161 parser.add_argument(
162     "--step", action="store_true", default=False, help="enable stepping through tests"
163 )
164
165 debug_help_string = """\
166 attach     - attach to already running vpp
167 core       - detect coredump and load core in gdb on crash
168 gdb        - print VPP PID and pause allowing attaching gdb
169 gdbserver  - same as above, but run gdb in gdbserver
170 """
171
172 parser.add_argument(
173     "--debug",
174     action="store",
175     choices=["attach", "core", "gdb", "gdbserver"],
176     help=debug_help_string,
177 )
178
179 parser.add_argument(
180     "--debug-framework",
181     action="store_true",
182     help="enable internal test framework debugging",
183 )
184
185 parser.add_argument(
186     "--compress-core",
187     action="store_true",
188     help="compress core files if not debugging them",
189 )
190
191 parser.add_argument("--extended", action="store_true", help="run extended tests")
192
193 parser.add_argument(
194     "--sanity", action="store_true", help="perform sanity vpp run before running tests"
195 )
196
197 parser.add_argument(
198     "--force-foreground",
199     action="store_true",
200     help="force running in foreground - don't fork",
201 )
202
203 parser.add_argument(
204     "--jobs",
205     action="store",
206     type=positive_int_or_auto,
207     default="auto",
208     help="maximum concurrent test jobs",
209 )
210
211 parser.add_argument(
212     "--venv-dir", action="store", type=directory, help="path to virtual environment"
213 )
214
215 default_rnd_seed = time.time()
216 parser.add_argument(
217     "--rnd-seed",
218     action="store",
219     default=default_rnd_seed,
220     type=positive_float_or_default(default_rnd_seed),
221     help="random generator seed (default: current time)",
222 )
223
224 parser.add_argument(
225     "--vpp-worker-count",
226     action="store",
227     type=worker_config,
228     default=0,
229     help="number of vpp workers",
230 )
231
232 parser.add_argument(
233     "--gcov", action="store_true", default=False, help="running gcov tests"
234 )
235
236 parser.add_argument(
237     "--cache-vpp-output",
238     action="store_true",
239     default=False,
240     help="cache VPP stdout/stderr and log as one block after test finishes",
241 )
242
243 parser.add_argument(
244     "--vpp-ws-dir",
245     action="store",
246     required=True,
247     type=directory,
248     help="vpp workspace directory",
249 )
250
251 parser.add_argument(
252     "--vpp-tag",
253     action="store",
254     default="vpp_debug",
255     metavar="VPP_TAG",
256     required=True,
257     help="vpp tag (e.g. vpp, vpp_debug, vpp_gcov)",
258 )
259
260 parser.add_argument(
261     "--vpp",
262     action="store",
263     help="path to vpp binary (default: derive from VPP_WS_DIR and VPP_TAG)",
264 )
265
266 parser.add_argument(
267     "--vpp-install-dir",
268     type=directory,
269     action="store",
270     help="path to vpp install directory"
271     "(default: derive from VPP_WS_DIR and VPP_TAG)",
272 )
273
274 parser.add_argument(
275     "--vpp-build-dir",
276     action="store",
277     type=directory,
278     help="vpp build directory (default: derive from VPP_WS_DIR and VPP_TAG)",
279 )
280
281 parser.add_argument(
282     "--vpp-plugin-dir",
283     action="append",
284     type=directory,
285     help="directory containing vpp plugins"
286     "(default: derive from VPP_WS_DIR and VPP_TAG)",
287 )
288
289 parser.add_argument(
290     "--vpp-test-plugin-dir",
291     action="append",
292     type=directory,
293     help="directory containing vpp api test plugins"
294     "(default: derive from VPP_WS_DIR and VPP_TAG)",
295 )
296
297 parser.add_argument(
298     "--extern-plugin-dir",
299     action="append",
300     type=directory,
301     default=[],
302     help="directory containing external plugins",
303 )
304
305 parser.add_argument(
306     "--coredump-size",
307     action="store",
308     default="unlimited",
309     help="specify vpp coredump size",
310 )
311
312 parser.add_argument(
313     "--max-vpp-cpus",
314     action="store",
315     type=int_or_auto,
316     default=0,
317     help="max cpus used by vpp",
318 )
319
320 variant_help_string = """\
321 specify which march node variant to unit test
322   e.g. --variant=skx - test the skx march variants
323   e.g. --variant=icl - test the icl march variants
324 """
325
326 parser.add_argument("--variant", action="store", help=variant_help_string)
327
328 parser.add_argument(
329     "--api-fuzz", action="store", default=None, help="specify api fuzzing parameters"
330 )
331
332 parser.add_argument(
333     "--wipe-tmp-dir",
334     action="store_true",
335     default=True,
336     help="remove test tmp directory before running test",
337 )
338
339 parser.add_argument(
340     "--tmp-dir",
341     action="store",
342     default="/tmp",
343     type=directory_verify_or_create,
344     help="directory where to store test temporary directories",
345 )
346
347 parser.add_argument(
348     "--log-dir",
349     action="store",
350     type=directory_verify_or_create,
351     help="directory where to store directories "
352     "containing log files (default: --tmp-dir)",
353 )
354
355 default_keep_pcaps = False
356 parser.add_argument(
357     "--keep-pcaps",
358     action="store_true",
359     default=default_keep_pcaps,
360     help="if set, keep all pcap files from a test run"
361     f" (default: {default_keep_pcaps})",
362 )
363
364 config = parser.parse_args()
365
366 ws = config.vpp_ws_dir
367 br = f"{ws}/build-root"
368 tag = config.vpp_tag
369
370 if config.vpp_install_dir is None:
371     config.vpp_install_dir = f"{br}/install-{tag}-native"
372
373 if config.vpp is None:
374     config.vpp = f"{config.vpp_install_dir}/vpp/bin/vpp"
375
376 if config.vpp_build_dir is None:
377     config.vpp_build_dir = f"{br}/build-{tag}-native"
378
379 libs = ["lib", "lib64"]
380
381 if config.vpp_plugin_dir is None:
382     config.vpp_plugin_dir = [
383         f"{config.vpp_install_dir}/vpp/{lib}/vpp_plugins" for lib in libs
384     ]
385
386 if config.vpp_test_plugin_dir is None:
387     config.vpp_test_plugin_dir = [
388         f"{config.vpp_install_dir}/vpp/{lib}/vpp_api_test_plugins" for lib in libs
389     ]
390
391 test_dirs = [f"{ws}/test"]
392
393 if config.test_src_dir is not None:
394     test_dirs.extend(config.test_src_dir)
395
396 config.test_src_dir = test_dirs
397
398
399 if config.venv_dir is None:
400     config.venv_dir = f"{ws}/test/venv"
401
402 available_cpus = psutil.Process().cpu_affinity()
403 num_cpus = len(available_cpus)
404
405 if config.max_vpp_cpus == "auto":
406     max_vpp_cpus = num_cpus
407 elif config.max_vpp_cpus > 0:
408     max_vpp_cpus = min(config.max_vpp_cpus, num_cpus)
409 else:
410     max_vpp_cpus = num_cpus
411
412 if __name__ == "__main__":
413     print("Provided arguments:")
414     for i in config.__dict__:
415         print(f"  {i} is {config.__dict__[i]}")