OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 import logging |
| 7 import os |
| 8 import sys |
| 9 |
| 10 from base_test_runner import BaseTestRunner |
| 11 import debug_info |
| 12 import run_tests_helper |
| 13 from test_package_executable import TestPackageExecutable |
| 14 from test_result import TestResults |
| 15 |
| 16 |
| 17 class SingleTestRunner(BaseTestRunner): |
| 18 """Single test suite attached to a single device. |
| 19 |
| 20 Args: |
| 21 device: Device to run the tests. |
| 22 test_suite: A specific test suite to run, empty to run all. |
| 23 gtest_filter: A gtest_filter flag. |
| 24 test_arguments: Additional arguments to pass to the test binary. |
| 25 timeout: Timeout for each test. |
| 26 rebaseline: Whether or not to run tests in isolation and update the filter. |
| 27 performance_test: Whether or not performance test(s). |
| 28 cleanup_test_files: Whether or not to cleanup test files on device. |
| 29 tool: Name of the Valgrind tool. |
| 30 dump_debug_info: Whether or not to dump debug information. |
| 31 """ |
| 32 |
| 33 def __init__(self, device, test_suite, gtest_filter, test_arguments, timeout, |
| 34 rebaseline, performance_test, cleanup_test_files, tool, |
| 35 dump_debug_info=False): |
| 36 BaseTestRunner.__init__(self, device) |
| 37 self._running_on_emulator = self.device.startswith('emulator') |
| 38 self._gtest_filter = gtest_filter |
| 39 self._test_arguments = test_arguments |
| 40 self.test_results = TestResults() |
| 41 if dump_debug_info: |
| 42 self.dump_debug_info = debug_info.GTestDebugInfo(self.adb, device, |
| 43 os.path.basename(test_suite), gtest_filter) |
| 44 else: |
| 45 self.dump_debug_info = None |
| 46 |
| 47 self.test_package = TestPackageExecutable(self.adb, device, |
| 48 test_suite, timeout, rebaseline, performance_test, cleanup_test_files, |
| 49 tool, self.dump_debug_info) |
| 50 |
| 51 def _GetHttpServerDocumentRootForTestSuite(self): |
| 52 """Returns the document root needed by the test suite.""" |
| 53 if self.test_package.test_suite_basename == 'page_cycler_tests': |
| 54 return os.path.join(run_tests_helper.CHROME_DIR, 'data', 'page_cycler') |
| 55 return None |
| 56 |
| 57 |
| 58 def _TestSuiteRequiresMockTestServer(self): |
| 59 """Returns True if the test suite requires mock test server.""" |
| 60 return False |
| 61 # TODO(yfriedman): Disabled because of flakiness. |
| 62 # (self.test_package.test_suite_basename == 'unit_tests' or |
| 63 # self.test_package.test_suite_basename == 'net_unittests' or |
| 64 # False) |
| 65 |
| 66 def _GetFilterFileName(self): |
| 67 """Returns the filename of gtest filter.""" |
| 68 filter_dir = os.path.join(sys.path[0], 'gtest_filter') |
| 69 filter_name = self.test_package.test_suite_basename + '_disabled' |
| 70 disabled_filter = os.path.join(filter_dir, filter_name) |
| 71 return disabled_filter |
| 72 |
| 73 def _GetAdditionalEmulatorFilterName(self): |
| 74 """Returns the filename of additional gtest filter for emulator.""" |
| 75 filter_dir = os.path.join(sys.path[0], 'gtest_filter') |
| 76 filter_name = '%s%s' % (self.test_package.test_suite_basename, |
| 77 '_emulator_additional_disabled') |
| 78 disabled_filter = os.path.join(filter_dir, filter_name) |
| 79 return disabled_filter |
| 80 |
| 81 def GetDisabledTests(self): |
| 82 """Returns a list of disabled tests. |
| 83 |
| 84 Returns: |
| 85 A list of disabled tests obtained from gtest_filter/test_suite_disabled. |
| 86 """ |
| 87 disabled_tests = run_tests_helper.GetExpectations(self._GetFilterFileName()) |
| 88 if self._running_on_emulator: |
| 89 # Append emulator's filter file. |
| 90 disabled_tests.extend(run_tests_helper.GetExpectations( |
| 91 self._GetAdditionalEmulatorFilterName())) |
| 92 return disabled_tests |
| 93 |
| 94 def UpdateFilter(self, failed_tests): |
| 95 """Updates test_suite_disabled file with the new filter (deletes if empty). |
| 96 |
| 97 If running in Emulator, only the failed tests which are not in the normal |
| 98 filter returned by _GetFilterFileName() are written to emulator's |
| 99 additional filter file. |
| 100 |
| 101 Args: |
| 102 failed_tests: A sorted list of failed tests. |
| 103 """ |
| 104 disabled_tests = [] |
| 105 if not self._running_on_emulator: |
| 106 filter_file_name = self._GetFilterFileName() |
| 107 else: |
| 108 filter_file_name = self._GetAdditionalEmulatorFilterName() |
| 109 disabled_tests.extend( |
| 110 run_tests_helper.GetExpectations(self._GetFilterFileName())) |
| 111 logging.info('About to update emulator\'s addtional filter (%s).' |
| 112 % filter_file_name) |
| 113 |
| 114 new_failed_tests = [] |
| 115 if failed_tests: |
| 116 for test in failed_tests: |
| 117 if test.name not in disabled_tests: |
| 118 new_failed_tests.append(test.name) |
| 119 |
| 120 if not new_failed_tests: |
| 121 if os.path.exists(filter_file_name): |
| 122 os.unlink(filter_file_name) |
| 123 return |
| 124 |
| 125 filter_file = file(filter_file_name, 'w') |
| 126 if self._running_on_emulator: |
| 127 filter_file.write('# Addtional list of suppressions from emulator\n') |
| 128 else: |
| 129 filter_file.write('# List of suppressions\n') |
| 130 filter_file.write("""This file was automatically generated by run_tests.py |
| 131 """) |
| 132 filter_file.write('\n'.join(sorted(new_failed_tests))) |
| 133 filter_file.write('\n') |
| 134 filter_file.close() |
| 135 |
| 136 def GetDataFilesForTestSuite(self): |
| 137 """Returns a list of data files/dirs needed by the test suite.""" |
| 138 # Ideally, we'd just push all test data. However, it has >100MB, and a lot |
| 139 # of the files are not relevant (some are used for browser_tests, others for |
| 140 # features not supported, etc..). |
| 141 if self.test_package.test_suite_basename in ['base_unittests', |
| 142 'sql_unittests', |
| 143 'unit_tests']: |
| 144 return [ |
| 145 'net/data/cache_tests/insert_load1', |
| 146 'net/data/cache_tests/dirty_entry5', |
| 147 'ui/base/test/data/data_pack_unittest', |
| 148 'chrome/test/data/bookmarks/History_with_empty_starred', |
| 149 'chrome/test/data/bookmarks/History_with_starred', |
| 150 'chrome/test/data/extensions/json_schema_test.js', |
| 151 'chrome/test/data/History/', |
| 152 'chrome/test/data/json_schema_validator/', |
| 153 'chrome/test/data/serializer_nested_test.js', |
| 154 'chrome/test/data/serializer_test.js', |
| 155 'chrome/test/data/serializer_test_nowhitespace.js', |
| 156 'chrome/test/data/top_sites/', |
| 157 'chrome/test/data/web_database', |
| 158 'chrome/test/data/zip', |
| 159 ] |
| 160 elif self.test_package.test_suite_basename == 'net_unittests': |
| 161 return [ |
| 162 'net/data/cache_tests', |
| 163 'net/data/filter_unittests', |
| 164 'net/data/ftp', |
| 165 'net/data/proxy_resolver_v8_unittest', |
| 166 'net/data/ssl/certificates', |
| 167 ] |
| 168 elif self.test_package.test_suite_basename == 'ui_tests': |
| 169 return [ |
| 170 'chrome/test/data/dromaeo', |
| 171 'chrome/test/data/json2.js', |
| 172 'chrome/test/data/sunspider', |
| 173 'chrome/test/data/v8_benchmark', |
| 174 'chrome/test/ui/sunspider_uitest.js', |
| 175 'chrome/test/ui/v8_benchmark_uitest.js', |
| 176 ] |
| 177 elif self.test_package.test_suite_basename == 'page_cycler_tests': |
| 178 data = [ |
| 179 'tools/page_cycler', |
| 180 'data/page_cycler', |
| 181 ] |
| 182 for d in data: |
| 183 if not os.path.exists(d): |
| 184 raise Exception('Page cycler data not found.') |
| 185 return data |
| 186 elif self.test_package.test_suite_basename == 'webkit_unit_tests': |
| 187 return [ |
| 188 'third_party/WebKit/Source/WebKit/chromium/tests/data', |
| 189 ] |
| 190 return [] |
| 191 |
| 192 def LaunchHelperToolsForTestSuite(self): |
| 193 """Launches helper tools for the test suite. |
| 194 |
| 195 Sometimes one test may need to run some helper tools first in order to |
| 196 successfully complete the test. |
| 197 """ |
| 198 document_root = self._GetHttpServerDocumentRootForTestSuite() |
| 199 if document_root: |
| 200 self.LaunchTestHttpServer(document_root) |
| 201 if self._TestSuiteRequiresMockTestServer(): |
| 202 self.LaunchChromeTestServerSpawner() |
| 203 |
| 204 def StripAndCopyFiles(self): |
| 205 """Strips and copies the required data files for the test suite.""" |
| 206 self.test_package.StripAndCopyExecutable() |
| 207 self.test_package.tool.CopyFiles() |
| 208 test_data = self.GetDataFilesForTestSuite() |
| 209 if test_data: |
| 210 if self.test_package.test_suite_basename == 'page_cycler_tests': |
| 211 # Since the test data for page cycler are huge (around 200M), we use |
| 212 # sdcard to store the data and create symbol links to map them to |
| 213 # data/local/tmp/ later. |
| 214 self.CopyTestData(test_data, '/sdcard/') |
| 215 for p in [os.path.dirname(d) for d in test_data if os.path.isdir(d)]: |
| 216 mapped_device_path = '/data/local/tmp/' + p |
| 217 # Unlink the mapped_device_path at first in case it was mapped to |
| 218 # a wrong path. Add option '-r' becuase the old path could be a dir. |
| 219 self.adb.RunShellCommand('rm -r %s' % mapped_device_path) |
| 220 self.adb.RunShellCommand( |
| 221 'ln -s /sdcard/%s %s' % (p, mapped_device_path)) |
| 222 else: |
| 223 self.CopyTestData(test_data, '/data/local/tmp/') |
| 224 |
| 225 def RunTestsWithFilter(self): |
| 226 """Runs a tests via a small, temporary shell script.""" |
| 227 self.test_package.CreateTestRunnerScript(self._gtest_filter, |
| 228 self._test_arguments) |
| 229 self.test_results = self.test_package.RunTestsAndListResults() |
| 230 |
| 231 def RebaselineTests(self): |
| 232 """Runs all available tests, restarting in case of failures.""" |
| 233 if self._gtest_filter: |
| 234 all_tests = set(self._gtest_filter.split(':')) |
| 235 else: |
| 236 all_tests = set(self.test_package.GetAllTests()) |
| 237 failed_results = set() |
| 238 executed_results = set() |
| 239 while True: |
| 240 executed_names = set([f.name for f in executed_results]) |
| 241 self._gtest_filter = ':'.join(all_tests - executed_names) |
| 242 self.RunTestsWithFilter() |
| 243 failed_results.update(self.test_results.crashed, |
| 244 self.test_results.failed) |
| 245 executed_results.update(self.test_results.crashed, |
| 246 self.test_results.failed, |
| 247 self.test_results.ok) |
| 248 executed_names = set([f.name for f in executed_results]) |
| 249 logging.info('*' * 80) |
| 250 logging.info(self.device) |
| 251 logging.info('Executed: ' + str(len(executed_names)) + ' of ' + |
| 252 str(len(all_tests))) |
| 253 logging.info('Failed so far: ' + str(len(failed_results)) + ' ' + |
| 254 str([f.name for f in failed_results])) |
| 255 logging.info('Remaining: ' + str(len(all_tests - executed_names)) + ' ' + |
| 256 str(all_tests - executed_names)) |
| 257 logging.info('*' * 80) |
| 258 if executed_names == all_tests: |
| 259 break |
| 260 self.test_results = TestResults.FromOkAndFailed(list(executed_results - |
| 261 failed_results), |
| 262 list(failed_results)) |
| 263 |
| 264 def _RunTestsForSuiteInternal(self): |
| 265 """Runs all tests (in rebaseline mode, run each test in isolation). |
| 266 |
| 267 Returns: |
| 268 A TestResults object. |
| 269 """ |
| 270 if self.test_package.rebaseline: |
| 271 self.RebaselineTests() |
| 272 else: |
| 273 if not self._gtest_filter: |
| 274 self._gtest_filter = ('-' + ':'.join(self.GetDisabledTests()) + ':' + |
| 275 ':'.join(['*.' + x + '*' for x in |
| 276 self.test_package.GetDisabledPrefixes()])) |
| 277 self.RunTestsWithFilter() |
| 278 |
| 279 def SetUp(self): |
| 280 """Sets up necessary test enviroment for the test suite.""" |
| 281 super(SingleTestRunner, self).SetUp() |
| 282 if self.test_package.performance_test: |
| 283 if run_tests_helper.IsRunningAsBuildbot(): |
| 284 self.adb.SetJavaAssertsEnabled(enable=False) |
| 285 self.adb.Reboot(full_reboot=False) |
| 286 self.adb.SetupPerformanceTest() |
| 287 if self.dump_debug_info: |
| 288 self.dump_debug_info.StartRecordingLog(True) |
| 289 self.StripAndCopyFiles() |
| 290 self.LaunchHelperToolsForTestSuite() |
| 291 self.test_package.tool.SetupEnvironment() |
| 292 |
| 293 def TearDown(self): |
| 294 """Cleans up the test enviroment for the test suite.""" |
| 295 super(SingleTestRunner, self).TearDown() |
| 296 self.test_package.tool.CleanUpEnvironment() |
| 297 if self.test_package.cleanup_test_files: |
| 298 self.adb.RemovePushedFiles() |
| 299 if self.dump_debug_info: |
| 300 self.dump_debug_info.StopRecordingLog() |
| 301 if self.test_package.performance_test: |
| 302 self.adb.TearDownPerformanceTest() |
| 303 |
| 304 def RunTests(self): |
| 305 """Runs the tests and cleans up the files once finished. |
| 306 |
| 307 Returns: |
| 308 A TestResults object. |
| 309 """ |
| 310 self.SetUp() |
| 311 try: |
| 312 self._RunTestsForSuiteInternal() |
| 313 finally: |
| 314 self.TearDown() |
| 315 return self.test_results |
OLD | NEW |