| OLD | NEW |
| (Empty) |
| 1 # Copyright 2013 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 | |
| 5 """Base class for host-driven test cases. | |
| 6 | |
| 7 This test case is intended to serve as the base class for any host-driven | |
| 8 test cases. It is similar to the Python unitttest module in that test cases | |
| 9 inherit from this class and add methods which will be run as tests. | |
| 10 | |
| 11 When a HostDrivenTestCase object is instantiated, its purpose is to run only one | |
| 12 test method in the derived class. The test runner gives it the name of the test | |
| 13 method the instance will run. The test runner calls SetUp with the device ID | |
| 14 which the test method will run against. The test runner runs the test method | |
| 15 itself, collecting the result, and calls TearDown. | |
| 16 | |
| 17 Tests can perform arbitrary Python commands and asserts in test methods. Tests | |
| 18 that run instrumentation tests can make use of the _RunJavaTestFilters helper | |
| 19 function to trigger Java tests and convert results into a single host-driven | |
| 20 test result. | |
| 21 """ | |
| 22 | |
| 23 import logging | |
| 24 import os | |
| 25 import time | |
| 26 | |
| 27 from pylib import constants | |
| 28 from pylib import forwarder | |
| 29 from pylib import valgrind_tools | |
| 30 from pylib.base import base_test_result | |
| 31 from pylib.device import device_utils | |
| 32 from pylib.instrumentation import test_package | |
| 33 from pylib.instrumentation import test_result | |
| 34 from pylib.instrumentation import test_runner | |
| 35 | |
| 36 # aka the parent of com.google.android | |
| 37 BASE_ROOT = 'src' + os.sep | |
| 38 | |
| 39 | |
| 40 class HostDrivenTestCase(object): | |
| 41 """Base class for host-driven test cases.""" | |
| 42 | |
| 43 _HOST_DRIVEN_TAG = 'HostDriven' | |
| 44 | |
| 45 def __init__(self, test_name, instrumentation_options=None): | |
| 46 """Create a test case initialized to run |test_name|. | |
| 47 | |
| 48 Args: | |
| 49 test_name: The name of the method to run as the test. | |
| 50 instrumentation_options: An InstrumentationOptions object. | |
| 51 """ | |
| 52 class_name = self.__class__.__name__ | |
| 53 self.device = None | |
| 54 self.device_id = '' | |
| 55 self.has_forwarded_ports = False | |
| 56 self.instrumentation_options = instrumentation_options | |
| 57 self.ports_to_forward = [] | |
| 58 self.shard_index = 0 | |
| 59 | |
| 60 # Use tagged_name when creating results, so that we can identify host-driven | |
| 61 # tests in the overall results. | |
| 62 self.test_name = test_name | |
| 63 self.qualified_name = '%s.%s' % (class_name, self.test_name) | |
| 64 self.tagged_name = '%s_%s' % (self._HOST_DRIVEN_TAG, self.qualified_name) | |
| 65 | |
| 66 # TODO(bulach): make ports_to_forward not optional and move the Forwarder | |
| 67 # mapping here. | |
| 68 def SetUp(self, device, shard_index, ports_to_forward=None): | |
| 69 if not ports_to_forward: | |
| 70 ports_to_forward = [] | |
| 71 self.device = device | |
| 72 self.shard_index = shard_index | |
| 73 self.device_id = str(self.device) | |
| 74 if ports_to_forward: | |
| 75 self.ports_to_forward = ports_to_forward | |
| 76 | |
| 77 def TearDown(self): | |
| 78 pass | |
| 79 | |
| 80 # TODO(craigdh): Remove GetOutDir once references have been removed | |
| 81 # downstream. | |
| 82 @staticmethod | |
| 83 def GetOutDir(): | |
| 84 return constants.GetOutDirectory() | |
| 85 | |
| 86 def Run(self): | |
| 87 logging.info('Running host-driven test: %s', self.tagged_name) | |
| 88 # Get the test method on the derived class and execute it | |
| 89 return getattr(self, self.test_name)() | |
| 90 | |
| 91 @staticmethod | |
| 92 def __GetHostForwarderLog(): | |
| 93 return ('-- Begin Full HostForwarder log\n' | |
| 94 '%s\n' | |
| 95 '--End Full HostForwarder log\n' % forwarder.Forwarder.GetHostLog()) | |
| 96 | |
| 97 def __StartForwarder(self): | |
| 98 logging.warning('Forwarding %s %s', self.ports_to_forward, | |
| 99 self.has_forwarded_ports) | |
| 100 if self.ports_to_forward and not self.has_forwarded_ports: | |
| 101 self.has_forwarded_ports = True | |
| 102 tool = valgrind_tools.CreateTool(None, self.device) | |
| 103 forwarder.Forwarder.Map([(port, port) for port in self.ports_to_forward], | |
| 104 self.device, tool) | |
| 105 | |
| 106 def __RunJavaTest(self, test, test_pkg, additional_flags=None): | |
| 107 """Runs a single Java test in a Java TestRunner. | |
| 108 | |
| 109 Args: | |
| 110 test: Fully qualified test name (ex. foo.bar.TestClass#testMethod) | |
| 111 test_pkg: TestPackage object. | |
| 112 additional_flags: A list of additional flags to add to the command line. | |
| 113 | |
| 114 Returns: | |
| 115 TestRunResults object with a single test result. | |
| 116 """ | |
| 117 # TODO(bulach): move this to SetUp() stage. | |
| 118 self.__StartForwarder() | |
| 119 | |
| 120 java_test_runner = test_runner.TestRunner( | |
| 121 self.instrumentation_options, self.device, self.shard_index, | |
| 122 test_pkg, additional_flags=additional_flags) | |
| 123 try: | |
| 124 java_test_runner.SetUp() | |
| 125 return java_test_runner.RunTest(test)[0] | |
| 126 finally: | |
| 127 java_test_runner.TearDown() | |
| 128 | |
| 129 def _RunJavaTestFilters(self, test_filters, additional_flags=None): | |
| 130 """Calls a list of tests and stops at the first test failure. | |
| 131 | |
| 132 This method iterates until either it encounters a non-passing test or it | |
| 133 exhausts the list of tests. Then it returns the appropriate overall result. | |
| 134 | |
| 135 Test cases may make use of this method internally to assist in running | |
| 136 instrumentation tests. This function relies on instrumentation_options | |
| 137 being defined. | |
| 138 | |
| 139 Args: | |
| 140 test_filters: A list of Java test filters. | |
| 141 additional_flags: A list of addition flags to add to the command line. | |
| 142 | |
| 143 Returns: | |
| 144 A TestRunResults object containing an overall result for this set of Java | |
| 145 tests. If any Java tests do not pass, this is a fail overall. | |
| 146 """ | |
| 147 test_type = base_test_result.ResultType.PASS | |
| 148 log = '' | |
| 149 | |
| 150 test_pkg = test_package.TestPackage( | |
| 151 self.instrumentation_options.test_apk_path, | |
| 152 self.instrumentation_options.test_apk_jar_path, | |
| 153 self.instrumentation_options.test_support_apk_path) | |
| 154 | |
| 155 start_ms = int(time.time()) * 1000 | |
| 156 done = False | |
| 157 for test_filter in test_filters: | |
| 158 tests = test_pkg.GetAllMatchingTests(None, None, test_filter) | |
| 159 # Filters should always result in >= 1 test. | |
| 160 if len(tests) == 0: | |
| 161 raise Exception('Java test filter "%s" returned no tests.' | |
| 162 % test_filter) | |
| 163 for test in tests: | |
| 164 # We're only running one test at a time, so this TestRunResults object | |
| 165 # will hold only one result. | |
| 166 java_result = self.__RunJavaTest(test, test_pkg, additional_flags) | |
| 167 assert len(java_result.GetAll()) == 1 | |
| 168 if not java_result.DidRunPass(): | |
| 169 result = java_result.GetNotPass().pop() | |
| 170 log = result.GetLog() | |
| 171 log += self.__GetHostForwarderLog() | |
| 172 test_type = result.GetType() | |
| 173 done = True | |
| 174 break | |
| 175 if done: | |
| 176 break | |
| 177 duration_ms = int(time.time()) * 1000 - start_ms | |
| 178 | |
| 179 overall_result = base_test_result.TestRunResults() | |
| 180 overall_result.AddResult( | |
| 181 test_result.InstrumentationTestResult( | |
| 182 self.tagged_name, test_type, start_ms, duration_ms, log=log)) | |
| 183 return overall_result | |
| 184 | |
| 185 def __str__(self): | |
| 186 return self.tagged_name | |
| 187 | |
| 188 def __repr__(self): | |
| 189 return self.tagged_name | |
| OLD | NEW |