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