blob: ea0b075519b4664c44ec529970c4aed7bcbef45c [file] [log] [blame]
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +08001# Copyright 2018 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import contextlib
6import json
7import logging
8from lxml import etree
9import os
10import StringIO
11
12from autotest_lib.client.common_lib import utils
Kuo Jen Wei377e99b2020-02-25 16:39:42 +080013from autotest_lib.server.cros.tradefed import tradefed_chromelogin as login
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080014
15
16class ChartFixture:
17 """Sets up chart tablet to display dummy scene image."""
18 DISPLAY_SCRIPT = '/usr/local/autotest/bin/display_chart.py'
Kuo Jen Weia10200e2020-03-13 16:03:54 +080019 OUTPUT_LOG = '/tmp/chart_service.log'
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080020
21 def __init__(self, chart_host, scene_uri):
22 self.host = chart_host
23 self.scene_uri = scene_uri
24 self.display_pid = None
Kuo Jen Weia10200e2020-03-13 16:03:54 +080025 self.host.run(['rm', '-f', self.OUTPUT_LOG])
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080026
27 def initialize(self):
28 """Prepare scene file and display it on chart host."""
29 logging.info('Prepare scene file')
Kuo Jen Weia10200e2020-03-13 16:03:54 +080030 tmpdir = self.host.get_tmp_dir()
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080031 scene_path = os.path.join(
Kuo Jen Weia10200e2020-03-13 16:03:54 +080032 tmpdir, self.scene_uri[self.scene_uri.rfind('/') + 1:])
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080033 self.host.run('wget', args=('-O', scene_path, self.scene_uri))
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080034
35 logging.info('Display scene file')
36 self.display_pid = self.host.run_background(
Kuo Jen Weia10200e2020-03-13 16:03:54 +080037 'python2 {script} {scene} >{log} 2>&1'.format(
38 script=self.DISPLAY_SCRIPT,
39 scene=scene_path,
40 log=self.OUTPUT_LOG))
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080041 # TODO(inker): Suppose chart should be displayed very soon. Or require
42 # of waiting until chart actually displayed.
43
44 def cleanup(self):
45 """Cleanup display script."""
46 if self.display_pid is not None:
Kuo Jen Weia10200e2020-03-13 16:03:54 +080047 self.host.run(
48 'kill',
49 args=('-2', str(self.display_pid)),
50 ignore_status=True)
51 self.host.get_file(self.OUTPUT_LOG, '.')
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080052
53
54def get_chart_address(host_address, args):
Kuo Jen Wei3e89c442019-01-29 14:27:26 +080055 """Get address of chart tablet from commandline args or mapping logic in
56 test lab.
57
58 @param host_address: a list of hostname strings.
59 @param args: a dict parse from commandline args.
60 @return:
61 A list of strings for chart tablet addresses.
62 """
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080063 address = utils.args_to_dict(args).get('chart')
64 if address is not None:
65 return address.split(',')
66 elif utils.is_in_container():
Kuo Jen Wei377e99b2020-02-25 16:39:42 +080067 return [utils.get_lab_chart_address(host) for host in host_address]
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080068 else:
69 return None
70
71
72class DUTFixture:
73 """Sets up camera filter for target camera facing on DUT."""
74 TEST_CONFIG_PATH = '/var/cache/camera/test_config.json'
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080075 CAMERA_PROFILE_PATH = ('/mnt/stateful_partition/encrypted/var/cache/camera'
76 '/media_profiles.xml')
Kuo Jen Wei6f854a02020-05-20 12:01:33 +080077 CAMERA_SCENE_LOG = '/tmp/scene.jpg'
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +080078
79 def __init__(self, test, host, facing):
80 self.test = test
81 self.host = host
82 self.facing = facing
83
84 @contextlib.contextmanager
85 def _set_selinux_permissive(self):
86 selinux_mode = self.host.run_output('getenforce')
87 self.host.run('setenforce 0')
88 yield
89 self.host.run('setenforce', args=(selinux_mode, ))
90
91 def _filter_camera_profile(self, content, facing):
92 """Filter camera profile of target facing from content of camera
93 profile.
94
95 @return:
96 New camera profile with only target facing, camera ids are
97 renumbered from 0.
98 """
99 tree = etree.parse(
100 StringIO.StringIO(content),
101 parser=etree.XMLParser(compact=False))
102 root = tree.getroot()
103 profiles = root.findall('CamcorderProfiles')
104 logging.debug('%d number of camera(s) found in camera profile',
105 len(profiles))
106 assert 1 <= len(profiles) <= 2
107 if len(profiles) == 2:
108 cam_id = 0 if facing == 'back' else 1
109 for p in profiles:
110 if cam_id == int(p.attrib['cameraId']):
111 p.attrib['cameraId'] = '0'
112 else:
113 root.remove(p)
114 else:
Kuo Jen Wei377e99b2020-02-25 16:39:42 +0800115 with login.login_chrome(
116 hosts=[self.host],
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800117 board=self.test._get_board_name(),
Kuo Jen Wei377e99b2020-02-25 16:39:42 +0800118 ), self._set_selinux_permissive():
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800119 has_front_camera = (
120 'feature:android.hardware.camera.front' in self.host.
121 run_output('android-sh -c "pm list features"'))
122 logging.debug('has_front_camera=%s', has_front_camera)
123 if (facing == 'front') != has_front_camera:
124 root.remove(profiles[0])
125 return etree.tostring(
126 tree, xml_declaration=True, encoding=tree.docinfo.encoding)
127
128 def _read_file(self, filepath):
129 """Read content of filepath from host."""
130 tmp_path = os.path.join(self.test.tmpdir, os.path.basename(filepath))
131 self.host.get_file(filepath, tmp_path, delete_dest=True)
132 with open(tmp_path) as f:
133 return f.read()
134
135 def _write_file(self, filepath, content, permission=None, owner=None):
136 """Write content to filepath on remote host.
137 @param permission: set permission to 0xxx octal number of remote file.
138 @param owner: set owner of remote file.
139 """
140 tmp_path = os.path.join(self.test.tmpdir, os.path.basename(filepath))
141 with open(tmp_path, 'w') as f:
142 f.write(content)
143 if permission is not None:
144 os.chmod(tmp_path, permission)
145 self.host.send_file(tmp_path, filepath, delete_dest=True)
146 if owner is not None:
147 self.host.run('chown', args=(owner, filepath))
148
149 def initialize(self):
150 """Filter out camera other than target facing on DUT."""
151 logging.info('Restart camera service with filter option')
152 self._write_file(
153 self.TEST_CONFIG_PATH,
154 json.dumps({
155 'enable_back_camera': self.facing == 'back',
156 'enable_front_camera': self.facing == 'front',
157 'enable_external_camera': False
158 }),
159 owner='arc-camera')
160 self.host.run('restart cros-camera')
161
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800162 logging.info('Replace camera profile in ARC++ container')
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800163 profile = self._read_file(self.CAMERA_PROFILE_PATH)
164 new_profile = self._filter_camera_profile(profile, self.facing)
165 self._write_file(self.CAMERA_PROFILE_PATH, new_profile)
166 self.host.run('restart ui')
167
Kuo Jen Wei6f854a02020-05-20 12:01:33 +0800168 @contextlib.contextmanager
169 def _stop_camera_service(self):
170 self.host.run('stop cros-camera')
171 yield
172 self.host.run('start cros-camera')
173
174 def log_camera_scene(self):
175 """Capture an image from camera as the log for debugging scene related
176 problem."""
177
178 gtest_filter = (
179 'Camera3StillCaptureTest/'
180 'Camera3DumpSimpleStillCaptureTest.DumpCaptureResult/0')
181 with self._stop_camera_service():
182 self.host.run(
183 'sudo',
184 args=('--user=arc-camera', 'cros_camera_test',
185 '--gtest_filter=' + gtest_filter,
186 '--camera_facing=' + self.facing,
187 '--dump_still_capture_path=' +
188 self.CAMERA_SCENE_LOG))
189
190 self.host.get_file(self.CAMERA_SCENE_LOG, '.')
191
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800192 def cleanup(self):
193 """Cleanup camera filter."""
194 logging.info('Remove filter option and restore camera service')
195 self.host.run('rm', args=('-f', self.TEST_CONFIG_PATH))
196 self.host.run('restart cros-camera')
197
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800198 logging.info('Restore camera profile in ARC++ container')
Kuo Jen Wei7f00bb32018-11-01 15:35:24 +0800199 self.host.run('restart ui')