Adding Peter Thatcher to the owners file.
[chromium-blink-merge.git] / build / android / pylib / gtest / gtest_test_instance.py
blob186b8fcf4c9739cbfa2e4541ef1c6e77d571fcfa
1 # 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.
5 import logging
6 import os
7 import re
8 import shutil
9 import sys
11 from pylib import constants
12 from pylib.base import base_test_result
13 from pylib.base import test_instance
15 sys.path.append(os.path.join(
16 constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib', 'common'))
17 import unittest_util
20 # Used for filtering large data deps at a finer grain than what's allowed in
21 # isolate files since pushing deps to devices is expensive.
22 # Wildcards are allowed.
23 _DEPS_EXCLUSION_LIST = [
24 'chrome/test/data/extensions/api_test',
25 'chrome/test/data/extensions/secure_shell',
26 'chrome/test/data/firefox*',
27 'chrome/test/data/gpu',
28 'chrome/test/data/image_decoding',
29 'chrome/test/data/import',
30 'chrome/test/data/page_cycler',
31 'chrome/test/data/perf',
32 'chrome/test/data/pyauto_private',
33 'chrome/test/data/safari_import',
34 'chrome/test/data/scroll',
35 'chrome/test/data/third_party',
36 'third_party/hunspell_dictionaries/*.dic',
37 # crbug.com/258690
38 'webkit/data/bmp_decoder',
39 'webkit/data/ico_decoder',
43 # TODO(jbudorick): Remove these once we're no longer parsing stdout to generate
44 # results.
45 _RE_TEST_STATUS = re.compile(
46 r'\[ +((?:RUN)|(?:FAILED)|(?:OK)) +\] ?([^ ]+)(?: \((\d+) ms\))?$')
47 _RE_TEST_RUN_STATUS = re.compile(
48 r'\[ +(PASSED|RUNNER_FAILED|CRASHED) \] ?[^ ]+')
51 # TODO(jbudorick): Make this a class method of GtestTestInstance once
52 # test_package_apk and test_package_exe are gone.
53 def ParseGTestListTests(raw_list):
54 """Parses a raw test list as provided by --gtest_list_tests.
56 Args:
57 raw_list: The raw test listing with the following format:
59 IPCChannelTest.
60 SendMessageInChannelConnected
61 IPCSyncChannelTest.
62 Simple
63 DISABLED_SendWithTimeoutMixedOKAndTimeout
65 Returns:
66 A list of all tests. For the above raw listing:
68 [IPCChannelTest.SendMessageInChannelConnected, IPCSyncChannelTest.Simple,
69 IPCSyncChannelTest.DISABLED_SendWithTimeoutMixedOKAndTimeout]
70 """
71 ret = []
72 current = ''
73 for test in raw_list:
74 if not test:
75 continue
76 if test[0] != ' ':
77 test_case = test.split()[0]
78 if test_case.endswith('.'):
79 current = test_case
80 elif not 'YOU HAVE' in test:
81 test_name = test.split()[0]
82 ret += [current + test_name]
83 return ret
86 class GtestTestInstance(test_instance.TestInstance):
88 def __init__(self, args, isolate_delegate, error_func):
89 super(GtestTestInstance, self).__init__()
90 # TODO(jbudorick): Support multiple test suites.
91 if len(args.suite_name) > 1:
92 raise ValueError('Platform mode currently supports only 1 gtest suite')
93 self._suite = args.suite_name[0]
95 if self._suite == 'content_browsertests':
96 error_func('content_browsertests are not currently supported '
97 'in platform mode.')
98 self._apk_path = os.path.join(
99 constants.GetOutDirectory(), 'apks', '%s.apk' % self._suite)
100 else:
101 self._apk_path = os.path.join(
102 constants.GetOutDirectory(), '%s_apk' % self._suite,
103 '%s-debug.apk' % self._suite)
104 self._exe_path = os.path.join(constants.GetOutDirectory(),
105 self._suite)
106 if not os.path.exists(self._apk_path):
107 self._apk_path = None
108 if not os.path.exists(self._exe_path):
109 self._exe_path = None
110 if not self._apk_path and not self._exe_path:
111 error_func('Could not find apk or executable for %s' % self._suite)
113 self._data_deps = []
114 if args.test_filter:
115 self._gtest_filter = args.test_filter
116 elif args.test_filter_file:
117 with open(args.test_filter_file, 'r') as f:
118 self._gtest_filter = ':'.join(l.strip() for l in f)
119 else:
120 self._gtest_filter = None
121 if args.isolate_file_path:
122 self._isolate_abs_path = os.path.abspath(args.isolate_file_path)
123 self._isolate_delegate = isolate_delegate
124 self._isolated_abs_path = os.path.join(
125 constants.GetOutDirectory(), '%s.isolated' % self._suite)
126 else:
127 logging.warning('No isolate file provided. No data deps will be pushed.');
128 self._isolate_delegate = None
130 #override
131 def TestType(self):
132 return 'gtest'
134 #override
135 def SetUp(self):
136 """Map data dependencies via isolate."""
137 if self._isolate_delegate:
138 self._isolate_delegate.Remap(
139 self._isolate_abs_path, self._isolated_abs_path)
140 self._isolate_delegate.PurgeExcluded(_DEPS_EXCLUSION_LIST)
141 self._isolate_delegate.MoveOutputDeps()
142 dest_dir = None
143 if self._suite == 'breakpad_unittests':
144 dest_dir = '/data/local/tmp/'
145 self._data_deps.extend([(constants.ISOLATE_DEPS_DIR, dest_dir)])
148 def GetDataDependencies(self):
149 """Returns the test suite's data dependencies.
151 Returns:
152 A list of (host_path, device_path) tuples to push. If device_path is
153 None, the client is responsible for determining where to push the file.
155 return self._data_deps
157 def FilterTests(self, test_list, disabled_prefixes=None):
158 """Filters |test_list| based on prefixes and, if present, a filter string.
160 Args:
161 test_list: The list of tests to filter.
162 disabled_prefixes: A list of test prefixes to filter. Defaults to
163 DISABLED_, FLAKY_, FAILS_, PRE_, and MANUAL_
164 Returns:
165 A filtered list of tests to run.
167 gtest_filter_strings = [
168 self._GenerateDisabledFilterString(disabled_prefixes)]
169 if self._gtest_filter:
170 gtest_filter_strings.append(self._gtest_filter)
172 filtered_test_list = test_list
173 for gtest_filter_string in gtest_filter_strings:
174 logging.debug('Filtering tests using: %s', gtest_filter_string)
175 filtered_test_list = unittest_util.FilterTestNames(
176 filtered_test_list, gtest_filter_string)
177 return filtered_test_list
179 def _GenerateDisabledFilterString(self, disabled_prefixes):
180 disabled_filter_items = []
182 if disabled_prefixes is None:
183 disabled_prefixes = ['DISABLED_', 'FLAKY_', 'FAILS_', 'PRE_', 'MANUAL_']
184 disabled_filter_items += ['%s*' % dp for dp in disabled_prefixes]
185 disabled_filter_items += ['*.%s*' % dp for dp in disabled_prefixes]
187 disabled_tests_file_path = os.path.join(
188 constants.DIR_SOURCE_ROOT, 'build', 'android', 'pylib', 'gtest',
189 'filter', '%s_disabled' % self._suite)
190 if disabled_tests_file_path and os.path.exists(disabled_tests_file_path):
191 with open(disabled_tests_file_path) as disabled_tests_file:
192 disabled_filter_items += [
193 '%s' % l for l in (line.strip() for line in disabled_tests_file)
194 if l and not l.startswith('#')]
196 return '*-%s' % ':'.join(disabled_filter_items)
198 def ParseGTestOutput(self, output):
199 """Parses raw gtest output and returns a list of results.
201 Args:
202 output: A list of output lines.
203 Returns:
204 A list of base_test_result.BaseTestResults.
206 results = []
207 for l in output:
208 matcher = _RE_TEST_STATUS.match(l)
209 if matcher:
210 result_type = None
211 if matcher.group(1) == 'OK':
212 result_type = base_test_result.ResultType.PASS
213 elif matcher.group(1) == 'FAILED':
214 result_type = base_test_result.ResultType.FAIL
216 if result_type:
217 test_name = matcher.group(2)
218 duration = matcher.group(3) if matcher.group(3) else 0
219 results.append(base_test_result.BaseTestResult(
220 test_name, result_type, duration))
221 logging.info(l)
222 return results
224 #override
225 def TearDown(self):
226 """Clear the mappings created by SetUp."""
227 if self._isolate_delegate:
228 self._isolate_delegate.Clear()
230 @property
231 def apk(self):
232 return self._apk_path
234 @property
235 def exe(self):
236 return self._exe_path
238 @property
239 def suite(self):
240 return self._suite