1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright 2018 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Liimport contextlib 7*9c5db199SXin Liimport json 8*9c5db199SXin Liimport logging 9*9c5db199SXin Liimport os 10*9c5db199SXin Liimport time 11*9c5db199SXin Li 12*9c5db199SXin Lifrom autotest_lib.client.common_lib import error, utils 13*9c5db199SXin Li 14*9c5db199SXin Li 15*9c5db199SXin Liclass ChartFixture: 16*9c5db199SXin Li """Sets up chart tablet to display placeholder scene image.""" 17*9c5db199SXin Li DISPLAY_SCRIPT = '/usr/local/autotest/bin/display_chart.py' 18*9c5db199SXin Li OUTPUT_LOG = '/tmp/chart_service.log' 19*9c5db199SXin Li 20*9c5db199SXin Li def __init__(self, chart_host, scene_uri, job=None): 21*9c5db199SXin Li self.host = chart_host 22*9c5db199SXin Li self.scene_uri = scene_uri 23*9c5db199SXin Li self.job = job 24*9c5db199SXin Li self.display_pid = None 25*9c5db199SXin Li self.host.run(['rm', '-f', self.OUTPUT_LOG], ignore_status=True) 26*9c5db199SXin Li 27*9c5db199SXin Li def initialize(self): 28*9c5db199SXin Li """Prepare scene file and display it on chart host.""" 29*9c5db199SXin Li logging.info('Prepare scene file') 30*9c5db199SXin Li chart_version = self.host.run( 31*9c5db199SXin Li 'cat /etc/lsb-release | grep CHROMEOS_RELEASE_BUILDER_PATH') 32*9c5db199SXin Li logging.info('Chart version: %s', chart_version) 33*9c5db199SXin Li if utils.is_in_container(): 34*9c5db199SXin Li # Reboot chart to clean the dirty state from last test. See 35*9c5db199SXin Li # b/201032899. 36*9c5db199SXin Li version = self.host.get_release_builder_path() 37*9c5db199SXin Li self.job.run_test('provision_QuickProvision', 38*9c5db199SXin Li host=self.host, 39*9c5db199SXin Li value=version, 40*9c5db199SXin Li force_update_engine=True) 41*9c5db199SXin Li 42*9c5db199SXin Li tmpdir = self.host.get_tmp_dir() 43*9c5db199SXin Li scene_path = os.path.join( 44*9c5db199SXin Li tmpdir, self.scene_uri[self.scene_uri.rfind('/') + 1:]) 45*9c5db199SXin Li self.host.run('wget', args=('-O', scene_path, self.scene_uri)) 46*9c5db199SXin Li 47*9c5db199SXin Li logging.info('Display scene file') 48*9c5db199SXin Li self.display_pid = self.host.run_background( 49*9c5db199SXin Li 'python {script} {scene} >{log} 2>&1'.format( 50*9c5db199SXin Li script=self.DISPLAY_SCRIPT, 51*9c5db199SXin Li scene=scene_path, 52*9c5db199SXin Li log=self.OUTPUT_LOG)) 53*9c5db199SXin Li 54*9c5db199SXin Li logging.info( 55*9c5db199SXin Li 'Poll for "is ready" message for ensuring chart is ready.') 56*9c5db199SXin Li timeout = 60 57*9c5db199SXin Li poll_time_step = 0.1 58*9c5db199SXin Li while timeout > 0: 59*9c5db199SXin Li if self.host.run( 60*9c5db199SXin Li 'grep', 61*9c5db199SXin Li args=('-q', 'Chart is ready.', self.OUTPUT_LOG), 62*9c5db199SXin Li ignore_status=True).exit_status == 0: 63*9c5db199SXin Li break 64*9c5db199SXin Li time.sleep(poll_time_step) 65*9c5db199SXin Li timeout -= poll_time_step 66*9c5db199SXin Li else: 67*9c5db199SXin Li raise error.TestError('Timeout waiting for chart ready') 68*9c5db199SXin Li 69*9c5db199SXin Li def cleanup(self): 70*9c5db199SXin Li """Cleanup display script.""" 71*9c5db199SXin Li if self.display_pid is not None: 72*9c5db199SXin Li self.host.run( 73*9c5db199SXin Li 'kill', 74*9c5db199SXin Li args=('-2', str(self.display_pid)), 75*9c5db199SXin Li ignore_status=True) 76*9c5db199SXin Li self.host.get_file(self.OUTPUT_LOG, '.') 77*9c5db199SXin Li 78*9c5db199SXin Li 79*9c5db199SXin Lidef get_chart_address(host_address, args): 80*9c5db199SXin Li """Get address of chart tablet from commandline args or mapping logic in 81*9c5db199SXin Li test lab. 82*9c5db199SXin Li 83*9c5db199SXin Li @param host_address: a list of hostname strings. 84*9c5db199SXin Li @param args: a dict parse from commandline args. 85*9c5db199SXin Li @return: 86*9c5db199SXin Li A list of strings for chart tablet addresses. 87*9c5db199SXin Li """ 88*9c5db199SXin Li address = utils.args_to_dict(args).get('chart') 89*9c5db199SXin Li if address is not None: 90*9c5db199SXin Li return address.split(',') 91*9c5db199SXin Li elif utils.is_in_container(): 92*9c5db199SXin Li return [utils.get_lab_chart_address(host) for host in host_address] 93*9c5db199SXin Li else: 94*9c5db199SXin Li return None 95*9c5db199SXin Li 96*9c5db199SXin Li 97*9c5db199SXin Liclass DUTFixture: 98*9c5db199SXin Li """Sets up camera filter for target camera facing on DUT.""" 99*9c5db199SXin Li TEST_CONFIG_PATH = '/var/cache/camera/test_config.json' 100*9c5db199SXin Li CAMERA_SCENE_LOG = '/tmp/scene.jpg' 101*9c5db199SXin Li 102*9c5db199SXin Li def __init__(self, test, host, facing): 103*9c5db199SXin Li self.test = test 104*9c5db199SXin Li self.host = host 105*9c5db199SXin Li self.facing = facing 106*9c5db199SXin Li 107*9c5db199SXin Li @contextlib.contextmanager 108*9c5db199SXin Li def _set_selinux_permissive(self): 109*9c5db199SXin Li selinux_mode = self.host.run_output('getenforce') 110*9c5db199SXin Li self.host.run('setenforce 0') 111*9c5db199SXin Li yield 112*9c5db199SXin Li self.host.run('setenforce', args=(selinux_mode, )) 113*9c5db199SXin Li 114*9c5db199SXin Li def _write_file(self, filepath, content, permission=None, owner=None): 115*9c5db199SXin Li """Write content to filepath on remote host. 116*9c5db199SXin Li @param permission: set permission to 0xxx octal number of remote file. 117*9c5db199SXin Li @param owner: set owner of remote file. 118*9c5db199SXin Li """ 119*9c5db199SXin Li tmp_path = os.path.join(self.test.tmpdir, os.path.basename(filepath)) 120*9c5db199SXin Li with open(tmp_path, 'w') as f: 121*9c5db199SXin Li f.write(content) 122*9c5db199SXin Li if permission is not None: 123*9c5db199SXin Li os.chmod(tmp_path, permission) 124*9c5db199SXin Li self.host.send_file(tmp_path, filepath, delete_dest=True) 125*9c5db199SXin Li if owner is not None: 126*9c5db199SXin Li self.host.run('chown', args=(owner, filepath)) 127*9c5db199SXin Li 128*9c5db199SXin Li def initialize(self): 129*9c5db199SXin Li """Filter out camera other than target facing on DUT.""" 130*9c5db199SXin Li self._write_file( 131*9c5db199SXin Li self.TEST_CONFIG_PATH, 132*9c5db199SXin Li json.dumps({ 133*9c5db199SXin Li 'enable_back_camera': self.facing == 'back', 134*9c5db199SXin Li 'enable_front_camera': self.facing == 'front', 135*9c5db199SXin Li 'enable_external_camera': False 136*9c5db199SXin Li }), 137*9c5db199SXin Li owner='arc-camera') 138*9c5db199SXin Li 139*9c5db199SXin Li # cros_camera_service will reference the test config to filter out 140*9c5db199SXin Li # undesired cameras. 141*9c5db199SXin Li logging.info('Restart camera service with filter option') 142*9c5db199SXin Li self.host.upstart_restart('cros-camera') 143*9c5db199SXin Li 144*9c5db199SXin Li # arc_setup will reference the test config to filter out the media 145*9c5db199SXin Li # profile of undesired cameras. 146*9c5db199SXin Li logging.info('Restart ARC++ container with camera test config') 147*9c5db199SXin Li self.host.run('restart ui') 148*9c5db199SXin Li 149*9c5db199SXin Li @contextlib.contextmanager 150*9c5db199SXin Li def _stop_camera_service(self): 151*9c5db199SXin Li # Ensure camera service is running or the 152*9c5db199SXin Li # upstart_stop()/upstart_restart() may failed due to in 153*9c5db199SXin Li # "start|post-stop" sleep for respawning state. See b/183904344 for 154*9c5db199SXin Li # detail. 155*9c5db199SXin Li logging.info('Wait for presence of camera service') 156*9c5db199SXin Li self.host.wait_for_service('cros-camera') 157*9c5db199SXin Li 158*9c5db199SXin Li self.host.upstart_stop('cros-camera') 159*9c5db199SXin Li yield 160*9c5db199SXin Li self.host.upstart_restart('cros-camera') 161*9c5db199SXin Li 162*9c5db199SXin Li def log_camera_scene(self): 163*9c5db199SXin Li """Capture an image from camera as the log for debugging scene related 164*9c5db199SXin Li problem.""" 165*9c5db199SXin Li 166*9c5db199SXin Li gtest_filter = ( 167*9c5db199SXin Li 'Camera3StillCaptureTest/' 168*9c5db199SXin Li 'Camera3DumpSimpleStillCaptureTest.DumpCaptureResult/0') 169*9c5db199SXin Li with self._stop_camera_service(): 170*9c5db199SXin Li self.host.run( 171*9c5db199SXin Li 'sudo', 172*9c5db199SXin Li args=('--user=arc-camera', 'cros_camera_test', 173*9c5db199SXin Li '--gtest_filter=' + gtest_filter, 174*9c5db199SXin Li '--camera_facing=' + self.facing, 175*9c5db199SXin Li '--dump_still_capture_path=' + 176*9c5db199SXin Li self.CAMERA_SCENE_LOG)) 177*9c5db199SXin Li 178*9c5db199SXin Li self.host.get_file(self.CAMERA_SCENE_LOG, '.') 179*9c5db199SXin Li 180*9c5db199SXin Li def cleanup(self): 181*9c5db199SXin Li """Cleanup camera filter.""" 182*9c5db199SXin Li logging.info('Remove filter option and restore camera service') 183*9c5db199SXin Li with self._stop_camera_service(): 184*9c5db199SXin Li self.host.run('rm', args=('-f', self.TEST_CONFIG_PATH)) 185*9c5db199SXin Li 186*9c5db199SXin Li logging.info('Restore camera profile in ARC++ container') 187*9c5db199SXin Li self.host.run('restart ui') 188