xref: /aosp_15_r20/cts/apps/CameraITS/tools/run_all_tests.py (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker# Copyright 2014 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 glob
16*b7c941bbSAndroid Build Coastguard Workerimport json
17*b7c941bbSAndroid Build Coastguard Workerimport logging
18*b7c941bbSAndroid Build Coastguard Workerimport os
19*b7c941bbSAndroid Build Coastguard Workerimport os.path
20*b7c941bbSAndroid Build Coastguard Workerimport re
21*b7c941bbSAndroid Build Coastguard Workerimport subprocess
22*b7c941bbSAndroid Build Coastguard Workerimport sys
23*b7c941bbSAndroid Build Coastguard Workerimport tempfile
24*b7c941bbSAndroid Build Coastguard Workerimport time
25*b7c941bbSAndroid Build Coastguard Workerimport types
26*b7c941bbSAndroid Build Coastguard Worker
27*b7c941bbSAndroid Build Coastguard Workerimport camera_properties_utils
28*b7c941bbSAndroid Build Coastguard Workerimport capture_request_utils
29*b7c941bbSAndroid Build Coastguard Workerimport image_processing_utils
30*b7c941bbSAndroid Build Coastguard Workerimport its_device_utils
31*b7c941bbSAndroid Build Coastguard Workerimport its_session_utils
32*b7c941bbSAndroid Build Coastguard Workerimport lighting_control_utils
33*b7c941bbSAndroid Build Coastguard Workerimport numpy as np
34*b7c941bbSAndroid Build Coastguard Workerimport yaml
35*b7c941bbSAndroid Build Coastguard Worker
36*b7c941bbSAndroid Build Coastguard Worker
37*b7c941bbSAndroid Build Coastguard WorkerYAML_FILE_DIR = os.environ['CAMERA_ITS_TOP']
38*b7c941bbSAndroid Build Coastguard WorkerCONFIG_FILE = os.path.join(YAML_FILE_DIR, 'config.yml')
39*b7c941bbSAndroid Build Coastguard WorkerTEST_KEY_TABLET = 'tablet'
40*b7c941bbSAndroid Build Coastguard WorkerTEST_KEY_SENSOR_FUSION = 'sensor_fusion'
41*b7c941bbSAndroid Build Coastguard WorkerACTIVITY_START_WAIT = 1.5  # seconds
42*b7c941bbSAndroid Build Coastguard WorkerMERGE_RESULTS_TIMEOUT = 3600  # seconds
43*b7c941bbSAndroid Build Coastguard Worker
44*b7c941bbSAndroid Build Coastguard WorkerNUM_TRIES = 2
45*b7c941bbSAndroid Build Coastguard WorkerRESULT_PASS = 'PASS'
46*b7c941bbSAndroid Build Coastguard WorkerRESULT_FAIL = 'FAIL'
47*b7c941bbSAndroid Build Coastguard WorkerRESULT_NOT_EXECUTED = 'NOT_EXECUTED'
48*b7c941bbSAndroid Build Coastguard WorkerRESULT_KEY = 'result'
49*b7c941bbSAndroid Build Coastguard WorkerMETRICS_KEY = 'mpc_metrics'
50*b7c941bbSAndroid Build Coastguard WorkerPERFORMANCE_KEY = 'performance_metrics'
51*b7c941bbSAndroid Build Coastguard WorkerFEATURE_QUERY_KEY = 'feature_query_proto'
52*b7c941bbSAndroid Build Coastguard WorkerSUMMARY_KEY = 'summary'
53*b7c941bbSAndroid Build Coastguard WorkerRESULT_VALUES = (RESULT_PASS, RESULT_FAIL, RESULT_NOT_EXECUTED)
54*b7c941bbSAndroid Build Coastguard WorkerCTS_VERIFIER_PACKAGE_NAME = 'com.android.cts.verifier'
55*b7c941bbSAndroid Build Coastguard WorkerACTION_ITS_RESULT = 'com.android.cts.verifier.camera.its.ACTION_ITS_RESULT'
56*b7c941bbSAndroid Build Coastguard WorkerEXTRA_VERSION = 'camera.its.extra.VERSION'
57*b7c941bbSAndroid Build Coastguard WorkerCURRENT_ITS_VERSION = '1.0'  # version number to sync with CtsVerifier
58*b7c941bbSAndroid Build Coastguard WorkerEXTRA_CAMERA_ID = 'camera.its.extra.CAMERA_ID'
59*b7c941bbSAndroid Build Coastguard WorkerEXTRA_RESULTS = 'camera.its.extra.RESULTS'
60*b7c941bbSAndroid Build Coastguard WorkerEXTRA_TABLET_NAME = 'camera.its.extra.TABLET_NAME'
61*b7c941bbSAndroid Build Coastguard WorkerTIME_KEY_START = 'start'
62*b7c941bbSAndroid Build Coastguard WorkerTIME_KEY_END = 'end'
63*b7c941bbSAndroid Build Coastguard WorkerVALID_CONTROLLERS = ('arduino', 'canakit')
64*b7c941bbSAndroid Build Coastguard Worker_FRONT_CAMERA_ID = '1'
65*b7c941bbSAndroid Build Coastguard Worker# recover replaced '_' in scene def
66*b7c941bbSAndroid Build Coastguard Worker_INT_STR_DICT = types.MappingProxyType({'11': '1_1', '12': '1_2'})
67*b7c941bbSAndroid Build Coastguard Worker_MAIN_TESTBED = 0
68*b7c941bbSAndroid Build Coastguard Worker_PROPERTIES_TO_MATCH = (
69*b7c941bbSAndroid Build Coastguard Worker    'ro.product.model', 'ro.product.name', 'ro.build.display.id', 'ro.revision'
70*b7c941bbSAndroid Build Coastguard Worker)
71*b7c941bbSAndroid Build Coastguard Worker
72*b7c941bbSAndroid Build Coastguard Worker# Scenes that can be automated through tablet display
73*b7c941bbSAndroid Build Coastguard Worker# Notes on scene names:
74*b7c941bbSAndroid Build Coastguard Worker#   scene*_1/2/... are same scene split to load balance run times for scenes
75*b7c941bbSAndroid Build Coastguard Worker#   scene*_a/b/... are similar scenes that share one or more tests
76*b7c941bbSAndroid Build Coastguard Worker_TABLET_SCENES = (
77*b7c941bbSAndroid Build Coastguard Worker    'scene0', 'scene1_1', 'scene1_2', 'scene1_3', 'scene2_a', 'scene2_b',
78*b7c941bbSAndroid Build Coastguard Worker    'scene2_c', 'scene2_d', 'scene2_e', 'scene2_f', 'scene2_g', 'scene3',
79*b7c941bbSAndroid Build Coastguard Worker    'scene4', 'scene6', 'scene6_tele', 'scene7', 'scene8', 'scene9',
80*b7c941bbSAndroid Build Coastguard Worker    os.path.join('scene_extensions', 'scene_hdr'),
81*b7c941bbSAndroid Build Coastguard Worker    os.path.join('scene_extensions', 'scene_low_light'),
82*b7c941bbSAndroid Build Coastguard Worker    'scene_video',
83*b7c941bbSAndroid Build Coastguard Worker)
84*b7c941bbSAndroid Build Coastguard Worker
85*b7c941bbSAndroid Build Coastguard Worker# Scenes that use the 'sensor_fusion' test rig
86*b7c941bbSAndroid Build Coastguard Worker_MOTION_SCENES = ('sensor_fusion', 'feature_combination',)
87*b7c941bbSAndroid Build Coastguard Worker
88*b7c941bbSAndroid Build Coastguard Worker# Scenes that uses lighting control
89*b7c941bbSAndroid Build Coastguard Worker_FLASH_SCENES = ('scene_flash',)
90*b7c941bbSAndroid Build Coastguard Worker
91*b7c941bbSAndroid Build Coastguard Worker# Scenes that uses checkerboard as chart
92*b7c941bbSAndroid Build Coastguard Worker_CHECKERBOARD_SCENES = ('sensor_fusion', 'scene_flash', 'feature_combination',)
93*b7c941bbSAndroid Build Coastguard Worker
94*b7c941bbSAndroid Build Coastguard Worker# Scenes that have to be run manually regardless of configuration
95*b7c941bbSAndroid Build Coastguard Worker_MANUAL_SCENES = ('scene5',)
96*b7c941bbSAndroid Build Coastguard Worker
97*b7c941bbSAndroid Build Coastguard Worker# Scene extensions
98*b7c941bbSAndroid Build Coastguard Worker_EXTENSIONS_SCENES = (os.path.join('scene_extensions', 'scene_hdr'),
99*b7c941bbSAndroid Build Coastguard Worker                      os.path.join('scene_extensions', 'scene_low_light'),
100*b7c941bbSAndroid Build Coastguard Worker                      )
101*b7c941bbSAndroid Build Coastguard Worker
102*b7c941bbSAndroid Build Coastguard Worker# All possible scenes
103*b7c941bbSAndroid Build Coastguard Worker_ALL_SCENES = _TABLET_SCENES + _MANUAL_SCENES + _MOTION_SCENES + _FLASH_SCENES
104*b7c941bbSAndroid Build Coastguard Worker
105*b7c941bbSAndroid Build Coastguard Worker# Scenes that are logically grouped and can be called as group
106*b7c941bbSAndroid Build Coastguard Worker# scene6_tele is not grouped with scene6 because it requires extension rig
107*b7c941bbSAndroid Build Coastguard Worker_GROUPED_SCENES = types.MappingProxyType({
108*b7c941bbSAndroid Build Coastguard Worker        'scene1': ('scene1_1', 'scene1_2'),
109*b7c941bbSAndroid Build Coastguard Worker        'scene2': ('scene2_a', 'scene2_b', 'scene2_c', 'scene2_d', 'scene2_e',
110*b7c941bbSAndroid Build Coastguard Worker                   'scene2_f', 'scene2_g')
111*b7c941bbSAndroid Build Coastguard Worker})
112*b7c941bbSAndroid Build Coastguard Worker
113*b7c941bbSAndroid Build Coastguard Worker# Scene requirements for manual testing.
114*b7c941bbSAndroid Build Coastguard Worker_SCENE_REQ = types.MappingProxyType({
115*b7c941bbSAndroid Build Coastguard Worker    'scene0': None,
116*b7c941bbSAndroid Build Coastguard Worker    'scene1_1': 'A grey card covering at least the middle 30% of the scene',
117*b7c941bbSAndroid Build Coastguard Worker    'scene1_2': 'A grey card covering at least the middle 30% of the scene',
118*b7c941bbSAndroid Build Coastguard Worker    'scene1_3': 'A grey card covering at least the middle 30% of the scene, '
119*b7c941bbSAndroid Build Coastguard Worker                'without a white border like scene1_1 or scene1_2',
120*b7c941bbSAndroid Build Coastguard Worker    'scene2_a': 'The picture with 3 faces in tests/scene2_a/scene2_a.png',
121*b7c941bbSAndroid Build Coastguard Worker    'scene2_b': 'The picture with 3 faces in tests/scene2_b/scene2_b.png',
122*b7c941bbSAndroid Build Coastguard Worker    'scene2_c': 'The picture with 3 faces in tests/scene2_c/scene2_c.png',
123*b7c941bbSAndroid Build Coastguard Worker    'scene2_d': 'The picture with 3 faces in tests/scene2_d/scene2_d.png',
124*b7c941bbSAndroid Build Coastguard Worker    'scene2_e': 'The picture with 3 faces in tests/scene2_e/scene2_e.png',
125*b7c941bbSAndroid Build Coastguard Worker    'scene2_f': 'The picture with 3 faces in tests/scene2_f/scene2_f.png',
126*b7c941bbSAndroid Build Coastguard Worker    'scene2_g': 'The picture with 3 profile faces in tests/scene2_g/scene2_g.png',
127*b7c941bbSAndroid Build Coastguard Worker    'scene3': 'The ISO12233 chart',
128*b7c941bbSAndroid Build Coastguard Worker    'scene4': 'A test chart of a circle covering at least the middle 50% of '
129*b7c941bbSAndroid Build Coastguard Worker              'the scene. See tests/scene4/scene4.png',
130*b7c941bbSAndroid Build Coastguard Worker    'scene5': 'Capture images with a diffuser attached to the camera. See '
131*b7c941bbSAndroid Build Coastguard Worker              'source.android.com/docs/compatibility/cts/camera-its-tests#scene5/diffuser '  # pylint: disable line-too-long
132*b7c941bbSAndroid Build Coastguard Worker              'for more details',
133*b7c941bbSAndroid Build Coastguard Worker    'scene6': 'A grid of ArUco markers on a white background. '
134*b7c941bbSAndroid Build Coastguard Worker              'See tests/scene6/scene6.png',
135*b7c941bbSAndroid Build Coastguard Worker    'scene6_tele': 'A grid of ArUco markers on a white background. Identical '
136*b7c941bbSAndroid Build Coastguard Worker                   'to scene6, but for tele cameras. '
137*b7c941bbSAndroid Build Coastguard Worker                   'See tests/scene6_tele/scene6_tele.png',
138*b7c941bbSAndroid Build Coastguard Worker    'scene7': 'The picture with 4 different colors, slanted edge and'
139*b7c941bbSAndroid Build Coastguard Worker              '4 ArUco markers. See tests/scene7/scene7.png',
140*b7c941bbSAndroid Build Coastguard Worker    'scene8': 'The picture with 4 faces in 4 different colors overlay.'
141*b7c941bbSAndroid Build Coastguard Worker              'See tests/scene8/scene8.png',
142*b7c941bbSAndroid Build Coastguard Worker    'scene9': 'A scene with high entropy consisting of random size and colored '
143*b7c941bbSAndroid Build Coastguard Worker              'circles. See tests/scene9/scene9.png',
144*b7c941bbSAndroid Build Coastguard Worker    # Use os.path to avoid confusion on other platforms
145*b7c941bbSAndroid Build Coastguard Worker    os.path.join('scene_extensions', 'scene_hdr'): (
146*b7c941bbSAndroid Build Coastguard Worker        'A tablet displayed scene with a face on the left '
147*b7c941bbSAndroid Build Coastguard Worker        'and a low-contrast QR code on the right. '
148*b7c941bbSAndroid Build Coastguard Worker        'See tests/scene_extensions/scene_hdr/scene_hdr.png'
149*b7c941bbSAndroid Build Coastguard Worker    ),
150*b7c941bbSAndroid Build Coastguard Worker    os.path.join('scene_extensions', 'scene_low_light'): (
151*b7c941bbSAndroid Build Coastguard Worker        'A tablet displayed scene with a grid of squares of varying '
152*b7c941bbSAndroid Build Coastguard Worker        'brightness. See '
153*b7c941bbSAndroid Build Coastguard Worker        'tests/scene_extensions/scene_low_light/scene_low_light.png'
154*b7c941bbSAndroid Build Coastguard Worker    ),
155*b7c941bbSAndroid Build Coastguard Worker    'sensor_fusion': 'A checkerboard pattern for phone to rotate in front of '
156*b7c941bbSAndroid Build Coastguard Worker                     'in tests/sensor_fusion/checkerboard.pdf\n'
157*b7c941bbSAndroid Build Coastguard Worker                     'See tests/sensor_fusion/SensorFusion.pdf for detailed '
158*b7c941bbSAndroid Build Coastguard Worker                     'instructions.\nNote that this test will be skipped '
159*b7c941bbSAndroid Build Coastguard Worker                     'on devices not supporting REALTIME camera timestamp.',
160*b7c941bbSAndroid Build Coastguard Worker    'feature_combination': 'The same scene as sensor_fusion, '
161*b7c941bbSAndroid Build Coastguard Worker                           'separated for easier testing.',
162*b7c941bbSAndroid Build Coastguard Worker    'scene_flash': 'A checkerboard pattern chart with lights off.',
163*b7c941bbSAndroid Build Coastguard Worker    'scene_video': 'A tablet displayed scene with a series of circles moving '
164*b7c941bbSAndroid Build Coastguard Worker                   'at different simulated frame rates. '
165*b7c941bbSAndroid Build Coastguard Worker                   'See tests/scene_video/scene_video.mp4',
166*b7c941bbSAndroid Build Coastguard Worker})
167*b7c941bbSAndroid Build Coastguard Worker
168*b7c941bbSAndroid Build Coastguard Worker# Made mutable to allow for test augmentation based on first API level
169*b7c941bbSAndroid Build Coastguard WorkerSUB_CAMERA_TESTS = {
170*b7c941bbSAndroid Build Coastguard Worker    'scene0': (
171*b7c941bbSAndroid Build Coastguard Worker        'test_jitter',
172*b7c941bbSAndroid Build Coastguard Worker        'test_metadata',
173*b7c941bbSAndroid Build Coastguard Worker        'test_request_capture_match',
174*b7c941bbSAndroid Build Coastguard Worker        'test_sensor_events',
175*b7c941bbSAndroid Build Coastguard Worker        'test_solid_color_test_pattern',
176*b7c941bbSAndroid Build Coastguard Worker        'test_unified_timestamps',
177*b7c941bbSAndroid Build Coastguard Worker    ),
178*b7c941bbSAndroid Build Coastguard Worker    'scene1_1': (
179*b7c941bbSAndroid Build Coastguard Worker        'test_burst_capture',
180*b7c941bbSAndroid Build Coastguard Worker        'test_burst_sameness_manual',
181*b7c941bbSAndroid Build Coastguard Worker        'test_exposure_x_iso',
182*b7c941bbSAndroid Build Coastguard Worker        'test_linearity',
183*b7c941bbSAndroid Build Coastguard Worker    ),
184*b7c941bbSAndroid Build Coastguard Worker    'scene1_2': (
185*b7c941bbSAndroid Build Coastguard Worker        'test_raw_exposure',
186*b7c941bbSAndroid Build Coastguard Worker    ),
187*b7c941bbSAndroid Build Coastguard Worker    'scene1_3': (
188*b7c941bbSAndroid Build Coastguard Worker        'test_dng_noise_model',
189*b7c941bbSAndroid Build Coastguard Worker        'test_raw_sensitivity',
190*b7c941bbSAndroid Build Coastguard Worker        'test_yuv_plus_raw',
191*b7c941bbSAndroid Build Coastguard Worker    ),
192*b7c941bbSAndroid Build Coastguard Worker    'scene2_a': (
193*b7c941bbSAndroid Build Coastguard Worker        'test_num_faces',
194*b7c941bbSAndroid Build Coastguard Worker    ),
195*b7c941bbSAndroid Build Coastguard Worker    'scene4': (
196*b7c941bbSAndroid Build Coastguard Worker        'test_aspect_ratio_and_crop',
197*b7c941bbSAndroid Build Coastguard Worker    ),
198*b7c941bbSAndroid Build Coastguard Worker    'scene6_tele': (
199*b7c941bbSAndroid Build Coastguard Worker        'test_zoom_tele',
200*b7c941bbSAndroid Build Coastguard Worker    ),
201*b7c941bbSAndroid Build Coastguard Worker    'scene_video': (
202*b7c941bbSAndroid Build Coastguard Worker        'test_preview_frame_drop',
203*b7c941bbSAndroid Build Coastguard Worker    ),
204*b7c941bbSAndroid Build Coastguard Worker    'sensor_fusion': (
205*b7c941bbSAndroid Build Coastguard Worker        'test_sensor_fusion',
206*b7c941bbSAndroid Build Coastguard Worker    ),
207*b7c941bbSAndroid Build Coastguard Worker}
208*b7c941bbSAndroid Build Coastguard Worker
209*b7c941bbSAndroid Build Coastguard Worker_LIGHTING_CONTROL_TESTS = (
210*b7c941bbSAndroid Build Coastguard Worker    'test_auto_flash.py',
211*b7c941bbSAndroid Build Coastguard Worker    'test_preview_min_frame_rate.py',
212*b7c941bbSAndroid Build Coastguard Worker    'test_led_snapshot.py',
213*b7c941bbSAndroid Build Coastguard Worker    'test_night_extension.py',
214*b7c941bbSAndroid Build Coastguard Worker    'test_low_light_boost_extension.py',
215*b7c941bbSAndroid Build Coastguard Worker    'test_hdr_extension.py',
216*b7c941bbSAndroid Build Coastguard Worker    )
217*b7c941bbSAndroid Build Coastguard Worker
218*b7c941bbSAndroid Build Coastguard Worker_EXTENSION_NAMES = (
219*b7c941bbSAndroid Build Coastguard Worker    'hdr',
220*b7c941bbSAndroid Build Coastguard Worker    'low_light',
221*b7c941bbSAndroid Build Coastguard Worker)
222*b7c941bbSAndroid Build Coastguard Worker
223*b7c941bbSAndroid Build Coastguard Worker_DST_SCENE_DIR = '/sdcard/Download/'
224*b7c941bbSAndroid Build Coastguard Worker_SUB_CAMERA_LEVELS = 2
225*b7c941bbSAndroid Build Coastguard WorkerMOBLY_TEST_SUMMARY_TXT_FILE = 'test_mobly_summary.txt'
226*b7c941bbSAndroid Build Coastguard Worker
227*b7c941bbSAndroid Build Coastguard Worker
228*b7c941bbSAndroid Build Coastguard Workerdef report_result(device_id, camera_id, tablet_name, results):
229*b7c941bbSAndroid Build Coastguard Worker  """Sends a pass/fail result to the device, via an intent.
230*b7c941bbSAndroid Build Coastguard Worker
231*b7c941bbSAndroid Build Coastguard Worker  Args:
232*b7c941bbSAndroid Build Coastguard Worker   device_id: The ID string of the device to report the results to.
233*b7c941bbSAndroid Build Coastguard Worker   camera_id: The ID string of the camera for which to report pass/fail.
234*b7c941bbSAndroid Build Coastguard Worker   tablet_name: The tablet name to identify model and build.
235*b7c941bbSAndroid Build Coastguard Worker   results: a dictionary contains all ITS scenes as key and result/summary of
236*b7c941bbSAndroid Build Coastguard Worker            current ITS run. See test_report_result unit test for an example.
237*b7c941bbSAndroid Build Coastguard Worker  """
238*b7c941bbSAndroid Build Coastguard Worker  adb = f'adb -s {device_id}'
239*b7c941bbSAndroid Build Coastguard Worker  its_device_utils.start_its_test_activity(device_id)
240*b7c941bbSAndroid Build Coastguard Worker  time.sleep(ACTIVITY_START_WAIT)
241*b7c941bbSAndroid Build Coastguard Worker
242*b7c941bbSAndroid Build Coastguard Worker  # Validate/process results argument
243*b7c941bbSAndroid Build Coastguard Worker  for scene in results:
244*b7c941bbSAndroid Build Coastguard Worker    if RESULT_KEY not in results[scene]:
245*b7c941bbSAndroid Build Coastguard Worker      raise ValueError(f'ITS result not found for {scene}')
246*b7c941bbSAndroid Build Coastguard Worker    if results[scene][RESULT_KEY] not in RESULT_VALUES:
247*b7c941bbSAndroid Build Coastguard Worker      raise ValueError(f'Unknown ITS result for {scene}: {results[RESULT_KEY]}')
248*b7c941bbSAndroid Build Coastguard Worker    if SUMMARY_KEY in results[scene]:
249*b7c941bbSAndroid Build Coastguard Worker      device_summary_path = f'/sdcard/its_camera{camera_id}_{scene}.txt'
250*b7c941bbSAndroid Build Coastguard Worker      its_device_utils.run(
251*b7c941bbSAndroid Build Coastguard Worker          f'{adb} push {results[scene][SUMMARY_KEY]} {device_summary_path}')
252*b7c941bbSAndroid Build Coastguard Worker      results[scene][SUMMARY_KEY] = device_summary_path
253*b7c941bbSAndroid Build Coastguard Worker
254*b7c941bbSAndroid Build Coastguard Worker  json_results = json.dumps(results)
255*b7c941bbSAndroid Build Coastguard Worker  cmd = (f"{adb} shell am broadcast -a {ACTION_ITS_RESULT} --es {EXTRA_VERSION}"
256*b7c941bbSAndroid Build Coastguard Worker         f" {CURRENT_ITS_VERSION} --es {EXTRA_CAMERA_ID} {camera_id} --es "
257*b7c941bbSAndroid Build Coastguard Worker         f"{EXTRA_TABLET_NAME} {tablet_name} --es "
258*b7c941bbSAndroid Build Coastguard Worker         f"{EXTRA_RESULTS} \'{json_results}\'")
259*b7c941bbSAndroid Build Coastguard Worker  its_device_utils.run(cmd)
260*b7c941bbSAndroid Build Coastguard Worker
261*b7c941bbSAndroid Build Coastguard Worker
262*b7c941bbSAndroid Build Coastguard Workerdef write_result(testbed_index, device_id, camera_id, results):
263*b7c941bbSAndroid Build Coastguard Worker  """Writes results to a temporary file for merging.
264*b7c941bbSAndroid Build Coastguard Worker
265*b7c941bbSAndroid Build Coastguard Worker  Args:
266*b7c941bbSAndroid Build Coastguard Worker    testbed_index: the index of a finished testbed.
267*b7c941bbSAndroid Build Coastguard Worker    device_id: the ID string of the device that created results.
268*b7c941bbSAndroid Build Coastguard Worker    camera_id: the ID string of the camera of the device.
269*b7c941bbSAndroid Build Coastguard Worker    results: a dictionary that contains all ITS scenes as key
270*b7c941bbSAndroid Build Coastguard Worker             and result/summary of current ITS run.
271*b7c941bbSAndroid Build Coastguard Worker  """
272*b7c941bbSAndroid Build Coastguard Worker  result = {'device_id': device_id, 'results': results}
273*b7c941bbSAndroid Build Coastguard Worker  file_name = f'testbed_{testbed_index}_camera_{camera_id}.tmp'
274*b7c941bbSAndroid Build Coastguard Worker  with open(file_name, 'w') as f:
275*b7c941bbSAndroid Build Coastguard Worker    json.dump(result, f)
276*b7c941bbSAndroid Build Coastguard Worker
277*b7c941bbSAndroid Build Coastguard Worker
278*b7c941bbSAndroid Build Coastguard Workerdef parse_testbeds(completed_testbeds):
279*b7c941bbSAndroid Build Coastguard Worker  """Parses completed testbeds and yields device_id, camera_id, and results.
280*b7c941bbSAndroid Build Coastguard Worker
281*b7c941bbSAndroid Build Coastguard Worker  Args:
282*b7c941bbSAndroid Build Coastguard Worker    completed_testbeds: an iterable of completed testbed indices.
283*b7c941bbSAndroid Build Coastguard Worker  Yields:
284*b7c941bbSAndroid Build Coastguard Worker    device_id: the device associated with the testbed.
285*b7c941bbSAndroid Build Coastguard Worker    camera_id: one of the camera_ids associated with the testbed.
286*b7c941bbSAndroid Build Coastguard Worker    results: the dictionary with scenes and result/summary of testbed's run.
287*b7c941bbSAndroid Build Coastguard Worker  """
288*b7c941bbSAndroid Build Coastguard Worker  for i in completed_testbeds:
289*b7c941bbSAndroid Build Coastguard Worker    for file_name in glob.glob(f'testbed_{i}_camera_*.tmp'):
290*b7c941bbSAndroid Build Coastguard Worker      camera_id = file_name.split('camera_')[1].split('.tmp')[0]
291*b7c941bbSAndroid Build Coastguard Worker      device_id = ''
292*b7c941bbSAndroid Build Coastguard Worker      results = {}
293*b7c941bbSAndroid Build Coastguard Worker      with open(file_name, 'r') as f:
294*b7c941bbSAndroid Build Coastguard Worker        testbed_data = json.load(f)
295*b7c941bbSAndroid Build Coastguard Worker        device_id = testbed_data['device_id']
296*b7c941bbSAndroid Build Coastguard Worker        results = testbed_data['results']
297*b7c941bbSAndroid Build Coastguard Worker      if not device_id or not results:
298*b7c941bbSAndroid Build Coastguard Worker        raise ValueError(f'device_id or results for {file_name} not found.')
299*b7c941bbSAndroid Build Coastguard Worker      yield device_id, camera_id, results
300*b7c941bbSAndroid Build Coastguard Worker
301*b7c941bbSAndroid Build Coastguard Worker
302*b7c941bbSAndroid Build Coastguard Workerdef get_device_property(device_id, property_name):
303*b7c941bbSAndroid Build Coastguard Worker  """Get property of a given device.
304*b7c941bbSAndroid Build Coastguard Worker
305*b7c941bbSAndroid Build Coastguard Worker  Args:
306*b7c941bbSAndroid Build Coastguard Worker    device_id: the ID string of a device.
307*b7c941bbSAndroid Build Coastguard Worker    property_name: the desired property string.
308*b7c941bbSAndroid Build Coastguard Worker  Returns:
309*b7c941bbSAndroid Build Coastguard Worker    The value of the property.
310*b7c941bbSAndroid Build Coastguard Worker  """
311*b7c941bbSAndroid Build Coastguard Worker  property_cmd = f'adb -s {device_id} shell getprop {property_name}'
312*b7c941bbSAndroid Build Coastguard Worker  raw_output = subprocess.check_output(
313*b7c941bbSAndroid Build Coastguard Worker      property_cmd, stderr=subprocess.STDOUT, shell=True)
314*b7c941bbSAndroid Build Coastguard Worker  return str(raw_output.decode('utf-8')).strip()
315*b7c941bbSAndroid Build Coastguard Worker
316*b7c941bbSAndroid Build Coastguard Worker
317*b7c941bbSAndroid Build Coastguard Workerdef are_devices_similar(device_id_1, device_id_2):
318*b7c941bbSAndroid Build Coastguard Worker  """Checks if key dimensions are the same between devices.
319*b7c941bbSAndroid Build Coastguard Worker
320*b7c941bbSAndroid Build Coastguard Worker  Args:
321*b7c941bbSAndroid Build Coastguard Worker    device_id_1: the ID string of the _MAIN_TESTBED device.
322*b7c941bbSAndroid Build Coastguard Worker    device_id_2: the ID string of another device.
323*b7c941bbSAndroid Build Coastguard Worker  Returns:
324*b7c941bbSAndroid Build Coastguard Worker    True if both devices share key dimensions.
325*b7c941bbSAndroid Build Coastguard Worker  """
326*b7c941bbSAndroid Build Coastguard Worker  for property_to_match in _PROPERTIES_TO_MATCH:
327*b7c941bbSAndroid Build Coastguard Worker    property_value_1 = get_device_property(device_id_1, property_to_match)
328*b7c941bbSAndroid Build Coastguard Worker    property_value_2 = get_device_property(device_id_2, property_to_match)
329*b7c941bbSAndroid Build Coastguard Worker    if property_value_1 != property_value_2:
330*b7c941bbSAndroid Build Coastguard Worker      logging.error('%s does not match %s for %s',
331*b7c941bbSAndroid Build Coastguard Worker                    property_value_1, property_value_2, property_to_match)
332*b7c941bbSAndroid Build Coastguard Worker      return False
333*b7c941bbSAndroid Build Coastguard Worker  return True
334*b7c941bbSAndroid Build Coastguard Worker
335*b7c941bbSAndroid Build Coastguard Worker
336*b7c941bbSAndroid Build Coastguard Workerdef check_manual_scenes(device_id, camera_id, scene, out_path):
337*b7c941bbSAndroid Build Coastguard Worker  """Halt run to change scenes.
338*b7c941bbSAndroid Build Coastguard Worker
339*b7c941bbSAndroid Build Coastguard Worker  Args:
340*b7c941bbSAndroid Build Coastguard Worker    device_id: id of device
341*b7c941bbSAndroid Build Coastguard Worker    camera_id: id of camera
342*b7c941bbSAndroid Build Coastguard Worker    scene: Name of the scene to copy image files.
343*b7c941bbSAndroid Build Coastguard Worker    out_path: output file location
344*b7c941bbSAndroid Build Coastguard Worker  """
345*b7c941bbSAndroid Build Coastguard Worker  hidden_physical_id = None
346*b7c941bbSAndroid Build Coastguard Worker  if its_session_utils.SUB_CAMERA_SEPARATOR in camera_id:
347*b7c941bbSAndroid Build Coastguard Worker    split_camera_ids = camera_id.split(its_session_utils.SUB_CAMERA_SEPARATOR)
348*b7c941bbSAndroid Build Coastguard Worker    if len(split_camera_ids) == _SUB_CAMERA_LEVELS:
349*b7c941bbSAndroid Build Coastguard Worker      camera_id = split_camera_ids[0]
350*b7c941bbSAndroid Build Coastguard Worker      hidden_physical_id = split_camera_ids[1]
351*b7c941bbSAndroid Build Coastguard Worker
352*b7c941bbSAndroid Build Coastguard Worker  with its_session_utils.ItsSession(
353*b7c941bbSAndroid Build Coastguard Worker      device_id=device_id,
354*b7c941bbSAndroid Build Coastguard Worker      camera_id=camera_id,
355*b7c941bbSAndroid Build Coastguard Worker      hidden_physical_id=hidden_physical_id) as cam:
356*b7c941bbSAndroid Build Coastguard Worker    props = cam.get_camera_properties()
357*b7c941bbSAndroid Build Coastguard Worker    props = cam.override_with_hidden_physical_camera_props(props)
358*b7c941bbSAndroid Build Coastguard Worker
359*b7c941bbSAndroid Build Coastguard Worker    while True:
360*b7c941bbSAndroid Build Coastguard Worker      input(f'\n Press <ENTER> after positioning camera {camera_id} with '
361*b7c941bbSAndroid Build Coastguard Worker            f'{scene}.\n The scene setup should be: \n  {_SCENE_REQ[scene]}\n')
362*b7c941bbSAndroid Build Coastguard Worker      # Converge 3A prior to capture
363*b7c941bbSAndroid Build Coastguard Worker      if scene == 'scene5':
364*b7c941bbSAndroid Build Coastguard Worker        cam.do_3a(do_af=False, lock_ae=camera_properties_utils.ae_lock(props),
365*b7c941bbSAndroid Build Coastguard Worker                  lock_awb=camera_properties_utils.awb_lock(props))
366*b7c941bbSAndroid Build Coastguard Worker      else:
367*b7c941bbSAndroid Build Coastguard Worker        cam.do_3a()
368*b7c941bbSAndroid Build Coastguard Worker      req, fmt = capture_request_utils.get_fastest_auto_capture_settings(props)
369*b7c941bbSAndroid Build Coastguard Worker      logging.info('Capturing an image to check the test scene')
370*b7c941bbSAndroid Build Coastguard Worker      cap = cam.do_capture(req, fmt)
371*b7c941bbSAndroid Build Coastguard Worker      img = image_processing_utils.convert_capture_to_rgb_image(cap)
372*b7c941bbSAndroid Build Coastguard Worker      img_name = os.path.join(out_path, f'test_{scene.replace("/", "_")}.jpg')
373*b7c941bbSAndroid Build Coastguard Worker      logging.info('Please check scene setup in %s', img_name)
374*b7c941bbSAndroid Build Coastguard Worker      image_processing_utils.write_image(img, img_name)
375*b7c941bbSAndroid Build Coastguard Worker      choice = input(f'Is the image okay for ITS {scene}? (Y/N)').lower()
376*b7c941bbSAndroid Build Coastguard Worker      if choice == 'y':
377*b7c941bbSAndroid Build Coastguard Worker        break
378*b7c941bbSAndroid Build Coastguard Worker
379*b7c941bbSAndroid Build Coastguard Worker
380*b7c941bbSAndroid Build Coastguard Workerdef get_config_file_contents():
381*b7c941bbSAndroid Build Coastguard Worker  """Read the config file contents from a YML file.
382*b7c941bbSAndroid Build Coastguard Worker
383*b7c941bbSAndroid Build Coastguard Worker  Args:
384*b7c941bbSAndroid Build Coastguard Worker    None
385*b7c941bbSAndroid Build Coastguard Worker
386*b7c941bbSAndroid Build Coastguard Worker  Returns:
387*b7c941bbSAndroid Build Coastguard Worker    config_file_contents: a dict read from config.yml
388*b7c941bbSAndroid Build Coastguard Worker  """
389*b7c941bbSAndroid Build Coastguard Worker  with open(CONFIG_FILE) as file:
390*b7c941bbSAndroid Build Coastguard Worker    config_file_contents = yaml.safe_load(file)
391*b7c941bbSAndroid Build Coastguard Worker  return config_file_contents
392*b7c941bbSAndroid Build Coastguard Worker
393*b7c941bbSAndroid Build Coastguard Worker
394*b7c941bbSAndroid Build Coastguard Workerdef get_test_params(config_file_contents):
395*b7c941bbSAndroid Build Coastguard Worker  """Reads the config file parameters.
396*b7c941bbSAndroid Build Coastguard Worker
397*b7c941bbSAndroid Build Coastguard Worker  Args:
398*b7c941bbSAndroid Build Coastguard Worker    config_file_contents: dict read from config.yml file
399*b7c941bbSAndroid Build Coastguard Worker
400*b7c941bbSAndroid Build Coastguard Worker  Returns:
401*b7c941bbSAndroid Build Coastguard Worker    dict of test parameters
402*b7c941bbSAndroid Build Coastguard Worker  """
403*b7c941bbSAndroid Build Coastguard Worker  test_params = None
404*b7c941bbSAndroid Build Coastguard Worker  for _, j in config_file_contents.items():
405*b7c941bbSAndroid Build Coastguard Worker    for datadict in j:
406*b7c941bbSAndroid Build Coastguard Worker      test_params = datadict.get('TestParams')
407*b7c941bbSAndroid Build Coastguard Worker  return test_params
408*b7c941bbSAndroid Build Coastguard Worker
409*b7c941bbSAndroid Build Coastguard Worker
410*b7c941bbSAndroid Build Coastguard Workerdef get_device_serial_number(device, config_file_contents):
411*b7c941bbSAndroid Build Coastguard Worker  """Returns the serial number of the device with label from the config file.
412*b7c941bbSAndroid Build Coastguard Worker
413*b7c941bbSAndroid Build Coastguard Worker  The config file contains TestBeds dictionary which contains Controllers and
414*b7c941bbSAndroid Build Coastguard Worker  Android Device dicts.The two devices used by the test per box are listed
415*b7c941bbSAndroid Build Coastguard Worker  here labels dut and tablet. Parse through the nested TestBeds dict to get
416*b7c941bbSAndroid Build Coastguard Worker  the Android device details.
417*b7c941bbSAndroid Build Coastguard Worker
418*b7c941bbSAndroid Build Coastguard Worker  Args:
419*b7c941bbSAndroid Build Coastguard Worker    device: String device label as specified in config file.dut/tablet
420*b7c941bbSAndroid Build Coastguard Worker    config_file_contents: dict read from config.yml file
421*b7c941bbSAndroid Build Coastguard Worker  """
422*b7c941bbSAndroid Build Coastguard Worker
423*b7c941bbSAndroid Build Coastguard Worker  for _, j in config_file_contents.items():
424*b7c941bbSAndroid Build Coastguard Worker    for datadict in j:
425*b7c941bbSAndroid Build Coastguard Worker      android_device_contents = datadict.get('Controllers')
426*b7c941bbSAndroid Build Coastguard Worker      for device_dict in android_device_contents.get('AndroidDevice'):
427*b7c941bbSAndroid Build Coastguard Worker        for _, label in device_dict.items():
428*b7c941bbSAndroid Build Coastguard Worker          if label == 'tablet':
429*b7c941bbSAndroid Build Coastguard Worker            tablet_device_id = str(device_dict.get('serial'))
430*b7c941bbSAndroid Build Coastguard Worker          if label == 'dut':
431*b7c941bbSAndroid Build Coastguard Worker            dut_device_id = str(device_dict.get('serial'))
432*b7c941bbSAndroid Build Coastguard Worker  if device == 'tablet':
433*b7c941bbSAndroid Build Coastguard Worker    return tablet_device_id
434*b7c941bbSAndroid Build Coastguard Worker  else:
435*b7c941bbSAndroid Build Coastguard Worker    return dut_device_id
436*b7c941bbSAndroid Build Coastguard Worker
437*b7c941bbSAndroid Build Coastguard Worker
438*b7c941bbSAndroid Build Coastguard Workerdef get_updated_yml_file(yml_file_contents):
439*b7c941bbSAndroid Build Coastguard Worker  """Create a new yml file and write the testbed contents in it.
440*b7c941bbSAndroid Build Coastguard Worker
441*b7c941bbSAndroid Build Coastguard Worker  This testbed file is per box and contains all the parameters and
442*b7c941bbSAndroid Build Coastguard Worker  device id used by the mobly tests.
443*b7c941bbSAndroid Build Coastguard Worker
444*b7c941bbSAndroid Build Coastguard Worker  Args:
445*b7c941bbSAndroid Build Coastguard Worker   yml_file_contents: Data to write in yml file.
446*b7c941bbSAndroid Build Coastguard Worker
447*b7c941bbSAndroid Build Coastguard Worker  Returns:
448*b7c941bbSAndroid Build Coastguard Worker    Updated yml file contents.
449*b7c941bbSAndroid Build Coastguard Worker  """
450*b7c941bbSAndroid Build Coastguard Worker  os.chmod(YAML_FILE_DIR, 0o755)
451*b7c941bbSAndroid Build Coastguard Worker  file_descriptor, new_yaml_file = tempfile.mkstemp(
452*b7c941bbSAndroid Build Coastguard Worker      suffix='.yml', prefix='config_', dir=YAML_FILE_DIR)
453*b7c941bbSAndroid Build Coastguard Worker  os.close(file_descriptor)
454*b7c941bbSAndroid Build Coastguard Worker  with open(new_yaml_file, 'w') as f:
455*b7c941bbSAndroid Build Coastguard Worker    yaml.dump(yml_file_contents, stream=f, default_flow_style=False)
456*b7c941bbSAndroid Build Coastguard Worker  new_yaml_file_name = os.path.basename(new_yaml_file)
457*b7c941bbSAndroid Build Coastguard Worker  return new_yaml_file_name
458*b7c941bbSAndroid Build Coastguard Worker
459*b7c941bbSAndroid Build Coastguard Worker
460*b7c941bbSAndroid Build Coastguard Workerdef enable_external_storage(device_id):
461*b7c941bbSAndroid Build Coastguard Worker  """Override apk mode to allow write to external storage.
462*b7c941bbSAndroid Build Coastguard Worker
463*b7c941bbSAndroid Build Coastguard Worker  Args:
464*b7c941bbSAndroid Build Coastguard Worker    device_id: Serial number of the device.
465*b7c941bbSAndroid Build Coastguard Worker
466*b7c941bbSAndroid Build Coastguard Worker  """
467*b7c941bbSAndroid Build Coastguard Worker  cmd = (f'adb -s {device_id} shell appops '
468*b7c941bbSAndroid Build Coastguard Worker         'set com.android.cts.verifier MANAGE_EXTERNAL_STORAGE allow')
469*b7c941bbSAndroid Build Coastguard Worker  its_device_utils.run(cmd)
470*b7c941bbSAndroid Build Coastguard Worker
471*b7c941bbSAndroid Build Coastguard Worker
472*b7c941bbSAndroid Build Coastguard Workerdef get_available_cameras(device_id, camera_id):
473*b7c941bbSAndroid Build Coastguard Worker  """Get available camera devices in the current state.
474*b7c941bbSAndroid Build Coastguard Worker
475*b7c941bbSAndroid Build Coastguard Worker  Args:
476*b7c941bbSAndroid Build Coastguard Worker    device_id: Serial number of the device.
477*b7c941bbSAndroid Build Coastguard Worker    camera_id: Logical camera_id
478*b7c941bbSAndroid Build Coastguard Worker
479*b7c941bbSAndroid Build Coastguard Worker  Returns:
480*b7c941bbSAndroid Build Coastguard Worker    List of all the available camera_ids.
481*b7c941bbSAndroid Build Coastguard Worker  """
482*b7c941bbSAndroid Build Coastguard Worker  with its_session_utils.ItsSession(
483*b7c941bbSAndroid Build Coastguard Worker      device_id=device_id,
484*b7c941bbSAndroid Build Coastguard Worker      camera_id=camera_id) as cam:
485*b7c941bbSAndroid Build Coastguard Worker    props = cam.get_camera_properties()
486*b7c941bbSAndroid Build Coastguard Worker    props = cam.override_with_hidden_physical_camera_props(props)
487*b7c941bbSAndroid Build Coastguard Worker    unavailable_physical_cameras = cam.get_unavailable_physical_cameras(
488*b7c941bbSAndroid Build Coastguard Worker        camera_id)
489*b7c941bbSAndroid Build Coastguard Worker    unavailable_physical_ids = unavailable_physical_cameras[
490*b7c941bbSAndroid Build Coastguard Worker        'unavailablePhysicalCamerasArray']
491*b7c941bbSAndroid Build Coastguard Worker    output = cam.get_camera_ids()
492*b7c941bbSAndroid Build Coastguard Worker    all_camera_ids = output['cameraIdArray']
493*b7c941bbSAndroid Build Coastguard Worker    # Concat camera_id, physical camera_id and sub camera separator
494*b7c941bbSAndroid Build Coastguard Worker    unavailable_physical_ids = [f'{camera_id}.{s}'
495*b7c941bbSAndroid Build Coastguard Worker                                for s in unavailable_physical_ids]
496*b7c941bbSAndroid Build Coastguard Worker    for i in unavailable_physical_ids:
497*b7c941bbSAndroid Build Coastguard Worker      if i in all_camera_ids:
498*b7c941bbSAndroid Build Coastguard Worker        all_camera_ids.remove(i)
499*b7c941bbSAndroid Build Coastguard Worker    logging.debug('available camera ids: %s', all_camera_ids)
500*b7c941bbSAndroid Build Coastguard Worker  return all_camera_ids
501*b7c941bbSAndroid Build Coastguard Worker
502*b7c941bbSAndroid Build Coastguard Worker
503*b7c941bbSAndroid Build Coastguard Workerdef get_unavailable_physical_cameras(device_id, camera_id):
504*b7c941bbSAndroid Build Coastguard Worker  """Get unavailable physical cameras in the current state.
505*b7c941bbSAndroid Build Coastguard Worker
506*b7c941bbSAndroid Build Coastguard Worker  Args:
507*b7c941bbSAndroid Build Coastguard Worker    device_id: Serial number of the device.
508*b7c941bbSAndroid Build Coastguard Worker    camera_id: Logical camera device id
509*b7c941bbSAndroid Build Coastguard Worker
510*b7c941bbSAndroid Build Coastguard Worker  Returns:
511*b7c941bbSAndroid Build Coastguard Worker    List of all the unavailable camera_ids.
512*b7c941bbSAndroid Build Coastguard Worker  """
513*b7c941bbSAndroid Build Coastguard Worker  with its_session_utils.ItsSession(
514*b7c941bbSAndroid Build Coastguard Worker      device_id=device_id,
515*b7c941bbSAndroid Build Coastguard Worker      camera_id=camera_id) as cam:
516*b7c941bbSAndroid Build Coastguard Worker    unavailable_physical_cameras = cam.get_unavailable_physical_cameras(
517*b7c941bbSAndroid Build Coastguard Worker        camera_id)
518*b7c941bbSAndroid Build Coastguard Worker    unavailable_physical_ids = unavailable_physical_cameras[
519*b7c941bbSAndroid Build Coastguard Worker        'unavailablePhysicalCamerasArray']
520*b7c941bbSAndroid Build Coastguard Worker    unavailable_physical_ids = [f'{camera_id}.{s}'
521*b7c941bbSAndroid Build Coastguard Worker                                for s in unavailable_physical_ids]
522*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Unavailable physical camera ids: %s',
523*b7c941bbSAndroid Build Coastguard Worker                  unavailable_physical_ids)
524*b7c941bbSAndroid Build Coastguard Worker  return unavailable_physical_ids
525*b7c941bbSAndroid Build Coastguard Worker
526*b7c941bbSAndroid Build Coastguard Worker
527*b7c941bbSAndroid Build Coastguard Workerdef is_device_folded(device_id):
528*b7c941bbSAndroid Build Coastguard Worker  """Returns True if the foldable device is in folded state.
529*b7c941bbSAndroid Build Coastguard Worker
530*b7c941bbSAndroid Build Coastguard Worker  Args:
531*b7c941bbSAndroid Build Coastguard Worker    device_id: Serial number of the foldable device.
532*b7c941bbSAndroid Build Coastguard Worker  """
533*b7c941bbSAndroid Build Coastguard Worker  cmd = (f'adb -s {device_id} shell cmd device_state state')
534*b7c941bbSAndroid Build Coastguard Worker  result = subprocess.getoutput(cmd)
535*b7c941bbSAndroid Build Coastguard Worker  if 'CLOSE' in result:
536*b7c941bbSAndroid Build Coastguard Worker    return True
537*b7c941bbSAndroid Build Coastguard Worker  return False
538*b7c941bbSAndroid Build Coastguard Worker
539*b7c941bbSAndroid Build Coastguard Worker
540*b7c941bbSAndroid Build Coastguard Workerdef augment_sub_camera_tests(first_api_level):
541*b7c941bbSAndroid Build Coastguard Worker  """Adds certain tests to SUB_CAMERA_TESTS depending on first_api_level.
542*b7c941bbSAndroid Build Coastguard Worker
543*b7c941bbSAndroid Build Coastguard Worker  Args:
544*b7c941bbSAndroid Build Coastguard Worker    first_api_level: First api level of the device.
545*b7c941bbSAndroid Build Coastguard Worker  """
546*b7c941bbSAndroid Build Coastguard Worker  if (first_api_level >= its_session_utils.ANDROID15_API_LEVEL):
547*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Augmenting sub camera tests')
548*b7c941bbSAndroid Build Coastguard Worker    SUB_CAMERA_TESTS['scene6'] = ('test_in_sensor_zoom',)
549*b7c941bbSAndroid Build Coastguard Worker
550*b7c941bbSAndroid Build Coastguard Worker
551*b7c941bbSAndroid Build Coastguard Workerdef main():
552*b7c941bbSAndroid Build Coastguard Worker  """Run all the Camera ITS automated tests.
553*b7c941bbSAndroid Build Coastguard Worker
554*b7c941bbSAndroid Build Coastguard Worker    Script should be run from the top-level CameraITS directory.
555*b7c941bbSAndroid Build Coastguard Worker
556*b7c941bbSAndroid Build Coastguard Worker    Command line arguments:
557*b7c941bbSAndroid Build Coastguard Worker        camera:  the camera(s) to be tested. Use comma to separate multiple
558*b7c941bbSAndroid Build Coastguard Worker                 camera Ids. Ex: "camera=0,1" or "camera=1"
559*b7c941bbSAndroid Build Coastguard Worker        scenes:  the test scene(s) to be executed. Use comma to separate
560*b7c941bbSAndroid Build Coastguard Worker                 multiple scenes. Ex: "scenes=scene0,scene1_1" or
561*b7c941bbSAndroid Build Coastguard Worker                 "scenes=0,1_1,sensor_fusion" (sceneX can be abbreviated by X
562*b7c941bbSAndroid Build Coastguard Worker                 where X is scene name minus 'scene')
563*b7c941bbSAndroid Build Coastguard Worker  """
564*b7c941bbSAndroid Build Coastguard Worker  logging.basicConfig(level=logging.INFO)
565*b7c941bbSAndroid Build Coastguard Worker  # Make output directories to hold the generated files.
566*b7c941bbSAndroid Build Coastguard Worker  topdir = tempfile.mkdtemp(prefix='CameraITS_')
567*b7c941bbSAndroid Build Coastguard Worker  try:
568*b7c941bbSAndroid Build Coastguard Worker    subprocess.call(['chmod', 'g+rx', topdir])
569*b7c941bbSAndroid Build Coastguard Worker  except OSError as e:
570*b7c941bbSAndroid Build Coastguard Worker    logging.info(repr(e))
571*b7c941bbSAndroid Build Coastguard Worker
572*b7c941bbSAndroid Build Coastguard Worker  scenes = []
573*b7c941bbSAndroid Build Coastguard Worker  camera_id_combos = []
574*b7c941bbSAndroid Build Coastguard Worker  testbed_index = None
575*b7c941bbSAndroid Build Coastguard Worker  num_testbeds = None
576*b7c941bbSAndroid Build Coastguard Worker  # Override camera, scenes and testbed with cmd line values if available
577*b7c941bbSAndroid Build Coastguard Worker  for s in list(sys.argv[1:]):
578*b7c941bbSAndroid Build Coastguard Worker    if 'scenes=' in s:
579*b7c941bbSAndroid Build Coastguard Worker      scenes = s.split('=')[1].split(',')
580*b7c941bbSAndroid Build Coastguard Worker    elif 'camera=' in s:
581*b7c941bbSAndroid Build Coastguard Worker      camera_id_combos = s.split('=')[1].split(',')
582*b7c941bbSAndroid Build Coastguard Worker    elif 'testbed_index=' in s:
583*b7c941bbSAndroid Build Coastguard Worker      testbed_index = int(s.split('=')[1])
584*b7c941bbSAndroid Build Coastguard Worker    elif 'num_testbeds=' in s:
585*b7c941bbSAndroid Build Coastguard Worker      num_testbeds = int(s.split('=')[1])
586*b7c941bbSAndroid Build Coastguard Worker    else:
587*b7c941bbSAndroid Build Coastguard Worker      raise ValueError(f'Unknown argument {s}')
588*b7c941bbSAndroid Build Coastguard Worker  if testbed_index is None and num_testbeds is not None:
589*b7c941bbSAndroid Build Coastguard Worker    raise ValueError(
590*b7c941bbSAndroid Build Coastguard Worker        'testbed_index must be specified if num_testbeds is specified.')
591*b7c941bbSAndroid Build Coastguard Worker  if (testbed_index is not None and num_testbeds is not None and
592*b7c941bbSAndroid Build Coastguard Worker      testbed_index >= num_testbeds):
593*b7c941bbSAndroid Build Coastguard Worker    raise ValueError('testbed_index must be less than num_testbeds. '
594*b7c941bbSAndroid Build Coastguard Worker                     'testbed_index starts at 0.')
595*b7c941bbSAndroid Build Coastguard Worker
596*b7c941bbSAndroid Build Coastguard Worker  # Prepend 'scene' if not specified at cmd line
597*b7c941bbSAndroid Build Coastguard Worker  for i, s in enumerate(scenes):
598*b7c941bbSAndroid Build Coastguard Worker    if (not s.startswith('scene') and
599*b7c941bbSAndroid Build Coastguard Worker        not s.startswith(('checkerboard', 'sensor_fusion',
600*b7c941bbSAndroid Build Coastguard Worker                          'flash', 'feature_combination', '<scene-name>'))):
601*b7c941bbSAndroid Build Coastguard Worker      scenes[i] = f'scene{s}'
602*b7c941bbSAndroid Build Coastguard Worker    if s.startswith('flash') or s.startswith('extensions'):
603*b7c941bbSAndroid Build Coastguard Worker      scenes[i] = f'scene_{s}'
604*b7c941bbSAndroid Build Coastguard Worker    # Handle scene_extensions
605*b7c941bbSAndroid Build Coastguard Worker    if any(s.startswith(extension) for extension in _EXTENSION_NAMES):
606*b7c941bbSAndroid Build Coastguard Worker      scenes[i] = f'scene_extensions/scene_{s}'
607*b7c941bbSAndroid Build Coastguard Worker    if (any(s.startswith('scene_' + extension)
608*b7c941bbSAndroid Build Coastguard Worker            for extension in _EXTENSION_NAMES)):
609*b7c941bbSAndroid Build Coastguard Worker      scenes[i] = f'scene_extensions/{s}'
610*b7c941bbSAndroid Build Coastguard Worker
611*b7c941bbSAndroid Build Coastguard Worker  # Read config file and extract relevant TestBed
612*b7c941bbSAndroid Build Coastguard Worker  config_file_contents = get_config_file_contents()
613*b7c941bbSAndroid Build Coastguard Worker  if testbed_index is None:
614*b7c941bbSAndroid Build Coastguard Worker    for i in config_file_contents['TestBeds']:
615*b7c941bbSAndroid Build Coastguard Worker      if scenes in (
616*b7c941bbSAndroid Build Coastguard Worker          ['sensor_fusion'], ['checkerboard'], ['scene_flash'],
617*b7c941bbSAndroid Build Coastguard Worker          ['feature_combination']
618*b7c941bbSAndroid Build Coastguard Worker      ):
619*b7c941bbSAndroid Build Coastguard Worker        if TEST_KEY_SENSOR_FUSION not in i['Name'].lower():
620*b7c941bbSAndroid Build Coastguard Worker          config_file_contents['TestBeds'].remove(i)
621*b7c941bbSAndroid Build Coastguard Worker      else:
622*b7c941bbSAndroid Build Coastguard Worker        if TEST_KEY_SENSOR_FUSION in i['Name'].lower():
623*b7c941bbSAndroid Build Coastguard Worker          config_file_contents['TestBeds'].remove(i)
624*b7c941bbSAndroid Build Coastguard Worker  else:
625*b7c941bbSAndroid Build Coastguard Worker    config_file_contents = {
626*b7c941bbSAndroid Build Coastguard Worker        'TestBeds': [config_file_contents['TestBeds'][testbed_index]]
627*b7c941bbSAndroid Build Coastguard Worker    }
628*b7c941bbSAndroid Build Coastguard Worker
629*b7c941bbSAndroid Build Coastguard Worker  # Get test parameters from config file
630*b7c941bbSAndroid Build Coastguard Worker  test_params_content = get_test_params(config_file_contents)
631*b7c941bbSAndroid Build Coastguard Worker  if not camera_id_combos:
632*b7c941bbSAndroid Build Coastguard Worker    camera_id_combos = str(test_params_content['camera']).split(',')
633*b7c941bbSAndroid Build Coastguard Worker  if not scenes:
634*b7c941bbSAndroid Build Coastguard Worker    scenes = str(test_params_content['scene']).split(',')
635*b7c941bbSAndroid Build Coastguard Worker    scenes = [_INT_STR_DICT.get(n, n) for n in scenes]  # recover '1_1' & '1_2'
636*b7c941bbSAndroid Build Coastguard Worker
637*b7c941bbSAndroid Build Coastguard Worker  device_id = get_device_serial_number('dut', config_file_contents)
638*b7c941bbSAndroid Build Coastguard Worker  # Enable external storage on DUT to send summary report to CtsVerifier.apk
639*b7c941bbSAndroid Build Coastguard Worker  enable_external_storage(device_id)
640*b7c941bbSAndroid Build Coastguard Worker
641*b7c941bbSAndroid Build Coastguard Worker  # Add to SUB_CAMERA_TESTS depending on first_api_level
642*b7c941bbSAndroid Build Coastguard Worker  augment_sub_camera_tests(its_session_utils.get_first_api_level(device_id))
643*b7c941bbSAndroid Build Coastguard Worker
644*b7c941bbSAndroid Build Coastguard Worker  # Verify that CTS Verifier is installed
645*b7c941bbSAndroid Build Coastguard Worker  its_session_utils.check_apk_installed(device_id, CTS_VERIFIER_PACKAGE_NAME)
646*b7c941bbSAndroid Build Coastguard Worker  # Check whether the dut is foldable or not
647*b7c941bbSAndroid Build Coastguard Worker  testing_foldable_device = True if test_params_content[
648*b7c941bbSAndroid Build Coastguard Worker      'foldable_device'] == 'True' else False
649*b7c941bbSAndroid Build Coastguard Worker  available_camera_ids_to_test_foldable = []
650*b7c941bbSAndroid Build Coastguard Worker  if testing_foldable_device:
651*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Testing foldable device.')
652*b7c941bbSAndroid Build Coastguard Worker    # Check the state of foldable device. True if device is folded,
653*b7c941bbSAndroid Build Coastguard Worker    # false if the device is opened.
654*b7c941bbSAndroid Build Coastguard Worker    device_folded = is_device_folded(device_id)
655*b7c941bbSAndroid Build Coastguard Worker    # list of available camera_ids to be tested in device state
656*b7c941bbSAndroid Build Coastguard Worker    available_camera_ids_to_test_foldable = get_available_cameras(
657*b7c941bbSAndroid Build Coastguard Worker        device_id, _FRONT_CAMERA_ID)
658*b7c941bbSAndroid Build Coastguard Worker
659*b7c941bbSAndroid Build Coastguard Worker  config_file_test_key = config_file_contents['TestBeds'][0]['Name'].lower()
660*b7c941bbSAndroid Build Coastguard Worker  logging.info('Saving %s output files to: %s', config_file_test_key, topdir)
661*b7c941bbSAndroid Build Coastguard Worker  if TEST_KEY_TABLET in config_file_test_key:
662*b7c941bbSAndroid Build Coastguard Worker    tablet_id = get_device_serial_number('tablet', config_file_contents)
663*b7c941bbSAndroid Build Coastguard Worker    tablet_name_cmd = f'adb -s {tablet_id} shell getprop ro.product.device'
664*b7c941bbSAndroid Build Coastguard Worker    raw_output = subprocess.check_output(
665*b7c941bbSAndroid Build Coastguard Worker        tablet_name_cmd, stderr=subprocess.STDOUT, shell=True)
666*b7c941bbSAndroid Build Coastguard Worker    tablet_name = str(raw_output.decode('utf-8')).strip()
667*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Tablet name: %s', tablet_name)
668*b7c941bbSAndroid Build Coastguard Worker    brightness = test_params_content['brightness']
669*b7c941bbSAndroid Build Coastguard Worker    its_session_utils.validate_tablet(tablet_name, brightness, tablet_id)
670*b7c941bbSAndroid Build Coastguard Worker  else:
671*b7c941bbSAndroid Build Coastguard Worker    tablet_id = None
672*b7c941bbSAndroid Build Coastguard Worker    tablet_name = "sensor_fusion"
673*b7c941bbSAndroid Build Coastguard Worker
674*b7c941bbSAndroid Build Coastguard Worker  testing_sensor_fusion_with_controller = False
675*b7c941bbSAndroid Build Coastguard Worker  if TEST_KEY_SENSOR_FUSION in config_file_test_key:
676*b7c941bbSAndroid Build Coastguard Worker    if test_params_content['rotator_cntl'].lower() in VALID_CONTROLLERS:
677*b7c941bbSAndroid Build Coastguard Worker      testing_sensor_fusion_with_controller = True
678*b7c941bbSAndroid Build Coastguard Worker
679*b7c941bbSAndroid Build Coastguard Worker  testing_flash_with_controller = False
680*b7c941bbSAndroid Build Coastguard Worker  if (test_params_content.get('lighting_cntl', 'None').lower() == 'arduino' and
681*b7c941bbSAndroid Build Coastguard Worker      'manual' not in config_file_test_key):
682*b7c941bbSAndroid Build Coastguard Worker    testing_flash_with_controller = True
683*b7c941bbSAndroid Build Coastguard Worker
684*b7c941bbSAndroid Build Coastguard Worker  # Expand GROUPED_SCENES and remove any duplicates
685*b7c941bbSAndroid Build Coastguard Worker  scenes = [_GROUPED_SCENES[s] if s in _GROUPED_SCENES else s for s in scenes]
686*b7c941bbSAndroid Build Coastguard Worker  scenes = np.hstack(scenes).tolist()
687*b7c941bbSAndroid Build Coastguard Worker  scenes = sorted(set(scenes), key=scenes.index)
688*b7c941bbSAndroid Build Coastguard Worker  # List of scenes to be executed in folded state will have '_folded'
689*b7c941bbSAndroid Build Coastguard Worker  # prefix. This will help distinguish the test results from folded vs
690*b7c941bbSAndroid Build Coastguard Worker  # open device state for front camera_ids.
691*b7c941bbSAndroid Build Coastguard Worker  folded_device_scenes = []
692*b7c941bbSAndroid Build Coastguard Worker  for scene in scenes:
693*b7c941bbSAndroid Build Coastguard Worker    folded_device_scenes.append(f'{scene}_folded')
694*b7c941bbSAndroid Build Coastguard Worker
695*b7c941bbSAndroid Build Coastguard Worker  logging.info('Running ITS on device: %s, camera(s): %s, scene(s): %s',
696*b7c941bbSAndroid Build Coastguard Worker               device_id, camera_id_combos, scenes)
697*b7c941bbSAndroid Build Coastguard Worker
698*b7c941bbSAndroid Build Coastguard Worker  # Determine if manual run
699*b7c941bbSAndroid Build Coastguard Worker  if tablet_id is not None and not set(scenes).intersection(_MANUAL_SCENES):
700*b7c941bbSAndroid Build Coastguard Worker    auto_scene_switch = True
701*b7c941bbSAndroid Build Coastguard Worker  else:
702*b7c941bbSAndroid Build Coastguard Worker    auto_scene_switch = False
703*b7c941bbSAndroid Build Coastguard Worker    logging.info('Manual, checkerboard scenes, or scene5 testing.')
704*b7c941bbSAndroid Build Coastguard Worker
705*b7c941bbSAndroid Build Coastguard Worker  folded_prompted = False
706*b7c941bbSAndroid Build Coastguard Worker  opened_prompted = False
707*b7c941bbSAndroid Build Coastguard Worker  for camera_id in camera_id_combos:
708*b7c941bbSAndroid Build Coastguard Worker    test_params_content['camera'] = camera_id
709*b7c941bbSAndroid Build Coastguard Worker    results = {}
710*b7c941bbSAndroid Build Coastguard Worker    unav_cameras = []
711*b7c941bbSAndroid Build Coastguard Worker    # Get the list of unavailable cameras in current device state.
712*b7c941bbSAndroid Build Coastguard Worker    # These camera_ids should not be tested in current device state.
713*b7c941bbSAndroid Build Coastguard Worker    if testing_foldable_device:
714*b7c941bbSAndroid Build Coastguard Worker      unav_cameras = get_unavailable_physical_cameras(
715*b7c941bbSAndroid Build Coastguard Worker          device_id, _FRONT_CAMERA_ID)
716*b7c941bbSAndroid Build Coastguard Worker
717*b7c941bbSAndroid Build Coastguard Worker    if testing_foldable_device:
718*b7c941bbSAndroid Build Coastguard Worker      device_state = 'folded' if device_folded else 'opened'
719*b7c941bbSAndroid Build Coastguard Worker
720*b7c941bbSAndroid Build Coastguard Worker    testing_folded_front_camera = (testing_foldable_device and
721*b7c941bbSAndroid Build Coastguard Worker                                   device_folded and
722*b7c941bbSAndroid Build Coastguard Worker                                   _FRONT_CAMERA_ID in camera_id)
723*b7c941bbSAndroid Build Coastguard Worker
724*b7c941bbSAndroid Build Coastguard Worker    # Raise an assertion error if there is any camera unavailable in
725*b7c941bbSAndroid Build Coastguard Worker    # current device state. Usually scenes with suffix 'folded' will
726*b7c941bbSAndroid Build Coastguard Worker    # be executed in folded state.
727*b7c941bbSAndroid Build Coastguard Worker    if (testing_foldable_device and
728*b7c941bbSAndroid Build Coastguard Worker        _FRONT_CAMERA_ID in camera_id and camera_id in unav_cameras):
729*b7c941bbSAndroid Build Coastguard Worker      raise AssertionError(
730*b7c941bbSAndroid Build Coastguard Worker          f'Camera {camera_id} is unavailable in device state {device_state}'
731*b7c941bbSAndroid Build Coastguard Worker          f' and cannot be tested with device {device_state}!')
732*b7c941bbSAndroid Build Coastguard Worker
733*b7c941bbSAndroid Build Coastguard Worker    if (testing_folded_front_camera and camera_id not in unav_cameras
734*b7c941bbSAndroid Build Coastguard Worker        and not folded_prompted):
735*b7c941bbSAndroid Build Coastguard Worker      folded_prompted = True
736*b7c941bbSAndroid Build Coastguard Worker      input('\nYou are testing a foldable device in folded state. '
737*b7c941bbSAndroid Build Coastguard Worker            'Please make sure the device is folded and press <ENTER> '
738*b7c941bbSAndroid Build Coastguard Worker            'after positioning properly.\n')
739*b7c941bbSAndroid Build Coastguard Worker
740*b7c941bbSAndroid Build Coastguard Worker    if (testing_foldable_device and
741*b7c941bbSAndroid Build Coastguard Worker        not device_folded and _FRONT_CAMERA_ID in camera_id and
742*b7c941bbSAndroid Build Coastguard Worker        camera_id not in unav_cameras and not opened_prompted):
743*b7c941bbSAndroid Build Coastguard Worker      opened_prompted = True
744*b7c941bbSAndroid Build Coastguard Worker      input('\nYou are testing a foldable device in opened state. '
745*b7c941bbSAndroid Build Coastguard Worker            'Please make sure the device is unfolded and press <ENTER> '
746*b7c941bbSAndroid Build Coastguard Worker            'after positioning properly.\n')
747*b7c941bbSAndroid Build Coastguard Worker
748*b7c941bbSAndroid Build Coastguard Worker    # Run through all scenes if user does not supply one and config file doesn't
749*b7c941bbSAndroid Build Coastguard Worker    # have specific scene name listed.
750*b7c941bbSAndroid Build Coastguard Worker    if its_session_utils.SUB_CAMERA_SEPARATOR in camera_id:
751*b7c941bbSAndroid Build Coastguard Worker      possible_scenes = list(SUB_CAMERA_TESTS.keys())
752*b7c941bbSAndroid Build Coastguard Worker      if auto_scene_switch:
753*b7c941bbSAndroid Build Coastguard Worker        possible_scenes.remove('sensor_fusion')
754*b7c941bbSAndroid Build Coastguard Worker    else:
755*b7c941bbSAndroid Build Coastguard Worker      if 'checkerboard' in scenes:
756*b7c941bbSAndroid Build Coastguard Worker        possible_scenes = _CHECKERBOARD_SCENES
757*b7c941bbSAndroid Build Coastguard Worker      elif 'scene_flash' in scenes:
758*b7c941bbSAndroid Build Coastguard Worker        possible_scenes = _FLASH_SCENES
759*b7c941bbSAndroid Build Coastguard Worker      elif 'scene_extensions' in scenes:
760*b7c941bbSAndroid Build Coastguard Worker        possible_scenes = _EXTENSIONS_SCENES
761*b7c941bbSAndroid Build Coastguard Worker      else:
762*b7c941bbSAndroid Build Coastguard Worker        possible_scenes = _TABLET_SCENES if auto_scene_switch else _ALL_SCENES
763*b7c941bbSAndroid Build Coastguard Worker
764*b7c941bbSAndroid Build Coastguard Worker    if ('<scene-name>' in scenes or 'checkerboard' in scenes or
765*b7c941bbSAndroid Build Coastguard Worker        'scene_extensions' in scenes):
766*b7c941bbSAndroid Build Coastguard Worker      per_camera_scenes = possible_scenes
767*b7c941bbSAndroid Build Coastguard Worker    else:
768*b7c941bbSAndroid Build Coastguard Worker      # Validate user input scene names
769*b7c941bbSAndroid Build Coastguard Worker      per_camera_scenes = []
770*b7c941bbSAndroid Build Coastguard Worker      for s in scenes:
771*b7c941bbSAndroid Build Coastguard Worker        if s in possible_scenes:
772*b7c941bbSAndroid Build Coastguard Worker          per_camera_scenes.append(s)
773*b7c941bbSAndroid Build Coastguard Worker      if not per_camera_scenes:
774*b7c941bbSAndroid Build Coastguard Worker        raise ValueError('No valid scene specified for this camera.')
775*b7c941bbSAndroid Build Coastguard Worker
776*b7c941bbSAndroid Build Coastguard Worker    # Folded state scenes will have 'folded' suffix only for
777*b7c941bbSAndroid Build Coastguard Worker    # front cameras since rear cameras are common in both folded
778*b7c941bbSAndroid Build Coastguard Worker    # and unfolded state.
779*b7c941bbSAndroid Build Coastguard Worker    foldable_per_camera_scenes = []
780*b7c941bbSAndroid Build Coastguard Worker    if testing_folded_front_camera:
781*b7c941bbSAndroid Build Coastguard Worker      if camera_id not in available_camera_ids_to_test_foldable:
782*b7c941bbSAndroid Build Coastguard Worker        raise AssertionError(f'camera {camera_id} is not available.')
783*b7c941bbSAndroid Build Coastguard Worker      for s in per_camera_scenes:
784*b7c941bbSAndroid Build Coastguard Worker        foldable_per_camera_scenes.append(f'{s}_folded')
785*b7c941bbSAndroid Build Coastguard Worker
786*b7c941bbSAndroid Build Coastguard Worker    if foldable_per_camera_scenes:
787*b7c941bbSAndroid Build Coastguard Worker      per_camera_scenes = foldable_per_camera_scenes
788*b7c941bbSAndroid Build Coastguard Worker
789*b7c941bbSAndroid Build Coastguard Worker    logging.info('camera: %s, scene(s): %s', camera_id, per_camera_scenes)
790*b7c941bbSAndroid Build Coastguard Worker
791*b7c941bbSAndroid Build Coastguard Worker    if testing_folded_front_camera:
792*b7c941bbSAndroid Build Coastguard Worker      all_scenes = [f'{s}_folded' for s in _ALL_SCENES]
793*b7c941bbSAndroid Build Coastguard Worker    else:
794*b7c941bbSAndroid Build Coastguard Worker      all_scenes = _ALL_SCENES
795*b7c941bbSAndroid Build Coastguard Worker
796*b7c941bbSAndroid Build Coastguard Worker    for s in all_scenes:
797*b7c941bbSAndroid Build Coastguard Worker      results[s] = {RESULT_KEY: RESULT_NOT_EXECUTED}
798*b7c941bbSAndroid Build Coastguard Worker
799*b7c941bbSAndroid Build Coastguard Worker      # assert device folded testing scenes with suffix 'folded'
800*b7c941bbSAndroid Build Coastguard Worker      if testing_foldable_device and 'folded' in s:
801*b7c941bbSAndroid Build Coastguard Worker        if not device_folded:
802*b7c941bbSAndroid Build Coastguard Worker          raise AssertionError('Device should be folded during'
803*b7c941bbSAndroid Build Coastguard Worker                               ' testing scenes with suffix "folded"')
804*b7c941bbSAndroid Build Coastguard Worker
805*b7c941bbSAndroid Build Coastguard Worker    # A subdir in topdir will be created for each camera_id. All scene test
806*b7c941bbSAndroid Build Coastguard Worker    # output logs for each camera id will be stored in this subdir.
807*b7c941bbSAndroid Build Coastguard Worker    # This output log path is a mobly param : LogPath
808*b7c941bbSAndroid Build Coastguard Worker    camera_id_str = (
809*b7c941bbSAndroid Build Coastguard Worker        camera_id.replace(its_session_utils.SUB_CAMERA_SEPARATOR, '_')
810*b7c941bbSAndroid Build Coastguard Worker    )
811*b7c941bbSAndroid Build Coastguard Worker    mobly_output_logs_path = os.path.join(topdir, f'cam_id_{camera_id_str}')
812*b7c941bbSAndroid Build Coastguard Worker    os.mkdir(mobly_output_logs_path)
813*b7c941bbSAndroid Build Coastguard Worker    tot_pass = 0
814*b7c941bbSAndroid Build Coastguard Worker    for s in per_camera_scenes:
815*b7c941bbSAndroid Build Coastguard Worker      results[s]['TEST_STATUS'] = []
816*b7c941bbSAndroid Build Coastguard Worker      results[s][METRICS_KEY] = []
817*b7c941bbSAndroid Build Coastguard Worker      results[s][PERFORMANCE_KEY] = []
818*b7c941bbSAndroid Build Coastguard Worker      results[s][FEATURE_QUERY_KEY] = []
819*b7c941bbSAndroid Build Coastguard Worker
820*b7c941bbSAndroid Build Coastguard Worker      # unit is millisecond for execution time record in CtsVerifier
821*b7c941bbSAndroid Build Coastguard Worker      scene_start_time = int(round(time.time() * 1000))
822*b7c941bbSAndroid Build Coastguard Worker      scene_test_summary = f'Cam{camera_id} {s}' + '\n'
823*b7c941bbSAndroid Build Coastguard Worker      mobly_scene_output_logs_path = os.path.join(mobly_output_logs_path, s)
824*b7c941bbSAndroid Build Coastguard Worker
825*b7c941bbSAndroid Build Coastguard Worker      # Since test directories do not have 'folded' in the name, we need
826*b7c941bbSAndroid Build Coastguard Worker      # to remove that suffix for the path of the scenes to be loaded
827*b7c941bbSAndroid Build Coastguard Worker      # on the tablets
828*b7c941bbSAndroid Build Coastguard Worker      testing_scene = s
829*b7c941bbSAndroid Build Coastguard Worker      if 'folded' in s:
830*b7c941bbSAndroid Build Coastguard Worker        testing_scene = s.split('_folded')[0]
831*b7c941bbSAndroid Build Coastguard Worker      test_params_content['scene'] = testing_scene
832*b7c941bbSAndroid Build Coastguard Worker      test_params_content['scene_with_suffix'] = s
833*b7c941bbSAndroid Build Coastguard Worker
834*b7c941bbSAndroid Build Coastguard Worker      if auto_scene_switch:
835*b7c941bbSAndroid Build Coastguard Worker        # Copy scene images onto the tablet
836*b7c941bbSAndroid Build Coastguard Worker        if 'scene0' not in testing_scene:
837*b7c941bbSAndroid Build Coastguard Worker          its_session_utils.copy_scenes_to_tablet(testing_scene, tablet_id)
838*b7c941bbSAndroid Build Coastguard Worker      else:
839*b7c941bbSAndroid Build Coastguard Worker        # Check manual scenes for correctness
840*b7c941bbSAndroid Build Coastguard Worker        if ('scene0' not in testing_scene and
841*b7c941bbSAndroid Build Coastguard Worker            not testing_sensor_fusion_with_controller):
842*b7c941bbSAndroid Build Coastguard Worker          check_manual_scenes(device_id, camera_id, testing_scene,
843*b7c941bbSAndroid Build Coastguard Worker                              mobly_output_logs_path)
844*b7c941bbSAndroid Build Coastguard Worker
845*b7c941bbSAndroid Build Coastguard Worker      scene_test_list = []
846*b7c941bbSAndroid Build Coastguard Worker      config_file_contents['TestBeds'][0]['TestParams'] = test_params_content
847*b7c941bbSAndroid Build Coastguard Worker      # Add the MoblyParams to config.yml file with the path to store camera_id
848*b7c941bbSAndroid Build Coastguard Worker      # test results. This is a separate dict other than TestBeds.
849*b7c941bbSAndroid Build Coastguard Worker      mobly_params_dict = {
850*b7c941bbSAndroid Build Coastguard Worker          'MoblyParams': {
851*b7c941bbSAndroid Build Coastguard Worker              'LogPath': mobly_scene_output_logs_path
852*b7c941bbSAndroid Build Coastguard Worker          }
853*b7c941bbSAndroid Build Coastguard Worker      }
854*b7c941bbSAndroid Build Coastguard Worker      config_file_contents.update(mobly_params_dict)
855*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Final config file contents: %s', config_file_contents)
856*b7c941bbSAndroid Build Coastguard Worker      new_yml_file_name = get_updated_yml_file(config_file_contents)
857*b7c941bbSAndroid Build Coastguard Worker      logging.info('Using %s as temporary config yml file', new_yml_file_name)
858*b7c941bbSAndroid Build Coastguard Worker      if camera_id.rfind(its_session_utils.SUB_CAMERA_SEPARATOR) == -1:
859*b7c941bbSAndroid Build Coastguard Worker        scene_dir = os.listdir(
860*b7c941bbSAndroid Build Coastguard Worker            os.path.join(os.environ['CAMERA_ITS_TOP'], 'tests', testing_scene))
861*b7c941bbSAndroid Build Coastguard Worker        for file_name in scene_dir:
862*b7c941bbSAndroid Build Coastguard Worker          if file_name.endswith('.py') and 'test' in file_name:
863*b7c941bbSAndroid Build Coastguard Worker            scene_test_list.append(file_name)
864*b7c941bbSAndroid Build Coastguard Worker      else:  # sub-camera
865*b7c941bbSAndroid Build Coastguard Worker        if SUB_CAMERA_TESTS.get(testing_scene):
866*b7c941bbSAndroid Build Coastguard Worker          scene_test_list = [f'{test}.py' for test in SUB_CAMERA_TESTS[
867*b7c941bbSAndroid Build Coastguard Worker              testing_scene]]
868*b7c941bbSAndroid Build Coastguard Worker        else:
869*b7c941bbSAndroid Build Coastguard Worker          scene_test_list = []
870*b7c941bbSAndroid Build Coastguard Worker      scene_test_list.sort()
871*b7c941bbSAndroid Build Coastguard Worker
872*b7c941bbSAndroid Build Coastguard Worker      # Run tests for scene
873*b7c941bbSAndroid Build Coastguard Worker      logging.info('Running tests for %s with camera %s',
874*b7c941bbSAndroid Build Coastguard Worker                   testing_scene, camera_id)
875*b7c941bbSAndroid Build Coastguard Worker      num_pass = 0
876*b7c941bbSAndroid Build Coastguard Worker      num_skip = 0
877*b7c941bbSAndroid Build Coastguard Worker      num_not_mandated_fail = 0
878*b7c941bbSAndroid Build Coastguard Worker      num_fail = 0
879*b7c941bbSAndroid Build Coastguard Worker      for test in scene_test_list:
880*b7c941bbSAndroid Build Coastguard Worker        # Handle repeated test
881*b7c941bbSAndroid Build Coastguard Worker        if 'tests/' in test:
882*b7c941bbSAndroid Build Coastguard Worker          cmd = [
883*b7c941bbSAndroid Build Coastguard Worker              'python3',
884*b7c941bbSAndroid Build Coastguard Worker              os.path.join(os.environ['CAMERA_ITS_TOP'], test), '-c',
885*b7c941bbSAndroid Build Coastguard Worker              f'{new_yml_file_name}'
886*b7c941bbSAndroid Build Coastguard Worker          ]
887*b7c941bbSAndroid Build Coastguard Worker        else:
888*b7c941bbSAndroid Build Coastguard Worker          cmd = [
889*b7c941bbSAndroid Build Coastguard Worker              'python3',
890*b7c941bbSAndroid Build Coastguard Worker              os.path.join(os.environ['CAMERA_ITS_TOP'], 'tests',
891*b7c941bbSAndroid Build Coastguard Worker                           testing_scene, test),
892*b7c941bbSAndroid Build Coastguard Worker              '-c',
893*b7c941bbSAndroid Build Coastguard Worker              f'{new_yml_file_name}'
894*b7c941bbSAndroid Build Coastguard Worker          ]
895*b7c941bbSAndroid Build Coastguard Worker        return_string = ''
896*b7c941bbSAndroid Build Coastguard Worker        for num_try in range(NUM_TRIES):
897*b7c941bbSAndroid Build Coastguard Worker          # Saves to mobly test summary file
898*b7c941bbSAndroid Build Coastguard Worker          # print only messages for manual lighting control testing
899*b7c941bbSAndroid Build Coastguard Worker          output = subprocess.Popen(
900*b7c941bbSAndroid Build Coastguard Worker              cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
901*b7c941bbSAndroid Build Coastguard Worker          )
902*b7c941bbSAndroid Build Coastguard Worker          with output.stdout, open(
903*b7c941bbSAndroid Build Coastguard Worker              os.path.join(topdir, MOBLY_TEST_SUMMARY_TXT_FILE), 'wb'
904*b7c941bbSAndroid Build Coastguard Worker          ) as file:
905*b7c941bbSAndroid Build Coastguard Worker            for line in iter(output.stdout.readline, b''):
906*b7c941bbSAndroid Build Coastguard Worker              out = line.decode('utf-8').strip()
907*b7c941bbSAndroid Build Coastguard Worker              if '<ENTER>' in out: print(out)
908*b7c941bbSAndroid Build Coastguard Worker              file.write(line)
909*b7c941bbSAndroid Build Coastguard Worker          output.wait()
910*b7c941bbSAndroid Build Coastguard Worker
911*b7c941bbSAndroid Build Coastguard Worker          # Parse mobly logs to determine PASS/FAIL(*)/SKIP & socket FAILs
912*b7c941bbSAndroid Build Coastguard Worker          with open(
913*b7c941bbSAndroid Build Coastguard Worker              os.path.join(topdir, MOBLY_TEST_SUMMARY_TXT_FILE), 'r') as file:
914*b7c941bbSAndroid Build Coastguard Worker            test_code = output.returncode
915*b7c941bbSAndroid Build Coastguard Worker            test_skipped = False
916*b7c941bbSAndroid Build Coastguard Worker            test_not_yet_mandated = False
917*b7c941bbSAndroid Build Coastguard Worker            test_mpc_req = ''
918*b7c941bbSAndroid Build Coastguard Worker            perf_test_metrics = ''
919*b7c941bbSAndroid Build Coastguard Worker            hdr_mpc_req = ''
920*b7c941bbSAndroid Build Coastguard Worker            feature_query_proto = ''
921*b7c941bbSAndroid Build Coastguard Worker            content = file.read()
922*b7c941bbSAndroid Build Coastguard Worker
923*b7c941bbSAndroid Build Coastguard Worker            # Find media performance class logging
924*b7c941bbSAndroid Build Coastguard Worker            lines = content.splitlines()
925*b7c941bbSAndroid Build Coastguard Worker            for one_line in lines:
926*b7c941bbSAndroid Build Coastguard Worker              # regular expression pattern must match
927*b7c941bbSAndroid Build Coastguard Worker              # MPC12_CAMERA_LAUNCH_PATTERN or MPC12_JPEG_CAPTURE_PATTERN in
928*b7c941bbSAndroid Build Coastguard Worker              # ItsTestActivity.java.
929*b7c941bbSAndroid Build Coastguard Worker              mpc_string_match = re.search(
930*b7c941bbSAndroid Build Coastguard Worker                  '^(1080p_jpeg_capture_time_ms:|camera_launch_time_ms:)',
931*b7c941bbSAndroid Build Coastguard Worker                  one_line)
932*b7c941bbSAndroid Build Coastguard Worker              if mpc_string_match:
933*b7c941bbSAndroid Build Coastguard Worker                test_mpc_req = one_line
934*b7c941bbSAndroid Build Coastguard Worker                break
935*b7c941bbSAndroid Build Coastguard Worker
936*b7c941bbSAndroid Build Coastguard Worker            for one_line in lines:
937*b7c941bbSAndroid Build Coastguard Worker              # regular expression pattern must match in ItsTestActivity.java.
938*b7c941bbSAndroid Build Coastguard Worker              gainmap_string_match = re.search('^has_gainmap:', one_line)
939*b7c941bbSAndroid Build Coastguard Worker              if gainmap_string_match:
940*b7c941bbSAndroid Build Coastguard Worker                hdr_mpc_req = one_line
941*b7c941bbSAndroid Build Coastguard Worker                break
942*b7c941bbSAndroid Build Coastguard Worker
943*b7c941bbSAndroid Build Coastguard Worker            # Find feature combination query proto
944*b7c941bbSAndroid Build Coastguard Worker            for one_line in lines:
945*b7c941bbSAndroid Build Coastguard Worker              # regular expression pattern must match in ItsTestActivity.java.
946*b7c941bbSAndroid Build Coastguard Worker              feature_comb_query_string_match = re.search(
947*b7c941bbSAndroid Build Coastguard Worker                  '^feature_query_proto:(.*)', one_line
948*b7c941bbSAndroid Build Coastguard Worker              )
949*b7c941bbSAndroid Build Coastguard Worker              if feature_comb_query_string_match:
950*b7c941bbSAndroid Build Coastguard Worker                feature_query_proto = feature_comb_query_string_match.group(1)
951*b7c941bbSAndroid Build Coastguard Worker                break
952*b7c941bbSAndroid Build Coastguard Worker
953*b7c941bbSAndroid Build Coastguard Worker            # Find performance metrics logging
954*b7c941bbSAndroid Build Coastguard Worker            for one_line in lines:
955*b7c941bbSAndroid Build Coastguard Worker              # regular expression pattern must match in ItsTestActivity.java.
956*b7c941bbSAndroid Build Coastguard Worker              perf_metrics_string_match = re.search(
957*b7c941bbSAndroid Build Coastguard Worker                  '^test.*:',
958*b7c941bbSAndroid Build Coastguard Worker                  one_line)
959*b7c941bbSAndroid Build Coastguard Worker              if perf_metrics_string_match:
960*b7c941bbSAndroid Build Coastguard Worker                perf_test_metrics = one_line
961*b7c941bbSAndroid Build Coastguard Worker                # each test can add multiple metrics
962*b7c941bbSAndroid Build Coastguard Worker                results[s][PERFORMANCE_KEY].append(perf_test_metrics)
963*b7c941bbSAndroid Build Coastguard Worker
964*b7c941bbSAndroid Build Coastguard Worker            if 'Test skipped' in content:
965*b7c941bbSAndroid Build Coastguard Worker              return_string = 'SKIP '
966*b7c941bbSAndroid Build Coastguard Worker              num_skip += 1
967*b7c941bbSAndroid Build Coastguard Worker              test_skipped = True
968*b7c941bbSAndroid Build Coastguard Worker              break
969*b7c941bbSAndroid Build Coastguard Worker
970*b7c941bbSAndroid Build Coastguard Worker            if its_session_utils.NOT_YET_MANDATED_MESSAGE in content:
971*b7c941bbSAndroid Build Coastguard Worker              return_string = 'FAIL*'
972*b7c941bbSAndroid Build Coastguard Worker              num_not_mandated_fail += 1
973*b7c941bbSAndroid Build Coastguard Worker              test_not_yet_mandated = True
974*b7c941bbSAndroid Build Coastguard Worker              break
975*b7c941bbSAndroid Build Coastguard Worker
976*b7c941bbSAndroid Build Coastguard Worker            if test_code == 0 and not test_skipped:
977*b7c941bbSAndroid Build Coastguard Worker              return_string = 'PASS '
978*b7c941bbSAndroid Build Coastguard Worker              num_pass += 1
979*b7c941bbSAndroid Build Coastguard Worker              break
980*b7c941bbSAndroid Build Coastguard Worker
981*b7c941bbSAndroid Build Coastguard Worker            if test_code == 1 and not test_not_yet_mandated:
982*b7c941bbSAndroid Build Coastguard Worker              return_string = 'FAIL '
983*b7c941bbSAndroid Build Coastguard Worker              if 'Problem with socket' in content and num_try != NUM_TRIES-1:
984*b7c941bbSAndroid Build Coastguard Worker                logging.info('Retry %s/%s', s, test)
985*b7c941bbSAndroid Build Coastguard Worker              else:
986*b7c941bbSAndroid Build Coastguard Worker                num_fail += 1
987*b7c941bbSAndroid Build Coastguard Worker                break
988*b7c941bbSAndroid Build Coastguard Worker            os.remove(os.path.join(topdir, MOBLY_TEST_SUMMARY_TXT_FILE))
989*b7c941bbSAndroid Build Coastguard Worker        status_prefix = ''
990*b7c941bbSAndroid Build Coastguard Worker        if testbed_index is not None:
991*b7c941bbSAndroid Build Coastguard Worker          status_prefix = config_file_test_key + ':'
992*b7c941bbSAndroid Build Coastguard Worker        logging.info('%s%s %s/%s', status_prefix, return_string, s, test)
993*b7c941bbSAndroid Build Coastguard Worker        test_name = test.split('/')[-1].split('.')[0]
994*b7c941bbSAndroid Build Coastguard Worker        results[s]['TEST_STATUS'].append({
995*b7c941bbSAndroid Build Coastguard Worker            'test': test_name,
996*b7c941bbSAndroid Build Coastguard Worker            'status': return_string.strip()})
997*b7c941bbSAndroid Build Coastguard Worker        if test_mpc_req:
998*b7c941bbSAndroid Build Coastguard Worker          results[s][METRICS_KEY].append(test_mpc_req)
999*b7c941bbSAndroid Build Coastguard Worker        if hdr_mpc_req:
1000*b7c941bbSAndroid Build Coastguard Worker          results[s][METRICS_KEY].append(hdr_mpc_req)
1001*b7c941bbSAndroid Build Coastguard Worker        if feature_query_proto:
1002*b7c941bbSAndroid Build Coastguard Worker          results[s][FEATURE_QUERY_KEY].append(feature_query_proto)
1003*b7c941bbSAndroid Build Coastguard Worker
1004*b7c941bbSAndroid Build Coastguard Worker        msg_short = f'{return_string} {test}'
1005*b7c941bbSAndroid Build Coastguard Worker        scene_test_summary += msg_short + '\n'
1006*b7c941bbSAndroid Build Coastguard Worker        if (test in _LIGHTING_CONTROL_TESTS and
1007*b7c941bbSAndroid Build Coastguard Worker            not testing_flash_with_controller):
1008*b7c941bbSAndroid Build Coastguard Worker          print('Turn lights ON in rig and press <ENTER> to continue.')
1009*b7c941bbSAndroid Build Coastguard Worker
1010*b7c941bbSAndroid Build Coastguard Worker      # unit is millisecond for execution time record in CtsVerifier
1011*b7c941bbSAndroid Build Coastguard Worker      scene_end_time = int(round(time.time() * 1000))
1012*b7c941bbSAndroid Build Coastguard Worker      skip_string = ''
1013*b7c941bbSAndroid Build Coastguard Worker      tot_tests = len(scene_test_list)
1014*b7c941bbSAndroid Build Coastguard Worker      tot_tests_run = tot_tests - num_skip
1015*b7c941bbSAndroid Build Coastguard Worker      if tot_tests_run != 0:
1016*b7c941bbSAndroid Build Coastguard Worker        tests_passed_ratio = (num_pass + num_not_mandated_fail) / tot_tests_run
1017*b7c941bbSAndroid Build Coastguard Worker      else:
1018*b7c941bbSAndroid Build Coastguard Worker        tests_passed_ratio = (num_pass + num_not_mandated_fail) / 100.0
1019*b7c941bbSAndroid Build Coastguard Worker      tests_passed_ratio_format = f'{(100 * tests_passed_ratio):.1f}%'
1020*b7c941bbSAndroid Build Coastguard Worker      if num_skip > 0:
1021*b7c941bbSAndroid Build Coastguard Worker        skip_string = f",{num_skip} test{'s' if num_skip > 1 else ''} skipped"
1022*b7c941bbSAndroid Build Coastguard Worker      test_result = (f'{num_pass + num_not_mandated_fail} / {tot_tests_run} '
1023*b7c941bbSAndroid Build Coastguard Worker                     f'tests passed ({tests_passed_ratio_format}){skip_string}')
1024*b7c941bbSAndroid Build Coastguard Worker      logging.info(test_result)
1025*b7c941bbSAndroid Build Coastguard Worker      if num_not_mandated_fail > 0:
1026*b7c941bbSAndroid Build Coastguard Worker        logging.info('(*) %s not_yet_mandated tests failed',
1027*b7c941bbSAndroid Build Coastguard Worker                     num_not_mandated_fail)
1028*b7c941bbSAndroid Build Coastguard Worker
1029*b7c941bbSAndroid Build Coastguard Worker      tot_pass += num_pass
1030*b7c941bbSAndroid Build Coastguard Worker      logging.info('scene tests: %s, Total tests passed: %s', tot_tests,
1031*b7c941bbSAndroid Build Coastguard Worker                   tot_pass)
1032*b7c941bbSAndroid Build Coastguard Worker      if tot_tests > 0:
1033*b7c941bbSAndroid Build Coastguard Worker        logging.info('%s compatibility score: %.f/100\n',
1034*b7c941bbSAndroid Build Coastguard Worker                     s, 100 * num_pass / tot_tests)
1035*b7c941bbSAndroid Build Coastguard Worker        scene_test_summary_path = os.path.join(mobly_scene_output_logs_path,
1036*b7c941bbSAndroid Build Coastguard Worker                                               'scene_test_summary.txt')
1037*b7c941bbSAndroid Build Coastguard Worker        with open(scene_test_summary_path, 'w') as f:
1038*b7c941bbSAndroid Build Coastguard Worker          f.write(scene_test_summary)
1039*b7c941bbSAndroid Build Coastguard Worker        results[s][RESULT_KEY] = (RESULT_PASS if num_fail == 0 else RESULT_FAIL)
1040*b7c941bbSAndroid Build Coastguard Worker        results[s][SUMMARY_KEY] = scene_test_summary_path
1041*b7c941bbSAndroid Build Coastguard Worker        results[s][TIME_KEY_START] = scene_start_time
1042*b7c941bbSAndroid Build Coastguard Worker        results[s][TIME_KEY_END] = scene_end_time
1043*b7c941bbSAndroid Build Coastguard Worker      else:
1044*b7c941bbSAndroid Build Coastguard Worker        logging.info('%s compatibility score: 0/100\n')
1045*b7c941bbSAndroid Build Coastguard Worker
1046*b7c941bbSAndroid Build Coastguard Worker      # Delete temporary yml file after scene run.
1047*b7c941bbSAndroid Build Coastguard Worker      new_yaml_file_path = os.path.join(YAML_FILE_DIR, new_yml_file_name)
1048*b7c941bbSAndroid Build Coastguard Worker      os.remove(new_yaml_file_path)
1049*b7c941bbSAndroid Build Coastguard Worker
1050*b7c941bbSAndroid Build Coastguard Worker    # Log results per camera
1051*b7c941bbSAndroid Build Coastguard Worker    if num_testbeds is None or testbed_index == _MAIN_TESTBED:
1052*b7c941bbSAndroid Build Coastguard Worker      logging.info('Reporting camera %s ITS results to CtsVerifier', camera_id)
1053*b7c941bbSAndroid Build Coastguard Worker      logging.info('ITS results to CtsVerifier: %s', results)
1054*b7c941bbSAndroid Build Coastguard Worker      report_result(device_id, camera_id, tablet_name, results)
1055*b7c941bbSAndroid Build Coastguard Worker    else:
1056*b7c941bbSAndroid Build Coastguard Worker      write_result(testbed_index, device_id, camera_id, results)
1057*b7c941bbSAndroid Build Coastguard Worker
1058*b7c941bbSAndroid Build Coastguard Worker  logging.info('Test execution completed.')
1059*b7c941bbSAndroid Build Coastguard Worker
1060*b7c941bbSAndroid Build Coastguard Worker  # Power down tablet
1061*b7c941bbSAndroid Build Coastguard Worker  if tablet_id:
1062*b7c941bbSAndroid Build Coastguard Worker    cmd = f'adb -s {tablet_id} shell input keyevent KEYCODE_POWER'
1063*b7c941bbSAndroid Build Coastguard Worker    subprocess.Popen(cmd.split())
1064*b7c941bbSAndroid Build Coastguard Worker
1065*b7c941bbSAndroid Build Coastguard Worker  # establish connection with lighting controller
1066*b7c941bbSAndroid Build Coastguard Worker  lighting_cntl = test_params_content.get('lighting_cntl', 'None')
1067*b7c941bbSAndroid Build Coastguard Worker  lighting_ch = test_params_content.get('lighting_ch', 'None')
1068*b7c941bbSAndroid Build Coastguard Worker  arduino_serial_port = lighting_control_utils.lighting_control(
1069*b7c941bbSAndroid Build Coastguard Worker      lighting_cntl, lighting_ch)
1070*b7c941bbSAndroid Build Coastguard Worker
1071*b7c941bbSAndroid Build Coastguard Worker  # turn OFF lights
1072*b7c941bbSAndroid Build Coastguard Worker  lighting_control_utils.set_lighting_state(
1073*b7c941bbSAndroid Build Coastguard Worker      arduino_serial_port, lighting_ch, 'OFF')
1074*b7c941bbSAndroid Build Coastguard Worker
1075*b7c941bbSAndroid Build Coastguard Worker  if num_testbeds is not None:
1076*b7c941bbSAndroid Build Coastguard Worker    if testbed_index == _MAIN_TESTBED:
1077*b7c941bbSAndroid Build Coastguard Worker      logging.info('Waiting for all testbeds to finish.')
1078*b7c941bbSAndroid Build Coastguard Worker      start = time.time()
1079*b7c941bbSAndroid Build Coastguard Worker      completed_testbeds = set()
1080*b7c941bbSAndroid Build Coastguard Worker      while time.time() < start + MERGE_RESULTS_TIMEOUT:
1081*b7c941bbSAndroid Build Coastguard Worker        for i in range(num_testbeds):
1082*b7c941bbSAndroid Build Coastguard Worker          if os.path.isfile(f'testbed_{i}_completed.tmp'):
1083*b7c941bbSAndroid Build Coastguard Worker            start = time.time()
1084*b7c941bbSAndroid Build Coastguard Worker            completed_testbeds.add(i)
1085*b7c941bbSAndroid Build Coastguard Worker        # Already reported _MAIN_TESTBED's results.
1086*b7c941bbSAndroid Build Coastguard Worker        if len(completed_testbeds) == num_testbeds - 1:
1087*b7c941bbSAndroid Build Coastguard Worker          logging.info('All testbeds completed, merging results.')
1088*b7c941bbSAndroid Build Coastguard Worker          for parsed_id, parsed_camera, parsed_results in (
1089*b7c941bbSAndroid Build Coastguard Worker              parse_testbeds(completed_testbeds)):
1090*b7c941bbSAndroid Build Coastguard Worker            logging.debug('Parsed id: %s, parsed cam: %s, parsed results: %s',
1091*b7c941bbSAndroid Build Coastguard Worker                          parsed_id, parsed_camera, parsed_results)
1092*b7c941bbSAndroid Build Coastguard Worker            if not are_devices_similar(device_id, parsed_id):
1093*b7c941bbSAndroid Build Coastguard Worker              logging.error('Device %s and device %s are not the same '
1094*b7c941bbSAndroid Build Coastguard Worker                            'model/type/build/revision.',
1095*b7c941bbSAndroid Build Coastguard Worker                            device_id, parsed_id)
1096*b7c941bbSAndroid Build Coastguard Worker              return
1097*b7c941bbSAndroid Build Coastguard Worker            report_result(device_id, parsed_camera, tablet_name, parsed_results)
1098*b7c941bbSAndroid Build Coastguard Worker          for temp_file in glob.glob('testbed_*.tmp'):
1099*b7c941bbSAndroid Build Coastguard Worker            os.remove(temp_file)
1100*b7c941bbSAndroid Build Coastguard Worker          break
1101*b7c941bbSAndroid Build Coastguard Worker      else:
1102*b7c941bbSAndroid Build Coastguard Worker        logging.error('No testbeds finished in the last %d seconds, '
1103*b7c941bbSAndroid Build Coastguard Worker                      'but still expected data. '
1104*b7c941bbSAndroid Build Coastguard Worker                      'Completed testbed indices: %s, '
1105*b7c941bbSAndroid Build Coastguard Worker                      'expected number of testbeds: %d',
1106*b7c941bbSAndroid Build Coastguard Worker                      MERGE_RESULTS_TIMEOUT, list(completed_testbeds),
1107*b7c941bbSAndroid Build Coastguard Worker                      num_testbeds)
1108*b7c941bbSAndroid Build Coastguard Worker    else:
1109*b7c941bbSAndroid Build Coastguard Worker      with open(f'testbed_{testbed_index}_completed.tmp', 'w') as _:
1110*b7c941bbSAndroid Build Coastguard Worker        pass
1111*b7c941bbSAndroid Build Coastguard Worker
1112*b7c941bbSAndroid Build Coastguard Workerif __name__ == '__main__':
1113*b7c941bbSAndroid Build Coastguard Worker  main()
1114