|  | #!/usr/bin/env python3 | 
|  |  | 
|  | """Dispatch to update_*_test_checks.py scripts automatically in bulk | 
|  |  | 
|  | Given a list of test files, this script will invoke the correct | 
|  | update_test_checks-style script, skipping any tests which have not previously | 
|  | had assertions autogenerated. | 
|  | """ | 
|  |  | 
|  | from __future__ import print_function | 
|  |  | 
|  | import argparse | 
|  | import os | 
|  | import re | 
|  | import subprocess | 
|  | import sys | 
|  | from concurrent.futures import ThreadPoolExecutor | 
|  |  | 
|  | RE_ASSERTIONS = re.compile( | 
|  | r"NOTE: Assertions have been autogenerated by ([^\s]+)( UTC_ARGS:.*)?$" | 
|  | ) | 
|  |  | 
|  |  | 
|  | def find_utc_tool(search_path, utc_name): | 
|  | """ | 
|  | Return the path to the given UTC tool in the search path, or None if not | 
|  | found. | 
|  | """ | 
|  | for path in search_path: | 
|  | candidate = os.path.join(path, utc_name) | 
|  | if os.path.isfile(candidate): | 
|  | return candidate | 
|  | return None | 
|  |  | 
|  |  | 
|  | def run_utc_tool(utc_name, utc_tool, testname): | 
|  | result = subprocess.run( | 
|  | [utc_tool, testname], stdout=subprocess.PIPE, stderr=subprocess.PIPE | 
|  | ) | 
|  | return (result.returncode, result.stdout, result.stderr) | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | from argparse import RawTextHelpFormatter | 
|  |  | 
|  | parser = argparse.ArgumentParser( | 
|  | description=__doc__, formatter_class=RawTextHelpFormatter | 
|  | ) | 
|  | parser.add_argument( | 
|  | "--jobs", | 
|  | "-j", | 
|  | default=1, | 
|  | type=int, | 
|  | help="Run the given number of jobs in parallel", | 
|  | ) | 
|  | parser.add_argument( | 
|  | "--utc-dir", | 
|  | nargs="*", | 
|  | help="Additional directories to scan for update_*_test_checks scripts", | 
|  | ) | 
|  | parser.add_argument("tests", nargs="+") | 
|  | config = parser.parse_args() | 
|  |  | 
|  | if config.utc_dir: | 
|  | utc_search_path = config.utc_dir[:] | 
|  | else: | 
|  | utc_search_path = [] | 
|  | script_name = os.path.abspath(__file__) | 
|  | utc_search_path.append(os.path.join(os.path.dirname(script_name), os.path.pardir)) | 
|  |  | 
|  | not_autogenerated = [] | 
|  | utc_tools = {} | 
|  | have_error = False | 
|  |  | 
|  | with ThreadPoolExecutor(max_workers=config.jobs) as executor: | 
|  | jobs = [] | 
|  |  | 
|  | for testname in config.tests: | 
|  | with open(testname, "r") as f: | 
|  | header = f.readline().strip() | 
|  | m = RE_ASSERTIONS.search(header) | 
|  | if m is None: | 
|  | not_autogenerated.append(testname) | 
|  | continue | 
|  |  | 
|  | utc_name = m.group(1) | 
|  | if utc_name not in utc_tools: | 
|  | utc_tools[utc_name] = find_utc_tool(utc_search_path, utc_name) | 
|  | if not utc_tools[utc_name]: | 
|  | print( | 
|  | f"{utc_name}: not found (used in {testname})", | 
|  | file=sys.stderr, | 
|  | ) | 
|  | have_error = True | 
|  | continue | 
|  |  | 
|  | future = executor.submit( | 
|  | run_utc_tool, utc_name, utc_tools[utc_name], testname | 
|  | ) | 
|  | jobs.append((testname, future)) | 
|  |  | 
|  | for testname, future in jobs: | 
|  | return_code, stdout, stderr = future.result() | 
|  |  | 
|  | print(f"Update {testname}") | 
|  | stdout = stdout.decode(errors="replace") | 
|  | if stdout: | 
|  | print(stdout, end="") | 
|  | if not stdout.endswith("\n"): | 
|  | print() | 
|  |  | 
|  | stderr = stderr.decode(errors="replace") | 
|  | if stderr: | 
|  | print(stderr, end="") | 
|  | if not stderr.endswith("\n"): | 
|  | print() | 
|  | if return_code != 0: | 
|  | print(f"Return code: {return_code}") | 
|  | have_error = True | 
|  |  | 
|  | if have_error: | 
|  | sys.exit(1) | 
|  |  | 
|  | if not_autogenerated: | 
|  | print("Tests without autogenerated assertions:") | 
|  | for testname in not_autogenerated: | 
|  | print(f"  {testname}") | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | main() |