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:
14
android_webview/android_webview_test_apk.isolate
Normal file
14
android_webview/android_webview_test_apk.isolate
Normal file
@ -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/',
|
||||
],
|
||||
},
|
||||
}],
|
||||
],
|
||||
}
|
62
build/android/pylib/base/base_setup.py
Normal file
62
build/android/pylib/base/base_setup.py
Normal file
@ -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,
|
||||
|
14
chrome/chrome_shell_test_apk.isolate
Normal file
14
chrome/chrome_shell_test_apk.isolate
Normal file
@ -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/',
|
||||
],
|
||||
},
|
||||
}],
|
||||
],
|
||||
}
|
14
content/content_shell_test_apk.isolate
Normal file
14
content/content_shell_test_apk.isolate
Normal file
@ -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/',
|
||||
],
|
||||
},
|
||||
}],
|
||||
],
|
||||
}
|
Reference in New Issue
Block a user