3 """Dispatch to update_*_test_checks.py scripts automatically in bulk
5 Given a list of test files, this script will invoke the correct
6 update_test_checks-style script, skipping any tests which have not previously
7 had assertions autogenerated. If test name starts with '@' it's treated as
8 a name of file containing test list.
11 from __future__
import print_function
18 from concurrent
.futures
import ThreadPoolExecutor
20 RE_ASSERTIONS
= re
.compile(
21 r
"NOTE: Assertions have been autogenerated by ([^\s]+)( UTC_ARGS:.*)?$"
25 def find_utc_tool(search_path
, utc_name
):
27 Return the path to the given UTC tool in the search path, or None if not
30 for path
in search_path
:
31 candidate
= os
.path
.join(path
, utc_name
)
32 if os
.path
.isfile(candidate
):
37 def run_utc_tool(utc_name
, utc_tool
, testname
):
38 result
= subprocess
.run(
39 [utc_tool
, testname
], stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
41 return (result
.returncode
, result
.stdout
, result
.stderr
)
44 def read_arguments_from_file(filename
):
46 with
open(filename
, "r") as file:
47 return [line
.rstrip() for line
in file.readlines()]
48 except FileNotFoundError
:
49 print(f
"Error: File '{filename}' not found.")
53 def expand_listfile_args(arg_list
):
56 if arg
.startswith("@"):
57 exp_arg_list
+= read_arguments_from_file(arg
[1:])
59 exp_arg_list
.append(arg
)
64 from argparse
import RawTextHelpFormatter
66 parser
= argparse
.ArgumentParser(
67 description
=__doc__
, formatter_class
=RawTextHelpFormatter
74 help="Run the given number of jobs in parallel",
79 help="Additional directories to scan for update_*_test_checks scripts",
81 parser
.add_argument("tests", nargs
="+")
82 config
= parser
.parse_args()
85 utc_search_path
= config
.utc_dir
[:]
88 script_name
= os
.path
.abspath(__file__
)
89 utc_search_path
.append(os
.path
.join(os
.path
.dirname(script_name
), os
.path
.pardir
))
91 not_autogenerated
= []
95 tests
= expand_listfile_args(config
.tests
)
97 with
ThreadPoolExecutor(max_workers
=config
.jobs
) as executor
:
100 for testname
in tests
:
101 with
open(testname
, "r") as f
:
102 header
= f
.readline().strip()
103 m
= RE_ASSERTIONS
.search(header
)
105 not_autogenerated
.append(testname
)
108 utc_name
= m
.group(1)
109 if utc_name
not in utc_tools
:
110 utc_tools
[utc_name
] = find_utc_tool(utc_search_path
, utc_name
)
111 if not utc_tools
[utc_name
]:
113 f
"{utc_name}: not found (used in {testname})",
119 future
= executor
.submit(
120 run_utc_tool
, utc_name
, utc_tools
[utc_name
], testname
122 jobs
.append((testname
, future
))
124 for testname
, future
in jobs
:
125 return_code
, stdout
, stderr
= future
.result()
127 print(f
"Update {testname}")
128 stdout
= stdout
.decode(errors
="replace")
130 print(stdout
, end
="")
131 if not stdout
.endswith("\n"):
134 stderr
= stderr
.decode(errors
="replace")
136 print(stderr
, end
="")
137 if not stderr
.endswith("\n"):
140 print(f
"Return code: {return_code}")
146 if not_autogenerated
:
147 print("Tests without autogenerated assertions:")
148 for testname
in not_autogenerated
:
149 print(f
" {testname}")
152 if __name__
== "__main__":