xref: /aosp_15_r20/cts/hostsidetests/multidevices/tools/run_all_tests.py (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker# Copyright 2024 The Android Open Source Project
2*b7c941bbSAndroid Build Coastguard Worker#
3*b7c941bbSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*b7c941bbSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*b7c941bbSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*b7c941bbSAndroid Build Coastguard Worker#
7*b7c941bbSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
8*b7c941bbSAndroid Build Coastguard Worker#
9*b7c941bbSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*b7c941bbSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*b7c941bbSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*b7c941bbSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*b7c941bbSAndroid Build Coastguard Worker# limitations under the License.
14*b7c941bbSAndroid Build Coastguard Worker
15*b7c941bbSAndroid Build Coastguard Workerimport argparse
16*b7c941bbSAndroid Build Coastguard Workerimport json
17*b7c941bbSAndroid Build Coastguard Workerimport logging
18*b7c941bbSAndroid Build Coastguard Workerimport multi_device_utils
19*b7c941bbSAndroid Build Coastguard Workerimport os
20*b7c941bbSAndroid Build Coastguard Workerimport os.path
21*b7c941bbSAndroid Build Coastguard Workerfrom pathlib import Path
22*b7c941bbSAndroid Build Coastguard Workerimport re
23*b7c941bbSAndroid Build Coastguard Workerimport subprocess
24*b7c941bbSAndroid Build Coastguard Workerimport tempfile
25*b7c941bbSAndroid Build Coastguard Workerimport time
26*b7c941bbSAndroid Build Coastguard Workerimport yaml
27*b7c941bbSAndroid Build Coastguard Worker
28*b7c941bbSAndroid Build Coastguard Worker
29*b7c941bbSAndroid Build Coastguard WorkerRESULT_KEY = 'result'
30*b7c941bbSAndroid Build Coastguard WorkerRESULT_PASS = 'PASS'
31*b7c941bbSAndroid Build Coastguard WorkerRESULT_FAIL = 'FAIL'
32*b7c941bbSAndroid Build Coastguard WorkerCONFIG_FILE = os.path.join(os.getcwd(), 'config.yml')
33*b7c941bbSAndroid Build Coastguard WorkerTESTS_DIR = os.path.join(os.getcwd(), 'tests')
34*b7c941bbSAndroid Build Coastguard WorkerCTS_VERIFIER_PACKAGE_NAME = 'com.android.cts.verifier'
35*b7c941bbSAndroid Build Coastguard WorkerMOBLY_TEST_SUMMARY_TXT_FILE = 'test_mobly_summary.txt'
36*b7c941bbSAndroid Build Coastguard WorkerMULTI_DEVICE_TEST_ACTIVITY = (
37*b7c941bbSAndroid Build Coastguard Worker    'com.android.cts.verifier/.multidevice.MultiDeviceTestsActivity'
38*b7c941bbSAndroid Build Coastguard Worker)
39*b7c941bbSAndroid Build Coastguard WorkerACTION_HOST_TEST_RESULT = 'com.android.cts.verifier.ACTION_HOST_TEST_RESULT'
40*b7c941bbSAndroid Build Coastguard WorkerEXTRA_VERSION = 'com.android.cts.verifier.extra.HOST_TEST_RESULT'
41*b7c941bbSAndroid Build Coastguard WorkerACTIVITY_START_WAIT = 2  # seconds
42*b7c941bbSAndroid Build Coastguard Worker
43*b7c941bbSAndroid Build Coastguard Worker
44*b7c941bbSAndroid Build Coastguard Workerdef get_config_file_contents():
45*b7c941bbSAndroid Build Coastguard Worker  """Read the config file contents from a YML file.
46*b7c941bbSAndroid Build Coastguard Worker
47*b7c941bbSAndroid Build Coastguard Worker  Args: None
48*b7c941bbSAndroid Build Coastguard Worker
49*b7c941bbSAndroid Build Coastguard Worker  Returns:
50*b7c941bbSAndroid Build Coastguard Worker    config_file_contents: a dict read from config.yml
51*b7c941bbSAndroid Build Coastguard Worker  """
52*b7c941bbSAndroid Build Coastguard Worker  with open(CONFIG_FILE) as file:
53*b7c941bbSAndroid Build Coastguard Worker    config_file_contents = yaml.safe_load(file)
54*b7c941bbSAndroid Build Coastguard Worker  return config_file_contents
55*b7c941bbSAndroid Build Coastguard Worker
56*b7c941bbSAndroid Build Coastguard Worker
57*b7c941bbSAndroid Build Coastguard Workerdef get_device_serial_number(config_file_contents):
58*b7c941bbSAndroid Build Coastguard Worker  """Returns the serial number of the dut devices.
59*b7c941bbSAndroid Build Coastguard Worker
60*b7c941bbSAndroid Build Coastguard Worker  Args:
61*b7c941bbSAndroid Build Coastguard Worker      config_file_contents: dict read from config.yml file.
62*b7c941bbSAndroid Build Coastguard Worker
63*b7c941bbSAndroid Build Coastguard Worker  Returns:
64*b7c941bbSAndroid Build Coastguard Worker      The serial numbers (str) or None if the device is not found.
65*b7c941bbSAndroid Build Coastguard Worker  """
66*b7c941bbSAndroid Build Coastguard Worker
67*b7c941bbSAndroid Build Coastguard Worker  device_serial_numbers = []
68*b7c941bbSAndroid Build Coastguard Worker  for _, testbed_data in config_file_contents.items():
69*b7c941bbSAndroid Build Coastguard Worker    for data_dict in testbed_data:
70*b7c941bbSAndroid Build Coastguard Worker      android_devices = data_dict.get('Controllers', {}).get(
71*b7c941bbSAndroid Build Coastguard Worker          'AndroidDevice', []
72*b7c941bbSAndroid Build Coastguard Worker      )
73*b7c941bbSAndroid Build Coastguard Worker
74*b7c941bbSAndroid Build Coastguard Worker      for device_dict in android_devices:
75*b7c941bbSAndroid Build Coastguard Worker        device_serial_numbers.append(device_dict.get('serial'))
76*b7c941bbSAndroid Build Coastguard Worker  return device_serial_numbers
77*b7c941bbSAndroid Build Coastguard Worker
78*b7c941bbSAndroid Build Coastguard Worker
79*b7c941bbSAndroid Build Coastguard Workerdef report_result(device_id, results):
80*b7c941bbSAndroid Build Coastguard Worker  """Sends a pass/fail result to the device, via an intent.
81*b7c941bbSAndroid Build Coastguard Worker
82*b7c941bbSAndroid Build Coastguard Worker  Args:
83*b7c941bbSAndroid Build Coastguard Worker   device_id: the serial number of the device.
84*b7c941bbSAndroid Build Coastguard Worker   results: a dictionary contains all multi-device test names as key and
85*b7c941bbSAndroid Build Coastguard Worker     result/summary of current test run.
86*b7c941bbSAndroid Build Coastguard Worker  """
87*b7c941bbSAndroid Build Coastguard Worker  adb = f'adb -s {device_id}'
88*b7c941bbSAndroid Build Coastguard Worker
89*b7c941bbSAndroid Build Coastguard Worker  # Start MultiDeviceTestsActivity to receive test results
90*b7c941bbSAndroid Build Coastguard Worker  cmd = (
91*b7c941bbSAndroid Build Coastguard Worker      f'{adb} shell am start'
92*b7c941bbSAndroid Build Coastguard Worker      f' {MULTI_DEVICE_TEST_ACTIVITY} --activity-brought-to-front'
93*b7c941bbSAndroid Build Coastguard Worker  )
94*b7c941bbSAndroid Build Coastguard Worker  multi_device_utils.run(cmd)
95*b7c941bbSAndroid Build Coastguard Worker  time.sleep(ACTIVITY_START_WAIT)
96*b7c941bbSAndroid Build Coastguard Worker
97*b7c941bbSAndroid Build Coastguard Worker  json_results = json.dumps(results)
98*b7c941bbSAndroid Build Coastguard Worker  cmd = (
99*b7c941bbSAndroid Build Coastguard Worker      f'{adb} shell am broadcast -a {ACTION_HOST_TEST_RESULT} --es'
100*b7c941bbSAndroid Build Coastguard Worker      f" {EXTRA_VERSION} '{json_results}'"
101*b7c941bbSAndroid Build Coastguard Worker  )
102*b7c941bbSAndroid Build Coastguard Worker  if len(cmd) > 4095:
103*b7c941bbSAndroid Build Coastguard Worker    logging.info('Command string might be too long! len:%s', len(cmd))
104*b7c941bbSAndroid Build Coastguard Worker  multi_device_utils.run(cmd)
105*b7c941bbSAndroid Build Coastguard Worker
106*b7c941bbSAndroid Build Coastguard Worker
107*b7c941bbSAndroid Build Coastguard Workerdef main():
108*b7c941bbSAndroid Build Coastguard Worker  """Run all Multi-device Mobly tests and collect results."""
109*b7c941bbSAndroid Build Coastguard Worker
110*b7c941bbSAndroid Build Coastguard Worker  logging.basicConfig(level=logging.INFO)
111*b7c941bbSAndroid Build Coastguard Worker  topdir = tempfile.mkdtemp(prefix='MultiDevice_')
112*b7c941bbSAndroid Build Coastguard Worker  subprocess.call(['chmod', 'g+rx', topdir])  # Add permissions
113*b7c941bbSAndroid Build Coastguard Worker
114*b7c941bbSAndroid Build Coastguard Worker  # Parse command-line arguments
115*b7c941bbSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
116*b7c941bbSAndroid Build Coastguard Worker  parser.add_argument(
117*b7c941bbSAndroid Build Coastguard Worker      '--test_cases',
118*b7c941bbSAndroid Build Coastguard Worker      nargs='+',
119*b7c941bbSAndroid Build Coastguard Worker      help='Specific test cases to run (space-separated)')
120*b7c941bbSAndroid Build Coastguard Worker  parser.add_argument(
121*b7c941bbSAndroid Build Coastguard Worker      '--test_files',
122*b7c941bbSAndroid Build Coastguard Worker      nargs='+',
123*b7c941bbSAndroid Build Coastguard Worker      help='Filter test files by name (substring match, space-separated)')
124*b7c941bbSAndroid Build Coastguard Worker  args = parser.parse_args()
125*b7c941bbSAndroid Build Coastguard Worker
126*b7c941bbSAndroid Build Coastguard Worker  config_file_contents = get_config_file_contents()
127*b7c941bbSAndroid Build Coastguard Worker  device_ids = get_device_serial_number(config_file_contents)
128*b7c941bbSAndroid Build Coastguard Worker
129*b7c941bbSAndroid Build Coastguard Worker  test_results = {}
130*b7c941bbSAndroid Build Coastguard Worker  test_summary_file_list = []
131*b7c941bbSAndroid Build Coastguard Worker
132*b7c941bbSAndroid Build Coastguard Worker  # Run tests
133*b7c941bbSAndroid Build Coastguard Worker  for root, _, files in os.walk(TESTS_DIR):
134*b7c941bbSAndroid Build Coastguard Worker    for test_file in files:
135*b7c941bbSAndroid Build Coastguard Worker      if test_file.endswith('-py-ctsv') and (
136*b7c941bbSAndroid Build Coastguard Worker          args.test_files is None or
137*b7c941bbSAndroid Build Coastguard Worker          test_file in args.test_files
138*b7c941bbSAndroid Build Coastguard Worker      ):
139*b7c941bbSAndroid Build Coastguard Worker        test_file_path = os.path.join(root, test_file)
140*b7c941bbSAndroid Build Coastguard Worker        logging.info('Start running test: %s', test_file)
141*b7c941bbSAndroid Build Coastguard Worker        cmd = [
142*b7c941bbSAndroid Build Coastguard Worker            test_file_path,  # Use the full path to the test file
143*b7c941bbSAndroid Build Coastguard Worker            '-c',
144*b7c941bbSAndroid Build Coastguard Worker            CONFIG_FILE,
145*b7c941bbSAndroid Build Coastguard Worker            '--testbed',
146*b7c941bbSAndroid Build Coastguard Worker            test_file,
147*b7c941bbSAndroid Build Coastguard Worker        ]
148*b7c941bbSAndroid Build Coastguard Worker
149*b7c941bbSAndroid Build Coastguard Worker        if args.test_cases:
150*b7c941bbSAndroid Build Coastguard Worker          cmd.extend(['--tests'])
151*b7c941bbSAndroid Build Coastguard Worker          for test_case in args.test_cases:
152*b7c941bbSAndroid Build Coastguard Worker            cmd.extend([test_case])
153*b7c941bbSAndroid Build Coastguard Worker
154*b7c941bbSAndroid Build Coastguard Worker        summary_file_path = os.path.join(topdir, MOBLY_TEST_SUMMARY_TXT_FILE)
155*b7c941bbSAndroid Build Coastguard Worker
156*b7c941bbSAndroid Build Coastguard Worker        test_completed = False
157*b7c941bbSAndroid Build Coastguard Worker        with open(summary_file_path, 'w+') as fp:
158*b7c941bbSAndroid Build Coastguard Worker          subprocess.run(cmd, stdout=fp, check=False)
159*b7c941bbSAndroid Build Coastguard Worker          fp.seek(0)
160*b7c941bbSAndroid Build Coastguard Worker          for line in fp:
161*b7c941bbSAndroid Build Coastguard Worker            if line.startswith('Test summary saved in'):
162*b7c941bbSAndroid Build Coastguard Worker              match = re.search(r'"(.*?)"', line)  # Get test artifacts file path
163*b7c941bbSAndroid Build Coastguard Worker              if match:
164*b7c941bbSAndroid Build Coastguard Worker                test_summary = Path(match.group(1))
165*b7c941bbSAndroid Build Coastguard Worker                test_artifact = test_summary.parent
166*b7c941bbSAndroid Build Coastguard Worker                logging.info(
167*b7c941bbSAndroid Build Coastguard Worker                    'Please check the test artifacts of %s under: %s', test_file, test_artifact
168*b7c941bbSAndroid Build Coastguard Worker                )
169*b7c941bbSAndroid Build Coastguard Worker                if test_summary.exists():
170*b7c941bbSAndroid Build Coastguard Worker                  test_summary_file_list.append(test_summary)
171*b7c941bbSAndroid Build Coastguard Worker                  test_completed = True
172*b7c941bbSAndroid Build Coastguard Worker                  break
173*b7c941bbSAndroid Build Coastguard Worker        if not test_completed:
174*b7c941bbSAndroid Build Coastguard Worker          logging.error('Failed to get test summary file path')
175*b7c941bbSAndroid Build Coastguard Worker        os.remove(os.path.join(topdir, MOBLY_TEST_SUMMARY_TXT_FILE))
176*b7c941bbSAndroid Build Coastguard Worker
177*b7c941bbSAndroid Build Coastguard Worker  # Parse test summary files
178*b7c941bbSAndroid Build Coastguard Worker  for test_summary_file in test_summary_file_list:
179*b7c941bbSAndroid Build Coastguard Worker    with open(test_summary_file) as file:
180*b7c941bbSAndroid Build Coastguard Worker      test_summary_content = yaml.safe_load_all(file)
181*b7c941bbSAndroid Build Coastguard Worker      for doc in test_summary_content:
182*b7c941bbSAndroid Build Coastguard Worker        if doc['Type'] == 'Record':
183*b7c941bbSAndroid Build Coastguard Worker          test_key = f"{doc['Test Class']}#{doc['Test Name']}"
184*b7c941bbSAndroid Build Coastguard Worker          result = (
185*b7c941bbSAndroid Build Coastguard Worker              RESULT_PASS if doc['Result'] in ('PASS', 'SKIP') else RESULT_FAIL
186*b7c941bbSAndroid Build Coastguard Worker          )
187*b7c941bbSAndroid Build Coastguard Worker          test_results.setdefault(test_key, {RESULT_KEY: result})
188*b7c941bbSAndroid Build Coastguard Worker
189*b7c941bbSAndroid Build Coastguard Worker  for device_id in device_ids:
190*b7c941bbSAndroid Build Coastguard Worker    report_result(device_id, test_results)
191*b7c941bbSAndroid Build Coastguard Worker
192*b7c941bbSAndroid Build Coastguard Worker  logging.info('Test execution completed. Results: %s', test_results)
193*b7c941bbSAndroid Build Coastguard Worker
194*b7c941bbSAndroid Build Coastguard Worker
195*b7c941bbSAndroid Build Coastguard Workerif __name__ == '__main__':
196*b7c941bbSAndroid Build Coastguard Worker  main()
197