blob: 4971fb0331e483222708f0f3094cb825359826a1 [file] [log] [blame]
Jeremy Johnson0ecfa372022-06-30 14:27:56 +01001#!/usr/bin/env python3
Jeremy Johnson35396f22023-01-04 17:05:25 +00002# Copyright (c) 2021-2023, ARM Limited.
Jeremy Johnson0ecfa372022-06-30 14:27:56 +01003# SPDX-License-Identifier: Apache-2.0
4"""Build conformance tests.
5
6Steps:
7- Specific input shapes (or tests) are specified and produced by using the
8 settings in the .json files.
9- Tests are selected to produce a good coverage.
10- Tests are run on the reference model to produce the correct output files.
11- Tests are converted into JSON format and saved to desired output directory.
12"""
13import argparse
14import json
15import logging
16import multiprocessing as mp
17import os
18import shlex
19import shutil
20import subprocess
21from functools import partial
22from itertools import tee
23from pathlib import Path
24
25from conformance.test_select import Operator
26from convert2conformance.convert2conformance import main as c2c_main
27from distutils.dir_util import copy_tree
28
29logging.basicConfig()
30logger = logging.getLogger("tosa_verif_conformance_generator")
31
32# Configuration for each TOSA profile
33PROFILE_OPS_INFO = {
Jeremy Johnson88588622022-07-12 16:42:29 +010034 "tosa-bi": {
Jeremy Johnson0ecfa372022-06-30 14:27:56 +010035 "operator_test_params": "tosa_base_profile_ops_info.json",
36 "framework_tests": "tosa_base_profile_framework_ops_info.json",
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +010037 },
38 "tosa-mi": {
39 # Note: This is just the extra tests not in the base profile!
40 "operator_test_params": "tosa_main_profile_ops_info.json",
41 "framework_tests": "tosa_main_profile_framework_ops_info.json",
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +010042 },
Jeremy Johnson0ecfa372022-06-30 14:27:56 +010043}
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +010044PROFILES_ALL = "all"
Jeremy Johnson0ecfa372022-06-30 14:27:56 +010045
46LOCATION_REF_MODEL_BINARY = Path("build/reference_model/tosa_reference_model")
47
Jeremy Johnson93d43902022-09-27 12:26:14 +010048DEFAULT_SEED = 42
49
Jeremy Johnson0ecfa372022-06-30 14:27:56 +010050
51class GenConformanceError(Exception):
52 """Generation error reporting exception."""
53
54 pass
55
56
57def _run_sh_command(args, cwd, full_cmd):
58 """Run an external command and capture stdout/stderr."""
59 # Quote the command line for printing
60 full_cmd_esc = [shlex.quote(x) for x in full_cmd]
61 if args.capture_output:
62 logger.debug(f"Command: {full_cmd_esc}")
63
64 rc = subprocess.run(
65 full_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd
66 )
67
68 if args.capture_output:
69 stdout = rc.stdout.decode("utf-8")
70 logger.debug(f"stdout: \n{stdout}")
71 if rc.returncode != 0:
72
73 raise Exception(
74 "Error running command: {}.\n{}".format(
75 " ".join(full_cmd_esc), rc.stderr.decode("utf-8")
76 )
77 )
78 return (rc.stdout, rc.stderr)
79
80
Jeremy Johnsond88c3b32022-12-01 14:46:14 +000081def build_op_tests(args, profile, operator, test_params):
Jeremy Johnson0ecfa372022-06-30 14:27:56 +010082 """Build tests for a given operator.
83
84 Builds a set of tests based on the parameters defined in test_params
85
86 Returns operator output directory
87 """
88 assert operator in test_params
89
90 build_tests_cmd = "tosa_verif_build_tests"
Jeremy Johnsond88c3b32022-12-01 14:46:14 +000091 op_build_dir = args.build_dir / profile
Jeremy Johnson0ecfa372022-06-30 14:27:56 +010092
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +000093 build_cmd_base = [
Jeremy Johnson0ecfa372022-06-30 14:27:56 +010094 build_tests_cmd,
95 "--filter",
96 operator,
97 "-o",
98 str(op_build_dir),
99 "--seed",
Jeremy Johnson93d43902022-09-27 12:26:14 +0100100 str(args.random_seed),
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100101 ]
102
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000103 build_cmds_list = []
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100104
105 if args.test_type in ["positive", "both"]:
106 # Append extra parameters and run test generator for each set of parameters.
107 for arglist in test_params[operator]["generator_args"]:
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000108 build_cmd_pos_test = build_cmd_base.copy()
109 build_cmd_pos_test.extend(["--test-type", "positive"])
110 build_cmd_pos_test.extend(arglist)
111 build_cmds_list.append(build_cmd_pos_test)
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100112
113 if args.test_type in ["negative", "both"]:
Jeremy Johnson35396f22023-01-04 17:05:25 +0000114 # Get target-dtypes options and any filter string to limit tests
Jeremy Johnson93d43902022-09-27 12:26:14 +0100115 target_dtypes_args = []
Jeremy Johnson35396f22023-01-04 17:05:25 +0000116 filter_str = None
Jeremy Johnson93d43902022-09-27 12:26:14 +0100117 for arglist in test_params[operator]["generator_args"]:
118 idx = 0
119 while idx < len(arglist):
120 if arglist[idx] == "--target-dtype":
121 if arglist[idx + 1] not in target_dtypes_args:
122 target_dtypes_args.extend(arglist[idx : idx + 2])
123 idx += 1 # skip over option (and then argument below)
Jeremy Johnson35396f22023-01-04 17:05:25 +0000124 elif arglist[idx] == "--filter":
125 filter_str = arglist[idx + 1]
126 idx += 1 # skip over option (and then argument below)
Jeremy Johnson93d43902022-09-27 12:26:14 +0100127 idx += 1
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000128 build_cmd_neg_test = build_cmd_base.copy()
Jeremy Johnson35396f22023-01-04 17:05:25 +0000129 if filter_str:
130 build_cmd_neg_test.extend(["--filter", filter_str])
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000131 build_cmd_neg_test.extend(["--test-type", "negative"])
Jeremy Johnson93d43902022-09-27 12:26:14 +0100132 # Limit sizes of negative tests
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000133 dim_range = (
134 test_params[operator]["generator_negative_dim_range"]
135 if "generator_negative_dim_range" in test_params[operator]
136 else "1,16"
137 )
138 build_cmd_neg_test.extend(["--tensor-dim-range", dim_range])
139 build_cmd_neg_test.extend(target_dtypes_args)
140 build_cmds_list.append(build_cmd_neg_test)
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100141
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000142 logger.debug(f"Creating {operator} tests with {len(build_cmds_list)} parameter(s)")
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100143 error = False
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000144 for i, cmd in enumerate(build_cmds_list):
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100145 try:
146 _run_sh_command(args, args.ref_model_dir.absolute(), cmd)
147 logger.info(
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000148 f"{operator} test batch {(i+1)}/{len(build_cmds_list)} created successfully"
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100149 )
150 except Exception as e:
151 logger.error(
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000152 f"{operator} test batch {(i+1)}/{len(build_cmds_list)} unsuccessful, skipping"
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100153 )
154 logger.error(f" build_op_tests error: {e} ")
155 error = True
156 if error:
157 raise (GenConformanceError())
158
159 return op_build_dir
160
161
162def _check_to_include_test(profile, test_name, exclude_negative_tests=False):
163 """Check test name for exclusions, return False to indicate excluded."""
164 excludes = ["ERRORIF"] if exclude_negative_tests else []
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100165
166 for exclusion in excludes:
167 if f"_{exclusion}_" in test_name:
168 return False
169 return True
170
171
172def _get_all_tests_list(
173 profile, test_root_dir, operator, exclude_negative_tests=False, include_all=False
174):
175 """Create test list based on tests in the test_dir."""
176 test_dir = test_root_dir / operator
177 if not test_dir.is_dir():
178 # Tests are split into multiple dirs, for example: conv2d_1x1, conv2d_3x3
179 test_dir = test_root_dir
180 directories = [
181 tdir for tdir in test_dir.glob("*") if tdir.name.startswith(operator)
182 ]
183 else:
184 directories = [test_dir]
185
186 tests = []
187 for tdir in directories:
188 tests.extend(
189 [
190 test
191 for test in tdir.glob("*")
192 if include_all
193 or _check_to_include_test(profile, test.name, exclude_negative_tests)
194 ]
195 )
196 return tests
197
198
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100199def generate_results(args, profile, operator, op_build_dir, tests=None):
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100200 """Run tests on reference model and save result to the test directory."""
201 num_cores = args.num_cores
202 run_tests_cmd = "tosa_verif_run_tests"
203
204 ref_model_path = args.ref_model_dir / LOCATION_REF_MODEL_BINARY
205 ref_cmd_base = ref_cmd = [
206 run_tests_cmd,
207 "--ref-model-path",
208 str(ref_model_path.absolute()),
209 "-j",
210 str(num_cores),
211 "-v",
212 "-t",
213 ]
214 ref_cmds = []
215
216 if not tests:
217 # Do not need to run ERRORIF tests as they don't have result files
218 tests = _get_all_tests_list(
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100219 profile, op_build_dir, operator, exclude_negative_tests=True
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100220 )
221
222 for test in tests:
223 ref_cmd = ref_cmd_base.copy()
224 ref_cmd.append(str(test))
225 ref_cmds.append(ref_cmd)
226
227 fail_string = "UNEXPECTED_FAILURE"
228 failed_counter = 0
229
230 job_pool = mp.Pool(args.num_cores)
231 sh_partial = partial(_run_sh_command, args, args.ref_model_dir.absolute())
232 pool_results = job_pool.map(sh_partial, ref_cmds)
233 job_pool.close()
234 job_pool.join()
235
236 # Use captured output for run_sh_command to work out if test passed.
237 for i, rc in enumerate(pool_results):
238 if fail_string in str(rc[0]):
239 logger.error(f"Test {i+1}/{len(ref_cmds)}: {ref_cmds[i][-1]} failed.")
240 failed_counter += 1
241 else:
242 logger.info(f"Test {i+1}/{len(ref_cmds)}: {ref_cmds[i][-1]} passed.")
243
244 logger.info(f"{len(ref_cmds)-failed_counter}/{len(ref_cmds)} tests passed")
245 logger.info("Ran tests on model and saved results of passing tests")
246
247
248def convert_tests(
249 args,
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100250 profile,
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100251 operator,
252 op_build_dir,
253 output_dir,
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100254 op_profiles_list,
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100255 tests=None,
256 group=None,
257 trim_op_subdir=False,
258):
259 """Convert tests to JSON and save to output directory."""
260 ref_model_dir = args.ref_model_dir
261
262 if group:
263 output_dir = output_dir / group
264
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000265 c2c_args_base = ["--strict", "--ref-model-directory", str(ref_model_dir)]
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100266 # This op maybe in more than one profile - e.g. tosa_bi and tosa_mi
267 # even if we are only producing tests for tosa_mi
268 for op_profile in op_profiles_list:
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000269 c2c_args_base.extend(["--profile", op_profile])
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100270 if args.framework_schema:
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000271 c2c_args_base.extend(["--framework-schema", str(args.framework_schema)])
272 c2c_args_base.append("--output-directory")
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100273
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000274 c2c_args_list = []
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100275
276 if not tests:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100277 tests = _get_all_tests_list(profile, op_build_dir, operator)
278 logger.info(f"Converting all {profile} profile tests")
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100279
280 # Controls if we copy the tests in their operator sub-directory or not
281 output_dir_relative_pos = -1 if trim_op_subdir else -2
282 for test in tests:
283 logger.info(f"Test chosen: {test}")
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000284 c2c_args = c2c_args_base.copy()
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100285 full_output_directory = output_dir / test.relative_to(
286 *test.parts[:output_dir_relative_pos]
287 )
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000288 c2c_args.append(str(full_output_directory))
289 c2c_args.append(str(test))
290 c2c_args_list.append(c2c_args)
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100291
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000292 if len(c2c_args_list) == 0:
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100293 logger.warning("No tests found. Nothing to convert")
294 return
295
296 job_pool = mp.Pool(args.num_cores)
297
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000298 pool_results = job_pool.map(c2c_main, c2c_args_list)
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100299 job_pool.close()
300 job_pool.join()
301
302 failed_counter = 0
303 for i, result in enumerate(pool_results):
304 if result != 0:
305 logger.error(
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000306 f"test {i+1}/{len(c2c_args_list)}: {c2c_args_list[i][-1]} failed to convert."
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100307 )
308 failed_counter += 1
309 else:
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000310 logger.info(
311 f"test {i+1}/{len(c2c_args_list)}: {c2c_args_list[i][-1]} converted"
312 )
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100313 logger.info(
Jeremy Johnsondd8d9c22022-12-12 14:18:10 +0000314 f"{len(c2c_args_list)-failed_counter}/{len(c2c_args_list)} tests successfully converted"
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100315 )
316
317 if failed_counter > 0:
318 logger.error(f"Stopping due to {failed_counter} test conversion errors")
319 raise (GenConformanceError())
320
321 logger.info("Converted tests to JSON and saved to output directory")
322
323 return output_dir
324
325
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100326def get_op_tests_selection(
327 args, profile, operator, op_build_dir, test_params, negative=False
328):
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100329 """Use test picker to get subsection of tests generated."""
330 assert operator in test_params
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100331 logger.info("Choosing {} tests".format(("negative" if negative else "positive")))
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100332 try:
333 op_params = test_params[operator]
334 op = Operator.registry[operator](
335 op_build_dir,
336 op_params,
337 negative,
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100338 )
339 except KeyError:
340 logger.error(f"{operator} operator is not supported by test_select")
341 raise (GenConformanceError())
342
343 return op.select_tests()
344
345
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100346def check_op_tests(args, profile, operator, output_dir):
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100347 """Move test folders than contain files larger than 30MB to new directory."""
348 destination_dir = str(args.output_dir) + "_large_files"
349
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100350 tests = _get_all_tests_list(profile, output_dir, operator, include_all=True)
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100351 if not tests:
352 logger.error(
353 f"Couldn't find any tests to size check for {operator} in {output_dir}"
354 )
355 raise (GenConformanceError())
356
357 for tdir in tests:
358 move_dir = False
359 test_files = [file for file in tdir.glob("*")]
360 for file in test_files:
361 file_size = os.stat(file).st_size / 1024**2
362 if file_size > 30:
363 move_dir = True
364
365 if move_dir:
366 move_destination = destination_dir / tdir.relative_to(output_dir)
367 logger.warning(
368 f"{tdir.relative_to(output_dir)} contains files that are too large (>30MB), test moved to new folder: {destination_dir}"
369 )
370
371 if move_destination.is_dir():
372 logger.warning(
373 f"{move_destination} directory already exists, deleting existing."
374 )
375 shutil.rmtree(str(move_destination))
376 shutil.move(str(tdir), move_destination)
377
378
379def copy_rename_framework_tests(args, operator, test_picks):
380 """Copy framework tests into new folder and rename them if needed.
381
382 The tests are renamed to match the framework operator names if an
383 alternate name has been used instead.
384 """
385 framework_tests_dir = args.framework_tests_dir
386 new_tests_dir = args.build_dir / "frameworks" / operator
387 os.makedirs(new_tests_dir, exist_ok=True)
388
389 # Get the framework tests operator name
390 if "alternate_names" in test_picks[operator]:
391 alternate_names = test_picks[operator]["alternate_names"]
392 else:
393 alternate_names = [operator]
394
395 # Get the alternate named test directories for the operator
396 for alt_name in alternate_names:
397 test_prefix = f"test_{alt_name}"
398 test_dirs = list(framework_tests_dir.glob(f"{test_prefix}_*"))
399
400 # Copy tests to new directory and rename to match framework operator names
401 # - if there is just 1 alternate name, replace the full test prefix
402 # test_add_... -> add_...
403 # - if there are multiple alternate names, just replace the "test"
404 # test_concatv2_... -> concatenation_concatv2_...
405 old_prefix = test_prefix if len(alternate_names) == 1 else "test"
406
407 for tdir in test_dirs:
408 new_test_name = tdir.name.replace(old_prefix, operator)
409 copy_destination = new_tests_dir / new_test_name
410 logger.debug(f"copying test folder {tdir} to {copy_destination}")
411 copy_tree(str(tdir), str(copy_destination))
412
413 logger.info(f"Copied and renamed {len(test_dirs)} framework test folders")
414 return new_tests_dir.parent
415
416
417def get_framework_tests_selection(args, operator, test_picks, op_build_dir):
418 """Get the list of pre-chosen tests with relative paths."""
419 try:
420 tests = test_picks[operator]["tests"]
421 except KeyError:
422 logger.error(f"Framework test selection not defined for {operator} operator")
423 raise (GenConformanceError())
424
425 test_paths = [op_build_dir / operator / test for test in tests]
426 return test_paths
427
428
429def parse_args(argv=None):
430 """Parse the arguments."""
431 parser = argparse.ArgumentParser()
Jeremy Johnson88588622022-07-12 16:42:29 +0100432 profiles = list(PROFILE_OPS_INFO.keys())
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100433 profiles.append(PROFILES_ALL)
Jeremy Johnson88588622022-07-12 16:42:29 +0100434 parser.add_argument(
435 "--profile",
436 dest="profile",
437 choices=profiles,
438 default=profiles[0],
439 type=str,
440 help=f"TOSA profile (default is {profiles[0]})",
441 )
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100442 parser.add_argument(
443 "--operators",
444 type=str,
445 nargs="*",
446 help="The operator(s) to create tests for, if not supplied all tests will be created",
447 )
448 parser.add_argument(
Jeremy Johnson88588622022-07-12 16:42:29 +0100449 "--unit-tests",
450 dest="unit_tests",
451 choices=["operator", "framework", "both"],
452 default="operator",
453 type=str,
454 help="Which unit tests are produced (default is operator)",
455 )
456 parser.add_argument(
457 "--test-type",
458 dest="test_type",
459 choices=["positive", "negative", "both"],
460 default="both",
461 type=str,
462 help="Type of tests produced (default is both)",
463 )
464 parser.add_argument(
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100465 "--ref-model-directory",
466 dest="ref_model_dir",
467 type=Path,
468 required=True,
469 help="Reference Model directory (must be pre-built)",
470 )
Jeremy Johnson88588622022-07-12 16:42:29 +0100471 parser.add_argument(
Jeremy Johnson93d43902022-09-27 12:26:14 +0100472 "--seed",
473 dest="random_seed",
474 default=DEFAULT_SEED,
475 type=int,
476 help="Random test seed",
477 )
478 parser.add_argument(
Jeremy Johnson88588622022-07-12 16:42:29 +0100479 "--framework-tests-directory",
480 dest="framework_tests_dir",
481 type=Path,
482 default=Path.cwd() / "tests",
483 help="The pre-built framework tests directory (default is tests)",
484 )
485 parser.add_argument(
486 "--framework-schema",
487 dest="framework_schema",
488 type=Path,
489 help="Framework flatbuffers schema needed to convert framework models",
490 )
491 parser.add_argument(
492 "--build-directory",
493 dest="build_dir",
494 type=Path,
495 default=Path.cwd() / "conformance_build",
496 help="Temporary build directory for files created during this process (default is conformance_build)",
497 )
498 parser.add_argument(
499 "--output-directory",
500 dest="output_dir",
501 type=Path,
502 default=Path.cwd() / "conformance",
503 help="Output directory (default is conformance)",
504 )
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100505 script_dir = Path(__file__).parent.absolute()
506 parser.add_argument(
507 "--test-param-json-directory",
508 dest="param_json_dir",
509 type=Path,
510 default=script_dir,
Jeremy Johnson88588622022-07-12 16:42:29 +0100511 help=f"Test parameters (ops info) JSON file directory (default is {script_dir})",
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100512 )
513 parser.add_argument(
514 "--convert-all-tests",
515 action="store_true",
516 help="Converts all tests instead of those picked by test_select",
517 )
518 parser.add_argument(
519 "--keep-large-files",
520 action="store_true",
521 help="Keeps tests that contain files larger than 30MB in output directory",
522 )
523 parser.add_argument(
524 "--capture-output",
525 action="store_true",
526 help="Prints output of running sh commands",
527 )
528 parser.add_argument(
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100529 "-j",
530 dest="num_cores",
531 type=int,
532 default=6,
533 help="Number of simultaneous jobs to split the tasks into for multiprocessing",
534 )
535 parser.add_argument(
536 "-v",
537 dest="verbosity",
538 action="count",
539 default=0,
540 help="Verbosity (can be used multiple times for more details)",
541 )
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100542 args = parser.parse_args(argv)
543
544 return args
545
546
547def main():
548 args = parse_args()
549
550 if not args.ref_model_dir.is_dir():
551 logger.error(
552 f"Missing or invalid reference model directory: {args.ref_model_dir}"
553 )
554 return 2
555 else:
556 ref_model = args.ref_model_dir / LOCATION_REF_MODEL_BINARY
557 if not ref_model.is_file():
558 logger.error(
559 f"{LOCATION_REF_MODEL_BINARY} not found in {args.ref_model_dir}\nHave you built the reference model?"
560 )
561 return 2
562 if args.unit_tests in ["framework", "both"]:
563 if not args.framework_schema:
564 logger.error(
565 "Need to supply location of Framework flatbuffers schema via --framework-schema"
566 )
567 return 2
568 if not args.framework_tests_dir.is_dir():
569 logger.error(
570 f"Missing or invalid framework tests directory: {args.framework_tests_dir}"
571 )
572 return 2
573
574 loglevels = (logging.WARNING, logging.INFO, logging.DEBUG)
575 loglevel = loglevels[min(args.verbosity, len(loglevels) - 1)]
576 logger.setLevel(loglevel)
577 # Set other loggers the same
578 logging.getLogger("test_select").setLevel(loglevel)
579 logging.getLogger("convert2conformance").setLevel(loglevel)
580
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100581 print(f"Output directory: {args.output_dir}")
582
Jeremy Johnson93d43902022-09-27 12:26:14 +0100583 if args.random_seed != DEFAULT_SEED:
584 logger.warning(
585 "Random test seed changed from default, tests will not match official conformance"
586 )
587
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100588 args.build_dir = args.build_dir.resolve()
589 logger.debug(f"Creating build directory: {args.build_dir}")
590 args.build_dir.mkdir(parents=True, exist_ok=True)
591
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100592 # TODO: For tosa-mi should really generate tosa-bi profile as well
593 # - for now leave it as subset instead of as superset (for testing)
594 if args.profile == PROFILES_ALL:
595 profiles = list(PROFILE_OPS_INFO.keys())
596 else:
597 profiles = [args.profile]
598
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100599 try:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100600 for profile in profiles:
601 print(f"Creating conformance tests for TOSA {profile} profile")
602 # Framework unit tests
603 if args.unit_tests in ["framework", "both"]:
604 logger.debug("Creating FRAMEWORK unit tests")
605 test_picks_file = (
606 args.param_json_dir / PROFILE_OPS_INFO[profile]["framework_tests"]
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100607 )
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100608 try:
609 with open(test_picks_file, "r") as fd:
610 test_picks = json.load(fd)
611 except Exception as e:
612 logger.error(
613 f"Couldn't load framework tests info - {test_picks_file}: {e}"
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100614 )
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100615 return 1
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100616
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100617 operators = args.operators
618 if not operators:
619 # Create tests for all the operators
620 operators = list(test_picks.keys())
621
622 root_output_dir = (
623 args.output_dir / "frameworks" / "tflite" / "operators"
624 )
625 for op in operators:
626 logger.info(f"FRAMEWORK OP: {op}")
627 if op not in test_picks:
628 logger.warning(
629 f"Framework op {op} not found in {test_picks_file} - skipping"
630 )
631 continue
632
633 op_profiles_list = test_picks[op]["profile"]
634 if (
635 args.profile != PROFILES_ALL
636 and args.profile not in op_profiles_list
637 ):
638 # Skip this operator as not part of the profile chosen
639 logger.debug(f"Skipping {op} as not part of {args.profile}")
640 continue
641
642 logger.debug(f"Copying and renaming {op}")
643 framework_test_dir = copy_rename_framework_tests(
644 args, op, test_picks
645 )
646
647 if args.convert_all_tests:
648 logger.debug("Running and converting all framework tests")
649 framework_tests = None # Don't select any
650 else:
651 logger.debug("Running and converting selected framework tests")
652 framework_tests = get_framework_tests_selection(
653 args, op, test_picks, framework_test_dir
654 )
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100655 convert_tests(
656 args,
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100657 profile,
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100658 op,
659 framework_test_dir,
660 root_output_dir,
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100661 op_profiles_list,
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100662 tests=framework_tests,
663 trim_op_subdir=True,
664 )
665
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100666 # Operator unit tests
667 if args.unit_tests in ["operator", "both"]:
668 logger.debug("Creating OPERATOR unit tests")
669 test_params_file = (
670 args.param_json_dir
671 / PROFILE_OPS_INFO[profile]["operator_test_params"]
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100672 )
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100673 try:
674 with open(test_params_file, "r") as fd:
675 test_params = json.load(fd)
676 except Exception as e:
677 logger.error(
678 f"Couldn't load operator test params - {test_params_file}: {e}"
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100679 )
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100680 return 1
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100681
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100682 operators = args.operators
683 if not operators:
684 # Create tests for all the operators
685 operators = list(test_params.keys())
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100686
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100687 for op in operators:
688 logger.info(f"OPERATOR: {op}")
689 if op not in test_params:
690 logger.warning(
691 f"{op} operator parameters not found in {test_params_file} - skipping"
692 )
693 continue
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100694
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100695 if (
696 args.test_type == "negative"
697 and "no_negative_tests" in test_params[op]
698 and test_params[op]["no_negative_tests"]
699 ):
700 logger.warning(f"No negative tests for {op}")
701 continue
702
703 op_profiles_list = test_params[op]["profile"]
704 if (
705 args.profile != PROFILES_ALL
706 and args.profile not in op_profiles_list
707 ):
708 # Skip this operator as not part of the profile chosen
709 logger.debug(f"Skipping {op} as not part of {args.profile}")
710 continue
711
Jeremy Johnsond88c3b32022-12-01 14:46:14 +0000712 op_build_dir = build_op_tests(args, profile, op, test_params)
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100713
714 operator_group = test_params[op]["group"]
715 root_output_dir = args.output_dir / "operators"
716 if args.convert_all_tests:
717 logger.debug(f"Running and converting all {op} tests")
718 generate_results(args, profile, op, op_build_dir)
719 operator_test_list = None
720 else:
721 logger.debug(f"Running and converting selection of {op} tests")
722 if args.test_type in ["positive", "both"]:
723 tests_gen, tests_gen2 = tee(
724 get_op_tests_selection(
725 args, profile, op, op_build_dir, test_params
726 )
727 )
728 generate_results(args, profile, op, op_build_dir, tests_gen)
729 operator_test_list = list(tests_gen2)
730 else:
731 operator_test_list = []
732 if args.test_type in ["negative", "both"] and (
733 "no_negative_tests" not in test_params[op]
734 or not test_params[op]["no_negative_tests"]
735 ):
736 operator_test_list.extend(
737 get_op_tests_selection(
738 args,
739 profile,
740 op,
741 op_build_dir,
742 test_params,
743 negative=True,
744 )
745 )
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100746 output_dir = convert_tests(
Jeremy Johnson88588622022-07-12 16:42:29 +0100747 args,
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100748 profile,
Jeremy Johnson88588622022-07-12 16:42:29 +0100749 op,
750 op_build_dir,
751 root_output_dir,
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100752 op_profiles_list,
753 tests=operator_test_list,
Jeremy Johnson88588622022-07-12 16:42:29 +0100754 group=operator_group,
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100755 )
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100756 if not args.keep_large_files:
757 check_op_tests(args, profile, op, output_dir)
Jeremy Johnson0ecfa372022-06-30 14:27:56 +0100758 except GenConformanceError:
759 return 1
760
761 return 0
762
763
764if __name__ == "__main__":
765 exit(main())