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

Side by Side Diff: build/android/pylib/python_test_base.py

Issue 10703165: Android: adds instrumentation test runners. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 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 | Annotate | Revision Log
« no previous file with comments | « build/android/pylib/json_perf_parser.py ('k') | build/android/pylib/python_test_caller.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2012 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 Android Python-driven tests.
6
7 This test case is intended to serve as the base class for any Python-driven
8 tests. It is similar to the Python unitttest module in that the user's tests
9 inherit from this case and add their tests in that case.
10
11 When a PythonTestBase object is instantiated, its purpose is to run only one of
12 its tests. The test runner gives it the name of the test the instance will
13 run. The test runner calls SetUp with the Android device ID which the test will
14 run against. The runner runs the test method itself, collecting the result,
15 and calls TearDown.
16
17 Tests can basically do whatever they want in the test methods, such as call
18 Java tests using _RunJavaTests. Those methods have the advantage of massaging
19 the Java test results into Python test results.
20 """
21
22 import logging
23 import os
24 import time
25
26 import android_commands
27 import apk_info
28 from run_java_tests import TestRunner
29 import test_options_parser
30 from test_result import SingleTestResult, TestResults, PYTHON
31
32
33 # aka the parent of com.google.android
34 BASE_ROOT = 'src' + os.sep
35
36
37 class PythonTestBase(object):
38 """Base class for Python-driven tests."""
39
40 def __init__(self, test_name):
41 # test_name must match one of the test methods defined on a subclass which
42 # inherits from this class.
43 # It's stored so we can do the attr lookup on demand, allowing this class
44 # to be pickled, a requirement for the multiprocessing module.
45 self.test_name = test_name
46 class_name = self.__class__.__name__
47 self.qualified_name = class_name + '.' + self.test_name
48 self.ports_to_forward = []
49
50 def SetUp(self, device_id, shard_index):
51 self.shard_index = shard_index
52 self.device_id = device_id
53 self.adb = android_commands.AndroidCommands(self.device_id)
54
55 def TearDown(self):
56 pass
57
58 def Run(self):
59 logging.warning('Running Python-driven test: %s', self.test_name)
60 return getattr(self, self.test_name)()
61
62 def _RunJavaTest(self, fname, suite, test):
63 """Runs a single Java test with a Java TestRunner.
64
65 Args:
66 fname: filename for the test (e.g. foo/bar/baz/tests/FooTest.py)
67 suite: name of the Java test suite (e.g. FooTest)
68 test: name of the test method to run (e.g. testFooBar)
69
70 Returns:
71 TestResults object with a single test result.
72 """
73 test = self._ComposeFullTestName(fname, suite, test)
74 # Get a set of default options
75 options = test_options_parser.ParseInstrumentationArgs([''])
76 apks = [apk_info.ApkInfo(options.test_apk_path, options.test_apk_jar_path)]
77 java_test_runner = TestRunner(options, self.device_id, [test], False,
78 self.shard_index,
79 apks,
80 self.ports_to_forward)
81 return java_test_runner.Run()
82
83 def _RunJavaTests(self, fname, tests):
84 """Calls a list of tests and stops at the first test failure.
85
86 This method iterates until either it encounters a non-passing test or it
87 exhausts the list of tests. Then it returns the appropriate Python result.
88
89 Args:
90 fname: filename for the Python test
91 tests: a list of Java test names which will be run
92
93 Returns:
94 A TestResults object containing a result for this Python test.
95 """
96 start_ms = int(time.time()) * 1000
97
98 result = None
99 for test in tests:
100 # We're only running one test at a time, so this TestResults object will
101 # hold only one result.
102 suite, test_name = test.split('.')
103 result = self._RunJavaTest(fname, suite, test_name)
104 # A non-empty list means the test did not pass.
105 if result.GetAllBroken():
106 break
107
108 duration_ms = int(time.time()) * 1000 - start_ms
109
110 # Do something with result.
111 return self._ProcessResults(result, start_ms, duration_ms)
112
113 def _ProcessResults(self, result, start_ms, duration_ms):
114 """Translates a Java test result into a Python result for this test.
115
116 The TestRunner class that we use under the covers will return a test result
117 for that specific Java test. However, to make reporting clearer, we have
118 this method to abstract that detail and instead report that as a failure of
119 this particular test case while still including the Java stack trace.
120
121 Args:
122 result: TestResults with a single Java test result
123 start_ms: the time the test started
124 duration_ms: the length of the test
125
126 Returns:
127 A TestResults object containing a result for this Python test.
128 """
129 test_results = TestResults()
130
131 # If our test is in broken, then it crashed/failed.
132 broken = result.GetAllBroken()
133 if broken:
134 # Since we have run only one test, take the first and only item.
135 single_result = broken[0]
136
137 log = single_result.log
138 if not log:
139 log = 'No logging information.'
140
141 short_error_msg = single_result.log.split('\n')[0]
142 # err_info is ostensibly for Sponge to consume; it's a short error
143 # message and a longer one.
144 err_info = (short_error_msg, log)
145
146 python_result = SingleTestResult(self.qualified_name, start_ms,
147 duration_ms,
148 PYTHON,
149 log,
150 err_info)
151
152 # Figure out where the test belonged. There's probably a cleaner way of
153 # doing this.
154 if single_result in result.crashed:
155 test_results.crashed = [python_result]
156 elif single_result in result.failed:
157 test_results.failed = [python_result]
158 elif single_result in result.unknown:
159 test_results.unknown = [python_result]
160
161 else:
162 python_result = SingleTestResult(self.qualified_name, start_ms,
163 duration_ms,
164 PYTHON)
165 test_results.ok = [python_result]
166
167 return test_results
168
169 def _ComposeFullTestName(self, fname, suite, test):
170 package_name = self._GetPackageName(fname)
171 return package_name + '.' + suite + '#' + test
172
173 def _GetPackageName(self, fname):
174 """Extracts the package name from the test file path."""
175 dirname = os.path.dirname(fname)
176 package = dirname[dirname.rfind(BASE_ROOT) + len(BASE_ROOT):]
177 return package.replace(os.sep, '.')
OLDNEW
« no previous file with comments | « build/android/pylib/json_perf_parser.py ('k') | build/android/pylib/python_test_caller.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698