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

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

Issue 2935503002: List Java Instru Test Information From JUnit Runner (Closed)
Patch Set: add tests Created 3 years, 5 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 copy 5 import copy
6 import json
6 import logging 7 import logging
7 import os 8 import os
8 import pickle 9 import pickle
9 import re 10 import re
10 11
11 from devil.android import apk_helper 12 from devil.android import apk_helper
13 from devil.android import device_temp_file
12 from devil.android import md5sum 14 from devil.android import md5sum
13 from pylib import constants 15 from pylib import constants
14 from pylib.base import base_test_result 16 from pylib.base import base_test_result
15 from pylib.base import test_exception 17 from pylib.base import test_exception
16 from pylib.base import test_instance 18 from pylib.base import test_instance
17 from pylib.constants import host_paths 19 from pylib.constants import host_paths
18 from pylib.instrumentation import test_result 20 from pylib.instrumentation import test_result
19 from pylib.instrumentation import instrumentation_parser 21 from pylib.instrumentation import instrumentation_parser
20 from pylib.utils import dexdump 22 from pylib.utils import dexdump
21 from pylib.utils import proguard 23 from pylib.utils import proguard
22 from pylib.utils import shared_preference_utils 24 from pylib.utils import shared_preference_utils
25 from py_utils import tempfile_ext
23 26
24 with host_paths.SysPath(host_paths.BUILD_COMMON_PATH): 27 with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
25 import unittest_util # pylint: disable=import-error 28 import unittest_util # pylint: disable=import-error
26 29
27 # Ref: http://developer.android.com/reference/android/app/Activity.html 30 # Ref: http://developer.android.com/reference/android/app/Activity.html
28 _ACTIVITY_RESULT_CANCELED = 0 31 _ACTIVITY_RESULT_CANCELED = 0
29 _ACTIVITY_RESULT_OK = -1 32 _ACTIVITY_RESULT_OK = -1
30 33
31 _COMMAND_LINE_PARAMETER = 'cmdlinearg-parameter' 34 _COMMAND_LINE_PARAMETER = 'cmdlinearg-parameter'
32 _DEFAULT_ANNOTATIONS = [ 35 _DEFAULT_ANNOTATIONS = [
33 'SmallTest', 'MediumTest', 'LargeTest', 'EnormousTest', 'IntegrationTest'] 36 'SmallTest', 'MediumTest', 'LargeTest', 'EnormousTest', 'IntegrationTest']
34 _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS = [ 37 _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS = [
35 'DisabledTest', 'FlakyTest'] 38 'DisabledTest', 'FlakyTest']
36 _VALID_ANNOTATIONS = set(['Manual'] + _DEFAULT_ANNOTATIONS + 39 _VALID_ANNOTATIONS = set(['Manual'] + _DEFAULT_ANNOTATIONS +
37 _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS) 40 _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS)
38 _EXTRA_DRIVER_TEST_LIST = ( 41 _EXTRA_DRIVER_TEST_LIST = (
39 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TestList') 42 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TestList')
40 _EXTRA_DRIVER_TEST_LIST_FILE = ( 43 _EXTRA_DRIVER_TEST_LIST_FILE = (
41 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TestListFile') 44 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TestListFile')
42 _EXTRA_DRIVER_TARGET_PACKAGE = ( 45 _EXTRA_DRIVER_TARGET_PACKAGE = (
43 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetPackage') 46 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetPackage')
44 _EXTRA_DRIVER_TARGET_CLASS = ( 47 _EXTRA_DRIVER_TARGET_CLASS = (
45 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetClass') 48 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetClass')
46 _EXTRA_TIMEOUT_SCALE = ( 49 _EXTRA_TIMEOUT_SCALE = (
47 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TimeoutScale') 50 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TimeoutScale')
51 _EXTRA_TEST_LIST = (
52 'org.chromium.base.test.BaseChromiumAndroidJUnitRunner.TestList')
48 53
49 _SKIP_PARAMETERIZATION = 'SkipCommandLineParameterization' 54 _SKIP_PARAMETERIZATION = 'SkipCommandLineParameterization'
50 _COMMANDLINE_PARAMETERIZATION = 'CommandLineParameter' 55 _COMMANDLINE_PARAMETERIZATION = 'CommandLineParameter'
51 _NATIVE_CRASH_RE = re.compile('(process|native) crash', re.IGNORECASE) 56 _NATIVE_CRASH_RE = re.compile('(process|native) crash', re.IGNORECASE)
52 _CMDLINE_NAME_SEGMENT_RE = re.compile( 57 _CMDLINE_NAME_SEGMENT_RE = re.compile(
53 r' with(?:out)? \{[^\}]*\}') 58 r' with(?:out)? \{[^\}]*\}')
54 _PICKLE_FORMAT_VERSION = 12 59 _PICKLE_FORMAT_VERSION = 12
55 60
56 61
57 class MissingSizeAnnotationError(test_exception.TestException): 62 class MissingSizeAnnotationError(test_exception.TestException):
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 160
156 return results 161 return results
157 162
158 163
159 def FilterTests(tests, test_filter=None, annotations=None, 164 def FilterTests(tests, test_filter=None, annotations=None,
160 excluded_annotations=None): 165 excluded_annotations=None):
161 """Filter a list of tests 166 """Filter a list of tests
162 167
163 Args: 168 Args:
164 tests: a list of tests. e.g. [ 169 tests: a list of tests. e.g. [
165 {'annotations": {}, 'class': 'com.example.TestA', 'methods':[]}, 170 {'annotations": {}, 'class': 'com.example.TestA', 'method':'test1'},
166 {'annotations": {}, 'class': 'com.example.TestB', 'methods':[]}] 171 {'annotations": {}, 'class': 'com.example.TestB', 'method':'test2'}]
167 test_filter: googletest-style filter string. 172 test_filter: googletest-style filter string.
168 annotations: a dict of wanted annotations for test methods. 173 annotations: a dict of wanted annotations for test methods.
169 exclude_annotations: a dict of annotations to exclude. 174 exclude_annotations: a dict of annotations to exclude.
170 175
171 Return: 176 Return:
172 A list of filtered tests 177 A list of filtered tests
173 """ 178 """
174 def gtest_filter(t): 179 def gtest_filter(t):
175 if not test_filter: 180 if not test_filter:
176 return True 181 return True
177 # Allow fully-qualified name as well as an omitted package. 182 # Allow fully-qualified name as well as an omitted package.
178 unqualified_class_test = { 183 unqualified_class_test = {
179 'class': t['class'].split('.')[-1], 184 'class': t['class'].split('.')[-1],
180 'method': t['method'] 185 'method': t['method']
181 } 186 }
182 names = [ 187 names = [
183 GetTestName(t, sep='.'), 188 GetTestName(t, sep='.'),
184 GetTestName(unqualified_class_test, sep='.'), 189 GetTestName(unqualified_class_test, sep='.'),
185 GetUniqueTestName(t, sep='.') 190 GetUniqueTestName(t, sep='.')
186 ] 191 ]
187 192
193 if t['is_junit4']:
194 names += [
195 GetTestNameWithoutParameterPostfix(t, sep='.'),
196 GetTestNameWithoutParameterPostfix(unqualified_class_test, sep='.')
197 ]
198
188 pattern_groups = test_filter.split('-') 199 pattern_groups = test_filter.split('-')
189 if len(pattern_groups) > 1: 200 if len(pattern_groups) > 1:
190 negative_filter = pattern_groups[1] 201 negative_filter = pattern_groups[1]
191 if unittest_util.FilterTestNames(names, negative_filter): 202 if unittest_util.FilterTestNames(names, negative_filter):
192 return [] 203 return []
193 204
194 positive_filter = pattern_groups[0] 205 positive_filter = pattern_groups[0]
195 return unittest_util.FilterTestNames(names, positive_filter) 206 return unittest_util.FilterTestNames(names, positive_filter)
196 207
197 def annotation_filter(all_annotations): 208 def annotation_filter(all_annotations):
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
232 243
233 if (not annotation_filter(t['annotations']) 244 if (not annotation_filter(t['annotations'])
234 or not excluded_annotation_filter(t['annotations'])): 245 or not excluded_annotation_filter(t['annotations'])):
235 continue 246 continue
236 247
237 filtered_tests.append(t) 248 filtered_tests.append(t)
238 249
239 return filtered_tests 250 return filtered_tests
240 251
241 252
253 def GetAllTestsFromRunner(device, test_apk, test_package,
254 test_runner=None, test_runner_junit4=None):
255 pickle_path = '%s-runner.pickle' % test_apk.path
256 try:
257 tests = _GetTestsFromPickle(pickle_path, test_apk.path)
258 except TestListPickleException as e:
259 logging.info('Could not get tests from pickle: %s', e)
260 logging.info('Getting tests by print tests in instrumentation runner')
261 tests = _GetAllTestsFromRunner(device, test_package, test_runner,
262 test_runner_junit4)
263 _SaveTestsToPickle(pickle_path, test_apk.path, tests)
264 return tests
265
266
267 # TODO(yolandyan): remove this once the tests are converted to junit4
242 def GetAllTestsFromJar(test_jar): 268 def GetAllTestsFromJar(test_jar):
243 pickle_path = '%s-proguard.pickle' % test_jar 269 pickle_path = '%s-proguard.pickle' % test_jar
244 try: 270 try:
245 tests = _GetTestsFromPickle(pickle_path, test_jar) 271 tests = _GetTestsFromPickle(pickle_path, test_jar)
246 except TestListPickleException as e: 272 except TestListPickleException as e:
247 logging.info('Could not get tests from pickle: %s', e) 273 logging.info('Could not get tests from pickle: %s', e)
248 logging.info('Getting tests from JAR via proguard.') 274 logging.info('Getting tests from JAR via proguard.')
249 tests = _GetTestsFromProguard(test_jar) 275 tests = _GetTestsFromProguard(test_jar)
250 _SaveTestsToPickle(pickle_path, test_jar, tests) 276 _SaveTestsToPickle(pickle_path, test_jar, tests)
251 return tests 277 return tests
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 for class_name, class_info in package_info['classes'].iteritems(): 355 for class_name, class_info in package_info['classes'].iteritems():
330 if class_name.endswith('Test'): 356 if class_name.endswith('Test'):
331 tests.append({ 357 tests.append({
332 'class': '%s.%s' % (package_name, class_name), 358 'class': '%s.%s' % (package_name, class_name),
333 'annotations': {}, 359 'annotations': {},
334 'methods': get_test_methods(class_info['methods']), 360 'methods': get_test_methods(class_info['methods']),
335 'superclass': class_info['superclass'], 361 'superclass': class_info['superclass'],
336 }) 362 })
337 return tests 363 return tests
338 364
365 def _GetAllTestsFromRunner(device, test_package, test_runner=None,
366 test_runner_junit4=None):
367 with device_temp_file.DeviceTempFile(
368 device.adb, suffix='.json', dir=device.GetExternalStoragePath()) as f:
369 if test_runner is None and test_runner_junit4 is None:
mikecase (-- gone --) 2017/07/18 23:59:12 test_runner basically unused here.
the real yoland 2017/07/19 00:12:49 Removed
370 raise Exception('Test runner does NOT exist for this test apk')
371 if test_runner_junit4:
372 extras = {}
373 extras[_EXTRA_TEST_LIST] = f.name
374 extras['log'] = 'true'
375 extras['package'] = 'org.chromium'
mikecase (-- gone --) 2017/07/18 23:59:12 so this won't work downstream right? I think some
the real yoland 2017/07/19 00:12:49 Changed This is just for the sake of speed up java
376 target = '%s/%s' % (test_package, test_runner_junit4)
377 output_string = ''.join(device.StartInstrumentation(
378 target, extras=extras, timeout=30, retries=2))
379 if output_string:
380 raise Exception('Test listing through %s failed on device:\n%s' % (
381 test_runner_junit4, output_string))
382 else:
383 raise Exception('JUnit4 Runner is not present in this apk')
384 with tempfile_ext.NamedTemporaryDirectory() as host_dir:
385 host_file = os.path.join(host_dir, 'list_tests.json')
386 device.PullFile(f.name, host_file)
387 with open(host_file, 'r') as host_file:
388 json_string = host_file.read()
389 return json.loads(json_string)
390
339 391
340 def _SaveTestsToPickle(pickle_path, jar_path, tests): 392 def _SaveTestsToPickle(pickle_path, jar_path, tests):
341 jar_md5 = md5sum.CalculateHostMd5Sums(jar_path)[jar_path] 393 jar_md5 = md5sum.CalculateHostMd5Sums(jar_path)[jar_path]
342 pickle_data = { 394 pickle_data = {
343 'VERSION': _PICKLE_FORMAT_VERSION, 395 'VERSION': _PICKLE_FORMAT_VERSION,
344 'JAR_MD5SUM': jar_md5, 396 'JAR_MD5SUM': jar_md5,
345 'TEST_METHODS': tests, 397 'TEST_METHODS': tests,
346 } 398 }
347 with open(pickle_path, 'w') as pickle_file: 399 with open(pickle_path, 'w') as pickle_file:
348 pickle.dump(pickle_data, pickle_file) 400 pickle.dump(pickle_data, pickle_file)
(...skipping 23 matching lines...) Expand all
372 424
373 Args: 425 Args:
374 test: the instrumentation test dict. 426 test: the instrumentation test dict.
375 sep: the character(s) that should join the class name and the method name. 427 sep: the character(s) that should join the class name and the method name.
376 Returns: 428 Returns:
377 The test name as a string. 429 The test name as a string.
378 """ 430 """
379 return '%s%s%s' % (test['class'], sep, test['method']) 431 return '%s%s%s' % (test['class'], sep, test['method'])
380 432
381 433
434 def GetTestNameWithoutParameterPostfix(
435 test, sep='#', parameter_postfix='__'):
mikecase (-- gone --) 2017/07/18 23:59:12 prefix? Or is it more of a parameter_postfix_prefi
the real yoland 2017/07/19 00:12:49 woah troll
436 """Gets the name of the given JUnit4 test withouth parameter postfix.
mikecase (-- gone --) 2017/07/18 23:59:12 withouth
the real yoland 2017/07/19 00:12:49 Done
437
438 For most WebView JUnit4 javatests, each test is parameterizatized with
439 "__sandboxed_mode" to run in both non-sandboxed mode and sandboxed mode.
440
441 This function returns the name of the test without parameterization
442 so test filters can match both parameterized and non-parameterized tests.
443
444 Args:
445 test: the instrumentation test dict.
446 sep: the character(s) that should join the class name and the method name.
447 parameterization_sep: the character(s) that seperate method name and method
mikecase (-- gone --) 2017/07/18 23:59:11 Update this variable name to what is actually is.
the real yoland 2017/07/19 00:12:49 Done
448 parameterization postfix.
449 Returns:
450 The test name without parameter postfix as a string.
451 """
452 name = GetTestName(test, sep=sep)
453 return name.split(parameter_postfix)[0]
454
455
382 def GetUniqueTestName(test, sep='#'): 456 def GetUniqueTestName(test, sep='#'):
383 """Gets the unique name of the given test. 457 """Gets the unique name of the given test.
384 458
385 This will include text to disambiguate between tests for which GetTestName 459 This will include text to disambiguate between tests for which GetTestName
386 would return the same name. 460 would return the same name.
387 461
388 Args: 462 Args:
389 test: the instrumentation test dict. 463 test: the instrumentation test dict.
390 sep: the character(s) that should join the class name and the method name. 464 sep: the character(s) that should join the class name and the method name.
391 Returns: 465 Returns:
(...skipping 369 matching lines...) Expand 10 before | Expand all | Expand 10 after
761 return 'instrumentation' 835 return 'instrumentation'
762 836
763 #override 837 #override
764 def SetUp(self): 838 def SetUp(self):
765 self._data_deps.extend( 839 self._data_deps.extend(
766 self._data_deps_delegate(self._runtime_deps_path)) 840 self._data_deps_delegate(self._runtime_deps_path))
767 841
768 def GetDataDependencies(self): 842 def GetDataDependencies(self):
769 return self._data_deps 843 return self._data_deps
770 844
771 def GetTests(self): 845 def GetTests(self, device=None):
772 if self.test_jar: 846 if self._test_runner_junit4:
847 tests = GetAllTestsFromRunner(
848 device, self.test_apk, self.test_package,
849 test_runner=self._test_runner,
850 test_runner_junit4=self.test_runner_junit4)
851 elif self.test_jar:
773 tests = GetAllTestsFromJar(self.test_jar) 852 tests = GetAllTestsFromJar(self.test_jar)
774 else: 853 else:
775 tests = GetAllTestsFromApk(self.test_apk.path) 854 tests = GetAllTestsFromApk(self.test_apk.path)
776 inflated_tests = self._ParameterizeTestsWithFlags(self._InflateTests(tests)) 855 inflated_tests = self._ParameterizeTestsWithFlags(self._InflateTests(tests))
777 if self._test_runner_junit4 is None and any( 856 if self._test_runner_junit4 is None and any(
778 t['is_junit4'] for t in inflated_tests): 857 t['is_junit4'] for t in inflated_tests):
779 raise MissingJUnit4RunnerException() 858 raise MissingJUnit4RunnerException()
780 filtered_tests = FilterTests( 859 filtered_tests = FilterTests(
781 inflated_tests, self._test_filter, self._annotations, 860 inflated_tests, self._test_filter, self._annotations,
782 self._excluded_annotations) 861 self._excluded_annotations)
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
840 919
841 @staticmethod 920 @staticmethod
842 def GenerateTestResults( 921 def GenerateTestResults(
843 result_code, result_bundle, statuses, start_ms, duration_ms): 922 result_code, result_bundle, statuses, start_ms, duration_ms):
844 return GenerateTestResults(result_code, result_bundle, statuses, 923 return GenerateTestResults(result_code, result_bundle, statuses,
845 start_ms, duration_ms) 924 start_ms, duration_ms)
846 925
847 #override 926 #override
848 def TearDown(self): 927 def TearDown(self):
849 pass 928 pass
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698