blob: dd3b9c1f9ea0cfd250b5999f70717811dc224baa [file] [log] [blame]
Klement Sekerab23ffd72021-05-31 16:08:53 +02001import argparse
2import os
3import psutil
4import textwrap
5import time
6
7
8def positive_int_or_default(default):
9 def positive_integer(v):
10 if v is None or v == "":
11 return default
Andrew Yourtchenkof56b0072022-03-18 17:05:53 +000012 if int(v) <= 0:
13 raise ValueError("value must be positive")
Klement Sekerab23ffd72021-05-31 16:08:53 +020014 return int(v)
15 return positive_integer
16
17
Andrew Yourtchenkof56b0072022-03-18 17:05:53 +000018def 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 return positive_float
26
27
Klement Sekerab23ffd72021-05-31 16:08:53 +020028def positive_int_or_auto(v):
29 if v is None or v in ("", "auto"):
30 return "auto"
31 if int(v) <= 0:
32 raise ValueError("value must be positive or auto")
33 return int(v)
34
35
36def int_or_auto(v):
37 if v is None or v in ("", "auto"):
38 return "auto"
39 if int(v) < 0:
40 raise ValueError("value must be positive or auto")
41 return int(v)
42
43
44def int_choice_or_default(options, default):
45 assert default in options
46
47 def choice(v):
48 if v is None or v == "":
49 return default
50 if int(v) in options:
51 return int(v)
52 raise ValueError("invalid choice")
53 return choice
54
55
56def worker_config(v):
57 if v is None or v == "":
58 return 0
59 if v.startswith("workers "):
60 return(int(v.split(" ")[1]))
61 return int(v)
62
63
64def directory(v):
65 if not os.path.isdir(v):
66 raise ValueError(f"provided path '{v}' doesn't exist "
67 "or is not a directory")
68 return v
69
70
71def directory_verify_or_create(v):
72 if not os.path.isdir(v):
73 os.mkdir(v)
74 return v
75
76
77parser = argparse.ArgumentParser(description="VPP unit tests",
78 formatter_class=argparse.RawTextHelpFormatter)
79
80parser.add_argument("--failfast", action="store_true",
81 help="stop running tests on first failure")
82
83parser.add_argument("--test-src-dir", action="append", type=directory,
84 help="directory containing test files "
85 "(may be specified multiple times) "
86 "(VPP_WS_DIR/test is added automatically to the set)")
87
88default_verbose = 0
89
90parser.add_argument("--verbose", action="store", default=default_verbose,
91 type=int_choice_or_default((0, 1, 2), default_verbose),
92 help="verbosity setting - 0 - least verbose, "
93 "2 - most verbose (default: 0)")
94
95default_test_run_timeout = 600
96
97parser.add_argument("--timeout", action="store",
98 type=positive_int_or_default(default_test_run_timeout),
99 default=default_test_run_timeout,
100 metavar="TEST_RUN_TIMEOUT",
101 help="test run timeout in seconds - per test "
102 f"(default: {default_test_run_timeout})")
103
104parser.add_argument("--failed-dir", action="store", type=directory,
105 help="directory containing failed tests")
106
107filter_help_string = """\
108expression consists of 3 string selectors separated by '.' separators:
109
110 <file>.<class>.<function>
111
112- selectors restrict which files/classes/functions are run
113- selector can be replaced with '*' or omitted entirely if not needed
114- <file> selector is automatically prepended with 'test_' if required
115- '.' separators are required only if selector(s) follow(s)
116
117examples:
118
1191. all of the following expressions are equivalent and will select
120 all test classes and functions from test_bfd.py:
121 'test_bfd' 'bfd' 'test_bfd..' 'bfd.' 'bfd.*.*' 'test_bfd.*.*'
1222. 'bfd.BFDAPITestCase' selects all tests from test_bfd.py,
123 which are part of BFDAPITestCase class
1243. 'bfd.BFDAPITestCase.test_add_bfd' selects a single test named
125 test_add_bfd from test_bfd.py/BFDAPITestCase
1264. '.*.test_add_bfd' selects all test functions named test_add_bfd
127 from all files/classes
128"""
129parser.add_argument("--filter", action="store",
130 metavar="FILTER_EXPRESSION", help=filter_help_string)
131
132default_retries = 0
133
134parser.add_argument("--retries", action="store", default=default_retries,
135 type=positive_int_or_default(default_retries),
136 help="retry failed tests RETRIES times")
137
138parser.add_argument("--step", action="store_true", default=False,
139 help="enable stepping through tests")
140
141debug_help_string = """\
142attach - attach to already running vpp
143core - detect coredump and load core in gdb on crash
144gdb - print VPP PID and pause allowing attaching gdb
145gdbserver - same as above, but run gdb in gdbserver
146"""
147
148parser.add_argument("--debug", action="store",
149 choices=["attach", "core", "gdb", "gdbserver"],
150 help=debug_help_string)
151
152parser.add_argument("--debug-framework", action="store_true",
153 help="enable internal test framework debugging")
154
155parser.add_argument("--compress-core", action="store_true",
156 help="compress core files if not debugging them")
157
158parser.add_argument("--extended", action="store_true",
159 help="run extended tests")
160
161parser.add_argument("--sanity", action="store_true",
162 help="perform sanity vpp run before running tests")
163
164parser.add_argument("--force-foreground", action="store_true",
165 help="force running in foreground - don't fork")
166
167parser.add_argument("--jobs", action="store", type=positive_int_or_auto,
168 default="auto", help="maximum concurrent test jobs")
169
170parser.add_argument("--venv-dir", action="store",
171 type=directory, help="path to virtual environment")
172
173default_rnd_seed = time.time()
174parser.add_argument("--rnd-seed", action="store", default=default_rnd_seed,
Andrew Yourtchenkof56b0072022-03-18 17:05:53 +0000175 type=positive_float_or_default(default_rnd_seed),
Klement Sekerab23ffd72021-05-31 16:08:53 +0200176 help="random generator seed (default: current time)")
177
178parser.add_argument("--vpp-worker-count", action="store", type=worker_config,
179 default=0, help="number of vpp workers")
180
181parser.add_argument("--gcov", action="store_true",
182 default=False, help="running gcov tests")
183
184parser.add_argument("--cache-vpp-output", action="store_true", default=False,
185 help="cache VPP stdout/stderr and log as one block "
186 "after test finishes")
187
188parser.add_argument("--vpp-ws-dir", action="store", required=True,
189 type=directory, help="vpp workspace directory")
190
191parser.add_argument("--vpp-tag", action="store", default="vpp_debug",
192 metavar="VPP_TAG", required=True,
193 help="vpp tag (e.g. vpp, vpp_debug, vpp_gcov)")
194
195parser.add_argument("--vpp", action="store", help="path to vpp binary "
196 "(default: derive from VPP_WS_DIR and VPP_TAG)")
197
198parser.add_argument("--vpp-install-dir", type=directory,
199 action="store", help="path to vpp install directory"
200 "(default: derive from VPP_WS_DIR and VPP_TAG)")
201
202parser.add_argument("--vpp-build-dir", action="store", type=directory,
203 help="vpp build directory"
204 "(default: derive from VPP_WS_DIR and VPP_TAG)")
205
206parser.add_argument("--vpp-plugin-dir", action="append", type=directory,
207 help="directory containing vpp plugins"
208 "(default: derive from VPP_WS_DIR and VPP_TAG)")
209
210parser.add_argument("--vpp-test-plugin-dir", action="append", type=directory,
211 help="directory containing vpp api test plugins"
212 "(default: derive from VPP_WS_DIR and VPP_TAG)")
213
214parser.add_argument("--extern-plugin-dir", action="append", type=directory,
215 default=[], help="directory containing external plugins")
216
217parser.add_argument("--coredump-size", action="store", default="unlimited",
218 help="specify vpp coredump size")
219
220parser.add_argument("--max-vpp-cpus", action="store", type=int_or_auto,
221 default=0, help="max cpus used by vpp")
222
223variant_help_string = """\
224specify which march node variant to unit test
225 e.g. --variant=skx - test the skx march variants
226 e.g. --variant=icl - test the icl march variants
227"""
228
229parser.add_argument("--variant", action="store", help=variant_help_string)
230
231parser.add_argument("--api-fuzz", action="store", default=None,
232 help="specify api fuzzing parameters")
233
234parser.add_argument("--wipe-tmp-dir", action="store_true", default=True,
235 help="remove test tmp directory before running test")
236
237parser.add_argument("--tmp-dir", action="store", default="/tmp",
238 type=directory_verify_or_create,
239 help="directory where to store test temporary directories")
240
241parser.add_argument("--log-dir", action="store",
242 type=directory_verify_or_create,
243 help="directory where to store directories "
244 "containing log files (default: --tmp-dir)")
245
246default_keep_pcaps = False
247parser.add_argument("--keep-pcaps", action="store_true",
248 default=default_keep_pcaps,
249 help="if set, keep all pcap files from a test run"
250 f" (default: {default_keep_pcaps})")
251
252config = parser.parse_args()
253
254ws = config.vpp_ws_dir
255br = f"{ws}/build-root"
256tag = config.vpp_tag
257
258if config.vpp_install_dir is None:
259 config.vpp_install_dir = f"{br}/install-{tag}-native"
260
261if config.vpp is None:
262 config.vpp = f"{config.vpp_install_dir}/vpp/bin/vpp"
263
264if config.vpp_build_dir is None:
265 config.vpp_build_dir = f"{br}/build-{tag}-native"
266
267libs = ["lib", "lib64"]
268
269if config.vpp_plugin_dir is None:
270 config.vpp_plugin_dir = [
271 f"{config.vpp_install_dir}/vpp/{lib}/vpp_plugins" for lib in libs]
272
273if config.vpp_test_plugin_dir is None:
274 config.vpp_test_plugin_dir = [
275 f"{config.vpp_install_dir}/vpp/{lib}/vpp_api_test_plugins"
276 for lib in libs]
277
278test_dirs = [f"{ws}/test"]
279
280if config.test_src_dir is not None:
281 test_dirs.extend(config.test_src_dir)
282
283config.test_src_dir = test_dirs
284
285
286if config.venv_dir is None:
287 config.venv_dir = f"{ws}/test/venv"
288
289available_cpus = psutil.Process().cpu_affinity()
290num_cpus = len(available_cpus)
291
292if config.max_vpp_cpus == 'auto':
293 max_vpp_cpus = num_cpus
294elif config.max_vpp_cpus > 0:
295 max_vpp_cpus = min(config.max_vpp_cpus, num_cpus)
296else:
297 max_vpp_cpus = num_cpus
298
299if __name__ == "__main__":
300 print("Provided arguments:")
301 for i in config.__dict__:
302 print(f" {i} is {config.__dict__[i]}")