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