1*6467f958SSadaf Ebrahimi#!/usr/bin/env python3 2*6467f958SSadaf Ebrahimi 3*6467f958SSadaf Ebrahimifrom typing import List, Optional, Tuple 4*6467f958SSadaf Ebrahimi 5*6467f958SSadaf Ebrahimiimport argparse 6*6467f958SSadaf Ebrahimiimport os 7*6467f958SSadaf Ebrahimiimport pipes 8*6467f958SSadaf Ebrahimiimport subprocess 9*6467f958SSadaf Ebrahimiimport sys 10*6467f958SSadaf Ebrahimiimport unittest 11*6467f958SSadaf Ebrahimi 12*6467f958SSadaf EbrahimiANDROID_RUNNER_REQUIRED_VERBOSITY = 2 13*6467f958SSadaf Ebrahimi 14*6467f958SSadaf Ebrahimi 15*6467f958SSadaf Ebrahimidef parse_args(args: Optional[List[str]] = None) -> argparse.Namespace: 16*6467f958SSadaf Ebrahimi parser = argparse.ArgumentParser() 17*6467f958SSadaf Ebrahimi parser.add_argument('test_name', type=str, help="Name of the test") 18*6467f958SSadaf Ebrahimi parser.add_argument('binary_path', type=str, 19*6467f958SSadaf Ebrahimi help="Full path to the binary on device") 20*6467f958SSadaf Ebrahimi parser.add_argument('--subtests', type=str, nargs='*', 21*6467f958SSadaf Ebrahimi help="Specific subtests to run") 22*6467f958SSadaf Ebrahimi parser.add_argument('--test_args', type=str, nargs='*', 23*6467f958SSadaf Ebrahimi help="Unfiltered arguments to pass to the run command") 24*6467f958SSadaf Ebrahimi 25*6467f958SSadaf Ebrahimi args = parser.parse_args(args) 26*6467f958SSadaf Ebrahimi args.subtests = args.subtests or [] 27*6467f958SSadaf Ebrahimi args.test_args = args.test_args or [] 28*6467f958SSadaf Ebrahimi 29*6467f958SSadaf Ebrahimi return args 30*6467f958SSadaf Ebrahimi 31*6467f958SSadaf Ebrahimi 32*6467f958SSadaf Ebrahimidef run_command(command: str) -> Tuple[int, str, str]: 33*6467f958SSadaf Ebrahimi serial_number = os.environ.get("ANDROID_SERIAL", "") 34*6467f958SSadaf Ebrahimi if not serial_number: 35*6467f958SSadaf Ebrahimi raise "$ANDROID_SERIAL is empty, device must be specified" 36*6467f958SSadaf Ebrahimi 37*6467f958SSadaf Ebrahimi full_command = ["adb", "-s", serial_number, "shell", command] 38*6467f958SSadaf Ebrahimi ret = subprocess.run( 39*6467f958SSadaf Ebrahimi full_command, capture_output=True, universal_newlines=True) 40*6467f958SSadaf Ebrahimi return ret.returncode, ret.stdout, ret.stderr 41*6467f958SSadaf Ebrahimi 42*6467f958SSadaf Ebrahimi 43*6467f958SSadaf Ebrahimidef get_all_subtests(binary_path: str) -> List[str]: 44*6467f958SSadaf Ebrahimi retcode, output, _ = run_command(f'{binary_path} --help') 45*6467f958SSadaf Ebrahimi 46*6467f958SSadaf Ebrahimi test_name_line = "Test names" 47*6467f958SSadaf Ebrahimi index = output.find(test_name_line) 48*6467f958SSadaf Ebrahimi if index == -1: 49*6467f958SSadaf Ebrahimi return [] 50*6467f958SSadaf Ebrahimi 51*6467f958SSadaf Ebrahimi test_names_output = output[index:] 52*6467f958SSadaf Ebrahimi test_names = [] 53*6467f958SSadaf Ebrahimi # Skip the first line which starts with "Test names" 54*6467f958SSadaf Ebrahimi for test_name in test_names_output.splitlines()[1:]: 55*6467f958SSadaf Ebrahimi if not test_name.startswith((" ", "\t")): 56*6467f958SSadaf Ebrahimi break 57*6467f958SSadaf Ebrahimi test_names.append(test_name.strip()) 58*6467f958SSadaf Ebrahimi 59*6467f958SSadaf Ebrahimi return test_names 60*6467f958SSadaf Ebrahimi 61*6467f958SSadaf Ebrahimi 62*6467f958SSadaf Ebrahimidef get_subtests(binary_path: str, subtests: List[str]) -> List[str]: 63*6467f958SSadaf Ebrahimi all_subtests = set(get_all_subtests(binary_path)) 64*6467f958SSadaf Ebrahimi if not subtests: 65*6467f958SSadaf Ebrahimi return all_subtests 66*6467f958SSadaf Ebrahimi 67*6467f958SSadaf Ebrahimi subtests = set(subtests) 68*6467f958SSadaf Ebrahimi selected_subtests = subtests & all_subtests 69*6467f958SSadaf Ebrahimi remaining_subtests = subtests - all_subtests 70*6467f958SSadaf Ebrahimi 71*6467f958SSadaf Ebrahimi if remaining_subtests: 72*6467f958SSadaf Ebrahimi print("Could not find subtests: {}".format(', '.join(remaining_subtests)), 73*6467f958SSadaf Ebrahimi file=sys.stderr) 74*6467f958SSadaf Ebrahimi 75*6467f958SSadaf Ebrahimi return sorted(list(selected_subtests)) 76*6467f958SSadaf Ebrahimi 77*6467f958SSadaf Ebrahimi 78*6467f958SSadaf Ebrahimiclass OpenCLTest(unittest.TestCase): 79*6467f958SSadaf Ebrahimi 80*6467f958SSadaf Ebrahimi def __init__(self, test_name: str, binary_path: str, args: List[str]): 81*6467f958SSadaf Ebrahimi 82*6467f958SSadaf Ebrahimi self._test_name = test_name 83*6467f958SSadaf Ebrahimi self._binary_path = binary_path 84*6467f958SSadaf Ebrahimi self._args = args 85*6467f958SSadaf Ebrahimi 86*6467f958SSadaf Ebrahimi self.command = " ".join( 87*6467f958SSadaf Ebrahimi [self._binary_path, self._test_name] + 88*6467f958SSadaf Ebrahimi list(map(pipes.quote, self._args)) 89*6467f958SSadaf Ebrahimi ) 90*6467f958SSadaf Ebrahimi 91*6467f958SSadaf Ebrahimi self.test_func_name = self._test_name 92*6467f958SSadaf Ebrahimi setattr(self, self.test_func_name, self.genericTest) 93*6467f958SSadaf Ebrahimi 94*6467f958SSadaf Ebrahimi super().__init__(methodName=self.test_func_name) 95*6467f958SSadaf Ebrahimi 96*6467f958SSadaf Ebrahimi def genericTest(self): 97*6467f958SSadaf Ebrahimi retcode, output, oerror = run_command(self.command) 98*6467f958SSadaf Ebrahimi 99*6467f958SSadaf Ebrahimi # TODO(layog): CTS currently return non-zero return code if the 100*6467f958SSadaf Ebrahimi # implementation is missing for some API even if the API is not supported by 101*6467f958SSadaf Ebrahimi # the version reported by the driver. Need to patch upstream. 102*6467f958SSadaf Ebrahimi missing_line = f"ERROR: Test '{self._test_name}' is missing implementation" 103*6467f958SSadaf Ebrahimi if missing_line in output or missing_line in oerror: 104*6467f958SSadaf Ebrahimi self.skipTest(f"{self._test_name} API not available in the driver") 105*6467f958SSadaf Ebrahimi 106*6467f958SSadaf Ebrahimi self.assertFalse(retcode, "Test exited with non-zero status") 107*6467f958SSadaf Ebrahimi 108*6467f958SSadaf Ebrahimi # TODO(b/158646251): Update upstream to exit with proper error code 109*6467f958SSadaf Ebrahimi passed_line = "PASSED test." 110*6467f958SSadaf Ebrahimi self.assertTrue(passed_line in output) 111*6467f958SSadaf Ebrahimi 112*6467f958SSadaf Ebrahimi 113*6467f958SSadaf Ebrahimidef main(): 114*6467f958SSadaf Ebrahimi """main entrypoint for test runner""" 115*6467f958SSadaf Ebrahimi args = parse_args(sys.argv[1:]) 116*6467f958SSadaf Ebrahimi 117*6467f958SSadaf Ebrahimi # HACK: Name hack to report the actual test name 118*6467f958SSadaf Ebrahimi OpenCLTest.__name__ = args.test_name 119*6467f958SSadaf Ebrahimi OpenCLTest.__qualname__ = args.test_name 120*6467f958SSadaf Ebrahimi 121*6467f958SSadaf Ebrahimi suite = unittest.TestSuite() 122*6467f958SSadaf Ebrahimi subtests = get_subtests(args.binary_path, args.subtests) 123*6467f958SSadaf Ebrahimi for subtest in subtests: 124*6467f958SSadaf Ebrahimi suite.addTest(OpenCLTest(subtest, args.binary_path, args.test_args)) 125*6467f958SSadaf Ebrahimi 126*6467f958SSadaf Ebrahimi runner = unittest.TextTestRunner( 127*6467f958SSadaf Ebrahimi stream=sys.stderr, verbosity=ANDROID_RUNNER_REQUIRED_VERBOSITY) 128*6467f958SSadaf Ebrahimi runner.run(suite) 129*6467f958SSadaf Ebrahimi 130*6467f958SSadaf Ebrahimi 131*6467f958SSadaf Ebrahimiif __name__ == "__main__": 132*6467f958SSadaf Ebrahimi main() 133