#!/usr/bin/env python3 # # Minimal failing test sequence finder # Copyright (c) 2022, Qualcomm Innovation Center, Inc. # # This software may be distributed under the terms of the BSD license. # See README for more details. import subprocess import sys from colorama import Fore, Style def red(s, bright=False): tmp = Style.BRIGHT if bright else '' return tmp + Fore.RED + s + Style.RESET_ALL def yellow(s, bright=False): tmp = Style.BRIGHT if bright else '' return tmp + Fore.YELLOW + s + Style.RESET_ALL def bright(s): return Style.BRIGHT + s + Style.RESET_ALL def run_tests(tests): print(yellow("Run test sequence: ") + ' '.join(tests)) arg = ['./vm-run.sh'] + tests cmd = subprocess.Popen(arg, stdout=subprocess.PIPE) out = cmd.stdout.read().decode() found = False for i in out.splitlines(): if i.startswith('FAIL '): t = i.split(' ')[1] if t == tests[-1]: found = True else: print(red("Unexpected FAIL: ", bright=True) + t) return None return found def reduce(tests): if len(tests) < 2: return None # Try to remove first half of the test cases to speed up the initial process if len(tests) > 10: a = list(tests[int(len(tests) / 2):]) res = run_tests(a) if res is None: return None if res: return a # Try to remove test cases one-by-one (starting with larger groups to speed # up) for count in [27, 9, 6, 3, 1]: for i in range(0, len(tests) - count, count): b = list(tests) del b[i:i + count] if len(b) < 2: continue res = run_tests(b) if res is None: return None if res: return b return None def main(): tests = sys.argv[1:] num_tests = len(tests) if not run_tests(tests): print(red("Full test sequence did not result in an error", bright=True)) return while True: new_tests = reduce(tests) if (not new_tests) or len(new_tests) == len(tests): break tests = new_tests print(yellow("Found a shorter sequence: ", bright=True) + ' '.join(tests)) if len(tests) < num_tests: print(bright("Minimal sequence:")) print(' '.join(tests)) else: print(yellow("Could not remove any test cases without losing the failure")) if __name__ == "__main__": main()