| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # | 2 # |
| 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 """Runs all the native unit tests. | 7 """Runs all the native unit tests. |
| 8 | 8 |
| 9 1. Copy over test binary to /data/local on device. | 9 1. Copy over test binary to /data/local on device. |
| 10 2. Resources: chrome/unit_tests requires resources (chrome.pak and en-US.pak) | 10 2. Resources: chrome/unit_tests requires resources (chrome.pak and en-US.pak) |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 ReadOnlyFileUtilTest.ContentsEqual | 43 ReadOnlyFileUtilTest.ContentsEqual |
| 44 | 44 |
| 45 This file is generated by the tests running on devices. If running on emulator, | 45 This file is generated by the tests running on devices. If running on emulator, |
| 46 additonal filter file which lists the tests only failed in emulator will be | 46 additonal filter file which lists the tests only failed in emulator will be |
| 47 loaded. We don't care about the rare testcases which succeeded on emuatlor, but | 47 loaded. We don't care about the rare testcases which succeeded on emuatlor, but |
| 48 failed on device. | 48 failed on device. |
| 49 """ | 49 """ |
| 50 | 50 |
| 51 import fnmatch | 51 import fnmatch |
| 52 import logging | 52 import logging |
| 53 import multiprocessing | |
| 54 import os | 53 import os |
| 55 import re | |
| 56 import subprocess | 54 import subprocess |
| 57 import sys | 55 import sys |
| 58 import time | 56 import time |
| 59 | 57 |
| 60 from pylib import android_commands | 58 from pylib import android_commands |
| 61 from pylib.base_test_sharder import BaseTestSharder | 59 from pylib.base_test_sharder import BaseTestSharder |
| 62 from pylib import cmd_helper | 60 from pylib import constants |
| 63 from pylib import debug_info | 61 from pylib import debug_info |
| 64 import emulator | 62 import emulator |
| 63 from pylib import ports |
| 65 from pylib import run_tests_helper | 64 from pylib import run_tests_helper |
| 65 from pylib import test_options_parser |
| 66 from pylib.single_test_runner import SingleTestRunner | 66 from pylib.single_test_runner import SingleTestRunner |
| 67 from pylib.test_package_executable import TestPackageExecutable | |
| 68 from pylib.test_result import BaseTestResult, TestResults | 67 from pylib.test_result import BaseTestResult, TestResults |
| 69 | 68 |
| 70 _TEST_SUITES = ['base_unittests', | 69 _TEST_SUITES = ['base_unittests', |
| 71 'content_unittests', | 70 'content_unittests', |
| 72 'gpu_unittests', | 71 'gpu_unittests', |
| 73 'ipc_tests', | 72 'ipc_tests', |
| 74 'net_unittests', | 73 'net_unittests', |
| 75 'sql_unittests', | 74 'sql_unittests', |
| 76 'sync_unit_tests', | 75 'sync_unit_tests', |
| 77 'ui_unittests', | 76 'ui_unittests', |
| 78 ] | 77 ] |
| 79 | 78 |
| 80 def FullyQualifiedTestSuites(apk): | 79 def FullyQualifiedTestSuites(apk): |
| 81 """Return a fully qualified list that represents all known suites. | 80 """Return a fully qualified list that represents all known suites. |
| 82 | 81 |
| 83 Args: | 82 Args: |
| 84 apk: if True, use the apk-based test runner""" | 83 apk: if True, use the apk-based test runner""" |
| 85 # If not specified, assume the test suites are in out/Release | 84 # If not specified, assume the test suites are in out/Release |
| 86 test_suite_dir = os.path.abspath(os.path.join(run_tests_helper.CHROME_DIR, | 85 test_suite_dir = os.path.abspath(os.path.join(constants.CHROME_DIR, |
| 87 'out', 'Release')) | 86 'out', 'Release')) |
| 88 if apk: | 87 if apk: |
| 89 # out/Release/$SUITE_apk/$SUITE-debug.apk | 88 # out/Release/$SUITE_apk/$SUITE-debug.apk |
| 90 suites = [os.path.join(test_suite_dir, | 89 suites = [os.path.join(test_suite_dir, |
| 91 t + '_apk', | 90 t + '_apk', |
| 92 t + '-debug.apk') | 91 t + '-debug.apk') |
| 93 for t in _TEST_SUITES] | 92 for t in _TEST_SUITES] |
| 94 else: | 93 else: |
| 95 suites = [os.path.join(test_suite_dir, t) for t in _TEST_SUITES] | 94 suites = [os.path.join(test_suite_dir, t) for t in _TEST_SUITES] |
| 96 return suites | 95 return suites |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 187 cleanup_test_files: Whether or not to cleanup test files on device. | 186 cleanup_test_files: Whether or not to cleanup test files on device. |
| 188 tool: Name of the Valgrind tool. | 187 tool: Name of the Valgrind tool. |
| 189 log_dump_name: Name of log dump file. | 188 log_dump_name: Name of log dump file. |
| 190 apk: boolean to state if we are using the apk based test runner | 189 apk: boolean to state if we are using the apk based test runner |
| 191 annotate: should we print buildbot-style annotations? | 190 annotate: should we print buildbot-style annotations? |
| 192 | 191 |
| 193 Returns: | 192 Returns: |
| 194 A TestResults object. | 193 A TestResults object. |
| 195 """ | 194 """ |
| 196 results = [] | 195 results = [] |
| 196 global _TEST_SUITES |
| 197 | 197 |
| 198 if test_suite: | 198 if test_suite: |
| 199 global _TEST_SUITES | 199 global _TEST_SUITES |
| 200 if (not os.path.exists(test_suite) and | 200 if (not os.path.exists(test_suite)): |
| 201 not os.path.splitext(test_suite)[1] == '.apk'): | |
| 202 logging.critical('Unrecognized test suite %s, supported: %s' % | 201 logging.critical('Unrecognized test suite %s, supported: %s' % |
| 203 (test_suite, _TEST_SUITES)) | 202 (test_suite, _TEST_SUITES)) |
| 204 if test_suite in _TEST_SUITES: | 203 if test_suite in _TEST_SUITES: |
| 205 logging.critical('(Remember to include the path: out/Release/%s)', | 204 logging.critical('(Remember to include the path: out/Release/%s)', |
| 206 test_suite) | 205 test_suite) |
| 207 return TestResults.FromRun(failed=[BaseTestResult(test_suite, '')]) | 206 test_suite_basename = os.path.basename(test_suite) |
| 207 if test_suite_basename in _TEST_SUITES: |
| 208 logging.critical('Try "make -j15 %s"' % test_suite_basename) |
| 209 else: |
| 210 logging.critical('Unrecognized test suite, supported: %s' % |
| 211 _TEST_SUITES) |
| 212 return TestResults.FromOkAndFailed([], [BaseTestResult(test_suite, '')], |
| 213 False, False) |
| 208 fully_qualified_test_suites = [test_suite] | 214 fully_qualified_test_suites = [test_suite] |
| 209 else: | 215 else: |
| 210 fully_qualified_test_suites = FullyQualifiedTestSuites(apk) | 216 fully_qualified_test_suites = FullyQualifiedTestSuites(apk) |
| 211 debug_info_list = [] | 217 debug_info_list = [] |
| 212 print 'Known suites: ' + str(_TEST_SUITES) | 218 print 'Known suites: ' + str(_TEST_SUITES) |
| 213 print 'Running these: ' + str(fully_qualified_test_suites) | 219 print 'Running these: ' + str(fully_qualified_test_suites) |
| 214 for t in fully_qualified_test_suites: | 220 for t in fully_qualified_test_suites: |
| 215 if annotate: | 221 if annotate: |
| 216 print '@@@BUILD_STEP Test suite %s@@@' % os.path.basename(t) | 222 print '@@@BUILD_STEP Test suite %s@@@' % os.path.basename(t) |
| 217 test = SingleTestRunner(device, t, gtest_filter, test_arguments, | 223 test = SingleTestRunner(device, t, gtest_filter, test_arguments, |
| 218 timeout, rebaseline, performance_test, | 224 timeout, rebaseline, performance_test, |
| 219 cleanup_test_files, tool, 0, not not log_dump_name) | 225 cleanup_test_files, tool, 0, not not log_dump_name) |
| 220 test.Run() | 226 test.Run() |
| 221 | 227 |
| 222 results += [test.test_results] | 228 results += [test.test_results] |
| 223 # Collect debug info. | 229 # Collect debug info. |
| 224 debug_info_list += [test.dump_debug_info] | 230 debug_info_list += [test.dump_debug_info] |
| 225 if rebaseline: | 231 if rebaseline: |
| 226 test.UpdateFilter(test.test_results.failed) | 232 test.UpdateFilter(test.test_results.failed) |
| 227 test.test_results.LogFull() | 233 test.test_results.LogFull('Unit test', os.path.basename(t)) |
| 228 # Zip all debug info outputs into a file named by log_dump_name. | 234 # Zip all debug info outputs into a file named by log_dump_name. |
| 229 debug_info.GTestDebugInfo.ZipAndCleanResults( | 235 debug_info.GTestDebugInfo.ZipAndCleanResults( |
| 230 os.path.join(run_tests_helper.CHROME_DIR, 'out', 'Release', | 236 os.path.join(constants.CHROME_DIR, 'out', 'Release', |
| 231 'debug_info_dumps'), | 237 'debug_info_dumps'), |
| 232 log_dump_name, [d for d in debug_info_list if d]) | 238 log_dump_name, [d for d in debug_info_list if d]) |
| 233 | 239 |
| 234 if annotate: | 240 if annotate: |
| 235 PrintAnnotationForTestResults(test.test_results) | 241 PrintAnnotationForTestResults(test.test_results) |
| 236 | 242 |
| 237 return TestResults.FromTestResults(results) | 243 return TestResults.FromTestResults(results) |
| 238 | 244 |
| 239 | 245 |
| 240 class TestSharder(BaseTestSharder): | 246 class TestSharder(BaseTestSharder): |
| 241 """Responsible for sharding the tests on the connected devices.""" | 247 """Responsible for sharding the tests on the connected devices.""" |
| 242 | 248 |
| 243 def __init__(self, attached_devices, test_suite, gtest_filter, | 249 def __init__(self, attached_devices, test_suite, gtest_filter, |
| 244 test_arguments, timeout, rebaseline, performance_test, | 250 test_arguments, timeout, rebaseline, performance_test, |
| 245 cleanup_test_files, tool, annotate): | 251 cleanup_test_files, tool, annotate): |
| 246 BaseTestSharder.__init__(self, attached_devices) | 252 BaseTestSharder.__init__(self, attached_devices) |
| 247 self.test_suite = test_suite | 253 self.test_suite = test_suite |
| 248 self.test_suite_basename = os.path.basename(test_suite) | 254 self.test_suite_basename = os.path.basename(test_suite) |
| 249 self.gtest_filter = gtest_filter | 255 self.gtest_filter = gtest_filter |
| 250 self.test_arguments = test_arguments | 256 self.test_arguments = test_arguments |
| 251 self.timeout = timeout | 257 self.timeout = timeout |
| 252 self.rebaseline = rebaseline | 258 self.rebaseline = rebaseline |
| 253 self.performance_test = performance_test | 259 self.performance_test = performance_test |
| 254 self.cleanup_test_files = cleanup_test_files | 260 self.cleanup_test_files = cleanup_test_files |
| 255 self.tool = tool | 261 self.tool = tool |
| 256 self.annotate = annotate | 262 self.annotate = annotate |
| 257 test = SingleTestRunner(self.attached_devices[0], test_suite, gtest_filter, | 263 test = SingleTestRunner(self.attached_devices[0], test_suite, gtest_filter, |
| 258 test_arguments, timeout, rebaseline, | 264 test_arguments, timeout, rebaseline, |
| 259 performance_test, cleanup_test_files, tool, 0) | 265 performance_test, cleanup_test_files, tool, 0) |
| 266 # The executable/apk needs to be copied before we can call GetAllTests. |
| 267 test.test_package.StripAndCopyExecutable() |
| 260 all_tests = test.test_package.GetAllTests() | 268 all_tests = test.test_package.GetAllTests() |
| 261 if not rebaseline: | 269 if not rebaseline: |
| 262 disabled_list = test.GetDisabledTests() | 270 disabled_list = test.GetDisabledTests() |
| 263 # Only includes tests that do not have any match in the disabled list. | 271 # Only includes tests that do not have any match in the disabled list. |
| 264 all_tests = filter(lambda t: | 272 all_tests = filter(lambda t: |
| 265 not any([fnmatch.fnmatch(t, disabled_pattern) | 273 not any([fnmatch.fnmatch(t, disabled_pattern) |
| 266 for disabled_pattern in disabled_list]), | 274 for disabled_pattern in disabled_list]), |
| 267 all_tests) | 275 all_tests) |
| 268 self.tests = all_tests | 276 self.tests = all_tests |
| 269 | 277 |
| 270 def CreateShardedTestRunner(self, device, index): | 278 def CreateShardedTestRunner(self, device, index): |
| 271 """Creates a suite-specific test runner. | 279 """Creates a suite-specific test runner. |
| 272 | 280 |
| 273 Args: | 281 Args: |
| 274 device: Device serial where this shard will run. | 282 device: Device serial where this shard will run. |
| 275 index: Index of this device in the pool. | 283 index: Index of this device in the pool. |
| 276 | 284 |
| 277 Returns: | 285 Returns: |
| 278 A SingleTestRunner object. | 286 A SingleTestRunner object. |
| 279 """ | 287 """ |
| 280 shard_size = len(self.tests) / len(self.attached_devices) | 288 device_num = len(self.attached_devices) |
| 289 shard_size = (len(self.tests) + device_num - 1) / device_num |
| 281 shard_test_list = self.tests[index * shard_size : (index + 1) * shard_size] | 290 shard_test_list = self.tests[index * shard_size : (index + 1) * shard_size] |
| 282 test_filter = ':'.join(shard_test_list) | 291 test_filter = ':'.join(shard_test_list) |
| 283 return SingleTestRunner(device, self.test_suite, | 292 return SingleTestRunner(device, self.test_suite, |
| 284 test_filter, self.test_arguments, self.timeout, | 293 test_filter, self.test_arguments, self.timeout, |
| 285 self.rebaseline, self.performance_test, | 294 self.rebaseline, self.performance_test, |
| 286 self.cleanup_test_files, self.tool, index) | 295 self.cleanup_test_files, self.tool, index) |
| 287 | 296 |
| 288 def OnTestsCompleted(self, test_runners, test_results): | 297 def OnTestsCompleted(self, test_runners, test_results): |
| 289 """Notifies that we completed the tests.""" | 298 """Notifies that we completed the tests.""" |
| 290 test_results.LogFull() | 299 test_results.LogFull('Unit test', os.path.basename(self.test_suite)) |
| 291 if self.annotate: | 300 if self.annotate: |
| 292 PrintAnnotationForTestResults(test_results) | 301 PrintAnnotationForTestResults(test_results) |
| 293 if test_results.failed and self.rebaseline: | 302 if test_results.failed and self.rebaseline: |
| 294 test_runners[0].UpdateFilter(test_results.failed) | 303 test_runners[0].UpdateFilter(test_results.failed) |
| 295 | 304 |
| 296 | 305 |
| 297 | 306 |
| 298 def _RunATestSuite(options): | 307 def _RunATestSuite(options): |
| 299 """Run a single test suite. | 308 """Run a single test suite. |
| 300 | 309 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 326 attached_devices = [options.test_device] | 335 attached_devices = [options.test_device] |
| 327 else: | 336 else: |
| 328 attached_devices = android_commands.GetAttachedDevices() | 337 attached_devices = android_commands.GetAttachedDevices() |
| 329 | 338 |
| 330 if not attached_devices: | 339 if not attached_devices: |
| 331 logging.critical('A device must be attached and online.') | 340 logging.critical('A device must be attached and online.') |
| 332 if options.annotate: | 341 if options.annotate: |
| 333 print '@@@STEP_FAILURE@@@' | 342 print '@@@STEP_FAILURE@@@' |
| 334 return 1 | 343 return 1 |
| 335 | 344 |
| 345 # Reset the test port allocation. It's important to do it before starting |
| 346 # to dispatch any tests. |
| 347 if not ports.ResetTestServerPortAllocation(): |
| 348 raise Exception('Failed to reset test server port.') |
| 349 |
| 336 if (len(attached_devices) > 1 and options.test_suite and | 350 if (len(attached_devices) > 1 and options.test_suite and |
| 337 not options.gtest_filter and not options.performance_test): | 351 not options.gtest_filter and not options.performance_test): |
| 338 sharder = TestSharder(attached_devices, options.test_suite, | 352 sharder = TestSharder(attached_devices, options.test_suite, |
| 339 options.gtest_filter, options.test_arguments, | 353 options.gtest_filter, options.test_arguments, |
| 340 options.timeout, options.rebaseline, | 354 options.timeout, options.rebaseline, |
| 341 options.performance_test, | 355 options.performance_test, |
| 342 options.cleanup_test_files, options.tool, | 356 options.cleanup_test_files, options.tool, |
| 343 options.annotate) | 357 options.annotate) |
| 344 test_results = sharder.RunShardedTests() | 358 test_results = sharder.RunShardedTests() |
| 345 else: | 359 else: |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 403 | 417 |
| 404 def ListTestSuites(): | 418 def ListTestSuites(): |
| 405 """Display a list of available test suites | 419 """Display a list of available test suites |
| 406 """ | 420 """ |
| 407 print 'Available test suites are:' | 421 print 'Available test suites are:' |
| 408 for test_suite in _TEST_SUITES: | 422 for test_suite in _TEST_SUITES: |
| 409 print test_suite | 423 print test_suite |
| 410 | 424 |
| 411 | 425 |
| 412 def main(argv): | 426 def main(argv): |
| 413 option_parser = run_tests_helper.CreateTestRunnerOptionParser(None, | 427 option_parser = test_options_parser.CreateTestRunnerOptionParser(None, |
| 414 default_timeout=0) | 428 default_timeout=0) |
| 415 option_parser.add_option('-s', '--suite', dest='test_suite', | 429 option_parser.add_option('-s', '--suite', dest='test_suite', |
| 416 help='Executable name of the test suite to run ' | 430 help='Executable name of the test suite to run ' |
| 417 '(use -s help to list them)') | 431 '(use -s help to list them)') |
| 418 option_parser.add_option('-d', '--device', dest='test_device', | 432 option_parser.add_option('-d', '--device', dest='test_device', |
| 419 help='Target device the test suite to run ') | 433 help='Target device the test suite to run ') |
| 420 option_parser.add_option('-r', dest='rebaseline', | 434 option_parser.add_option('-r', dest='rebaseline', |
| 421 help='Rebaseline and update *testsuite_disabled', | 435 help='Rebaseline and update *testsuite_disabled', |
| 422 action='store_true', | 436 action='store_true', |
| 423 default=False) | 437 default=False) |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 472 # from all suites, but the buildbot associates the exit status only with the | 486 # from all suites, but the buildbot associates the exit status only with the |
| 473 # most recent step). | 487 # most recent step). |
| 474 if options.annotate: | 488 if options.annotate: |
| 475 return 0 | 489 return 0 |
| 476 else: | 490 else: |
| 477 return failed_tests_count | 491 return failed_tests_count |
| 478 | 492 |
| 479 | 493 |
| 480 if __name__ == '__main__': | 494 if __name__ == '__main__': |
| 481 sys.exit(main(sys.argv)) | 495 sys.exit(main(sys.argv)) |
| OLD | NEW |