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

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

Issue 1851143002: Find annotated tests by exposing API in instrumentation_test_instance (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Minor change Created 4 years, 8 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
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 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 import collections 5 import collections
6 import copy 6 import copy
7 import logging 7 import logging
8 import os 8 import os
9 import pickle 9 import pickle
10 import re 10 import re
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 crashed = (result_code == _ACTIVITY_RESULT_CANCELED 130 crashed = (result_code == _ACTIVITY_RESULT_CANCELED
131 and any(_NATIVE_CRASH_RE.search(l) 131 and any(_NATIVE_CRASH_RE.search(l)
132 for l in result_bundle.itervalues())) 132 for l in result_bundle.itervalues()))
133 if crashed: 133 if crashed:
134 current_result.SetType(base_test_result.ResultType.CRASH) 134 current_result.SetType(base_test_result.ResultType.CRASH)
135 135
136 results.append(current_result) 136 results.append(current_result)
137 137
138 return results 138 return results
139 139
140
perezju 2016/04/12 10:29:02 nit: bring that blank line back
Yoland Yan(Google) 2016/04/13 01:03:10 Done.
141 def ParseCommandLineFlagParameters(annotations): 140 def ParseCommandLineFlagParameters(annotations):
142 """Determines whether the test is parameterized to be run with different 141 """Determines whether the test is parameterized to be run with different
143 command-line flags. 142 command-line flags.
144 143
145 Args: 144 Args:
146 annotations: The annotations of the test. 145 annotations: The annotations of the test.
147 146
148 Returns: 147 Returns:
149 If the test is parameterized, returns a list of named tuples 148 If the test is parameterized, returns a list of named tuples
150 with lists of flags, e.g.: 149 with lists of flags, e.g.:
(...skipping 28 matching lines...) Expand all
179 if p['tag'] == _COMMAND_LINE_PARAMETER: 178 if p['tag'] == _COMMAND_LINE_PARAMETER:
180 to_add = [] 179 to_add = []
181 to_remove = [] 180 to_remove = []
182 for a in p.get('arguments', []): 181 for a in p.get('arguments', []):
183 if a['name'] == 'add': 182 if a['name'] == 'add':
184 to_add = ['--%s' % f for f in a['stringArray']] 183 to_add = ['--%s' % f for f in a['stringArray']]
185 elif a['name'] == 'remove': 184 elif a['name'] == 'remove':
186 to_remove = ['--%s' % f for f in a['stringArray']] 185 to_remove = ['--%s' % f for f in a['stringArray']]
187 result.append(ParamsTuple(to_add, to_remove)) 186 result.append(ParamsTuple(to_add, to_remove))
188 return result if result else None 187 return result if result else None
189 188
perezju 2016/04/12 10:29:03 nit: two blank lines between module level elements
Yoland Yan(Google) 2016/04/13 01:03:11 Done.
189 def TotalTestCount(test_jar):
190 """Return the total amount of tests in a test apk base on proguard dump"""
191 total_test_count = 0
192 tests = _GetAllTests(test_jar)
193 for c in tests:
194 total_test_count += len(c['methods'])
195 return total_test_count
perezju 2016/04/12 10:29:03 nit: return sum(len(c['methods']) for c in _GetAll
Yoland Yan(Google) 2016/04/13 01:03:10 Done.
196
197 def GetFilteredTests(test_jar, test_filter, annotations, exclude_annotations):
perezju 2016/04/12 10:29:03 make test_filter, annotations, and exclude_annotat
Yoland Yan(Google) 2016/04/13 01:03:10 No longer an issue, but changed the now public Fil
198 """Get a list of filtered tests in a test apk jar file.
199
200 Args:
201 test_jar: the full path to the test apk jar file.
202 test_filter: googletest-style filter string.
203 annotations: a dict of wanted annotations for test methods.
204 exclude_annotations: a dict of annotations to exclude.
perezju 2016/04/12 10:29:03 Why are these dicts? How do they look like?
Yoland Yan(Google) 2016/04/13 01:03:11 No longer an issue here in the next patch because
perezju 2016/04/25 12:45:07 I think that's a bit awkward to use. Probably the
205
206 Return:
207 A list of filtered tests
perezju 2016/04/12 10:29:03 Add an example of what each such "test" looks like
Yoland Yan(Google) 2016/04/13 01:03:11 Done.
208 """
perezju 2016/04/12 10:29:03 nit: move those quotes three spaces to the left.
Yoland Yan(Google) 2016/04/13 01:03:10 Done.
209 tests = _GetAllTests(test_jar)
210 filtered_tests = _FilterTests(tests, test_filter, annotations,
211 exclude_annotations)
perezju 2016/04/12 10:29:03 nit: one space to the right
Yoland Yan(Google) 2016/04/13 01:03:10 Done.
212 return filtered_tests
213
214 def _GetTestsFromPickle(pickle_path, jar_path):
215 if not os.path.exists(pickle_path):
216 raise ProguardPickleException('%s does not exist.' % pickle_path)
217 if os.path.getmtime(pickle_path) <= os.path.getmtime(jar_path):
218 raise ProguardPickleException(
219 '%s newer than %s.' % (jar_path, pickle_path))
220
221 with open(pickle_path, 'r') as pickle_file:
222 pickle_data = pickle.loads(pickle_file.read())
223 jar_md5 = md5sum.CalculateHostMd5Sums(jar_path)[jar_path]
224
225 try:
226 if pickle_data['VERSION'] != _PICKLE_FORMAT_VERSION:
227 raise ProguardPickleException('PICKLE_FORMAT_VERSION has changed.')
228 if pickle_data['JAR_MD5SUM'] != jar_md5:
229 raise ProguardPickleException('JAR file MD5 sum differs.')
230 return pickle_data['TEST_METHODS']
231 except TypeError as e:
perezju 2016/04/12 10:29:03 what are you hoping to catch with TypeError?
Yoland Yan(Google) 2016/04/13 01:03:10 If from before, I think might have been some calcu
232 logging.error(pickle_data)
233 raise ProguardPickleException(str(e))
234
235 # pylint: disable=no-self-use
perezju 2016/04/12 10:29:03 not needed anymore
Yoland Yan(Google) 2016/04/13 01:03:11 Done.
236 def _GetTestsFromProguard(jar_path):
237 p = proguard.Dump(jar_path)
238
239 def is_test_class(c):
240 return c['class'].endswith('Test')
241
242 def is_test_method(m):
243 return m['method'].startswith('test')
244
245 class_lookup = dict((c['class'], c) for c in p['classes'])
perezju 2016/04/12 10:29:03 nit: {c['class']: c for c in p['classes']} and mo
Yoland Yan(Google) 2016/04/13 01:03:11 Done.
246 def recursive_get_class_annotations(c):
perezju 2016/04/12 10:29:03 nit: recursive_class_annotations should be a fine
Yoland Yan(Google) 2016/04/13 01:03:10 Done.
247 s = c['superclass']
248 if s in class_lookup:
249 a = recursive_get_class_annotations(class_lookup[s])
250 else:
251 a = {}
252 a.update(c['annotations'])
perezju 2016/04/12 10:29:02 you could write this non-recursively: a = {}
Yoland Yan(Google) 2016/04/13 02:18:43 I am not sure whether order is important, will loo
perezju 2016/04/25 12:45:07 It's fine, leave it as is.
253 return a
254
255 def stripped_test_class(c):
256 return {
257 'class': c['class'],
258 'annotations': recursive_get_class_annotations(c),
259 'methods': [m for m in c['methods'] if is_test_method(m)],
260 }
261
262 return [stripped_test_class(c) for c in p['classes']
263 if is_test_class(c)]
264
265 def _SaveTestsToPickle(pickle_path, jar_path, tests):
266 jar_md5 = md5sum.CalculateHostMd5Sums(jar_path)[jar_path]
267 pickle_data = {
268 'VERSION': _PICKLE_FORMAT_VERSION,
269 'JAR_MD5SUM': jar_md5,
270 'TEST_METHODS': tests,
271 }
272 with open(pickle_path, 'w') as pickle_file:
273 pickle.dump(pickle_data, pickle_file)
274
275 def _FilterTests(tests, test_filter, annotations, excluded_annotations):
276
277 def gtest_filter(c, m):
278 t = ['%s.%s' % (c['class'].split('.')[-1], m['method'])]
279 return (not test_filter
280 or unittest_util.FilterTestNames(t, test_filter))
281
282 def annotation_filter(all_annotations):
283 if not annotations:
284 return True
285 return any_annotation_matches(annotations, all_annotations)
perezju 2016/04/12 10:29:02 Not particularly proud of this suggestion, but you
Yoland Yan(Google) 2016/04/13 01:03:11 I did a timeit speed run, and the difference isn't
perezju 2016/04/13 09:53:46 Acknowledged.
Yoland Yan(Google) 2016/04/22 17:50:26 Done.
286
287 def excluded_annotation_filter(all_annotations):
288 if not excluded_annotations:
289 return True
290 return not any_annotation_matches(excluded_annotations,
291 all_annotations)
292
293 def any_annotation_matches(annotations, all_annotations):
294 return any(
295 ak in all_annotations and (av is None or av == all_annotations[ak])
296 for ak, av in annotations.iteritems())
297
298 filtered_classes = []
299 for c in tests:
300 filtered_methods = []
301 for m in c['methods']:
302 # Gtest filtering
303 if not gtest_filter(c, m):
304 continue
305
306 all_annotations = dict(c['annotations'])
307 all_annotations.update(m['annotations'])
308 if (not annotation_filter(all_annotations)
309 or not excluded_annotation_filter(all_annotations)):
310 continue
311
312 filtered_methods.append(m)
313
314 if filtered_methods:
315 filtered_class = dict(c)
316 filtered_class['methods'] = filtered_methods
317 filtered_classes.append(filtered_class)
318
319 return filtered_classes
320
321 def _GetAllTests(test_jar):
322 pickle_path = '%s-proguard.pickle' % test_jar
323 try:
324 tests = _GetTestsFromPickle(pickle_path, test_jar)
325 except ProguardPickleException as e:
326 logging.info('Getting tests from JAR via proguard. (%s)', str(e))
perezju 2016/04/12 10:29:03 maybe: logging.info('Could not get tests from
Yoland Yan(Google) 2016/04/13 01:03:10 Done.
327 tests = _GetTestsFromProguard(test_jar)
328 _SaveTestsToPickle(pickle_path, test_jar, tests)
329 return tests
330
331 class ProguardPickleException(Exception):
perezju 2016/04/12 10:29:03 Move declarations of exceptions near the top of th
Yoland Yan(Google) 2016/04/13 01:03:10 Done.
332 pass
190 333
191 class InstrumentationTestInstance(test_instance.TestInstance): 334 class InstrumentationTestInstance(test_instance.TestInstance):
192 335
193 def __init__(self, args, isolate_delegate, error_func): 336 def __init__(self, args, isolate_delegate, error_func):
194 super(InstrumentationTestInstance, self).__init__() 337 super(InstrumentationTestInstance, self).__init__()
195 338
196 self._additional_apks = [] 339 self._additional_apks = []
197 self._apk_under_test = None 340 self._apk_under_test = None
198 self._apk_under_test_incremental_install_script = None 341 self._apk_under_test_incremental_install_script = None
199 self._package_info = None 342 self._package_info = None
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after
458 device_rel_path, host_rel_path = t.split(':') 601 device_rel_path, host_rel_path = t.split(':')
459 host_abs_path = os.path.join(host_paths.DIR_SOURCE_ROOT, host_rel_path) 602 host_abs_path = os.path.join(host_paths.DIR_SOURCE_ROOT, host_rel_path)
460 self._data_deps.extend( 603 self._data_deps.extend(
461 [(host_abs_path, 604 [(host_abs_path,
462 [None, 'chrome', 'test', 'data', device_rel_path])]) 605 [None, 'chrome', 'test', 'data', device_rel_path])])
463 606
464 def GetDataDependencies(self): 607 def GetDataDependencies(self):
465 return self._data_deps 608 return self._data_deps
466 609
467 def GetTests(self): 610 def GetTests(self):
468 pickle_path = '%s-proguard.pickle' % self.test_jar 611 filtered_tests = GetFilteredTests(
469 try: 612 self.test_jar,
470 tests = self._GetTestsFromPickle(pickle_path, self.test_jar) 613 self._test_filter,
471 except self.ProguardPickleException as e: 614 self._annotations,
472 logging.info('Getting tests from JAR via proguard. (%s)', str(e)) 615 self._excluded_annotations)
473 tests = self._GetTestsFromProguard(self.test_jar) 616 return self._ParametrizeTestsWithFlags(self._InflateTests(filtered_tests))
474 self._SaveTestsToPickle(pickle_path, self.test_jar, tests)
475 return self._ParametrizeTestsWithFlags(
476 self._InflateTests(self._FilterTests(tests)))
477
478 class ProguardPickleException(Exception):
479 pass
480
481 def _GetTestsFromPickle(self, pickle_path, jar_path):
482 if not os.path.exists(pickle_path):
483 raise self.ProguardPickleException('%s does not exist.' % pickle_path)
484 if os.path.getmtime(pickle_path) <= os.path.getmtime(jar_path):
485 raise self.ProguardPickleException(
486 '%s newer than %s.' % (jar_path, pickle_path))
487
488 with open(pickle_path, 'r') as pickle_file:
489 pickle_data = pickle.loads(pickle_file.read())
490 jar_md5 = md5sum.CalculateHostMd5Sums(jar_path)[jar_path]
491
492 try:
493 if pickle_data['VERSION'] != _PICKLE_FORMAT_VERSION:
494 raise self.ProguardPickleException('PICKLE_FORMAT_VERSION has changed.')
495 if pickle_data['JAR_MD5SUM'] != jar_md5:
496 raise self.ProguardPickleException('JAR file MD5 sum differs.')
497 return pickle_data['TEST_METHODS']
498 except TypeError as e:
499 logging.error(pickle_data)
500 raise self.ProguardPickleException(str(e))
501
502 # pylint: disable=no-self-use
503 def _GetTestsFromProguard(self, jar_path):
504 p = proguard.Dump(jar_path)
505
506 def is_test_class(c):
507 return c['class'].endswith('Test')
508
509 def is_test_method(m):
510 return m['method'].startswith('test')
511
512 class_lookup = dict((c['class'], c) for c in p['classes'])
513 def recursive_get_class_annotations(c):
514 s = c['superclass']
515 if s in class_lookup:
516 a = recursive_get_class_annotations(class_lookup[s])
517 else:
518 a = {}
519 a.update(c['annotations'])
520 return a
521
522 def stripped_test_class(c):
523 return {
524 'class': c['class'],
525 'annotations': recursive_get_class_annotations(c),
526 'methods': [m for m in c['methods'] if is_test_method(m)],
527 }
528
529 return [stripped_test_class(c) for c in p['classes']
530 if is_test_class(c)]
531
532 def _SaveTestsToPickle(self, pickle_path, jar_path, tests):
533 jar_md5 = md5sum.CalculateHostMd5Sums(jar_path)[jar_path]
534 pickle_data = {
535 'VERSION': _PICKLE_FORMAT_VERSION,
536 'JAR_MD5SUM': jar_md5,
537 'TEST_METHODS': tests,
538 }
539 with open(pickle_path, 'w') as pickle_file:
540 pickle.dump(pickle_data, pickle_file)
541
542 def _FilterTests(self, tests):
543
544 def gtest_filter(c, m):
545 t = ['%s.%s' % (c['class'].split('.')[-1], m['method'])]
546 return (not self._test_filter
547 or unittest_util.FilterTestNames(t, self._test_filter))
548
549 def annotation_filter(all_annotations):
550 if not self._annotations:
551 return True
552 return any_annotation_matches(self._annotations, all_annotations)
553
554 def excluded_annotation_filter(all_annotations):
555 if not self._excluded_annotations:
556 return True
557 return not any_annotation_matches(self._excluded_annotations,
558 all_annotations)
559
560 def any_annotation_matches(annotations, all_annotations):
561 return any(
562 ak in all_annotations and (av is None or av == all_annotations[ak])
563 for ak, av in annotations.iteritems())
564
565 filtered_classes = []
566 for c in tests:
567 filtered_methods = []
568 for m in c['methods']:
569 # Gtest filtering
570 if not gtest_filter(c, m):
571 continue
572
573 all_annotations = dict(c['annotations'])
574 all_annotations.update(m['annotations'])
575 if (not annotation_filter(all_annotations)
576 or not excluded_annotation_filter(all_annotations)):
577 continue
578
579 filtered_methods.append(m)
580
581 if filtered_methods:
582 filtered_class = dict(c)
583 filtered_class['methods'] = filtered_methods
584 filtered_classes.append(filtered_class)
585
586 return filtered_classes
587 617
588 def _InflateTests(self, tests): 618 def _InflateTests(self, tests):
589 inflated_tests = [] 619 inflated_tests = []
590 for c in tests: 620 for c in tests:
591 for m in c['methods']: 621 for m in c['methods']:
592 a = dict(c['annotations']) 622 a = dict(c['annotations'])
593 a.update(m['annotations']) 623 a.update(m['annotations'])
594 inflated_tests.append({ 624 inflated_tests.append({
595 'class': c['class'], 625 'class': c['class'],
596 'method': m['method'], 626 'method': m['method'],
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
634 @staticmethod 664 @staticmethod
635 def GenerateTestResults( 665 def GenerateTestResults(
636 result_code, result_bundle, statuses, start_ms, duration_ms): 666 result_code, result_bundle, statuses, start_ms, duration_ms):
637 return GenerateTestResults(result_code, result_bundle, statuses, 667 return GenerateTestResults(result_code, result_bundle, statuses,
638 start_ms, duration_ms) 668 start_ms, duration_ms)
639 669
640 #override 670 #override
641 def TearDown(self): 671 def TearDown(self):
642 if self._isolate_delegate: 672 if self._isolate_delegate:
643 self._isolate_delegate.Clear() 673 self._isolate_delegate.Clear()
644
OLDNEW
« no previous file with comments | « no previous file | tools/android/find_disabled_tests.py » ('j') | tools/android/find_disabled_tests.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698