Index: build/android/pylib/gtest/test_runner.py |
diff --git a/build/android/pylib/gtest/test_runner.py b/build/android/pylib/gtest/test_runner.py |
index ad8d9324a17ccc57d34a2db3c37bb481e8de7f95..159d66a4083254ebca34038dd92b4c5c89943837 100644 |
--- a/build/android/pylib/gtest/test_runner.py |
+++ b/build/android/pylib/gtest/test_runner.py |
@@ -4,16 +4,17 @@ |
import logging |
import os |
+import re |
from pylib import android_commands |
from pylib import constants |
-from pylib.android_commands import errors |
+from pylib import pexpect |
from pylib.base import base_test_result |
from pylib.base import base_test_runner |
from pylib.utils import run_tests_helper |
import test_package_apk |
-import test_package_executable |
+import test_package_exe |
def _TestSuiteRequiresMockTestServer(test_suite_basename): |
@@ -26,32 +27,37 @@ def _TestSuiteRequiresMockTestServer(test_suite_basename): |
class TestRunner(base_test_runner.BaseTestRunner): |
- """Single test suite attached to a single device. |
- |
- Args: |
- device: Device to run the tests. |
- test_suite: A specific test suite to run, empty to run all. |
- test_arguments: Additional arguments to pass to the test binary. |
- timeout: Timeout for each test. |
- cleanup_test_files: Whether or not to cleanup test files on device. |
- tool_name: Name of the Valgrind tool. |
- build_type: 'Release' or 'Debug'. |
- in_webkit_checkout: Whether the suite is being run from a WebKit checkout. |
- push_deps: If True, push all dependencies to the device. |
- test_apk_package_name: Apk package name for tests running in APKs. |
- test_activity_name: Test activity to invoke for APK tests. |
- command_line_file: Filename to use to pass arguments to tests. |
- """ |
- |
def __init__(self, device, test_suite, test_arguments, timeout, |
cleanup_test_files, tool_name, build_type, |
in_webkit_checkout, push_deps, test_apk_package_name=None, |
test_activity_name=None, command_line_file=None): |
- super(TestRunner, self).__init__(device, tool_name, build_type, push_deps) |
+ """Single test suite attached to a single device. |
+ |
+ Args: |
+ device: Device to run the tests. |
+ test_suite: A specific test suite to run, empty to run all. |
+ test_arguments: Additional arguments to pass to the test binary. |
+ timeout: Timeout for each test. |
+ cleanup_test_files: Whether or not to cleanup test files on device. |
+ tool_name: Name of the Valgrind tool. |
+ build_type: 'Release' or 'Debug'. |
+ in_webkit_checkout: Whether the suite is being run from a WebKit checkout. |
+ push_deps: If True, push all dependencies to the device. |
+ test_apk_package_name: Apk package name for tests running in APKs. |
+ test_activity_name: Test activity to invoke for APK tests. |
+ command_line_file: Filename to use to pass arguments to tests. |
+ """ |
+ super(TestRunner, self).__init__(device, tool_name, build_type, push_deps, |
+ cleanup_test_files) |
self._running_on_emulator = self.device.startswith('emulator') |
self._test_arguments = test_arguments |
self.in_webkit_checkout = in_webkit_checkout |
- self._cleanup_test_files = cleanup_test_files |
+ if timeout == 0: |
+ timeout = 60 |
+ # On a VM (e.g. chromium buildbots), this timeout is way too small. |
+ if os.environ.get('BUILDBOT_SLAVENAME'): |
+ timeout = timeout * 2 |
+ self.timeout = timeout * self.tool.GetTimeoutScale() |
logging.warning('Test suite: ' + test_suite) |
if os.path.splitext(test_suite)[1] == '.apk': |
@@ -59,8 +65,6 @@ class TestRunner(base_test_runner.BaseTestRunner): |
self.adb, |
device, |
test_suite, |
- timeout, |
- self._cleanup_test_files, |
self.tool, |
test_apk_package_name, |
test_activity_name, |
@@ -70,18 +74,16 @@ class TestRunner(base_test_runner.BaseTestRunner): |
# generation. |
symbols_dir = os.path.join(constants.DIR_SOURCE_ROOT, 'out', build_type, |
'lib.target') |
- self.test_package = test_package_executable.TestPackageExecutable( |
+ self.test_package = test_package_exe.TestPackageExecutable( |
self.adb, |
device, |
test_suite, |
- timeout, |
- self._cleanup_test_files, |
self.tool, |
symbols_dir) |
#override |
def InstallTestPackage(self): |
- self.test_package.StripAndCopyExecutable() |
+ self.test_package.Install() |
#override |
def PushDataDeps(self): |
@@ -143,6 +145,85 @@ class TestRunner(base_test_runner.BaseTestRunner): |
gtest_filter_base_path + '_emulator_additional_disabled')) |
return disabled_tests |
+ def _ParseTestOutput(self, p): |
+ """Process the test output. |
+ |
+ Args: |
+ p: An instance of pexpect spawn class. |
+ |
+ Returns: |
+ A TestRunResults object. |
+ """ |
+ results = base_test_result.TestRunResults() |
+ |
+ # Test case statuses. |
+ re_run = re.compile('\[ RUN \] ?(.*)\r\n') |
+ re_fail = re.compile('\[ FAILED \] ?(.*)\r\n') |
+ re_ok = re.compile('\[ OK \] ?(.*?) .*\r\n') |
+ |
+ # Test run statuses. |
+ re_passed = re.compile('\[ PASSED \] ?(.*)\r\n') |
+ re_runner_fail = re.compile('\[ RUNNER_FAILED \] ?(.*)\r\n') |
+ # Signal handlers are installed before starting tests |
+ # to output the CRASHED marker when a crash happens. |
+ re_crash = re.compile('\[ CRASHED \](.*)\r\n') |
+ |
+ log = '' |
+ try: |
+ while True: |
+ full_test_name = None |
+ found = p.expect([re_run, re_passed, re_runner_fail], |
+ timeout=self.timeout) |
+ if found == 1: # re_passed |
+ break |
+ elif found == 2: # re_runner_fail |
+ break |
+ else: # re_run |
+ full_test_name = p.match.group(1).replace('\r', '') |
+ found = p.expect([re_ok, re_fail, re_crash], timeout=self.timeout) |
+ log = p.before.replace('\r', '') |
+ if found == 0: # re_ok |
+ if full_test_name == p.match.group(1).replace('\r', ''): |
+ results.AddResult(base_test_result.BaseTestResult( |
+ full_test_name, base_test_result.ResultType.PASS, |
+ log=log)) |
+ elif found == 2: # re_crash |
+ results.AddResult(base_test_result.BaseTestResult( |
+ full_test_name, base_test_result.ResultType.CRASH, |
+ log=log)) |
+ break |
+ else: # re_fail |
+ results.AddResult(base_test_result.BaseTestResult( |
+ full_test_name, base_test_result.ResultType.FAIL, log=log)) |
+ except pexpect.EOF: |
+ logging.error('Test terminated - EOF') |
+ # We're here because either the device went offline, or the test harness |
+ # crashed without outputting the CRASHED marker (crbug.com/175538). |
+ if not self.adb.IsOnline(): |
+ raise android_commands.errors.DeviceUnresponsiveError( |
+ 'Device %s went offline.' % self.device) |
+ if full_test_name: |
+ results.AddResult(base_test_result.BaseTestResult( |
+ full_test_name, base_test_result.ResultType.CRASH, |
+ log=p.before.replace('\r', ''))) |
+ except pexpect.TIMEOUT: |
+ logging.error('Test terminated after %d second timeout.', |
+ self.timeout) |
+ if full_test_name: |
+ results.AddResult(base_test_result.BaseTestResult( |
+ full_test_name, base_test_result.ResultType.TIMEOUT, |
+ log=p.before.replace('\r', ''))) |
+ finally: |
+ p.close() |
+ |
+ ret_code = self.test_package.GetGTestReturnCode() |
+ if ret_code: |
+ logging.critical( |
+ 'gtest exit code: %d\npexpect.before: %s\npexpect.after: %s', |
+ ret_code, p.before, p.after) |
+ |
+ return results |
+ |
#override |
def RunTest(self, test): |
test_results = base_test_result.TestRunResults() |
@@ -151,8 +232,9 @@ class TestRunner(base_test_runner.BaseTestRunner): |
try: |
self.test_package.ClearApplicationState() |
- self.test_package.CreateTestRunnerScript(test, self._test_arguments) |
- test_results = self.test_package.RunTestsAndListResults() |
+ self.test_package.CreateCommandLineFileOnDevice( |
+ test, self._test_arguments) |
+ test_results = self._ParseTestOutput(self.test_package.SpawnTestProcess()) |
finally: |
self.CleanupSpawningServerState() |
# Calculate unknown test results. |
@@ -177,6 +259,4 @@ class TestRunner(base_test_runner.BaseTestRunner): |
def TearDown(self): |
"""Cleans up the test enviroment for the test suite.""" |
self.tool.CleanUpEnvironment() |
- if self._cleanup_test_files: |
- self.adb.RemovePushedFiles() |
super(TestRunner, self).TearDown() |