blob: 8858297a70f0b3765ca46e562a151c914d4166ad [file] [log] [blame]
jbudorick27e6905f2014-12-03 23:27:481# Copyright 2014 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
jbudorick5cea02e2016-09-23 17:06:165import HTMLParser
jbudorick27e6905f2014-12-03 23:27:486import logging
7import os
jbudorick8f495282014-12-15 22:23:298import re
jbudorick5ee45892015-06-10 18:46:229import tempfile
jbudorick5cea02e2016-09-23 17:06:1610import xml.etree.ElementTree
jbudorick27e6905f2014-12-03 23:27:4811
jbudorick061629442015-09-03 18:00:5712from devil.android import apk_helper
jbudorick27e6905f2014-12-03 23:27:4813from pylib import constants
jbudorickd28554a2016-01-11 16:22:5914from pylib.constants import host_paths
jbudorick8f495282014-12-15 22:23:2915from pylib.base import base_test_result
jbudorick27e6905f2014-12-03 23:27:4816from pylib.base import test_instance
agrievee41ae190d2016-04-25 14:12:5117from pylib.utils import isolator
jbudorick27e6905f2014-12-03 23:27:4818
jbudorickd28554a2016-01-11 16:22:5919with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
20 import unittest_util # pylint: disable=import-error
jbudorick27e6905f2014-12-03 23:27:4821
22
jbudorick08b097a932015-05-22 02:54:5723BROWSER_TEST_SUITES = [
24 'components_browsertests',
25 'content_browsertests',
26]
27
jbudorick566592ab2015-09-21 15:32:4728RUN_IN_SUB_THREAD_TEST_SUITES = ['net_unittests']
29
jbudorick08b097a932015-05-22 02:54:5730
jbudorick27e6905f2014-12-03 23:27:4831# Used for filtering large data deps at a finer grain than what's allowed in
32# isolate files since pushing deps to devices is expensive.
33# Wildcards are allowed.
34_DEPS_EXCLUSION_LIST = [
35 'chrome/test/data/extensions/api_test',
36 'chrome/test/data/extensions/secure_shell',
37 'chrome/test/data/firefox*',
38 'chrome/test/data/gpu',
39 'chrome/test/data/image_decoding',
40 'chrome/test/data/import',
41 'chrome/test/data/page_cycler',
42 'chrome/test/data/perf',
43 'chrome/test/data/pyauto_private',
44 'chrome/test/data/safari_import',
45 'chrome/test/data/scroll',
46 'chrome/test/data/third_party',
47 'third_party/hunspell_dictionaries/*.dic',
48 # crbug.com/258690
49 'webkit/data/bmp_decoder',
50 'webkit/data/ico_decoder',
51]
52
53
jbudorick590a3d72015-06-12 23:53:3154_EXTRA_NATIVE_TEST_ACTIVITY = (
55 'org.chromium.native_test.NativeTestInstrumentationTestRunner.'
56 'NativeTestActivity')
jbudorick566592ab2015-09-21 15:32:4757_EXTRA_RUN_IN_SUB_THREAD = (
ynovikov389d9e442016-05-27 02:34:5658 'org.chromium.native_test.NativeTest.RunInSubThread')
jbudorick6ef3cbd2015-09-30 09:04:1959EXTRA_SHARD_NANO_TIMEOUT = (
60 'org.chromium.native_test.NativeTestInstrumentationTestRunner.'
61 'ShardNanoTimeout')
jbudorick58b4d362015-09-08 16:44:5962_EXTRA_SHARD_SIZE_LIMIT = (
jbudorick590a3d72015-06-12 23:53:3163 'org.chromium.native_test.NativeTestInstrumentationTestRunner.'
64 'ShardSizeLimit')
65
jbudorick8f495282014-12-15 22:23:2966# TODO(jbudorick): Remove these once we're no longer parsing stdout to generate
67# results.
68_RE_TEST_STATUS = re.compile(
hzl09361b72016-03-09 00:00:1569 r'\[ +((?:RUN)|(?:FAILED)|(?:OK)|(?:CRASHED)) +\]'
70 r' ?([^ ]+)?(?: \((\d+) ms\))?$')
hzl09361b72016-03-09 00:00:1571# Crash detection constants.
72_RE_TEST_ERROR = re.compile(r'FAILURES!!! Tests run: \d+,'
73 r' Failures: \d+, Errors: 1')
74_RE_TEST_CURRENTLY_RUNNING = re.compile(r'\[ERROR:.*?\]'
75 r' Currently running: (.*)')
jbudorick8f495282014-12-15 22:23:2976
jbudorick8f495282014-12-15 22:23:2977def ParseGTestListTests(raw_list):
78 """Parses a raw test list as provided by --gtest_list_tests.
79
80 Args:
81 raw_list: The raw test listing with the following format:
82
83 IPCChannelTest.
84 SendMessageInChannelConnected
85 IPCSyncChannelTest.
86 Simple
87 DISABLED_SendWithTimeoutMixedOKAndTimeout
88
89 Returns:
90 A list of all tests. For the above raw listing:
91
92 [IPCChannelTest.SendMessageInChannelConnected, IPCSyncChannelTest.Simple,
93 IPCSyncChannelTest.DISABLED_SendWithTimeoutMixedOKAndTimeout]
94 """
95 ret = []
96 current = ''
97 for test in raw_list:
98 if not test:
99 continue
100 if test[0] != ' ':
101 test_case = test.split()[0]
102 if test_case.endswith('.'):
103 current = test_case
104 elif not 'YOU HAVE' in test:
105 test_name = test.split()[0]
106 ret += [current + test_name]
107 return ret
108
109
jbudorickf7461112016-05-27 04:30:49110def ParseGTestOutput(output):
111 """Parses raw gtest output and returns a list of results.
112
113 Args:
114 output: A list of output lines.
115 Returns:
116 A list of base_test_result.BaseTestResults.
117 """
John Budorickc4f40732016-07-12 18:13:59118 duration = 0
119 fallback_result_type = None
jbudorickf7461112016-05-27 04:30:49120 log = []
121 result_type = None
122 results = []
123 test_name = None
124
125 def handle_possibly_unknown_test():
126 if test_name is not None:
127 results.append(base_test_result.BaseTestResult(
John Budorickc4f40732016-07-12 18:13:59128 test_name,
129 fallback_result_type or base_test_result.ResultType.UNKNOWN,
130 duration, log=('\n'.join(log) if log else '')))
jbudorickf7461112016-05-27 04:30:49131
132 for l in output:
133 logging.info(l)
134 matcher = _RE_TEST_STATUS.match(l)
135 if matcher:
136 if matcher.group(1) == 'RUN':
137 handle_possibly_unknown_test()
John Budorickc4f40732016-07-12 18:13:59138 duration = 0
139 fallback_result_type = None
jbudorickf7461112016-05-27 04:30:49140 log = []
John Budorickc4f40732016-07-12 18:13:59141 result_type = None
jbudorickf7461112016-05-27 04:30:49142 elif matcher.group(1) == 'OK':
143 result_type = base_test_result.ResultType.PASS
144 elif matcher.group(1) == 'FAILED':
145 result_type = base_test_result.ResultType.FAIL
146 elif matcher.group(1) == 'CRASHED':
John Budorickc4f40732016-07-12 18:13:59147 fallback_result_type = base_test_result.ResultType.CRASH
jbudorickf7461112016-05-27 04:30:49148 # Be aware that test name and status might not appear on same line.
149 test_name = matcher.group(2) if matcher.group(2) else test_name
150 duration = int(matcher.group(3)) if matcher.group(3) else 0
151
152 else:
153 # Needs another matcher here to match crashes, like those of DCHECK.
154 matcher = _RE_TEST_CURRENTLY_RUNNING.match(l)
155 if matcher:
156 test_name = matcher.group(1)
157 result_type = base_test_result.ResultType.CRASH
158 duration = 0 # Don't know.
159
160 if log is not None:
161 log.append(l)
162
jbudorick244896c2016-05-31 23:35:06163 if result_type and test_name:
jbudorickf7461112016-05-27 04:30:49164 results.append(base_test_result.BaseTestResult(
165 test_name, result_type, duration,
166 log=('\n'.join(log) if log else '')))
jbudorickf7461112016-05-27 04:30:49167 test_name = None
168
169 handle_possibly_unknown_test()
170
171 return results
172
173
jbudorick5cea02e2016-09-23 17:06:16174def ParseGTestXML(xml_content):
175 """Parse gtest XML result."""
176 results = []
177
178 html = HTMLParser.HTMLParser()
179
180 # TODO(jbudorick): Unclear how this handles crashes.
181 testsuites = xml.etree.ElementTree.fromstring(xml_content)
182 for testsuite in testsuites:
183 suite_name = testsuite.attrib['name']
184 for testcase in testsuite:
185 case_name = testcase.attrib['name']
186 result_type = base_test_result.ResultType.PASS
187 log = []
188 for failure in testcase:
189 result_type = base_test_result.ResultType.FAIL
190 log.append(html.unescape(failure.attrib['message']))
191
192 results.append(base_test_result.BaseTestResult(
193 '%s.%s' % (suite_name, case_name),
194 result_type,
195 int(float(testcase.attrib['time']) * 1000),
196 log=('\n'.join(log) if log else '')))
197
198 return results
199
200
jbudorick27e6905f2014-12-03 23:27:48201class GtestTestInstance(test_instance.TestInstance):
202
jbudorick8f495282014-12-15 22:23:29203 def __init__(self, args, isolate_delegate, error_func):
jbudorick27e6905f2014-12-03 23:27:48204 super(GtestTestInstance, self).__init__()
rnephew5c499782014-12-12 19:08:55205 # TODO(jbudorick): Support multiple test suites.
jbudorick8f495282014-12-15 22:23:29206 if len(args.suite_name) > 1:
rnephew5c499782014-12-12 19:08:55207 raise ValueError('Platform mode currently supports only 1 gtest suite')
agrieve0f5e53e2016-02-04 03:47:37208 self._extract_test_list_from_filter = args.extract_test_list_from_filter
jbudorick24616eb2015-10-06 02:40:57209 self._shard_timeout = args.shard_timeout
agrieve0f5e53e2016-02-04 03:47:37210 self._suite = args.suite_name[0]
agrieve62ab00282016-04-05 02:03:45211 self._exe_dist_dir = None
jbudorick24616eb2015-10-06 02:40:57212
agrieve62ab00282016-04-05 02:03:45213 # GYP:
214 if args.executable_dist_dir:
215 self._exe_dist_dir = os.path.abspath(args.executable_dist_dir)
216 else:
217 # TODO(agrieve): Remove auto-detection once recipes pass flag explicitly.
218 exe_dist_dir = os.path.join(constants.GetOutDirectory(),
219 '%s__dist' % self._suite)
220
221 if os.path.exists(exe_dist_dir):
222 self._exe_dist_dir = exe_dist_dir
agrieve4931af8c2016-02-10 19:25:26223
224 incremental_part = ''
225 if args.test_apk_incremental_install_script:
226 incremental_part = '_incremental'
227
agrieve7c7f0802015-10-09 19:08:50228 apk_path = os.path.join(
jbudorick08b097a932015-05-22 02:54:57229 constants.GetOutDirectory(), '%s_apk' % self._suite,
agrieve1a02e582015-10-15 21:35:39230 '%s-debug%s.apk' % (self._suite, incremental_part))
agrieve4931af8c2016-02-10 19:25:26231 self._test_apk_incremental_install_script = (
232 args.test_apk_incremental_install_script)
agrieve7c7f0802015-10-09 19:08:50233 if not os.path.exists(apk_path):
234 self._apk_helper = None
jbudorick590a3d72015-06-12 23:53:31235 else:
agrieve7c7f0802015-10-09 19:08:50236 self._apk_helper = apk_helper.ApkHelper(apk_path)
jbudorick590a3d72015-06-12 23:53:31237 self._extras = {
agrieve7c7f0802015-10-09 19:08:50238 _EXTRA_NATIVE_TEST_ACTIVITY: self._apk_helper.GetActivityName(),
jbudorick590a3d72015-06-12 23:53:31239 }
jbudorick566592ab2015-09-21 15:32:47240 if self._suite in RUN_IN_SUB_THREAD_TEST_SUITES:
241 self._extras[_EXTRA_RUN_IN_SUB_THREAD] = 1
jbudorick590a3d72015-06-12 23:53:31242 if self._suite in BROWSER_TEST_SUITES:
243 self._extras[_EXTRA_SHARD_SIZE_LIMIT] = 1
jbudorick24616eb2015-10-06 02:40:57244 self._extras[EXTRA_SHARD_NANO_TIMEOUT] = int(1e9 * self._shard_timeout)
jbudorick8932b412016-05-19 17:52:23245 self._shard_timeout = 10 * self._shard_timeout
jbudorick590a3d72015-06-12 23:53:31246
agrieve62ab00282016-04-05 02:03:45247 if not self._apk_helper and not self._exe_dist_dir:
jbudorick8f495282014-12-15 22:23:29248 error_func('Could not find apk or executable for %s' % self._suite)
249
jbudorick27e6905f2014-12-03 23:27:48250 self._data_deps = []
jbudorick442a6932015-02-03 03:01:15251 if args.test_filter:
252 self._gtest_filter = args.test_filter
253 elif args.test_filter_file:
254 with open(args.test_filter_file, 'r') as f:
255 self._gtest_filter = ':'.join(l.strip() for l in f)
256 else:
257 self._gtest_filter = None
jbudorick590a3d72015-06-12 23:53:31258
agrievee41ae190d2016-04-25 14:12:51259 if (args.isolate_file_path and
260 not isolator.IsIsolateEmpty(args.isolate_file_path)):
jbudorick8f495282014-12-15 22:23:29261 self._isolate_abs_path = os.path.abspath(args.isolate_file_path)
jbudorick27e6905f2014-12-03 23:27:48262 self._isolate_delegate = isolate_delegate
263 self._isolated_abs_path = os.path.join(
jbudorick8f495282014-12-15 22:23:29264 constants.GetOutDirectory(), '%s.isolated' % self._suite)
jbudorick27e6905f2014-12-03 23:27:48265 else:
jbudorickeaf49622016-08-30 18:32:38266 logging.warning('%s isolate file provided. No data deps will be pushed.',
267 'Empty' if args.isolate_file_path else 'No')
jbudorick27e6905f2014-12-03 23:27:48268 self._isolate_delegate = None
jbudorick27e6905f2014-12-03 23:27:48269
jbudorick5ee45892015-06-10 18:46:22270 if args.app_data_files:
271 self._app_data_files = args.app_data_files
272 if args.app_data_file_dir:
273 self._app_data_file_dir = args.app_data_file_dir
274 else:
275 self._app_data_file_dir = tempfile.mkdtemp()
276 logging.critical('Saving app files to %s', self._app_data_file_dir)
277 else:
278 self._app_data_files = None
279 self._app_data_file_dir = None
280
jbudoricka58ea512015-10-20 14:36:58281 self._test_arguments = args.test_arguments
282
jbudorick5cea02e2016-09-23 17:06:16283 # TODO(jbudorick): Remove this once it's deployed.
284 self._enable_xml_result_parsing = args.enable_xml_result_parsing
285
jbudoricka58ea512015-10-20 14:36:58286 @property
287 def activity(self):
288 return self._apk_helper and self._apk_helper.GetActivityName()
289
290 @property
291 def apk(self):
292 return self._apk_helper and self._apk_helper.path
293
294 @property
295 def apk_helper(self):
296 return self._apk_helper
297
298 @property
299 def app_file_dir(self):
300 return self._app_data_file_dir
301
302 @property
303 def app_files(self):
304 return self._app_data_files
305
306 @property
jbudorick5cea02e2016-09-23 17:06:16307 def enable_xml_result_parsing(self):
308 return self._enable_xml_result_parsing
309
310 @property
agrieve62ab00282016-04-05 02:03:45311 def exe_dist_dir(self):
312 return self._exe_dist_dir
jbudoricka58ea512015-10-20 14:36:58313
314 @property
315 def extras(self):
316 return self._extras
317
agrieve065d7462015-10-13 23:23:36318 @property
319 def gtest_filter(self):
320 return self._gtest_filter
321
jbudoricka58ea512015-10-20 14:36:58322 @property
323 def package(self):
324 return self._apk_helper and self._apk_helper.GetPackageName()
325
326 @property
327 def permissions(self):
328 return self._apk_helper and self._apk_helper.GetPermissions()
329
330 @property
331 def runner(self):
332 return self._apk_helper and self._apk_helper.GetInstrumentationName()
333
334 @property
335 def shard_timeout(self):
336 return self._shard_timeout
337
338 @property
339 def suite(self):
340 return self._suite
341
342 @property
agrieve4931af8c2016-02-10 19:25:26343 def test_apk_incremental_install_script(self):
344 return self._test_apk_incremental_install_script
345
346 @property
jbudoricka58ea512015-10-20 14:36:58347 def test_arguments(self):
348 return self._test_arguments
349
agrieve0f5e53e2016-02-04 03:47:37350 @property
351 def extract_test_list_from_filter(self):
352 return self._extract_test_list_from_filter
353
jbudorick27e6905f2014-12-03 23:27:48354 #override
355 def TestType(self):
356 return 'gtest'
357
358 #override
359 def SetUp(self):
360 """Map data dependencies via isolate."""
361 if self._isolate_delegate:
362 self._isolate_delegate.Remap(
363 self._isolate_abs_path, self._isolated_abs_path)
364 self._isolate_delegate.PurgeExcluded(_DEPS_EXCLUSION_LIST)
365 self._isolate_delegate.MoveOutputDeps()
jbudorick8f495282014-12-15 22:23:29366 dest_dir = None
jbudorick5cf8aa172015-10-01 11:29:02367 self._data_deps.extend([
368 (self._isolate_delegate.isolate_deps_dir, dest_dir)])
jbudorick8f495282014-12-15 22:23:29369
jbudorick27e6905f2014-12-03 23:27:48370
371 def GetDataDependencies(self):
372 """Returns the test suite's data dependencies.
373
374 Returns:
375 A list of (host_path, device_path) tuples to push. If device_path is
376 None, the client is responsible for determining where to push the file.
377 """
378 return self._data_deps
379
380 def FilterTests(self, test_list, disabled_prefixes=None):
381 """Filters |test_list| based on prefixes and, if present, a filter string.
382
383 Args:
384 test_list: The list of tests to filter.
385 disabled_prefixes: A list of test prefixes to filter. Defaults to
386 DISABLED_, FLAKY_, FAILS_, PRE_, and MANUAL_
387 Returns:
388 A filtered list of tests to run.
389 """
390 gtest_filter_strings = [
391 self._GenerateDisabledFilterString(disabled_prefixes)]
392 if self._gtest_filter:
393 gtest_filter_strings.append(self._gtest_filter)
394
395 filtered_test_list = test_list
396 for gtest_filter_string in gtest_filter_strings:
jbudorick442a6932015-02-03 03:01:15397 logging.debug('Filtering tests using: %s', gtest_filter_string)
jbudorick27e6905f2014-12-03 23:27:48398 filtered_test_list = unittest_util.FilterTestNames(
399 filtered_test_list, gtest_filter_string)
jbudorick27e6905f2014-12-03 23:27:48400 return filtered_test_list
401
402 def _GenerateDisabledFilterString(self, disabled_prefixes):
403 disabled_filter_items = []
404
405 if disabled_prefixes is None:
406 disabled_prefixes = ['DISABLED_', 'FLAKY_', 'FAILS_', 'PRE_', 'MANUAL_']
407 disabled_filter_items += ['%s*' % dp for dp in disabled_prefixes]
jbudorick8f495282014-12-15 22:23:29408 disabled_filter_items += ['*.%s*' % dp for dp in disabled_prefixes]
jbudorick27e6905f2014-12-03 23:27:48409
410 disabled_tests_file_path = os.path.join(
jbudorickd28554a2016-01-11 16:22:59411 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'pylib', 'gtest',
jbudorick27e6905f2014-12-03 23:27:48412 'filter', '%s_disabled' % self._suite)
413 if disabled_tests_file_path and os.path.exists(disabled_tests_file_path):
414 with open(disabled_tests_file_path) as disabled_tests_file:
415 disabled_filter_items += [
416 '%s' % l for l in (line.strip() for line in disabled_tests_file)
417 if l and not l.startswith('#')]
418
419 return '*-%s' % ':'.join(disabled_filter_items)
420
jbudorick27e6905f2014-12-03 23:27:48421 #override
422 def TearDown(self):
423 """Clear the mappings created by SetUp."""
424 if self._isolate_delegate:
425 self._isolate_delegate.Clear()
426