152 lines
5.5 KiB
Python
152 lines
5.5 KiB
Python
|
# Copyright 2013 The Chromium Authors. All rights reserved.
|
||
|
# Use of this source code is governed by a BSD-style license that can be
|
||
|
# found in the LICENSE file.
|
||
|
|
||
|
"""Base class for host-driven test cases.
|
||
|
|
||
|
This test case is intended to serve as the base class for any host-driven
|
||
|
test cases. It is similar to the Python unitttest module in that test cases
|
||
|
inherit from this class and add methods which will be run as tests.
|
||
|
|
||
|
When a HostDrivenTestCase object is instantiated, its purpose is to run only one
|
||
|
test method in the derived class. The test runner gives it the name of the test
|
||
|
method the instance will run. The test runner calls SetUp with the device ID
|
||
|
which the test method will run against. The test runner runs the test method
|
||
|
itself, collecting the result, and calls TearDown.
|
||
|
|
||
|
Tests can perform arbitrary Python commands and asserts in test methods. Tests
|
||
|
that run instrumentation tests can make use of the _RunJavaTests helper function
|
||
|
to trigger Java tests and convert results into a single host-driven test result.
|
||
|
"""
|
||
|
|
||
|
import logging
|
||
|
import os
|
||
|
import time
|
||
|
|
||
|
from pylib import android_commands
|
||
|
from pylib.base import base_test_result
|
||
|
from pylib.instrumentation import test_package
|
||
|
from pylib.instrumentation import test_result
|
||
|
from pylib.instrumentation import test_runner
|
||
|
|
||
|
# aka the parent of com.google.android
|
||
|
BASE_ROOT = 'src' + os.sep
|
||
|
|
||
|
|
||
|
class HostDrivenTestCase(object):
|
||
|
"""Base class for host-driven test cases."""
|
||
|
|
||
|
_HOST_DRIVEN_TAG = 'HostDriven'
|
||
|
|
||
|
def __init__(self, test_name, instrumentation_options=None):
|
||
|
"""Create a test case initialized to run |test_name|.
|
||
|
|
||
|
Args:
|
||
|
test_name: The name of the method to run as the test.
|
||
|
instrumentation_options: An InstrumentationOptions object.
|
||
|
"""
|
||
|
self.test_name = test_name
|
||
|
class_name = self.__class__.__name__
|
||
|
self.qualified_name = '%s.%s' % (class_name, self.test_name)
|
||
|
# Use tagged_name when creating results, so that we can identify host-driven
|
||
|
# tests in the overall results.
|
||
|
self.tagged_name = '%s_%s' % (self._HOST_DRIVEN_TAG, self.qualified_name)
|
||
|
|
||
|
self.instrumentation_options = instrumentation_options
|
||
|
self.ports_to_forward = []
|
||
|
|
||
|
def SetUp(self, device, shard_index, build_type, push_deps,
|
||
|
cleanup_test_files):
|
||
|
self.device_id = device
|
||
|
self.shard_index = shard_index
|
||
|
self.build_type = build_type
|
||
|
self.adb = android_commands.AndroidCommands(self.device_id)
|
||
|
self.push_deps = push_deps
|
||
|
self.cleanup_test_files = cleanup_test_files
|
||
|
|
||
|
def TearDown(self):
|
||
|
pass
|
||
|
|
||
|
def GetOutDir(self):
|
||
|
return os.path.join(os.environ['CHROME_SRC'], 'out',
|
||
|
self.build_type)
|
||
|
|
||
|
def Run(self):
|
||
|
logging.info('Running host-driven test: %s', self.tagged_name)
|
||
|
# Get the test method on the derived class and execute it
|
||
|
return getattr(self, self.test_name)()
|
||
|
|
||
|
def __RunJavaTest(self, package_name, test_case, test_method):
|
||
|
"""Runs a single Java test method with a Java TestRunner.
|
||
|
|
||
|
Args:
|
||
|
package_name: Package name in which the java tests live
|
||
|
(e.g. foo.bar.baz.tests)
|
||
|
test_case: Name of the Java test case (e.g. FooTest)
|
||
|
test_method: Name of the test method to run (e.g. testFooBar)
|
||
|
|
||
|
Returns:
|
||
|
TestRunResults object with a single test result.
|
||
|
"""
|
||
|
test = '%s.%s#%s' % (package_name, test_case, test_method)
|
||
|
test_pkg = test_package.TestPackage(
|
||
|
self.instrumentation_options.test_apk_path,
|
||
|
self.instrumentation_options.test_apk_jar_path)
|
||
|
java_test_runner = test_runner.TestRunner(self.instrumentation_options,
|
||
|
self.device_id,
|
||
|
self.shard_index, test_pkg,
|
||
|
self.ports_to_forward)
|
||
|
try:
|
||
|
java_test_runner.SetUp()
|
||
|
return java_test_runner.RunTest(test)[0]
|
||
|
finally:
|
||
|
java_test_runner.TearDown()
|
||
|
|
||
|
def _RunJavaTests(self, package_name, tests):
|
||
|
"""Calls a list of tests and stops at the first test failure.
|
||
|
|
||
|
This method iterates until either it encounters a non-passing test or it
|
||
|
exhausts the list of tests. Then it returns the appropriate overall result.
|
||
|
|
||
|
Test cases may make use of this method internally to assist in running
|
||
|
instrumentation tests. This function relies on instrumentation_options
|
||
|
being defined.
|
||
|
|
||
|
Args:
|
||
|
package_name: Package name in which the java tests live
|
||
|
(e.g. foo.bar.baz.tests)
|
||
|
tests: A list of Java test names which will be run
|
||
|
|
||
|
Returns:
|
||
|
A TestRunResults object containing an overall result for this set of Java
|
||
|
tests. If any Java tests do not pass, this is a fail overall.
|
||
|
"""
|
||
|
test_type = base_test_result.ResultType.PASS
|
||
|
log = ''
|
||
|
|
||
|
start_ms = int(time.time()) * 1000
|
||
|
for test in tests:
|
||
|
# We're only running one test at a time, so this TestRunResults object
|
||
|
# will hold only one result.
|
||
|
suite, test_name = test.split('.')
|
||
|
java_result = self.__RunJavaTest(package_name, suite, test_name)
|
||
|
assert len(java_result.GetAll()) == 1
|
||
|
if not java_result.DidRunPass():
|
||
|
result = java_result.GetNotPass().pop()
|
||
|
log = result.GetLog()
|
||
|
test_type = result.GetType()
|
||
|
break
|
||
|
duration_ms = int(time.time()) * 1000 - start_ms
|
||
|
|
||
|
overall_result = base_test_result.TestRunResults()
|
||
|
overall_result.AddResult(
|
||
|
test_result.InstrumentationTestResult(
|
||
|
self.tagged_name, test_type, start_ms, duration_ms, log=log))
|
||
|
return overall_result
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.tagged_name
|
||
|
|
||
|
def __repr__(self):
|
||
|
return self.tagged_name
|