0

Add option to push files to device using isolate for instrumentation tests.

BUG=400499

Review URL: https://codereview.chromium.org/689293002

Cr-Commit-Position: refs/heads/master@{#304870}
This commit is contained in:
mikecase
2014-11-19 12:02:05 -08:00
committed by Commit bot
parent 5e71ea74f7
commit 526d68ea2d
11 changed files with 201 additions and 131 deletions

@ -0,0 +1,14 @@
# Copyright (c) 2014 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.
{
'conditions': [
['OS=="android"', {
'variables': {
'isolate_dependency_untracked': [
'<(DEPTH)/android_webview/test/data/device_files/',
],
},
}],
],
}

@ -0,0 +1,62 @@
# Copyright (c) 2014 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 script for doing test setup."""
import logging
import os
from pylib import constants
from pylib import valgrind_tools
from pylib.utils import isolator
def GenerateDepsDirUsingIsolate(suite_name, isolate_file_path,
isolate_file_paths, deps_exclusion_list):
"""Generate the dependency dir for the test suite using isolate.
Args:
suite_name: Name of the test suite (e.g. base_unittests).
isolate_file_path: .isolate file path to use. If there is a default .isolate
file path for the suite_name, this will override it.
isolate_file_paths: Dictionary with the default .isolate file paths for
the test suites.
deps_exclusion_list: A list of files that are listed as dependencies in the
.isolate files but should not be pushed to the device.
"""
if isolate_file_path:
if os.path.isabs(isolate_file_path):
isolate_abs_path = isolate_file_path
else:
isolate_abs_path = os.path.join(constants.DIR_SOURCE_ROOT,
isolate_file_path)
else:
isolate_rel_path = isolate_file_paths.get(suite_name)
if not isolate_rel_path:
logging.info('Did not find an isolate file for the test suite.')
return
isolate_abs_path = os.path.join(constants.DIR_SOURCE_ROOT, isolate_rel_path)
isolated_abs_path = os.path.join(
constants.GetOutDirectory(), '%s.isolated' % suite_name)
assert os.path.exists(isolate_abs_path), 'Cannot find %s' % isolate_abs_path
i = isolator.Isolator(constants.ISOLATE_DEPS_DIR)
i.Clear()
i.Remap(isolate_abs_path, isolated_abs_path)
# We're relying on the fact that timestamps are preserved
# by the remap command (hardlinked). Otherwise, all the data
# will be pushed to the device once we move to using time diff
# instead of md5sum. Perform a sanity check here.
i.VerifyHardlinks()
i.PurgeExcluded(deps_exclusion_list)
i.MoveOutputDeps()
def PushDataDeps(device, device_dir, test_options):
valgrind_tools.PushFilesForTool(test_options.tool, device)
if os.path.exists(constants.ISOLATE_DEPS_DIR):
device.PushChangedFiles([
(os.path.join(constants.ISOLATE_DEPS_DIR, p),
'%s/%s' % (device_dir, p))
for p in os.listdir(constants.ISOLATE_DEPS_DIR)])

@ -70,14 +70,9 @@ class BaseTestRunner(object):
"""Installs the test package once before all tests are run."""
pass
def PushDataDeps(self):
"""Push all data deps to device once before all tests are run."""
pass
def SetUp(self):
"""Run once before all tests are run."""
self.InstallTestPackage()
self.PushDataDeps()
def TearDown(self):
"""Run once after all tests are run."""

@ -10,15 +10,14 @@ import os
import sys
from pylib import constants
from pylib import valgrind_tools
from pylib.base import base_setup
from pylib.base import base_test_result
from pylib.base import test_dispatcher
from pylib.device import device_utils
from pylib.gtest import test_package_apk
from pylib.gtest import test_package_exe
from pylib.gtest import test_runner
from pylib.utils import isolator
sys.path.insert(0,
os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib',
@ -26,7 +25,7 @@ sys.path.insert(0,
import unittest_util # pylint: disable=F0401
_ISOLATE_FILE_PATHS = {
ISOLATE_FILE_PATHS = {
'base_unittests': 'base/base_unittests.isolate',
'blink_heap_unittests':
'third_party/WebKit/Source/platform/heap/BlinkHeapUnitTests.isolate',
@ -48,7 +47,7 @@ _ISOLATE_FILE_PATHS = {
# Used for filtering large data deps at a finer grain than what's allowed in
# isolate files since pushing deps to devices is expensive.
# Wildcards are allowed.
_DEPS_EXCLUSION_LIST = [
DEPS_EXCLUSION_LIST = [
'chrome/test/data/extensions/api_test',
'chrome/test/data/extensions/secure_shell',
'chrome/test/data/firefox*',
@ -68,43 +67,6 @@ _DEPS_EXCLUSION_LIST = [
]
def _GenerateDepsDirUsingIsolate(suite_name, isolate_file_path=None):
"""Generate the dependency dir for the test suite using isolate.
Args:
suite_name: Name of the test suite (e.g. base_unittests).
isolate_file_path: .isolate file path to use. If there is a default .isolate
file path for the suite_name, this will override it.
"""
if isolate_file_path:
if os.path.isabs(isolate_file_path):
isolate_abs_path = isolate_file_path
else:
isolate_abs_path = os.path.join(constants.DIR_SOURCE_ROOT,
isolate_file_path)
else:
isolate_rel_path = _ISOLATE_FILE_PATHS.get(suite_name)
if not isolate_rel_path:
logging.info('Did not find an isolate file for the test suite.')
return
isolate_abs_path = os.path.join(constants.DIR_SOURCE_ROOT, isolate_rel_path)
isolated_abs_path = os.path.join(
constants.GetOutDirectory(), '%s.isolated' % suite_name)
assert os.path.exists(isolate_abs_path), 'Cannot find %s' % isolate_abs_path
i = isolator.Isolator(constants.ISOLATE_DEPS_DIR)
i.Clear()
i.Remap(isolate_abs_path, isolated_abs_path)
# We're relying on the fact that timestamps are preserved
# by the remap command (hardlinked). Otherwise, all the data
# will be pushed to the device once we move to using time diff
# instead of md5sum. Perform a sanity check here.
i.VerifyHardlinks()
i.PurgeExcluded(_DEPS_EXCLUSION_LIST)
i.MoveOutputDeps()
def _GetDisabledTestsFilterFromFile(suite_name):
"""Returns a gtest filter based on the *_disabled file.
@ -218,19 +180,6 @@ def _FilterDisabledTests(tests, suite_name, has_gtest_filter):
return tests
def PushDataDeps(device, test_options, test_package):
valgrind_tools.PushFilesForTool(test_options.tool, device)
if os.path.exists(constants.ISOLATE_DEPS_DIR):
device_dir = (
constants.TEST_EXECUTABLE_DIR
if test_package.suite_name == 'breakpad_unittests'
else device.GetExternalStoragePath())
device.PushChangedFiles([
(os.path.join(constants.ISOLATE_DEPS_DIR, p),
'%s/%s' % (device_dir, p))
for p in os.listdir(constants.ISOLATE_DEPS_DIR)])
def Setup(test_options, devices):
"""Create the test runner factory and tests.
@ -255,11 +204,16 @@ def Setup(test_options, devices):
test_package = exe_test_package
logging.warning('Found target %s', test_package.suite_path)
_GenerateDepsDirUsingIsolate(test_options.suite_name,
test_options.isolate_file_path)
device_utils.DeviceUtils.parallel(devices).pMap(
PushDataDeps, test_options, test_package)
base_setup.GenerateDepsDirUsingIsolate(test_options.suite_name,
test_options.isolate_file_path,
ISOLATE_FILE_PATHS,
DEPS_EXCLUSION_LIST)
def push_data_deps_to_device_dir(device):
device_dir = (constants.TEST_EXECUTABLE_DIR
if test_package.suite_name == 'breakpad_unittests'
else device.GetExternalStoragePath())
base_setup.PushDataDeps(device, device_dir, test_options)
device_utils.DeviceUtils.parallel(devices).pMap(push_data_deps_to_device_dir)
tests = _GetTests(test_options, test_package, devices)

@ -7,11 +7,63 @@
import logging
import os
from pylib import constants
from pylib import valgrind_tools
from pylib.base import base_setup
from pylib.device import device_utils
from pylib.instrumentation import test_package
from pylib.instrumentation import test_runner
DEVICE_DATA_DIR = 'chrome/test/data'
def Setup(test_options):
ISOLATE_FILE_PATHS = {
'AndroidWebViewTest': 'android_webview/android_webview_test_apk.isolate',
'ChromeShellTest': 'chrome/chrome_shell_test_apk.isolate',
'ContentShellTest': 'content/content_shell_test_apk.isolate',
}
DEPS_EXCLUSION_LIST = []
# TODO(mikecase): Remove this function and the constant DEVICE_DATA_DIR
# once all data deps are pushed to the same location on the device.
def _PushExtraSuiteDataDeps(device, test_apk):
"""Pushes some extra data files/dirs needed by some test suite.
Args:
test_apk: The test suite basename for which to return file paths.
"""
if test_apk in ['ChromeTest', 'ContentShellTest']:
test_files = 'net/data/ssl/certificates'
host_device_file_tuple = [
(os.path.join(constants.DIR_SOURCE_ROOT, test_files),
os.path.join(device.GetExternalStoragePath(), test_files))]
device.PushChangedFiles(host_device_file_tuple)
# TODO(mikecase): Remove this function once everything uses
# base_setup.PushDataDeps to push data deps to the device.
def _PushDataDeps(device, test_options):
valgrind_tools.PushFilesForTool(test_options.tool, device)
host_device_file_tuples = []
for dest_host_pair in test_options.test_data:
dst_src = dest_host_pair.split(':', 1)
dst_layer = dst_src[0]
host_src = dst_src[1]
host_test_files_path = os.path.join(constants.DIR_SOURCE_ROOT, host_src)
if os.path.exists(host_test_files_path):
host_device_file_tuples += [(
host_test_files_path,
'%s/%s/%s' % (
device.GetExternalStoragePath(),
DEVICE_DATA_DIR,
dst_layer))]
if host_device_file_tuples:
device.PushChangedFiles(host_device_file_tuples)
def Setup(test_options, devices):
"""Create and return the test runner factory and tests.
Args:
@ -34,6 +86,24 @@ def Setup(test_options):
if not tests:
logging.error('No instrumentation tests to run with current args.')
if test_options.test_data:
device_utils.DeviceUtils.parallel(devices).pMap(
_PushDataDeps, test_options)
else:
base_setup.GenerateDepsDirUsingIsolate(test_options.test_apk,
test_options.isolate_file_path,
ISOLATE_FILE_PATHS,
DEPS_EXCLUSION_LIST)
def push_data_deps_to_device_dir(device):
device_dir = os.path.join(device.GetExternalStoragePath(),
DEVICE_DATA_DIR)
base_setup.PushDataDeps(device, device_dir, test_options)
device_utils.DeviceUtils.parallel(devices).pMap(
push_data_deps_to_device_dir)
device_utils.DeviceUtils.parallel(devices).pMap(
_PushExtraSuiteDataDeps, test_options.test_apk)
def TestRunnerFactory(device, shard_index):
return test_runner.TestRunner(test_options, device, shard_index,
test_pkg)

@ -22,4 +22,5 @@ InstrumentationOptions = collections.namedtuple('InstrumentationOptions', [
'test_apk_jar_path',
'test_runner',
'test_support_apk_path',
'device_flags'])
'device_flags',
'isolate_file_path'])

@ -27,32 +27,13 @@ import perf_tests_results_helper # pylint: disable=F0401
_PERF_TEST_ANNOTATION = 'PerfTest'
def _GetDataFilesForTestSuite(suite_basename):
"""Returns a list of data files/dirs needed by the test suite.
Args:
suite_basename: The test suite basename for which to return file paths.
Returns:
A list of test file and directory paths.
"""
test_files = []
if suite_basename in ['ChromeTest', 'ContentShellTest']:
test_files += [
'net/data/ssl/certificates/',
]
return test_files
class TestRunner(base_test_runner.BaseTestRunner):
"""Responsible for running a series of tests connected to a single device."""
_DEVICE_DATA_DIR = 'chrome/test/data'
_DEVICE_COVERAGE_DIR = 'chrome/test/coverage'
_HOSTMACHINE_PERF_OUTPUT_FILE = '/tmp/chrome-profile'
_DEVICE_PERF_OUTPUT_SEARCH_PREFIX = (constants.DEVICE_PERF_OUTPUT_DIR +
'/chrome-profile*')
_DEVICE_HAS_TEST_FILES = {}
def __init__(self, test_options, device, shard_index, test_pkg,
additional_flags=None):
@ -89,45 +70,6 @@ class TestRunner(base_test_runner.BaseTestRunner):
def InstallTestPackage(self):
self.test_pkg.Install(self.device)
#override
def PushDataDeps(self):
# TODO(frankf): Implement a general approach for copying/installing
# once across test runners.
if TestRunner._DEVICE_HAS_TEST_FILES.get(self.device, False):
logging.warning('Already copied test files to device %s, skipping.',
str(self.device))
return
host_device_file_tuples = []
test_data = _GetDataFilesForTestSuite(self.test_pkg.GetApkName())
if test_data:
# Make sure SD card is ready.
self.device.WaitUntilFullyBooted(timeout=20)
host_device_file_tuples += [
(os.path.join(constants.DIR_SOURCE_ROOT, p),
os.path.join(self.device.GetExternalStoragePath(), p))
for p in test_data]
# TODO(frankf): Specify test data in this file as opposed to passing
# as command-line.
for dest_host_pair in self.options.test_data:
dst_src = dest_host_pair.split(':', 1)
dst_layer = dst_src[0]
host_src = dst_src[1]
host_test_files_path = os.path.join(constants.DIR_SOURCE_ROOT,
host_src)
if os.path.exists(host_test_files_path):
host_device_file_tuples += [(
host_test_files_path,
'%s/%s/%s' % (
self.device.GetExternalStoragePath(),
TestRunner._DEVICE_DATA_DIR,
dst_layer))]
if host_device_file_tuples:
self.device.PushChangedFiles(host_device_file_tuples)
self.tool.CopyFiles(self.device)
TestRunner._DEVICE_HAS_TEST_FILES[str(self.device)] = True
def _GetInstrumentationArgs(self):
ret = {}
if self.options.wait_for_debugger:

@ -40,7 +40,8 @@ class TestRunner(instr_test_runner.TestRunner):
test_apk_jar_path=None,
test_runner=None,
test_support_apk_path=None,
device_flags=None)
device_flags=None,
isolate_file_path=None)
super(TestRunner, self).__init__(instrumentation_options, device,
shard_index, test_pkg)
@ -55,10 +56,6 @@ class TestRunner(instr_test_runner.TestRunner):
def InstallTestPackage(self):
self.test_pkg.Install(self.device)
#override
def PushDataDeps(self):
pass
#override
def _RunTest(self, test, timeout):
self.device.ClearApplicationState(self._package)

@ -272,6 +272,11 @@ def AddInstrumentationTestOptions(option_parser):
option_parser.add_option('--device-flags', dest='device_flags', default='',
help='The relative filepath to a file containing '
'command-line flags to set on the device')
option_parser.add_option('--isolate_file_path',
'--isolate-file-path',
dest='isolate_file_path',
help='.isolate file path to override the default '
'path')
def ProcessInstrumentationOptions(options, error_func):
@ -334,7 +339,8 @@ def ProcessInstrumentationOptions(options, error_func):
options.test_apk_jar_path,
options.test_runner,
options.test_support_apk_path,
options.device_flags
options.device_flags,
options.isolate_file_path
)
@ -657,7 +663,8 @@ def _RunInstrumentationTests(options, error_func, devices):
exit_code = 0
if options.run_java_tests:
runner_factory, tests = instrumentation_setup.Setup(instrumentation_options)
runner_factory, tests = instrumentation_setup.Setup(
instrumentation_options, devices)
test_results, exit_code = test_dispatcher.RunTests(
tests, runner_factory, devices, shard=True, test_timeout=None,

@ -0,0 +1,14 @@
# Copyright (c) 2014 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.
{
'conditions': [
['OS=="android"', {
'variables': {
'isolate_dependency_untracked': [
'<(DEPTH)/chrome/test/data/android/device_files/',
],
},
}],
],
}

@ -0,0 +1,14 @@
# Copyright (c) 2014 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.
{
'conditions': [
['OS=="android"', {
'variables': {
'isolate_dependency_untracked': [
'<(DEPTH)/content/test/data/android/device_files/',
],
},
}],
],
}