Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(108)

Side by Side Diff: build/android/pylib/instrumentation/test_runner.py

Issue 12378048: [Android] Switch instrumentation tests to the new shard/runner paradigm. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add retries to the end of the queue instead of the beginning Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Runs the Java tests. See more information on run_instrumentation_tests.py.""" 5 """Runs the Java tests. See more information on run_instrumentation_tests.py."""
6 6
7 import logging 7 import logging
8 import os 8 import os
9 import re 9 import re
10 import shutil 10 import shutil
11 import sys 11 import sys
12 import time 12 import time
13 13
14 from pylib import android_commands 14 from pylib import android_commands
15 from pylib import cmd_helper 15 from pylib import cmd_helper
16 from pylib import constants 16 from pylib import constants
17 from pylib import forwarder 17 from pylib import forwarder
18 from pylib import json_perf_parser 18 from pylib import json_perf_parser
19 from pylib import perf_tests_helper 19 from pylib import perf_tests_helper
20 from pylib import valgrind_tools 20 from pylib import valgrind_tools
21 from pylib.base import base_test_runner 21 from pylib.base import base_test_runner
22 from pylib.base import base_test_sharder
23 from pylib.base import test_result 22 from pylib.base import test_result
24 23
25 import apk_info 24 import apk_info
26 25
27 26
28 _PERF_TEST_ANNOTATION = 'PerfTest' 27 _PERF_TEST_ANNOTATION = 'PerfTest'
29 28
30 29
31 class TestRunner(base_test_runner.BaseTestRunner): 30 class TestRunner(base_test_runner.BaseTestRunner):
32 """Responsible for running a series of tests connected to a single device.""" 31 """Responsible for running a series of tests connected to a single device."""
33 32
34 _DEVICE_DATA_DIR = 'chrome/test/data' 33 _DEVICE_DATA_DIR = 'chrome/test/data'
35 _EMMA_JAR = os.path.join(os.environ.get('ANDROID_BUILD_TOP', ''), 34 _EMMA_JAR = os.path.join(os.environ.get('ANDROID_BUILD_TOP', ''),
36 'external/emma/lib/emma.jar') 35 'external/emma/lib/emma.jar')
37 _COVERAGE_MERGED_FILENAME = 'unittest_coverage.es' 36 _COVERAGE_MERGED_FILENAME = 'unittest_coverage.es'
38 _COVERAGE_WEB_ROOT_DIR = os.environ.get('EMMA_WEB_ROOTDIR') 37 _COVERAGE_WEB_ROOT_DIR = os.environ.get('EMMA_WEB_ROOTDIR')
39 _COVERAGE_FILENAME = 'coverage.ec' 38 _COVERAGE_FILENAME = 'coverage.ec'
40 _COVERAGE_RESULT_PATH = ('/data/data/com.google.android.apps.chrome/files/' + 39 _COVERAGE_RESULT_PATH = ('/data/data/com.google.android.apps.chrome/files/' +
41 _COVERAGE_FILENAME) 40 _COVERAGE_FILENAME)
42 _COVERAGE_META_INFO_PATH = os.path.join(os.environ.get('ANDROID_BUILD_TOP', 41 _COVERAGE_META_INFO_PATH = os.path.join(os.environ.get('ANDROID_BUILD_TOP',
43 ''), 42 ''),
44 'out/target/common/obj/APPS', 43 'out/target/common/obj/APPS',
45 'Chrome_intermediates/coverage.em') 44 'Chrome_intermediates/coverage.em')
46 _HOSTMACHINE_PERF_OUTPUT_FILE = '/tmp/chrome-profile' 45 _HOSTMACHINE_PERF_OUTPUT_FILE = '/tmp/chrome-profile'
47 _DEVICE_PERF_OUTPUT_SEARCH_PREFIX = (constants.DEVICE_PERF_OUTPUT_DIR + 46 _DEVICE_PERF_OUTPUT_SEARCH_PREFIX = (constants.DEVICE_PERF_OUTPUT_DIR +
48 '/chrome-profile*') 47 '/chrome-profile*')
49 _DEVICE_HAS_TEST_FILES = {} 48 _DEVICE_HAS_TEST_FILES = {}
50 49
51 def __init__(self, options, device, tests_iter, coverage, shard_index, apks, 50 def __init__(self, options, device, shard_index, coverage, apks,
52 ports_to_forward): 51 ports_to_forward):
53 """Create a new TestRunner. 52 """Create a new TestRunner.
54 53
55 Args: 54 Args:
56 options: An options object with the following required attributes: 55 options: An options object with the following required attributes:
57 - build_type: 'Release' or 'Debug'. 56 - build_type: 'Release' or 'Debug'.
58 - install_apk: Re-installs the apk if opted. 57 - install_apk: Re-installs the apk if opted.
59 - save_perf_json: Whether or not to save the JSON file from UI perf 58 - save_perf_json: Whether or not to save the JSON file from UI perf
60 tests. 59 tests.
61 - screenshot_failures: Take a screenshot for a test failure 60 - screenshot_failures: Take a screenshot for a test failure
62 - tool: Name of the Valgrind tool. 61 - tool: Name of the Valgrind tool.
63 - wait_for_debugger: blocks until the debugger is connected. 62 - wait_for_debugger: blocks until the debugger is connected.
64 - disable_assertions: Whether to disable java assertions on the device. 63 - disable_assertions: Whether to disable java assertions on the device.
65 device: Attached android device. 64 device: Attached android device.
66 tests_iter: A list of tests to be run. 65 shard_index: Shard index.
67 coverage: Collects coverage information if opted. 66 coverage: Collects coverage information if opted.
68 shard_index: shard # for this TestRunner, used to create unique port
69 numbers.
70 apks: A list of ApkInfo objects need to be installed. The first element 67 apks: A list of ApkInfo objects need to be installed. The first element
71 should be the tests apk, the rests could be the apks used in test. 68 should be the tests apk, the rests could be the apks used in test.
72 The default is ChromeTest.apk. 69 The default is ChromeTest.apk.
73 ports_to_forward: A list of port numbers for which to set up forwarders. 70 ports_to_forward: A list of port numbers for which to set up forwarders.
74 Can be optionally requested by a test case. 71 Can be optionally requested by a test case.
75 Raises: 72 Raises:
76 Exception: if coverage metadata is not available. 73 Exception: if coverage metadata is not available.
77 """ 74 """
78 super(TestRunner, self).__init__( 75 super(TestRunner, self).__init__(device, options.tool, options.build_type)
79 device, options.tool, shard_index, options.build_type) 76 self._lighttp_port = constants.LIGHTTPD_RANDOM_PORT_FIRST + shard_index
80 77
81 if not apks: 78 if not apks:
82 apks = [apk_info.ApkInfo(options.test_apk_path, 79 apks = [apk_info.ApkInfo(options.test_apk_path,
83 options.test_apk_jar_path)] 80 options.test_apk_jar_path)]
84 81
85 self.build_type = options.build_type 82 self.build_type = options.build_type
86 self.install_apk = options.install_apk 83 self.install_apk = options.install_apk
87 self.test_data = options.test_data 84 self.test_data = options.test_data
88 self.save_perf_json = options.save_perf_json 85 self.save_perf_json = options.save_perf_json
89 self.screenshot_failures = options.screenshot_failures 86 self.screenshot_failures = options.screenshot_failures
90 self.wait_for_debugger = options.wait_for_debugger 87 self.wait_for_debugger = options.wait_for_debugger
91 self.disable_assertions = options.disable_assertions 88 self.disable_assertions = options.disable_assertions
92 89
93 self.tests_iter = tests_iter
94 self.coverage = coverage 90 self.coverage = coverage
95 self.apks = apks 91 self.apks = apks
96 self.test_apk = apks[0] 92 self.test_apk = apks[0]
97 self.instrumentation_class_path = self.test_apk.GetPackageName() 93 self.instrumentation_class_path = self.test_apk.GetPackageName()
98 self.ports_to_forward = ports_to_forward 94 self.ports_to_forward = ports_to_forward
99 95
100 self.test_results = test_result.TestResults()
101 self.forwarder = None 96 self.forwarder = None
102 97
103 if self.coverage: 98 if self.coverage:
104 if os.path.exists(TestRunner._COVERAGE_MERGED_FILENAME): 99 if os.path.exists(TestRunner._COVERAGE_MERGED_FILENAME):
105 os.remove(TestRunner._COVERAGE_MERGED_FILENAME) 100 os.remove(TestRunner._COVERAGE_MERGED_FILENAME)
106 if not os.path.exists(TestRunner._COVERAGE_META_INFO_PATH): 101 if not os.path.exists(TestRunner._COVERAGE_META_INFO_PATH):
107 raise Exception('FATAL ERROR in ' + sys.argv[0] + 102 raise Exception('FATAL ERROR in ' + sys.argv[0] +
108 ' : Coverage meta info [' + 103 ' : Coverage meta info [' +
109 TestRunner._COVERAGE_META_INFO_PATH + 104 TestRunner._COVERAGE_META_INFO_PATH +
110 '] does not exist.') 105 '] does not exist.')
111 if (not TestRunner._COVERAGE_WEB_ROOT_DIR or 106 if (not TestRunner._COVERAGE_WEB_ROOT_DIR or
112 not os.path.exists(TestRunner._COVERAGE_WEB_ROOT_DIR)): 107 not os.path.exists(TestRunner._COVERAGE_WEB_ROOT_DIR)):
113 raise Exception('FATAL ERROR in ' + sys.argv[0] + 108 raise Exception('FATAL ERROR in ' + sys.argv[0] +
114 ' : Path specified in $EMMA_WEB_ROOTDIR [' + 109 ' : Path specified in $EMMA_WEB_ROOTDIR [' +
115 TestRunner._COVERAGE_WEB_ROOT_DIR + 110 TestRunner._COVERAGE_WEB_ROOT_DIR +
116 '] does not exist.') 111 '] does not exist.')
117 112
118 def _GetTestsIter(self):
119 if not self.tests_iter:
120 # multiprocessing.Queue can't be pickled across processes if we have it as
121 # a member set during constructor. Grab one here instead.
122 self.tests_iter = (base_test_sharder.BaseTestSharder.tests_container)
123 assert self.tests_iter
124 return self.tests_iter
125
126 def CopyTestFilesOnce(self): 113 def CopyTestFilesOnce(self):
127 """Pushes the test data files to the device. Installs the apk if opted.""" 114 """Pushes the test data files to the device. Installs the apk if opted."""
128 if TestRunner._DEVICE_HAS_TEST_FILES.get(self.device, False): 115 if TestRunner._DEVICE_HAS_TEST_FILES.get(self.device, False):
129 logging.warning('Already copied test files to device %s, skipping.', 116 logging.warning('Already copied test files to device %s, skipping.',
130 self.device) 117 self.device)
131 return 118 return
132 for dest_host_pair in self.test_data: 119 for dest_host_pair in self.test_data:
133 dst_src = dest_host_pair.split(':',1) 120 dst_src = dest_host_pair.split(':',1)
134 dst_layer = dst_src[0] 121 dst_layer = dst_src[0]
135 host_src = dst_src[1] 122 host_src = dst_src[1]
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 logging.warning('Unable to enable java asserts for %s, non rooted device', 206 logging.warning('Unable to enable java asserts for %s, non rooted device',
220 self.device) 207 self.device)
221 else: 208 else:
222 if self.adb.SetJavaAssertsEnabled(enable=not self.disable_assertions): 209 if self.adb.SetJavaAssertsEnabled(enable=not self.disable_assertions):
223 self.adb.Reboot(full_reboot=False) 210 self.adb.Reboot(full_reboot=False)
224 211
225 # We give different default value to launch HTTP server based on shard index 212 # We give different default value to launch HTTP server based on shard index
226 # because it may have race condition when multiple processes are trying to 213 # because it may have race condition when multiple processes are trying to
227 # launch lighttpd with same port at same time. 214 # launch lighttpd with same port at same time.
228 http_server_ports = self.LaunchTestHttpServer( 215 http_server_ports = self.LaunchTestHttpServer(
229 os.path.join(constants.CHROME_DIR), 216 os.path.join(constants.CHROME_DIR), self._lighttp_port)
230 (constants.LIGHTTPD_RANDOM_PORT_FIRST + self.shard_index))
231 if self.ports_to_forward: 217 if self.ports_to_forward:
232 port_pairs = [(port, port) for port in self.ports_to_forward] 218 port_pairs = [(port, port) for port in self.ports_to_forward]
233 # We need to remember which ports the HTTP server is using, since the 219 # We need to remember which ports the HTTP server is using, since the
234 # forwarder will stomp on them otherwise. 220 # forwarder will stomp on them otherwise.
235 port_pairs.append(http_server_ports) 221 port_pairs.append(http_server_ports)
236 self.forwarder = forwarder.Forwarder(self.adb, self.build_type) 222 self.forwarder = forwarder.Forwarder(self.adb, self.build_type)
237 self.forwarder.Run(port_pairs, self.tool, '127.0.0.1') 223 self.forwarder.Run(port_pairs, self.tool, '127.0.0.1')
238 self.CopyTestFilesOnce() 224 self.CopyTestFilesOnce()
239 self.flags.AddFlags(['--enable-test-intents']) 225 self.flags.AddFlags(['--enable-test-intents'])
240 226
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
383 if 'Manual' in annotations: 369 if 'Manual' in annotations:
384 return 600 * 60 370 return 600 * 60
385 if 'External' in annotations: 371 if 'External' in annotations:
386 return 10 * 60 372 return 10 * 60
387 if 'LargeTest' in annotations or _PERF_TEST_ANNOTATION in annotations: 373 if 'LargeTest' in annotations or _PERF_TEST_ANNOTATION in annotations:
388 return 5 * 60 374 return 5 * 60
389 if 'MediumTest' in annotations: 375 if 'MediumTest' in annotations:
390 return 3 * 60 376 return 3 * 60
391 return 1 * 60 377 return 1 * 60
392 378
393 def RunTests(self): 379 def RunTest(self, test):
394 """Runs the tests, generating the coverage if needed. 380 """Runs the test, generating the coverage if needed.
395 381
396 Returns: 382 Returns:
397 A test_result.TestResults object. 383 A test_result.TestResults object.
398 """ 384 """
399 instrumentation_path = (self.instrumentation_class_path + 385 instrumentation_path = (self.instrumentation_class_path +
400 '/android.test.InstrumentationTestRunner') 386 '/android.test.InstrumentationTestRunner')
401 instrumentation_args = self._GetInstrumentationArgs() 387 instrumentation_args = self._GetInstrumentationArgs()
402 for test in self._GetTestsIter(): 388 raw_result = None
389 start_date_ms = None
390 test_results = test_result.TestResults()
391 try:
392 self.TestSetup(test)
393 start_date_ms = int(time.time()) * 1000
394 args_with_filter = dict(instrumentation_args)
395 args_with_filter['class'] = test
396 # |raw_results| is a list that should contain
397 # a single TestResult object.
398 logging.warn(args_with_filter)
399 (raw_results, _) = self.adb.Adb().StartInstrumentation(
400 instrumentation_path=instrumentation_path,
401 instrumentation_args=args_with_filter,
402 timeout_time=(self._GetIndividualTestTimeoutSecs(test) *
403 self._GetIndividualTestTimeoutScale(test) *
404 self.tool.GetTimeoutScale()))
405 duration_ms = int(time.time()) * 1000 - start_date_ms
406 assert len(raw_results) == 1
407 raw_result = raw_results[0]
408 status_code = raw_result.GetStatusCode()
409 if status_code:
410 log = raw_result.GetFailureReason()
411 if not log:
412 log = 'No information.'
413 if self.screenshot_failures or log.find('INJECT_EVENTS perm') >= 0:
414 self._TakeScreenshot(test)
415 test_results.failed = [test_result.SingleTestResult(
416 test, start_date_ms, duration_ms, log)]
417 else:
418 test_results.ok = [test_result.SingleTestResult(test, start_date_ms,
419 duration_ms)]
420 # Catch exceptions thrown by StartInstrumentation().
421 # See ../../third_party/android/testrunner/adb_interface.py
422 except (android_commands.errors.WaitForResponseTimedOutError,
423 android_commands.errors.DeviceUnresponsiveError,
424 android_commands.errors.InstrumentationError), e:
425 if start_date_ms:
426 duration_ms = int(time.time()) * 1000 - start_date_ms
427 else:
428 start_date_ms = int(time.time()) * 1000
429 duration_ms = 0
430 message = str(e)
431 if not message:
432 message = 'No information.'
433 test_results.crashed = [test_result.SingleTestResult(
434 test, start_date_ms, duration_ms, message)]
403 raw_result = None 435 raw_result = None
404 start_date_ms = None 436 self.TestTeardown(test, raw_result)
405 try: 437 return (test_results, None if test_results.ok else test)
406 self.TestSetup(test)
407 start_date_ms = int(time.time()) * 1000
408 args_with_filter = dict(instrumentation_args)
409 args_with_filter['class'] = test
410 # |raw_results| is a list that should contain
411 # a single TestResult object.
412 logging.warn(args_with_filter)
413 (raw_results, _) = self.adb.Adb().StartInstrumentation(
414 instrumentation_path=instrumentation_path,
415 instrumentation_args=args_with_filter,
416 timeout_time=(self._GetIndividualTestTimeoutSecs(test) *
417 self._GetIndividualTestTimeoutScale(test) *
418 self.tool.GetTimeoutScale()))
419 duration_ms = int(time.time()) * 1000 - start_date_ms
420 assert len(raw_results) == 1
421 raw_result = raw_results[0]
422 status_code = raw_result.GetStatusCode()
423 if status_code:
424 log = raw_result.GetFailureReason()
425 if not log:
426 log = 'No information.'
427 if self.screenshot_failures or log.find('INJECT_EVENTS perm') >= 0:
428 self._TakeScreenshot(test)
429 self.test_results.failed += [test_result.SingleTestResult(
430 test, start_date_ms, duration_ms, log)]
431 else:
432 result = [test_result.SingleTestResult(test, start_date_ms,
433 duration_ms)]
434 self.test_results.ok += result
435 # Catch exceptions thrown by StartInstrumentation().
436 # See ../../third_party/android/testrunner/adb_interface.py
437 except (android_commands.errors.WaitForResponseTimedOutError,
438 android_commands.errors.DeviceUnresponsiveError,
439 android_commands.errors.InstrumentationError), e:
440 if start_date_ms:
441 duration_ms = int(time.time()) * 1000 - start_date_ms
442 else:
443 start_date_ms = int(time.time()) * 1000
444 duration_ms = 0
445 message = str(e)
446 if not message:
447 message = 'No information.'
448 self.test_results.crashed += [test_result.SingleTestResult(
449 test, start_date_ms, duration_ms, message)]
450 raw_result = None
451 self.TestTeardown(test, raw_result)
452 return self.test_results
OLDNEW
« no previous file with comments | « build/android/pylib/instrumentation/dispatch.py ('k') | build/android/pylib/instrumentation/test_sharder.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698