1 # Copyright (c) 2012 The Chromium 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 5 """Defines TestPackageExecutable to help run stand-alone executables.""" 6 7 import logging 8 import os 9 import sys 10 import tempfile 11 12 from pylib import cmd_helper 13 from pylib import constants 14 from pylib import pexpect 15 from pylib.device import device_errors 16 from pylib.gtest.test_package import TestPackage 17 18 19 class TestPackageExecutable(TestPackage): 20 """A helper class for running stand-alone executables.""" 21 22 _TEST_RUNNER_RET_VAL_FILE = 'gtest_retval' 23 24 def __init__(self, suite_name): 25 """ 26 Args: 27 suite_name: Name of the test suite (e.g. base_unittests). 28 """ 29 TestPackage.__init__(self, suite_name) 30 self.suite_path = os.path.join(constants.GetOutDirectory(), suite_name) 31 self._symbols_dir = os.path.join(constants.GetOutDirectory(), 32 'lib.target') 33 34 #override 35 def GetGTestReturnCode(self, device): 36 ret = None 37 ret_code = 1 # Assume failure if we can't find it 38 ret_code_file = tempfile.NamedTemporaryFile() 39 try: 40 if not device.PullFile( 41 constants.TEST_EXECUTABLE_DIR + '/' + 42 TestPackageExecutable._TEST_RUNNER_RET_VAL_FILE, 43 ret_code_file.name): 44 logging.critical('Unable to pull gtest ret val file %s', 45 ret_code_file.name) 46 raise ValueError 47 ret_code = file(ret_code_file.name).read() 48 ret = int(ret_code) 49 except ValueError: 50 logging.critical('Error reading gtest ret val file %s [%s]', 51 ret_code_file.name, ret_code) 52 ret = 1 53 return ret 54 55 @staticmethod 56 def _AddNativeCoverageExports(device): 57 # export GCOV_PREFIX set the path for native coverage results 58 # export GCOV_PREFIX_STRIP indicates how many initial directory 59 # names to strip off the hardwired absolute paths. 60 # This value is calculated in buildbot.sh and 61 # depends on where the tree is built. 62 # Ex: /usr/local/google/code/chrome will become 63 # /code/chrome if GCOV_PREFIX_STRIP=3 64 try: 65 depth = os.environ['NATIVE_COVERAGE_DEPTH_STRIP'] 66 export_string = ('export GCOV_PREFIX="%s/gcov"\n' % 67 device.GetExternalStoragePath()) 68 export_string += 'export GCOV_PREFIX_STRIP=%s\n' % depth 69 return export_string 70 except KeyError: 71 logging.info('NATIVE_COVERAGE_DEPTH_STRIP is not defined: ' 72 'No native coverage.') 73 return '' 74 except device_errors.CommandFailedError: 75 logging.info('No external storage found: No native coverage.') 76 return '' 77 78 #override 79 def ClearApplicationState(self, device): 80 try: 81 # We don't expect the executable to be running, so we don't attempt 82 # to retry on failure. 83 device.KillAll(self.suite_name, blocking=True, timeout=30, retries=0) 84 except device_errors.CommandFailedError: 85 # KillAll raises an exception if it can't find a process with the given 86 # name. We only care that there is no process with the given name, so 87 # we can safely eat the exception. 88 pass 89 90 #override 91 def CreateCommandLineFileOnDevice(self, device, test_filter, test_arguments): 92 tool_wrapper = self.tool.GetTestWrapper() 93 sh_script_file = tempfile.NamedTemporaryFile() 94 # We need to capture the exit status from the script since adb shell won't 95 # propagate to us. 96 sh_script_file.write('cd %s\n' 97 '%s' 98 '%s %s/%s --gtest_filter=%s %s\n' 99 'echo $? > %s' % 100 (constants.TEST_EXECUTABLE_DIR, 101 self._AddNativeCoverageExports(device), 102 tool_wrapper, constants.TEST_EXECUTABLE_DIR, 103 self.suite_name, 104 test_filter, test_arguments, 105 TestPackageExecutable._TEST_RUNNER_RET_VAL_FILE)) 106 sh_script_file.flush() 107 cmd_helper.RunCmd(['chmod', '+x', sh_script_file.name]) 108 device.PushChangedFiles( 109 sh_script_file.name, 110 constants.TEST_EXECUTABLE_DIR + '/chrome_test_runner.sh') 111 logging.info('Conents of the test runner script: ') 112 for line in open(sh_script_file.name).readlines(): 113 logging.info(' ' + line.rstrip()) 114 115 #override 116 def GetAllTests(self, device): 117 all_tests = device.RunShellCommand( 118 '%s %s/%s --gtest_list_tests' % 119 (self.tool.GetTestWrapper(), 120 constants.TEST_EXECUTABLE_DIR, 121 self.suite_name)) 122 return self._ParseGTestListTests(all_tests) 123 124 #override 125 def SpawnTestProcess(self, device): 126 args = ['adb', '-s', str(device), 'shell', 'sh', 127 constants.TEST_EXECUTABLE_DIR + '/chrome_test_runner.sh'] 128 logging.info(args) 129 return pexpect.spawn(args[0], args[1:], logfile=sys.stdout) 130 131 #override 132 def Install(self, device): 133 if self.tool.NeedsDebugInfo(): 134 target_name = self.suite_path 135 else: 136 target_name = self.suite_path + '_stripped' 137 if not os.path.isfile(target_name): 138 raise Exception('Did not find %s, build target %s' % 139 (target_name, self.suite_name + '_stripped')) 140 141 target_mtime = os.stat(target_name).st_mtime 142 source_mtime = os.stat(self.suite_path).st_mtime 143 if target_mtime < source_mtime: 144 raise Exception( 145 'stripped binary (%s, timestamp %d) older than ' 146 'source binary (%s, timestamp %d), build target %s' % 147 (target_name, target_mtime, self.suite_path, source_mtime, 148 self.suite_name + '_stripped')) 149 150 test_binary = constants.TEST_EXECUTABLE_DIR + '/' + self.suite_name 151 device.PushChangedFiles(target_name, test_binary) 152