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