liyujie
2025-08-28 786ff4f4ca2374bdd9177f2e24b503d43e7a3b93
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
 
import logging
import os
import re
import shutil
import tempfile
import xml.etree.ElementTree as ET
 
import common
from autotest_lib.client.bin import test, utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib import file_utils
from autotest_lib.client.cros import constants
 
 
class ChromeBinaryTest(test.test):
    """
    Base class for tests to run chrome test binaries without signing in and
    running Chrome.
    """
 
    CHROME_TEST_DEP = 'chrome_test'
    CHROME_SANDBOX = '/opt/google/chrome/chrome-sandbox'
    COMPONENT_LIB = '/opt/google/chrome/lib'
    home_dir = None
    cr_source_dir = None
    test_binary_dir = None
 
    def setup(self):
        """
        Sets up a test.
        """
        self.job.setup_dep([self.CHROME_TEST_DEP])
 
    def initialize(self):
        """
        Initializes members after setup().
        """
        test_dep_dir = os.path.join(self.autodir, 'deps', self.CHROME_TEST_DEP)
        self.job.install_pkg(self.CHROME_TEST_DEP, 'dep', test_dep_dir)
 
        self.cr_source_dir = '%s/test_src' % test_dep_dir
        self.test_binary_dir = '%s/out/Release' % self.cr_source_dir
        # If chrome is a component build then need to create a symlink such
        # that the _unittest binaries can find the chrome component libraries.
        Release_lib = os.path.join(self.test_binary_dir, 'lib')
        if os.path.isdir(self.COMPONENT_LIB):
            logging.info('Detected component build. This assumes binary '
                         'compatibility between chrome and *unittest.')
            if not os.path.islink(Release_lib):
                os.symlink(self.COMPONENT_LIB, Release_lib)
        self.home_dir = tempfile.mkdtemp()
 
    def cleanup(self):
        """
        Cleans up working directory after run.
        """
        if self.home_dir:
            shutil.rmtree(self.home_dir, ignore_errors=True)
 
    def get_chrome_binary_path(self, binary_to_run):
        """
        Gets test binary's full path.
 
        @returns full path of the test binary to run.
        """
        return os.path.join(self.test_binary_dir, binary_to_run)
 
    def parse_fail_reason(self, err, gtest_xml):
        """
        Parses reason of failure from CmdError and gtest result.
 
        @param err: CmdError raised from utils.system().
        @param gtest_xml: filename of gtest result xml.
        @returns reason string
        """
        reasons = {}
 
        # Parse gtest result.
        if os.path.exists(gtest_xml):
            tree = ET.parse(gtest_xml)
            root = tree.getroot()
            for suite in root.findall('testsuite'):
                for case in suite.findall('testcase'):
                    failure = case.find('failure')
                    if failure is None:
                        continue
                    testname = '%s.%s' % (suite.get('name'), case.get('name'))
                    reasons[testname] = failure.attrib['message']
 
        # Parse messages from chrome's test_launcher.
        # This provides some information not available from gtest, like timeout.
        for line in err.result_obj.stdout.splitlines():
            m = re.match(r'\[\d+/\d+\] (\S+) \(([A-Z ]+)\)$', line)
            if not m:
                continue
            testname, reason = m.group(1, 2)
            # Existing reason from gtest has more detail, don't overwrite.
            if testname not in reasons:
                reasons[testname] = reason
 
        if reasons:
            message = '%d failures' % len(reasons)
            for testname, reason in sorted(reasons.items()):
                message += '; <%s>: %s' % (testname, reason.replace('\n', '; '))
            return message
 
        return 'Unable to parse fail reason: ' + str(err)
 
    def run_chrome_test_binary(self,
                               binary_to_run,
                               extra_params='',
                               prefix='',
                               as_chronos=True,
                               timeout=None):
        """
        Runs chrome test binary.
 
        @param binary_to_run: The name of the browser test binary.
        @param extra_params: Arguments for the browser test binary.
        @param prefix: Prefix to the command that invokes the test binary.
        @param as_chronos: Boolean indicating if the tests should run in a
            chronos shell.
        @param timeout: timeout in seconds
 
        @raises: error.TestFail if there is error running the command.
        @raises: CmdTimeoutError: the command timed out and |timeout| is
            specified and not None.
        """
        gtest_xml = tempfile.mktemp(prefix='gtest_xml', suffix='.xml')
        binary_path = self.get_chrome_binary_path(binary_to_run)
        env_vars = ' '.join([
            'HOME=' + self.home_dir,
            'CR_SOURCE_ROOT=' + self.cr_source_dir,
            'CHROME_DEVEL_SANDBOX=' + self.CHROME_SANDBOX,
            'GTEST_OUTPUT=xml:' + gtest_xml,
            ])
        cmd = ' '.join([env_vars, prefix, binary_path, extra_params])
 
        try:
            if as_chronos:
                utils.system("su chronos -c '%s'" % cmd,
                             timeout=timeout)
            else:
                utils.system(cmd, timeout=timeout)
        except error.CmdError as e:
            return_code = e.result_obj.exit_status
            if return_code == 126:
                path_permission = '; '.join(
                    file_utils.recursive_path_permission(binary_path))
                fail_reason = ('Cannot execute command %s. Permissions: %s' %
                               (binary_path, path_permission))
            elif return_code == 127:
                fail_reason = ('Command not found: %s' % binary_path)
            else:
                fail_reason = self.parse_fail_reason(e, gtest_xml)
 
            raise error.TestFail(fail_reason)
 
 
def nuke_chrome(func):
    """
    Decorator to nuke the Chrome browser processes.
    """
 
    def wrapper(*args, **kargs):
        """
        Nukes Chrome browser processes before invoking func().
 
        Also, restarts Chrome after func() returns.
        """
        open(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE, 'w').close()
        try:
            try:
                utils.nuke_process_by_name(name=constants.BROWSER,
                                           with_prejudice=True)
            except error.AutoservPidAlreadyDeadError:
                pass
            return func(*args, **kargs)
        finally:
            # Allow chrome to be restarted again later.
            os.unlink(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE)
 
    return wrapper