1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/webrtc/trunk/build/android/pylib/python_test_base.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,168 @@ 1.4 +# Copyright (c) 2012 The Chromium Authors. All rights reserved. 1.5 +# Use of this source code is governed by a BSD-style license that can be 1.6 +# found in the LICENSE file. 1.7 + 1.8 +"""Base class for Android Python-driven tests. 1.9 + 1.10 +This test case is intended to serve as the base class for any Python-driven 1.11 +tests. It is similar to the Python unitttest module in that the user's tests 1.12 +inherit from this case and add their tests in that case. 1.13 + 1.14 +When a PythonTestBase object is instantiated, its purpose is to run only one of 1.15 +its tests. The test runner gives it the name of the test the instance will 1.16 +run. The test runner calls SetUp with the Android device ID which the test will 1.17 +run against. The runner runs the test method itself, collecting the result, 1.18 +and calls TearDown. 1.19 + 1.20 +Tests can basically do whatever they want in the test methods, such as call 1.21 +Java tests using _RunJavaTests. Those methods have the advantage of massaging 1.22 +the Java test results into Python test results. 1.23 +""" 1.24 + 1.25 +import logging 1.26 +import os 1.27 +import time 1.28 + 1.29 +import android_commands 1.30 +import apk_info 1.31 +from run_java_tests import TestRunner 1.32 +from test_result import SingleTestResult, TestResults 1.33 + 1.34 + 1.35 +# aka the parent of com.google.android 1.36 +BASE_ROOT = 'src' + os.sep 1.37 + 1.38 + 1.39 +class PythonTestBase(object): 1.40 + """Base class for Python-driven tests.""" 1.41 + 1.42 + def __init__(self, test_name): 1.43 + # test_name must match one of the test methods defined on a subclass which 1.44 + # inherits from this class. 1.45 + # It's stored so we can do the attr lookup on demand, allowing this class 1.46 + # to be pickled, a requirement for the multiprocessing module. 1.47 + self.test_name = test_name 1.48 + class_name = self.__class__.__name__ 1.49 + self.qualified_name = class_name + '.' + self.test_name 1.50 + 1.51 + def SetUp(self, options): 1.52 + self.options = options 1.53 + self.shard_index = self.options.shard_index 1.54 + self.device_id = self.options.device_id 1.55 + self.adb = android_commands.AndroidCommands(self.device_id) 1.56 + self.ports_to_forward = [] 1.57 + 1.58 + def TearDown(self): 1.59 + pass 1.60 + 1.61 + def Run(self): 1.62 + logging.warning('Running Python-driven test: %s', self.test_name) 1.63 + return getattr(self, self.test_name)() 1.64 + 1.65 + def _RunJavaTest(self, fname, suite, test): 1.66 + """Runs a single Java test with a Java TestRunner. 1.67 + 1.68 + Args: 1.69 + fname: filename for the test (e.g. foo/bar/baz/tests/FooTest.py) 1.70 + suite: name of the Java test suite (e.g. FooTest) 1.71 + test: name of the test method to run (e.g. testFooBar) 1.72 + 1.73 + Returns: 1.74 + TestResults object with a single test result. 1.75 + """ 1.76 + test = self._ComposeFullTestName(fname, suite, test) 1.77 + apks = [apk_info.ApkInfo(self.options.test_apk_path, 1.78 + self.options.test_apk_jar_path)] 1.79 + java_test_runner = TestRunner(self.options, self.device_id, [test], False, 1.80 + self.shard_index, 1.81 + apks, 1.82 + self.ports_to_forward) 1.83 + return java_test_runner.Run() 1.84 + 1.85 + def _RunJavaTests(self, fname, tests): 1.86 + """Calls a list of tests and stops at the first test failure. 1.87 + 1.88 + This method iterates until either it encounters a non-passing test or it 1.89 + exhausts the list of tests. Then it returns the appropriate Python result. 1.90 + 1.91 + Args: 1.92 + fname: filename for the Python test 1.93 + tests: a list of Java test names which will be run 1.94 + 1.95 + Returns: 1.96 + A TestResults object containing a result for this Python test. 1.97 + """ 1.98 + start_ms = int(time.time()) * 1000 1.99 + 1.100 + result = None 1.101 + for test in tests: 1.102 + # We're only running one test at a time, so this TestResults object will 1.103 + # hold only one result. 1.104 + suite, test_name = test.split('.') 1.105 + result = self._RunJavaTest(fname, suite, test_name) 1.106 + # A non-empty list means the test did not pass. 1.107 + if result.GetAllBroken(): 1.108 + break 1.109 + 1.110 + duration_ms = int(time.time()) * 1000 - start_ms 1.111 + 1.112 + # Do something with result. 1.113 + return self._ProcessResults(result, start_ms, duration_ms) 1.114 + 1.115 + def _ProcessResults(self, result, start_ms, duration_ms): 1.116 + """Translates a Java test result into a Python result for this test. 1.117 + 1.118 + The TestRunner class that we use under the covers will return a test result 1.119 + for that specific Java test. However, to make reporting clearer, we have 1.120 + this method to abstract that detail and instead report that as a failure of 1.121 + this particular test case while still including the Java stack trace. 1.122 + 1.123 + Args: 1.124 + result: TestResults with a single Java test result 1.125 + start_ms: the time the test started 1.126 + duration_ms: the length of the test 1.127 + 1.128 + Returns: 1.129 + A TestResults object containing a result for this Python test. 1.130 + """ 1.131 + test_results = TestResults() 1.132 + 1.133 + # If our test is in broken, then it crashed/failed. 1.134 + broken = result.GetAllBroken() 1.135 + if broken: 1.136 + # Since we have run only one test, take the first and only item. 1.137 + single_result = broken[0] 1.138 + 1.139 + log = single_result.log 1.140 + if not log: 1.141 + log = 'No logging information.' 1.142 + 1.143 + python_result = SingleTestResult(self.qualified_name, start_ms, 1.144 + duration_ms, 1.145 + log) 1.146 + 1.147 + # Figure out where the test belonged. There's probably a cleaner way of 1.148 + # doing this. 1.149 + if single_result in result.crashed: 1.150 + test_results.crashed = [python_result] 1.151 + elif single_result in result.failed: 1.152 + test_results.failed = [python_result] 1.153 + elif single_result in result.unknown: 1.154 + test_results.unknown = [python_result] 1.155 + 1.156 + else: 1.157 + python_result = SingleTestResult(self.qualified_name, start_ms, 1.158 + duration_ms) 1.159 + test_results.ok = [python_result] 1.160 + 1.161 + return test_results 1.162 + 1.163 + def _ComposeFullTestName(self, fname, suite, test): 1.164 + package_name = self._GetPackageName(fname) 1.165 + return package_name + '.' + suite + '#' + test 1.166 + 1.167 + def _GetPackageName(self, fname): 1.168 + """Extracts the package name from the test file path.""" 1.169 + dirname = os.path.dirname(fname) 1.170 + package = dirname[dirname.rfind(BASE_ROOT) + len(BASE_ROOT):] 1.171 + return package.replace(os.sep, '.')