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 6import logging 7import os 8import shutil 9import sys 10import tempfile 11 12import cmd_helper 13import constants 14from test_package import TestPackage 15from pylib import pexpect 16 17 18class TestPackageExecutable(TestPackage): 19 """A helper class for running stand-alone executables.""" 20 21 _TEST_RUNNER_RET_VAL_FILE = 'gtest_retval' 22 23 def __init__(self, adb, device, test_suite, timeout, rebaseline, 24 performance_test, cleanup_test_files, tool, dump_debug_info, 25 symbols_dir=None): 26 """ 27 Args: 28 adb: ADB interface the tests are using. 29 device: Device to run the tests. 30 test_suite: A specific test suite to run, empty to run all. 31 timeout: Timeout for each test. 32 rebaseline: Whether or not to run tests in isolation and update the 33 filter. 34 performance_test: Whether or not performance test(s). 35 cleanup_test_files: Whether or not to cleanup test files on device. 36 tool: Name of the Valgrind tool. 37 dump_debug_info: A debug_info object. 38 symbols_dir: Directory to put the stripped binaries. 39 """ 40 TestPackage.__init__(self, adb, device, test_suite, timeout, 41 rebaseline, performance_test, cleanup_test_files, 42 tool, dump_debug_info) 43 self.symbols_dir = symbols_dir 44 45 def _GetGTestReturnCode(self): 46 ret = None 47 ret_code = 1 # Assume failure if we can't find it 48 ret_code_file = tempfile.NamedTemporaryFile() 49 try: 50 if not self.adb.Adb().Pull( 51 self.adb.GetExternalStorage() + '/' + 52 TestPackageExecutable._TEST_RUNNER_RET_VAL_FILE, 53 ret_code_file.name): 54 logging.critical('Unable to pull gtest ret val file %s', 55 ret_code_file.name) 56 raise ValueError 57 ret_code = file(ret_code_file.name).read() 58 ret = int(ret_code) 59 except ValueError: 60 logging.critical('Error reading gtest ret val file %s [%s]', 61 ret_code_file.name, ret_code) 62 ret = 1 63 return ret 64 65 def _AddNativeCoverageExports(self): 66 # export GCOV_PREFIX set the path for native coverage results 67 # export GCOV_PREFIX_STRIP indicates how many initial directory 68 # names to strip off the hardwired absolute paths. 69 # This value is calculated in buildbot.sh and 70 # depends on where the tree is built. 71 # Ex: /usr/local/google/code/chrome will become 72 # /code/chrome if GCOV_PREFIX_STRIP=3 73 try: 74 depth = os.environ['NATIVE_COVERAGE_DEPTH_STRIP'] 75 except KeyError: 76 logging.info('NATIVE_COVERAGE_DEPTH_STRIP is not defined: ' 77 'No native coverage.') 78 return '' 79 export_string = ('export GCOV_PREFIX="%s/gcov"\n' % 80 self.adb.GetExternalStorage()) 81 export_string += 'export GCOV_PREFIX_STRIP=%s\n' % depth 82 return export_string 83 84 def GetAllTests(self): 85 """Returns a list of all tests available in the test suite.""" 86 all_tests = self.adb.RunShellCommand( 87 '%s %s/%s --gtest_list_tests' % 88 (self.tool.GetTestWrapper(), 89 constants.TEST_EXECUTABLE_DIR, 90 self.test_suite_basename)) 91 return self._ParseGTestListTests(all_tests) 92 93 def CreateTestRunnerScript(self, gtest_filter, test_arguments): 94 """Creates a test runner script and pushes to the device. 95 96 Args: 97 gtest_filter: A gtest_filter flag. 98 test_arguments: Additional arguments to pass to the test binary. 99 """ 100 tool_wrapper = self.tool.GetTestWrapper() 101 sh_script_file = tempfile.NamedTemporaryFile() 102 # We need to capture the exit status from the script since adb shell won't 103 # propagate to us. 104 sh_script_file.write('cd %s\n' 105 '%s' 106 '%s %s/%s --gtest_filter=%s %s\n' 107 'echo $? > %s' % 108 (constants.TEST_EXECUTABLE_DIR, 109 self._AddNativeCoverageExports(), 110 tool_wrapper, constants.TEST_EXECUTABLE_DIR, 111 self.test_suite_basename, 112 gtest_filter, test_arguments, 113 TestPackageExecutable._TEST_RUNNER_RET_VAL_FILE)) 114 sh_script_file.flush() 115 cmd_helper.RunCmd(['chmod', '+x', sh_script_file.name]) 116 self.adb.PushIfNeeded( 117 sh_script_file.name, 118 constants.TEST_EXECUTABLE_DIR + '/chrome_test_runner.sh') 119 logging.info('Conents of the test runner script: ') 120 for line in open(sh_script_file.name).readlines(): 121 logging.info(' ' + line.rstrip()) 122 123 def RunTestsAndListResults(self): 124 """Runs all the tests and checks for failures. 125 126 Returns: 127 A TestResults object. 128 """ 129 args = ['adb', '-s', self.device, 'shell', 'sh', 130 constants.TEST_EXECUTABLE_DIR + '/chrome_test_runner.sh'] 131 logging.info(args) 132 p = pexpect.spawn(args[0], args[1:], logfile=sys.stdout) 133 return self._WatchTestOutput(p) 134 135 def StripAndCopyExecutable(self): 136 """Strips and copies the executable to the device.""" 137 if self.tool.NeedsDebugInfo(): 138 target_name = self.test_suite 139 else: 140 target_name = self.test_suite + '_' + self.device + '_stripped' 141 should_strip = True 142 if os.path.isfile(target_name): 143 logging.info('Found target file %s' % target_name) 144 target_mtime = os.stat(target_name).st_mtime 145 source_mtime = os.stat(self.test_suite).st_mtime 146 if target_mtime > source_mtime: 147 logging.info('Target mtime (%d) is newer than source (%d), assuming ' 148 'no change.' % (target_mtime, source_mtime)) 149 should_strip = False 150 151 if should_strip: 152 logging.info('Did not find up-to-date stripped binary. Generating a ' 153 'new one (%s).' % target_name) 154 # Whenever we generate a stripped binary, copy to the symbols dir. If we 155 # aren't stripping a new binary, assume it's there. 156 if self.symbols_dir: 157 if not os.path.exists(self.symbols_dir): 158 os.makedirs(self.symbols_dir) 159 shutil.copy(self.test_suite, self.symbols_dir) 160 strip = os.environ['STRIP'] 161 cmd_helper.RunCmd([strip, self.test_suite, '-o', target_name]) 162 test_binary = constants.TEST_EXECUTABLE_DIR + '/' + self.test_suite_basename 163 self.adb.PushIfNeeded(target_name, test_binary) 164 165 def _GetTestSuiteBaseName(self): 166 """Returns the base name of the test suite.""" 167 return os.path.basename(self.test_suite) 168