blob: d654469fd1e150bdc4ebb9fdd0d81bace86e6f7a [file] [log] [blame]
Derek Beckett5fb683c2020-08-19 15:24:13 -07001# Lint as: python2, python3
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +08002# Copyright 2018 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import contextlib
7import json
8import logging
9from lxml import etree
10import os
Derek Beckett5fb683c2020-08-19 15:24:13 -070011import six
Kuo Jen Wei987b7eb2020-05-28 15:39:45 +080012import time
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080013
Kuo Jen Wei987b7eb2020-05-28 15:39:45 +080014from autotest_lib.client.common_lib import error, utils
Kuo Jen Wei377e99b2020-02-25 16:39:42 +080015from autotest_lib.server.cros.tradefed import tradefed_chromelogin as login
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080016
17
18class ChartFixture:
19 """Sets up chart tablet to display dummy scene image."""
20 DISPLAY_SCRIPT = '/usr/local/autotest/bin/display_chart.py'
Kuo Jen Weia10200e2020-03-13 16:03:54 +080021 OUTPUT_LOG = '/tmp/chart_service.log'
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080022
23 def __init__(self, chart_host, scene_uri):
24 self.host = chart_host
25 self.scene_uri = scene_uri
26 self.display_pid = None
Kuo Jen Weia10200e2020-03-13 16:03:54 +080027 self.host.run(['rm', '-f', self.OUTPUT_LOG])
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080028
29 def initialize(self):
30 """Prepare scene file and display it on chart host."""
31 logging.info('Prepare scene file')
Kuo Jen Weia10200e2020-03-13 16:03:54 +080032 tmpdir = self.host.get_tmp_dir()
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080033 scene_path = os.path.join(
Kuo Jen Weia10200e2020-03-13 16:03:54 +080034 tmpdir, self.scene_uri[self.scene_uri.rfind('/') + 1:])
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080035 self.host.run('wget', args=('-O', scene_path, self.scene_uri))
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080036
37 logging.info('Display scene file')
38 self.display_pid = self.host.run_background(
Kuo Jen Weia10200e2020-03-13 16:03:54 +080039 'python2 {script} {scene} >{log} 2>&1'.format(
40 script=self.DISPLAY_SCRIPT,
41 scene=scene_path,
42 log=self.OUTPUT_LOG))
Kuo Jen Wei987b7eb2020-05-28 15:39:45 +080043
44 logging.info(
45 'Poll for "is ready" message for ensuring chart is ready.')
Kuo Jen Weie2766072020-12-08 10:25:45 +080046 timeout = 60
Kuo Jen Wei987b7eb2020-05-28 15:39:45 +080047 poll_time_step = 0.1
48 while timeout > 0:
49 if self.host.run(
50 'grep',
51 args=('-q', 'Chart is ready.', self.OUTPUT_LOG),
52 ignore_status=True).exit_status == 0:
53 break
54 time.sleep(poll_time_step)
55 timeout -= poll_time_step
56 else:
57 raise error.TestError('Timeout waiting for chart ready')
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080058
59 def cleanup(self):
60 """Cleanup display script."""
61 if self.display_pid is not None:
Kuo Jen Weia10200e2020-03-13 16:03:54 +080062 self.host.run(
63 'kill',
64 args=('-2', str(self.display_pid)),
65 ignore_status=True)
66 self.host.get_file(self.OUTPUT_LOG, '.')
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080067
68
69def get_chart_address(host_address, args):
Kuo Jen Wei3e89c442019-01-29 14:27:26 +080070 """Get address of chart tablet from commandline args or mapping logic in
71 test lab.
72
73 @param host_address: a list of hostname strings.
74 @param args: a dict parse from commandline args.
75 @return:
76 A list of strings for chart tablet addresses.
77 """
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080078 address = utils.args_to_dict(args).get('chart')
79 if address is not None:
80 return address.split(',')
81 elif utils.is_in_container():
Kuo Jen Wei377e99b2020-02-25 16:39:42 +080082 return [utils.get_lab_chart_address(host) for host in host_address]
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080083 else:
84 return None
85
86
87class DUTFixture:
88 """Sets up camera filter for target camera facing on DUT."""
89 TEST_CONFIG_PATH = '/var/cache/camera/test_config.json'
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080090 CAMERA_PROFILE_PATH = ('/mnt/stateful_partition/encrypted/var/cache/camera'
91 '/media_profiles.xml')
Kuo Jen Wei6f854a02020-05-20 12:01:33 +080092 CAMERA_SCENE_LOG = '/tmp/scene.jpg'
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080093
94 def __init__(self, test, host, facing):
95 self.test = test
96 self.host = host
97 self.facing = facing
98
99 @contextlib.contextmanager
100 def _set_selinux_permissive(self):
101 selinux_mode = self.host.run_output('getenforce')
102 self.host.run('setenforce 0')
103 yield
104 self.host.run('setenforce', args=(selinux_mode, ))
105
106 def _filter_camera_profile(self, content, facing):
107 """Filter camera profile of target facing from content of camera
108 profile.
109
110 @return:
111 New camera profile with only target facing, camera ids are
112 renumbered from 0.
113 """
114 tree = etree.parse(
Derek Beckett5fb683c2020-08-19 15:24:13 -0700115 six.StringIO(content),
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800116 parser=etree.XMLParser(compact=False))
117 root = tree.getroot()
118 profiles = root.findall('CamcorderProfiles')
119 logging.debug('%d number of camera(s) found in camera profile',
120 len(profiles))
121 assert 1 <= len(profiles) <= 2
122 if len(profiles) == 2:
123 cam_id = 0 if facing == 'back' else 1
124 for p in profiles:
125 if cam_id == int(p.attrib['cameraId']):
126 p.attrib['cameraId'] = '0'
127 else:
128 root.remove(p)
129 else:
Kuo Jen Wei377e99b2020-02-25 16:39:42 +0800130 with login.login_chrome(
131 hosts=[self.host],
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800132 board=self.test._get_board_name(),
Kuo Jen Wei377e99b2020-02-25 16:39:42 +0800133 ), self._set_selinux_permissive():
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800134 has_front_camera = (
135 'feature:android.hardware.camera.front' in self.host.
136 run_output('android-sh -c "pm list features"'))
137 logging.debug('has_front_camera=%s', has_front_camera)
138 if (facing == 'front') != has_front_camera:
139 root.remove(profiles[0])
140 return etree.tostring(
141 tree, xml_declaration=True, encoding=tree.docinfo.encoding)
142
143 def _read_file(self, filepath):
144 """Read content of filepath from host."""
145 tmp_path = os.path.join(self.test.tmpdir, os.path.basename(filepath))
146 self.host.get_file(filepath, tmp_path, delete_dest=True)
147 with open(tmp_path) as f:
148 return f.read()
149
150 def _write_file(self, filepath, content, permission=None, owner=None):
151 """Write content to filepath on remote host.
152 @param permission: set permission to 0xxx octal number of remote file.
153 @param owner: set owner of remote file.
154 """
155 tmp_path = os.path.join(self.test.tmpdir, os.path.basename(filepath))
156 with open(tmp_path, 'w') as f:
157 f.write(content)
158 if permission is not None:
159 os.chmod(tmp_path, permission)
160 self.host.send_file(tmp_path, filepath, delete_dest=True)
161 if owner is not None:
162 self.host.run('chown', args=(owner, filepath))
163
164 def initialize(self):
165 """Filter out camera other than target facing on DUT."""
166 logging.info('Restart camera service with filter option')
167 self._write_file(
168 self.TEST_CONFIG_PATH,
169 json.dumps({
170 'enable_back_camera': self.facing == 'back',
171 'enable_front_camera': self.facing == 'front',
172 'enable_external_camera': False
173 }),
174 owner='arc-camera')
Kuo Jen Wei9d68af82020-06-23 10:43:21 +0800175 self.host.upstart_restart('cros-camera')
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800176
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800177 logging.info('Replace camera profile in ARC++ container')
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800178 profile = self._read_file(self.CAMERA_PROFILE_PATH)
179 new_profile = self._filter_camera_profile(profile, self.facing)
180 self._write_file(self.CAMERA_PROFILE_PATH, new_profile)
181 self.host.run('restart ui')
182
Kuo Jen Wei6f854a02020-05-20 12:01:33 +0800183 @contextlib.contextmanager
184 def _stop_camera_service(self):
Kuo Jen Wei9d68af82020-06-23 10:43:21 +0800185 self.host.upstart_stop('cros-camera')
Kuo Jen Wei6f854a02020-05-20 12:01:33 +0800186 yield
Kuo Jen Wei9d68af82020-06-23 10:43:21 +0800187 self.host.upstart_restart('cros-camera')
Kuo Jen Wei6f854a02020-05-20 12:01:33 +0800188
189 def log_camera_scene(self):
190 """Capture an image from camera as the log for debugging scene related
191 problem."""
192
193 gtest_filter = (
194 'Camera3StillCaptureTest/'
195 'Camera3DumpSimpleStillCaptureTest.DumpCaptureResult/0')
196 with self._stop_camera_service():
197 self.host.run(
198 'sudo',
199 args=('--user=arc-camera', 'cros_camera_test',
200 '--gtest_filter=' + gtest_filter,
201 '--camera_facing=' + self.facing,
202 '--dump_still_capture_path=' +
203 self.CAMERA_SCENE_LOG))
204
205 self.host.get_file(self.CAMERA_SCENE_LOG, '.')
206
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800207 def cleanup(self):
208 """Cleanup camera filter."""
209 logging.info('Remove filter option and restore camera service')
210 self.host.run('rm', args=('-f', self.TEST_CONFIG_PATH))
Kuo Jen Wei9d68af82020-06-23 10:43:21 +0800211 self.host.upstart_restart('cros-camera')
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800212
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800213 logging.info('Restore camera profile in ARC++ container')
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800214 self.host.run('restart ui')