#!/usr/bin/env python3 """ This uses all available cores to spawn processes that build all test projects (Demo_*/) """ import os import sys import time import signal import multiprocessing # Tell the orchestrator to.. # # - stop complaining about POHI issues under multicores os.putenv("DISABLE_MULTICORE_CHECK", "1") # - immediately abort in case of error (No "Hit ENTER...") os.putenv("CLEANUP", "1") def get_tests(): """Collect all Demo_... folders not excluded via 'NOTEST' """ for folder in sorted(os.listdir(".")): if all([folder.startswith('Demo'), not os.path.exists(folder + "/NOTEST")]): yield folder def build_me(folder): """ Performs a build in the passed-in folder, and also spawns regression.py (if it exists in that folder). Spawned from a multiprocessing pool, returns the result back in tuple form for asynchronous reporting. """ root = os.getcwd() os.chdir(folder) try: res = os.system("./bui*.sh > build.log 2>&1 || exit 1") if res != 0: return folder, False, "Build failed..." if os.path.exists("regression.py"): res = os.system("./regression.py > regression.log 2>&1 || exit 1") if res != 0: return folder, False, "Regression check failed..." return folder, True, "OK" finally: os.chdir(root) def color_me(result, msg): """Color the output according to the result.""" if sys.stdout.isatty(): msg = chr(27) + "[3" + ( "2" if result else "1") + "m" + msg + chr(27) + "[0m" return msg def main(): """Run the TASTE regression-checking project suite""" cwd = os.path.dirname(os.path.realpath(__file__)) # Stop Maxime's GUI progress bar from appearing os.putenv("DISABLE_PROGRESS_BAR", "1") # Navigate to the folder hosting all test projects (i.e. "Demo_*/") os.chdir(cwd) os.system("rm -rf Demo*/binary*") # Detect how many workers we will use total_cpus = multiprocessing.cpu_count() total_tests = len(list(get_tests())) # Measure time to execute start_time = time.time() print("Spawning", total_cpus, "workers to run", total_tests, "tests...\n") pool = multiprocessing.Pool(total_cpus) try: failed_tests = 0 run_tests = 0 results = pool.imap_unordered(build_me, get_tests()) for res in results: run_tests += 1 test, result, msg = res print("%40s: %s" % (test, color_me(result, msg))) if not result: failed_tests += 1 except KeyboardInterrupt: # The devil is always in the details... This is the only # way I found to propagate the SIGINT to the builds done # by the worker processes of the pool AND immediately # terminate. Numerous people cry about it on the web... # ...and I had to look at the code of concurrent.futures # (which I abandoned), multiprocessing/pool.py, # multiprocessing/process.py AND subprocess.py to see # what I had to do. Gotta love our profession. for job in pool._pool: # pylint: disable=W0212 os.kill(job.pid, signal.SIGINT) job.terminate() pool.terminate() pool.join() else: pool.close() pool.join() print("\nFinished in %d seconds." % (time.time() - start_time)) print(run_tests, "tests,", failed_tests, "failure" + ( "s" if (failed_tests > 1 or failed_tests == 0) else "") + ".") if __name__ == "__main__": main() # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4