[Android] Lint the rest of pylib.
[chromium-blink-merge.git] / build / android / pylib / host_driven / test_case.py
blob42c33a0193dee0e6cc4a2afe7031abbc2d30198d
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.
5 """Base class for host-driven test cases.
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.
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.
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 """
23 import logging
24 import os
25 import time
27 from pylib import android_commands
28 from pylib import constants
29 from pylib import forwarder
30 from pylib import valgrind_tools
31 from pylib.base import base_test_result
32 from pylib.instrumentation import test_package
33 from pylib.instrumentation import test_result
34 from pylib.instrumentation import test_runner
36 # aka the parent of com.google.android
37 BASE_ROOT = 'src' + os.sep
40 class HostDrivenTestCase(object):
41 """Base class for host-driven test cases."""
43 _HOST_DRIVEN_TAG = 'HostDriven'
45 def __init__(self, test_name, instrumentation_options=None):
46 """Create a test case initialized to run |test_name|.
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.adb = None
54 self.cleanup_test_files = False
55 self.device_id = ''
56 self.has_forwarded_ports = False
57 self.instrumentation_options = instrumentation_options
58 self.ports_to_forward = []
59 self.push_deps = False
60 self.shard_index = 0
62 # Use tagged_name when creating results, so that we can identify host-driven
63 # tests in the overall results.
64 self.test_name = test_name
65 self.qualified_name = '%s.%s' % (class_name, self.test_name)
66 self.tagged_name = '%s_%s' % (self._HOST_DRIVEN_TAG, self.qualified_name)
68 # TODO(bulach): make ports_to_forward not optional and move the Forwarder
69 # mapping here.
70 def SetUp(self, device, shard_index, push_deps,
71 cleanup_test_files, ports_to_forward=None):
72 if not ports_to_forward:
73 ports_to_forward = []
74 self.device_id = device
75 self.shard_index = shard_index
76 self.adb = android_commands.AndroidCommands(self.device_id)
77 self.push_deps = push_deps
78 self.cleanup_test_files = cleanup_test_files
79 if ports_to_forward:
80 self.ports_to_forward = ports_to_forward
82 def TearDown(self):
83 pass
85 # TODO(craigdh): Remove GetOutDir once references have been removed
86 # downstream.
87 @staticmethod
88 def GetOutDir():
89 return constants.GetOutDirectory()
91 def Run(self):
92 logging.info('Running host-driven test: %s', self.tagged_name)
93 # Get the test method on the derived class and execute it
94 return getattr(self, self.test_name)()
96 @staticmethod
97 def __GetHostForwarderLog():
98 return ('-- Begin Full HostForwarder log\n'
99 '%s\n'
100 '--End Full HostForwarder log\n' % forwarder.Forwarder.GetHostLog())
102 def __StartForwarder(self):
103 logging.warning('Forwarding %s %s', self.ports_to_forward,
104 self.has_forwarded_ports)
105 if self.ports_to_forward and not self.has_forwarded_ports:
106 self.has_forwarded_ports = True
107 tool = valgrind_tools.CreateTool(None, self.adb)
108 forwarder.Forwarder.Map([(port, port) for port in self.ports_to_forward],
109 self.adb, tool)
111 def __RunJavaTest(self, test, test_pkg, additional_flags=None):
112 """Runs a single Java test in a Java TestRunner.
114 Args:
115 test: Fully qualified test name (ex. foo.bar.TestClass#testMethod)
116 test_pkg: TestPackage object.
117 additional_flags: A list of additional flags to add to the command line.
119 Returns:
120 TestRunResults object with a single test result.
122 # TODO(bulach): move this to SetUp() stage.
123 self.__StartForwarder()
125 java_test_runner = test_runner.TestRunner(self.instrumentation_options,
126 self.device_id,
127 self.shard_index, test_pkg,
128 additional_flags=additional_flags)
129 try:
130 java_test_runner.SetUp()
131 return java_test_runner.RunTest(test)[0]
132 finally:
133 java_test_runner.TearDown()
135 def _RunJavaTestFilters(self, test_filters, additional_flags=None):
136 """Calls a list of tests and stops at the first test failure.
138 This method iterates until either it encounters a non-passing test or it
139 exhausts the list of tests. Then it returns the appropriate overall result.
141 Test cases may make use of this method internally to assist in running
142 instrumentation tests. This function relies on instrumentation_options
143 being defined.
145 Args:
146 test_filters: A list of Java test filters.
147 additional_flags: A list of addition flags to add to the command line.
149 Returns:
150 A TestRunResults object containing an overall result for this set of Java
151 tests. If any Java tests do not pass, this is a fail overall.
153 test_type = base_test_result.ResultType.PASS
154 log = ''
156 test_pkg = test_package.TestPackage(
157 self.instrumentation_options.test_apk_path,
158 self.instrumentation_options.test_apk_jar_path)
160 start_ms = int(time.time()) * 1000
161 done = False
162 for test_filter in test_filters:
163 tests = test_pkg.GetAllMatchingTests(None, None, test_filter)
164 # Filters should always result in >= 1 test.
165 if len(tests) == 0:
166 raise Exception('Java test filter "%s" returned no tests.'
167 % test_filter)
168 for test in tests:
169 # We're only running one test at a time, so this TestRunResults object
170 # will hold only one result.
171 java_result = self.__RunJavaTest(test, test_pkg, additional_flags)
172 assert len(java_result.GetAll()) == 1
173 if not java_result.DidRunPass():
174 result = java_result.GetNotPass().pop()
175 log = result.GetLog()
176 log += self.__GetHostForwarderLog()
177 test_type = result.GetType()
178 done = True
179 break
180 if done:
181 break
182 duration_ms = int(time.time()) * 1000 - start_ms
184 overall_result = base_test_result.TestRunResults()
185 overall_result.AddResult(
186 test_result.InstrumentationTestResult(
187 self.tagged_name, test_type, start_ms, duration_ms, log=log))
188 return overall_result
190 def __str__(self):
191 return self.tagged_name
193 def __repr__(self):
194 return self.tagged_name