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

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: 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 threading
frankf 2013/03/01 20:32:07 where's this used?
craigdh 2013/03/01 21:45:11 Done.
12 import time 13 import time
13 14
14 from pylib import android_commands 15 from pylib import android_commands
15 from pylib import cmd_helper 16 from pylib import cmd_helper
16 from pylib import constants 17 from pylib import constants
17 from pylib import forwarder 18 from pylib import forwarder
18 from pylib import json_perf_parser 19 from pylib import json_perf_parser
19 from pylib import perf_tests_helper 20 from pylib import perf_tests_helper
20 from pylib import valgrind_tools 21 from pylib import valgrind_tools
21 from pylib.base import base_test_runner 22 from pylib.base import base_test_runner
22 from pylib.base import base_test_sharder
23 from pylib.base import test_result 23 from pylib.base import test_result
24 24
25 import apk_info 25 import apk_info
26 26
27 27
28 _PERF_TEST_ANNOTATION = 'PerfTest' 28 _PERF_TEST_ANNOTATION = 'PerfTest'
29 29
30 30
31 class TestRunner(base_test_runner.BaseTestRunner): 31 class TestRunner(base_test_runner.BaseTestRunner):
32 """Responsible for running a series of tests connected to a single device.""" 32 """Responsible for running a series of tests connected to a single device."""
33 33
34 _DEVICE_DATA_DIR = 'chrome/test/data' 34 _DEVICE_DATA_DIR = 'chrome/test/data'
35 _EMMA_JAR = os.path.join(os.environ.get('ANDROID_BUILD_TOP', ''), 35 _EMMA_JAR = os.path.join(os.environ.get('ANDROID_BUILD_TOP', ''),
36 'external/emma/lib/emma.jar') 36 'external/emma/lib/emma.jar')
37 _COVERAGE_MERGED_FILENAME = 'unittest_coverage.es' 37 _COVERAGE_MERGED_FILENAME = 'unittest_coverage.es'
38 _COVERAGE_WEB_ROOT_DIR = os.environ.get('EMMA_WEB_ROOTDIR') 38 _COVERAGE_WEB_ROOT_DIR = os.environ.get('EMMA_WEB_ROOTDIR')
39 _COVERAGE_FILENAME = 'coverage.ec' 39 _COVERAGE_FILENAME = 'coverage.ec'
40 _COVERAGE_RESULT_PATH = ('/data/data/com.google.android.apps.chrome/files/' + 40 _COVERAGE_RESULT_PATH = ('/data/data/com.google.android.apps.chrome/files/' +
41 _COVERAGE_FILENAME) 41 _COVERAGE_FILENAME)
42 _COVERAGE_META_INFO_PATH = os.path.join(os.environ.get('ANDROID_BUILD_TOP', 42 _COVERAGE_META_INFO_PATH = os.path.join(os.environ.get('ANDROID_BUILD_TOP',
43 ''), 43 ''),
44 'out/target/common/obj/APPS', 44 'out/target/common/obj/APPS',
45 'Chrome_intermediates/coverage.em') 45 'Chrome_intermediates/coverage.em')
46 _HOSTMACHINE_PERF_OUTPUT_FILE = '/tmp/chrome-profile' 46 _HOSTMACHINE_PERF_OUTPUT_FILE = '/tmp/chrome-profile'
47 _DEVICE_PERF_OUTPUT_SEARCH_PREFIX = (constants.DEVICE_PERF_OUTPUT_DIR + 47 _DEVICE_PERF_OUTPUT_SEARCH_PREFIX = (constants.DEVICE_PERF_OUTPUT_DIR +
48 '/chrome-profile*') 48 '/chrome-profile*')
49 _DEVICE_HAS_TEST_FILES = {} 49 _DEVICE_HAS_TEST_FILES = {}
50 50
51 def __init__(self, options, device, tests_iter, coverage, shard_index, apks, 51 def __init__(self, options, device, shard_index, coverage, apks,
52 ports_to_forward): 52 ports_to_forward):
53 """Create a new TestRunner. 53 """Create a new TestRunner.
54 54
55 Args: 55 Args:
56 options: An options object with the following required attributes: 56 options: An options object with the following required attributes:
57 - build_type: 'Release' or 'Debug'. 57 - build_type: 'Release' or 'Debug'.
58 - install_apk: Re-installs the apk if opted. 58 - install_apk: Re-installs the apk if opted.
59 - save_perf_json: Whether or not to save the JSON file from UI perf 59 - save_perf_json: Whether or not to save the JSON file from UI perf
60 tests. 60 tests.
61 - screenshot_failures: Take a screenshot for a test failure 61 - screenshot_failures: Take a screenshot for a test failure
62 - tool: Name of the Valgrind tool. 62 - tool: Name of the Valgrind tool.
63 - wait_for_debugger: blocks until the debugger is connected. 63 - wait_for_debugger: blocks until the debugger is connected.
64 - disable_assertions: Whether to disable java assertions on the device. 64 - disable_assertions: Whether to disable java assertions on the device.
65 device: Attached android device. 65 device: Attached android device.
66 tests_iter: A list of tests to be run. 66 shard_index: Shard index.
67 coverage: Collects coverage information if opted. 67 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 68 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. 69 should be the tests apk, the rests could be the apks used in test.
72 The default is ChromeTest.apk. 70 The default is ChromeTest.apk.
73 ports_to_forward: A list of port numbers for which to set up forwarders. 71 ports_to_forward: A list of port numbers for which to set up forwarders.
74 Can be optionally requested by a test case. 72 Can be optionally requested by a test case.
75 Raises: 73 Raises:
76 Exception: if coverage metadata is not available. 74 Exception: if coverage metadata is not available.
77 """ 75 """
78 super(TestRunner, self).__init__( 76 super(TestRunner, self).__init__(device, options.tool, options.build_type)
79 device, options.tool, shard_index, options.build_type) 77 self._lighttp_port = constants.LIGHTTPD_RANDOM_PORT_FIRST + shard_index
80 78
81 if not apks: 79 if not apks:
82 apks = [apk_info.ApkInfo(options.test_apk_path, 80 apks = [apk_info.ApkInfo(options.test_apk_path,
83 options.test_apk_jar_path)] 81 options.test_apk_jar_path)]
84 82
85 self.build_type = options.build_type 83 self.build_type = options.build_type
86 self.install_apk = options.install_apk 84 self.install_apk = options.install_apk
87 self.test_data = options.test_data 85 self.test_data = options.test_data
88 self.save_perf_json = options.save_perf_json 86 self.save_perf_json = options.save_perf_json
89 self.screenshot_failures = options.screenshot_failures 87 self.screenshot_failures = options.screenshot_failures
90 self.wait_for_debugger = options.wait_for_debugger 88 self.wait_for_debugger = options.wait_for_debugger
91 self.disable_assertions = options.disable_assertions 89 self.disable_assertions = options.disable_assertions
92 90
93 self.tests_iter = tests_iter
94 self.coverage = coverage 91 self.coverage = coverage
95 self.apks = apks 92 self.apks = apks
96 self.test_apk = apks[0] 93 self.test_apk = apks[0]
97 self.instrumentation_class_path = self.test_apk.GetPackageName() 94 self.instrumentation_class_path = self.test_apk.GetPackageName()
98 self.ports_to_forward = ports_to_forward 95 self.ports_to_forward = ports_to_forward
99 96
100 self.test_results = test_result.TestResults()
101 self.forwarder = None 97 self.forwarder = None
102 98
103 if self.coverage: 99 if self.coverage:
104 if os.path.exists(TestRunner._COVERAGE_MERGED_FILENAME): 100 if os.path.exists(TestRunner._COVERAGE_MERGED_FILENAME):
105 os.remove(TestRunner._COVERAGE_MERGED_FILENAME) 101 os.remove(TestRunner._COVERAGE_MERGED_FILENAME)
106 if not os.path.exists(TestRunner._COVERAGE_META_INFO_PATH): 102 if not os.path.exists(TestRunner._COVERAGE_META_INFO_PATH):
107 raise Exception('FATAL ERROR in ' + sys.argv[0] + 103 raise Exception('FATAL ERROR in ' + sys.argv[0] +
108 ' : Coverage meta info [' + 104 ' : Coverage meta info [' +
109 TestRunner._COVERAGE_META_INFO_PATH + 105 TestRunner._COVERAGE_META_INFO_PATH +
110 '] does not exist.') 106 '] does not exist.')
111 if (not TestRunner._COVERAGE_WEB_ROOT_DIR or 107 if (not TestRunner._COVERAGE_WEB_ROOT_DIR or
112 not os.path.exists(TestRunner._COVERAGE_WEB_ROOT_DIR)): 108 not os.path.exists(TestRunner._COVERAGE_WEB_ROOT_DIR)):
113 raise Exception('FATAL ERROR in ' + sys.argv[0] + 109 raise Exception('FATAL ERROR in ' + sys.argv[0] +
114 ' : Path specified in $EMMA_WEB_ROOTDIR [' + 110 ' : Path specified in $EMMA_WEB_ROOTDIR [' +
115 TestRunner._COVERAGE_WEB_ROOT_DIR + 111 TestRunner._COVERAGE_WEB_ROOT_DIR +
116 '] does not exist.') 112 '] does not exist.')
117 113
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): 114 def CopyTestFilesOnce(self):
127 """Pushes the test data files to the device. Installs the apk if opted.""" 115 """Pushes the test data files to the device. Installs the apk if opted."""
128 if TestRunner._DEVICE_HAS_TEST_FILES.get(self.device, False): 116 if TestRunner._DEVICE_HAS_TEST_FILES.get(self.device, False):
129 logging.warning('Already copied test files to device %s, skipping.', 117 logging.warning('Already copied test files to device %s, skipping.',
130 self.device) 118 self.device)
131 return 119 return
132 for dest_host_pair in self.test_data: 120 for dest_host_pair in self.test_data:
133 dst_src = dest_host_pair.split(':',1) 121 dst_src = dest_host_pair.split(':',1)
134 dst_layer = dst_src[0] 122 dst_layer = dst_src[0]
135 host_src = dst_src[1] 123 host_src = dst_src[1]
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 ret['debug'] = 'true' 194 ret['debug'] = 'true'
207 return ret 195 return ret
208 196
209 def _TakeScreenshot(self, test): 197 def _TakeScreenshot(self, test):
210 """Takes a screenshot from the device.""" 198 """Takes a screenshot from the device."""
211 screenshot_name = os.path.join(constants.SCREENSHOTS_DIR, test + '.png') 199 screenshot_name = os.path.join(constants.SCREENSHOTS_DIR, test + '.png')
212 logging.info('Taking screenshot named %s', screenshot_name) 200 logging.info('Taking screenshot named %s', screenshot_name)
213 self.adb.TakeScreenshot(screenshot_name) 201 self.adb.TakeScreenshot(screenshot_name)
214 202
215 def SetUp(self): 203 def SetUp(self):
204 super(TestRunner, self).SetUp()
frankf 2013/03/01 20:32:07 why is this before the docstring?
craigdh 2013/03/01 21:45:11 No idea...
216 """Sets up the test harness and device before all tests are run.""" 205 """Sets up the test harness and device before all tests are run."""
217 super(TestRunner, self).SetUp()
218 if not self.adb.IsRootEnabled(): 206 if not self.adb.IsRootEnabled():
219 logging.warning('Unable to enable java asserts for %s, non rooted device', 207 logging.warning('Unable to enable java asserts for %s, non rooted device',
220 self.device) 208 self.device)
221 else: 209 else:
222 if self.adb.SetJavaAssertsEnabled(enable=not self.disable_assertions): 210 if self.adb.SetJavaAssertsEnabled(enable=not self.disable_assertions):
223 self.adb.Reboot(full_reboot=False) 211 self.adb.Reboot(full_reboot=False)
224 212
225 # We give different default value to launch HTTP server based on shard index 213 # 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 214 # because it may have race condition when multiple processes are trying to
227 # launch lighttpd with same port at same time. 215 # launch lighttpd with same port at same time.
228 http_server_ports = self.LaunchTestHttpServer( 216 http_server_ports = self.LaunchTestHttpServer(
229 os.path.join(constants.CHROME_DIR), 217 os.path.join(constants.CHROME_DIR), self._lighttp_port)
230 (constants.LIGHTTPD_RANDOM_PORT_FIRST + self.shard_index))
231 if self.ports_to_forward: 218 if self.ports_to_forward:
232 port_pairs = [(port, port) for port in self.ports_to_forward] 219 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 220 # We need to remember which ports the HTTP server is using, since the
234 # forwarder will stomp on them otherwise. 221 # forwarder will stomp on them otherwise.
235 port_pairs.append(http_server_ports) 222 port_pairs.append(http_server_ports)
236 self.forwarder = forwarder.Forwarder(self.adb, self.build_type) 223 self.forwarder = forwarder.Forwarder(self.adb, self.build_type)
237 self.forwarder.Run(port_pairs, self.tool, '127.0.0.1') 224 self.forwarder.Run(port_pairs, self.tool, '127.0.0.1')
238 self.CopyTestFilesOnce() 225 self.CopyTestFilesOnce()
239 self.flags.AddFlags(['--enable-test-intents']) 226 self.flags.AddFlags(['--enable-test-intents'])
240 227
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
383 if 'Manual' in annotations: 370 if 'Manual' in annotations:
384 return 600 * 60 371 return 600 * 60
385 if 'External' in annotations: 372 if 'External' in annotations:
386 return 10 * 60 373 return 10 * 60
387 if 'LargeTest' in annotations or _PERF_TEST_ANNOTATION in annotations: 374 if 'LargeTest' in annotations or _PERF_TEST_ANNOTATION in annotations:
388 return 5 * 60 375 return 5 * 60
389 if 'MediumTest' in annotations: 376 if 'MediumTest' in annotations:
390 return 3 * 60 377 return 3 * 60
391 return 1 * 60 378 return 1 * 60
392 379
393 def RunTests(self): 380 def RunTest(self, test):
394 """Runs the tests, generating the coverage if needed. 381 """Runs the tests, generating the coverage if needed.
frankf 2013/03/01 20:32:07 tests -> test
craigdh 2013/03/01 21:45:11 Done.
395 382
396 Returns: 383 Returns:
397 A test_result.TestResults object. 384 A test_result.TestResults object.
398 """ 385 """
399 instrumentation_path = (self.instrumentation_class_path + 386 instrumentation_path = (self.instrumentation_class_path +
400 '/android.test.InstrumentationTestRunner') 387 '/android.test.InstrumentationTestRunner')
401 instrumentation_args = self._GetInstrumentationArgs() 388 instrumentation_args = self._GetInstrumentationArgs()
402 for test in self._GetTestsIter(): 389 raw_result = None
390 start_date_ms = None
391 test_results = test_result.TestResults()
392 try:
393 self.TestSetup(test)
394 start_date_ms = int(time.time()) * 1000
395 args_with_filter = dict(instrumentation_args)
396 args_with_filter['class'] = test
397 # |raw_results| is a list that should contain
398 # a single TestResult object.
399 logging.warn(args_with_filter)
400 (raw_results, _) = self.adb.Adb().StartInstrumentation(
401 instrumentation_path=instrumentation_path,
402 instrumentation_args=args_with_filter,
403 timeout_time=(self._GetIndividualTestTimeoutSecs(test) *
404 self._GetIndividualTestTimeoutScale(test) *
405 self.tool.GetTimeoutScale()))
406 duration_ms = int(time.time()) * 1000 - start_date_ms
407 assert len(raw_results) == 1
408 raw_result = raw_results[0]
409 status_code = raw_result.GetStatusCode()
410 if status_code:
411 log = raw_result.GetFailureReason()
412 if not log:
413 log = 'No information.'
414 if self.screenshot_failures or log.find('INJECT_EVENTS perm') >= 0:
415 self._TakeScreenshot(test)
416 test_results.failed = [test_result.SingleTestResult(
417 test, start_date_ms, duration_ms, log)]
418 else:
419 test_results.ok = [test_result.SingleTestResult(test, start_date_ms,
420 duration_ms)]
421 # Catch exceptions thrown by StartInstrumentation().
422 # See ../../third_party/android/testrunner/adb_interface.py
423 except (android_commands.errors.WaitForResponseTimedOutError,
424 android_commands.errors.DeviceUnresponsiveError,
425 android_commands.errors.InstrumentationError), e:
426 if start_date_ms:
427 duration_ms = int(time.time()) * 1000 - start_date_ms
428 else:
429 start_date_ms = int(time.time()) * 1000
430 duration_ms = 0
431 message = str(e)
432 if not message:
433 message = 'No information.'
434 test_results.crashed = [test_result.SingleTestResult(
435 test, start_date_ms, duration_ms, message)]
403 raw_result = None 436 raw_result = None
404 start_date_ms = None 437 self.TestTeardown(test, raw_result)
405 try: 438 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

Powered by Google App Engine
This is Rietveld 408576698