#!/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()