diff options
author | Julien Desprez <jdesprez@google.com> | 2021-11-01 11:20:46 -0700 |
---|---|---|
committer | Julien Desprez <jdesprez@google.com> | 2021-11-01 11:21:10 -0700 |
commit | 3d434fe202b50dba0ce57f804469d7f8ca7d8f1f (patch) | |
tree | 01ef71a70b80b9cd81205bc95f710ad5233f5017 | |
parent | d8305cd73c33110a034922a48df80273a1b007d1 (diff) | |
download | platform_testing-3d434fe202b50dba0ce57f804469d7f8ca7d8f1f.tar.gz |
Move tf python lib to platform_testing
Test: presubmit
Bug: 202742315
Change-Id: Ic45f2fdb4ab9429d0c857df02cca5528f57ac7c9
Merged-In: Ieee715dc88cd17592d2d0f80db5edd2c772313b7
-rw-r--r-- | libraries/tradefed-python-lib/Android.bp | 30 | ||||
-rw-r--r-- | libraries/tradefed-python-lib/OWNERS | 5 | ||||
-rw-r--r-- | libraries/tradefed-python-lib/__init__.py | 0 | ||||
-rw-r--r-- | libraries/tradefed-python-lib/helloWorld/Android.bp | 36 | ||||
-rw-r--r-- | libraries/tradefed-python-lib/helloWorld/AndroidTest.xml | 21 | ||||
-rw-r--r-- | libraries/tradefed-python-lib/helloWorld/test_hello_world.py | 52 | ||||
-rw-r--r-- | libraries/tradefed-python-lib/tradefed_py/__init__.py | 0 | ||||
-rw-r--r-- | libraries/tradefed-python-lib/tradefed_py/adb_handler.py | 57 | ||||
-rw-r--r-- | libraries/tradefed-python-lib/tradefed_py/android_device.py | 61 | ||||
-rw-r--r-- | libraries/tradefed-python-lib/tradefed_py/base_test.py | 53 | ||||
-rw-r--r-- | libraries/tradefed-python-lib/tradefed_py/tf_main.py | 97 | ||||
-rw-r--r-- | libraries/tradefed-python-lib/tradefed_py/tf_runner.py | 227 |
12 files changed, 639 insertions, 0 deletions
diff --git a/libraries/tradefed-python-lib/Android.bp b/libraries/tradefed-python-lib/Android.bp new file mode 100644 index 000000000..13a3ff415 --- /dev/null +++ b/libraries/tradefed-python-lib/Android.bp @@ -0,0 +1,30 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Base library that should be extended/included to run as part of Tradefed +python_library_host { + name: "tradefed_python_lib", + srcs: [ + "tradefed_py/*.py", + ], + version: { + py2: { + enabled: true, + }, + py3: { + enabled: false, + }, + } +} + diff --git a/libraries/tradefed-python-lib/OWNERS b/libraries/tradefed-python-lib/OWNERS new file mode 100644 index 000000000..23f5f25de --- /dev/null +++ b/libraries/tradefed-python-lib/OWNERS @@ -0,0 +1,5 @@ +# Root Owners of the Tradefed python repo for code reviews +dshi@google.com +frankfeng@google.com +guangzhu@google.com +jdesprez@google.com diff --git a/libraries/tradefed-python-lib/__init__.py b/libraries/tradefed-python-lib/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/libraries/tradefed-python-lib/__init__.py diff --git a/libraries/tradefed-python-lib/helloWorld/Android.bp b/libraries/tradefed-python-lib/helloWorld/Android.bp new file mode 100644 index 000000000..529331ce6 --- /dev/null +++ b/libraries/tradefed-python-lib/helloWorld/Android.bp @@ -0,0 +1,36 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Example hello world test that can run in Tradefed +python_binary_host { + name: "tradefed_hello_world", + main: "test_hello_world.py", + srcs: [ + "test_hello_world.py", + ], + libs: [ + "tradefed_python_lib", + ], + test_suites: ["null-suite"], + version: { + py2: { + enabled: true, + embedded_launcher: true, + }, + py3: { + enabled: false, + }, + } +} + diff --git a/libraries/tradefed-python-lib/helloWorld/AndroidTest.xml b/libraries/tradefed-python-lib/helloWorld/AndroidTest.xml new file mode 100644 index 000000000..bce1f0ab1 --- /dev/null +++ b/libraries/tradefed-python-lib/helloWorld/AndroidTest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Config for python tradefed hello world"> + <option name="test-suite-tag" value="python-tradefed" /> + <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" > + <option name="par-file-name" value="tradefed_hello_world" /> + </test> +</configuration> diff --git a/libraries/tradefed-python-lib/helloWorld/test_hello_world.py b/libraries/tradefed-python-lib/helloWorld/test_hello_world.py new file mode 100644 index 000000000..4b5ad8368 --- /dev/null +++ b/libraries/tradefed-python-lib/helloWorld/test_hello_world.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest +from tradefed_py import base_test +from tradefed_py import tf_main + +class HelloWorldTest(base_test._TradefedTestClass): + """An example showing a possible implementation of python test""" + + def test_hello(self): + self.assertEqual('hello'.upper(), 'HELLO') + + def test_world_failed(self): + self.assertEqual('world'.upper(), 'WORLD2') + + @unittest.skip('demonstrating skipping') + def test_skipped(self): + self.fail('should have been skipped') + + @unittest.expectedFailure + def test_expectation(self): + self.fail('failed') + + @unittest.expectedFailure + def test_failedExpectation(self): + pass + + def test_device(self): + """If a serial was provided this test will check that we can query the + device. It will throw if the serial is invalid. + """ + if self.serial is not None: + res = self.android_device.executeShellCommand('id') + self.assertTrue('uid' in res) + +if __name__ == '__main__': + tf_main.main() diff --git a/libraries/tradefed-python-lib/tradefed_py/__init__.py b/libraries/tradefed-python-lib/tradefed_py/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/libraries/tradefed-python-lib/tradefed_py/__init__.py diff --git a/libraries/tradefed-python-lib/tradefed_py/adb_handler.py b/libraries/tradefed-python-lib/tradefed_py/adb_handler.py new file mode 100644 index 000000000..0464dba39 --- /dev/null +++ b/libraries/tradefed-python-lib/tradefed_py/adb_handler.py @@ -0,0 +1,57 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import subprocess + +class AdbHandler(object): + """Adb wrapper to execute shell and adb command to the device.""" + + def __init__(self, serial=None): + self.serial = serial + self.adb_cmd = 'adb -s {}'.format(serial) + + def exec_adb_command(self, cmd): + """Method to execute an adb command against the device.""" + cmd = "{} {}".format(self.adb_cmd, cmd) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + (out, err) = proc.communicate() + ret = proc.returncode + if ret == 0: + return out + raise AdbError(cmd=cmd, stdout=out, stderr=err, ret_code=ret) + + def exec_shell_command(self, cmd): + """Method to execute a shell command against the device.""" + cmd = '{} shell {}'.format(self.adb_cmd, cmd) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + (out, err) = proc.communicate() + ret = proc.returncode + if ret == 0: + return out + raise AdbError(cmd=cmd, stdout=out, stderr=err, ret_code=ret) + +class AdbError(Exception): + """Raised when there is an error in adb operations.""" + + def __init__(self, cmd, stdout, stderr, ret_code): + self.cmd = cmd + self.stdout = stdout + self.stderr = stderr + self.ret_code = ret_code + + def __str__(self): + return ('Error executing adb cmd "%s". ret: %d, stdout: %s, stderr: %s' + ) % (self.cmd, self.ret_code, self.stdout, self.stderr) diff --git a/libraries/tradefed-python-lib/tradefed_py/android_device.py b/libraries/tradefed-python-lib/tradefed_py/android_device.py new file mode 100644 index 000000000..568054fc6 --- /dev/null +++ b/libraries/tradefed-python-lib/tradefed_py/android_device.py @@ -0,0 +1,61 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import adb_handler + +class AndroidTestDevice(object): + """Class representing an android device. + + Each instance represents a different device connected to adb. + """ + + def __init__(self, serial=None, stream=None): + # TODO: Implement and flesh out the device interface + self.serial = serial + self._logging = stream + self.adb = adb_handler.AdbHandler(serial) + + def executeShellCommand(self, cmd): + """Convenience method to call the adb wrapper to execute a shell command. + + Args: + cmd: The command to be executed in 'adb shell' + + Returns: + The stdout of the command if succeed. Or raise AdbError if failed. + """ + return self.adb.exec_shell_command(cmd) + + def getProp(self, name): + if not name: + raise DeviceCommandError('getProp', 'Name of property cannot be None') + out = self.executeShellCommand('getprop %s' % name) + return out.strip() + + def _printHostLog(self, message): + self._logging.write('%s \n' % message) + +class DeviceCommandError(Exception): + """ Exception raised when an error is encountered while running a command. + """ + + def __init__(self, cmd, message): + self.cmd = cmd + self.message = message + + def __str__(self): + return ('Error executing device cmd "%s". message: "%s"' + ) % (self.cmd, self.message) diff --git a/libraries/tradefed-python-lib/tradefed_py/base_test.py b/libraries/tradefed-python-lib/tradefed_py/base_test.py new file mode 100644 index 000000000..4fd916eb7 --- /dev/null +++ b/libraries/tradefed-python-lib/tradefed_py/base_test.py @@ -0,0 +1,53 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import android_device +import json +import unittest + +_DATA_NAME = 'dataName' +_DATA_TYPE = 'dataType' +_DATA_FILE = 'dataFile' + +class _TradefedTestClass(unittest.TestCase): + """ A base test class to extends to receive a device object for testing in python + + All tests should extends this class to be properly supported by Tradefed. + """ + + def setUpDevice(self, serial, stream, options): + """ Setter method that will allow the test to receive the device object + + Args: + serial: The serial of the device allocated for the test. + stream: The output stream. + options: Additional options given to the tests that can be used. + """ + self.serial = serial + self.stream = stream + self.extra_options = options + self.android_device = android_device.AndroidTestDevice(serial, stream) + + def logFileToTradefed(self, name, filePath, fileType): + """ Callback to log a file that will be picked up by Tradefed. + + Args: + name: The name under which log the particular data. + filePath: Absolute file path of the file to be logged. + fileType: The type of the file. (TEXT, PNG, etc.) + """ + resp = {_DATA_NAME: name, _DATA_TYPE: fileType, _DATA_FILE: filePath} + self.stream.write('TEST_LOG %s\n' % json.dumps(resp)) diff --git a/libraries/tradefed-python-lib/tradefed_py/tf_main.py b/libraries/tradefed-python-lib/tradefed_py/tf_main.py new file mode 100644 index 000000000..43a6aa59e --- /dev/null +++ b/libraries/tradefed-python-lib/tradefed_py/tf_main.py @@ -0,0 +1,97 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import getopt +import os +import sys +import tf_runner +from unittest import loader +import unittest + +class TradefedProgram(unittest.TestProgram): + """ Main Runner Class that should be used to run the tests. This runner ensure that the + reporting is compatible with Tradefed. + """ + + def __init__(self, module='__main__', defaultTest=None, + argv=None, testRunner=None, + testLoader=loader.defaultTestLoader, exit=True, + verbosity=1, failfast=None, catchbreak=None, buffer=None, serial=None): + self.serial = None + self.extra_options = [] + super(TradefedProgram, self).__init__() + + def parseArgs(self, argv): + if len(argv) > 1 and argv[1].lower() == 'discover': + self._do_discovery(argv[2:]) + return + + long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer', + 'serial=', 'extra_options='] + try: + options, args = getopt.getopt(argv[1:], 'hHvqfcbs:e:', long_opts) + for opt, value in options: + if opt in ('-h','-H','--help'): + self.usageExit() + if opt in ('-q','--quiet'): + self.verbosity = 0 + if opt in ('-v','--verbose'): + self.verbosity = 2 + if opt in ('-f','--failfast'): + if self.failfast is None: + self.failfast = True + # Should this raise an exception if -f is not valid? + if opt in ('-c','--catch'): + if self.catchbreak is None and installHandler is not None: + self.catchbreak = True + # Should this raise an exception if -c is not valid? + if opt in ('-b','--buffer'): + if self.buffer is None: + self.buffer = True + # Should this raise an exception if -b is not valid? + if opt in ('-s', '--serial'): + if self.serial is None: + self.serial = value + if opt in ('-e', '--extra_options'): + self.extra_options.append(value) + if len(args) == 0 and self.defaultTest is None: + # createTests will load tests from self.module + self.testNames = None + elif len(args) > 0: + self.testNames = args + if __name__ == '__main__': + # to support python -m unittest ... + self.module = None + else: + self.testNames = (self.defaultTest,) + self.createTests() + except getopt.error, msg: + self.usageExit(msg) + + def runTests(self): + if self.testRunner is None: + self.testRunner = tf_runner.TfTextTestRunner(verbosity=self.verbosity, + failfast=self.failfast, + buffer=self.buffer, + resultclass=tf_runner.TextTestResult, + serial=self.serial, + extra_options=self.extra_options) + super(TradefedProgram, self).runTests() + +main = TradefedProgram + +def main_run(): + TradefedProgram(module=None) diff --git a/libraries/tradefed-python-lib/tradefed_py/tf_runner.py b/libraries/tradefed-python-lib/tradefed_py/tf_runner.py new file mode 100644 index 000000000..d571082f3 --- /dev/null +++ b/libraries/tradefed-python-lib/tradefed_py/tf_runner.py @@ -0,0 +1,227 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import sys +import json +import time +import traceback +import unittest +from unittest.util import strclass +from unittest.signals import registerResult + +# Tags that Tradefed can understand in SubprocessResultParser to get results +_CLASSNAME_TAG = 'className' +_METHOD_NAME_TAG = 'testName' +_START_TIME_TAG = 'start_time' +_END_TIME_TAG = 'end_time' +_TRACE_TAG = 'trace' +_TEST_COUNT_TAG = 'testCount' +_REASON_TAG = 'reason' +_TIME_TAG = 'time' + +class TextTestResult(unittest.TextTestResult): + """ Class for callbacks based on test state""" + + def _getClassName(self, test): + return strclass(test.__class__) + + def _getMethodName(self, test): + return test._testMethodName + + def startTestRun(self, count): + """ Callback that marks a test run has started. + + Args: + count: The number of expected tests. + """ + resp = {_TEST_COUNT_TAG: count, 'runName': 'python-tradefed'} + self.stream.write('TEST_RUN_STARTED %s\n' % json.dumps(resp)) + super(TextTestResult, self).startTestRun() + + def startTest(self, test): + """ Callback that marks a test has started to run. + + Args: + test: The test that started. + """ + resp = {_START_TIME_TAG: time.time(), _CLASSNAME_TAG: self._getClassName(test), _METHOD_NAME_TAG: self._getMethodName(test)} + self.stream.write('TEST_STARTED %s\n' % json.dumps(resp)) + super(TextTestResult, self).startTest(test) + + def addSuccess(self, test): + """ Callback that marks a test has finished and passed + + Args: + test: The test that passed. + """ + resp = {_END_TIME_TAG: time.time(), _CLASSNAME_TAG: self._getClassName(test), _METHOD_NAME_TAG: self._getMethodName(test)} + self.stream.write('TEST_ENDED %s\n' % json.dumps(resp)) + super(TextTestResult, self).addSuccess(test) + + def addFailure(self, test, err): + """ Callback that marks a test has failed + + Args: + test: The test that failed. + err: the error generated that should be reported. + """ + resp = {_CLASSNAME_TAG: self._getClassName(test), _METHOD_NAME_TAG: self._getMethodName(test), _TRACE_TAG: '\n'.join(traceback.format_exception(*err))} + self.stream.write('TEST_FAILED %s\n' % json.dumps(resp)) + resp = {_END_TIME_TAG: time.time(), _CLASSNAME_TAG: self._getClassName(test), _METHOD_NAME_TAG: self._getMethodName(test)} + self.stream.write('TEST_ENDED %s\n' % json.dumps(resp)) + super(TextTestResult, self).addFailure(test, err) + + def addSkip(self, test, reason): + """ Callback that marks a test was being skipped + + Args: + test: The test being skipped. + reason: the message generated that should be reported. + """ + resp = {_CLASSNAME_TAG: self._getClassName(test), _METHOD_NAME_TAG: self._getMethodName(test)} + self.stream.write('TEST_IGNORED %s\n' % json.dumps(resp)) + resp = {_END_TIME_TAG: time.time(), _CLASSNAME_TAG: self._getClassName(test), _METHOD_NAME_TAG: self._getMethodName(test)} + self.stream.write('TEST_ENDED %s\n' % json.dumps(resp)) + super(TextTestResult, self).addSkip(test, reason) + + def addExpectedFailure(self, test, err): + """ Callback that marks a test was expected to fail and failed. + + Args: + test: The test responsible for the error. + err: the error generated that should be reported. + """ + resp = {_CLASSNAME_TAG: self._getClassName(test), _METHOD_NAME_TAG: self._getMethodName(test), _TRACE_TAG: '\n'.join(traceback.format_exception(*err))} + self.stream.write('TEST_ASSUMPTION_FAILURE %s\n' % json.dumps(resp)) + resp = {_END_TIME_TAG: time.time(), _CLASSNAME_TAG: self._getClassName(test), _METHOD_NAME_TAG: self._getMethodName(test)} + self.stream.write('TEST_ENDED %s\n' % json.dumps(resp)) + super(TextTestResult, self).addExpectedFailure(test, err) + + def addUnexpectedSuccess(self, test): + """ Callback that marks a test was expected to fail but passed. + + Args: + test: The test responsible for the unexpected success. + """ + resp = {_CLASSNAME_TAG: self._getClassName(test), _METHOD_NAME_TAG: self._getMethodName(test), _TRACE_TAG: 'Unexpected success'} + self.stream.write('TEST_ASSUMPTION_FAILURE %s\n' % json.dumps(resp)) + resp = {_END_TIME_TAG: time.time(), _CLASSNAME_TAG: self._getClassName(test), _METHOD_NAME_TAG: self._getMethodName(test)} + self.stream.write('TEST_ENDED %s\n' % json.dumps(resp)) + super(TextTestResult, self).addUnexpectedSuccess(test) + + def addError(self, test, err): + """ Callback that marks a run as failed because of an error. + + Args: + test: The test responsible for the error. + err: the error generated that should be reported. + """ + resp = {_REASON_TAG: '\n'.join(traceback.format_exception(*err))} + self.stream.write('TEST_RUN_FAILED %s\n' % json.dumps(resp)) + super(TextTestResult, self).addError(test, err) + + def stopTestRun(self, elapsedTime): + """ Callback that marks the end of a test run + + Args: + elapsedTime: The elapsed time of the run. + """ + resp = {_TIME_TAG: elapsedTime} + self.stream.write('TEST_RUN_ENDED %s\n' % json.dumps(resp)) + super(TextTestResult, self).stopTestRun() + +class TfTextTestRunner(unittest.TextTestRunner): + """ Class runner that ensure the callbacks order""" + + def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1, + failfast=False, buffer=False, resultclass=None, serial=None, extra_options=None): + self.serial = serial + self.extra_options = extra_options + unittest.TextTestRunner.__init__(self, stream, descriptions, verbosity, failfast, buffer, resultclass) + + def _injectDevice(self, testSuites): + """ Method to inject options to the base Python Tradefed class + + Args: + testSuites: the current test holder. + """ + if self.serial is not None: + for testSuite in testSuites: + # each test in the test suite + for test in testSuite._tests: + try: + test.setUpDevice(self.serial, self.stream, self.extra_options) + except AttributeError: + self.stream.writeln('Test %s does not implement _TradefedTestClass.' % test) + + def run(self, test): + """ Run the given test case or test suite. Copied from unittest to replace the startTestRun + callback""" + result = self._makeResult() + result.failfast = self.failfast + result.buffer = self.buffer + registerResult(result) + startTime = time.time() + startTestRun = getattr(result, 'startTestRun', None) + if startTestRun is not None: + startTestRun(test.countTestCases()) + try: + self._injectDevice(test) + test(result) + finally: + stopTestRun = getattr(result, 'stopTestRun', None) + if stopTestRun is not None: + stopTestRun(time.time() - startTime) + else: + result.printErrors() + stopTime = time.time() + timeTaken = stopTime - startTime + if hasattr(result, 'separator2'): + self.stream.writeln(result.separator2) + run = result.testsRun + self.stream.writeln('Ran %d test%s in %.3fs' % + (run, run != 1 and 's' or '', timeTaken)) + self.stream.writeln() + + expectedFails = unexpectedSuccesses = skipped = 0 + try: + results = map(len, (result.expectedFailures, + result.unexpectedSuccesses, + result.skipped)) + expectedFails, unexpectedSuccesses, skipped = results + except AttributeError: + pass + infos = [] + if not result.wasSuccessful(): + self.stream.write('FAILED') + failed, errored = map(len, (result.failures, result.errors)) + if failed: + infos.append('failures=%d' % failed) + if errored: + infos.append('errors=%d' % errored) + else: + self.stream.write('OK') + if skipped: + infos.append('skipped=%d' % skipped) + if expectedFails: + infos.append('expected failures=%d' % expectedFails) + if unexpectedSuccesses: + infos.append('unexpected successes=%d' % unexpectedSuccesses) + if infos: + self.stream.writeln(' (%s)' % (', '.join(infos),)) + else: + self.stream.write('\n') + return result |