xref: /aosp_15_r20/external/autotest/server/cros/camerabox_utils.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
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