media/webrtc/trunk/build/android/pylib/python_test_base.py

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial