Revved to chromium 4dfb55c9cf0950b8bac8b10070c9b8f3e7de66c2 refs/remotes/origin/HEAD
diff --git a/build/android/gyp/create_device_library_links.py b/build/android/gyp/create_device_library_links.py
index 30e050c..3e630b6 100755
--- a/build/android/gyp/create_device_library_links.py
+++ b/build/android/gyp/create_device_library_links.py
@@ -64,7 +64,8 @@
mkdir_cmd = ('if [ ! -e %(dir)s ]; then mkdir -p %(dir)s; fi ' %
{ 'dir': device_dir })
RunShellCommand(device, mkdir_cmd)
- device.PushChangedFiles(options.script_host_path, options.script_device_path)
+ device.PushChangedFiles([(options.script_host_path,
+ options.script_device_path)])
trigger_cmd = (
'APK_LIBRARIES_DIR=%(apk_libraries_dir)s; '
diff --git a/build/android/gyp/java_cpp_enum.py b/build/android/gyp/java_cpp_enum.py
index ad09742..6a1d5c1 100755
--- a/build/android/gyp/java_cpp_enum.py
+++ b/build/android/gyp/java_cpp_enum.py
@@ -36,22 +36,22 @@
assert self.entries
def _AssignEntryIndices(self):
- # Supporting the same set enum value assignments the compiler does is rather
- # complicated, so we limit ourselves to these cases:
- # - all the enum constants have values assigned,
- # - enum constants reference other enum constants or have no value assigned.
-
+ # Enums, if given no value, are given the value of the previous enum + 1.
if not all(self.entries.values()):
- index = 0
+ prev_enum_value = -1
for key, value in self.entries.iteritems():
if not value:
- self.entries[key] = index
- index = index + 1
+ self.entries[key] = prev_enum_value + 1
elif value in self.entries:
self.entries[key] = self.entries[value]
else:
- raise Exception('You can only reference other enum constants unless '
- 'you assign values to all of the constants.')
+ try:
+ self.entries[key] = int(value)
+ except ValueError:
+ raise Exception('Could not interpret integer from enum value "%s" '
+ 'for key %s.' % (value, key))
+ prev_enum_value = self.entries[key]
+
def _StripPrefix(self):
if not self.prefix_to_strip:
@@ -69,7 +69,7 @@
single_line_comment_re = re.compile(r'\s*//')
multi_line_comment_start_re = re.compile(r'\s*/\*')
enum_start_re = re.compile(r'^\s*enum\s+(\w+)\s+{\s*$')
- enum_line_re = re.compile(r'^\s*(\w+)(\s*\=\s*([^,\n]+))?,?\s*$')
+ enum_line_re = re.compile(r'^\s*(\w+)(\s*\=\s*([^,\n]+))?,?')
enum_end_re = re.compile(r'^\s*}\s*;\s*$')
generator_directive_re = re.compile(
r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*([\.\w]+)$')
diff --git a/build/android/gyp/push_libraries.py b/build/android/gyp/push_libraries.py
index 63421e9..6b31a2e 100755
--- a/build/android/gyp/push_libraries.py
+++ b/build/android/gyp/push_libraries.py
@@ -40,7 +40,7 @@
if needs_directory:
device.RunShellCommand('mkdir -p ' + options.device_dir)
needs_directory[:] = [] # = False
- device.PushChangedFiles(host_path, device_path)
+ device.PushChangedFiles([(host_path, device_path)])
record_path = '%s.%s.push.md5.stamp' % (host_path, serial_number)
md5_check.CallAndRecordIfStale(
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index ab70a79..722d18c 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -171,6 +171,9 @@
config['resources'] = {}
config['resources']['dependency_zips'] = [
c['resources_zip'] for c in all_resources_deps]
+ config['resources']['extra_package_names'] = []
+
+ if options.type == 'android_apk':
config['resources']['extra_package_names'] = [
c['package_name'] for c in all_resources_deps if 'package_name' in c]
diff --git a/build/android/java_cpp_template.gypi b/build/android/java_cpp_template.gypi
index fe4238a..036f32c 100644
--- a/build/android/java_cpp_template.gypi
+++ b/build/android/java_cpp_template.gypi
@@ -22,8 +22,8 @@
#
# The 'sources' entry should only list template file. The template file
# itself should use the 'ClassName.template' format, and will generate
-# 'gen/templates/<package-name>/ClassName.java. The files which template
-# dependents on and typically included by the template should be listed
+# 'gen/templates/<target-name>/<package-name>/ClassName.java. The files which
+# template dependents on and typically included by the template should be listed
# in template_deps variables. Any change to them will force a rebuild of
# the template, and hence of any source that depends on it.
#
@@ -32,7 +32,7 @@
# Location where all generated Java sources will be placed.
'variables': {
'include_path%': '<(DEPTH)',
- 'output_dir': '<(SHARED_INTERMEDIATE_DIR)/templates/<(package_name)',
+ 'output_dir': '<(SHARED_INTERMEDIATE_DIR)/templates/<(_target_name)/<(package_name)',
},
'direct_dependent_settings': {
'variables': {
diff --git a/build/android/provision_devices.py b/build/android/provision_devices.py
index 54c90c3..468ef3f 100755
--- a/build/android/provision_devices.py
+++ b/build/android/provision_devices.py
@@ -72,7 +72,7 @@
logging.info(' Pushing adb_reboot ...')
adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT,
'out/%s/adb_reboot' % target)
- device.PushChangedFiles(adb_reboot, '/data/local/tmp/')
+ device.PushChangedFiles([(adb_reboot, '/data/local/tmp/')])
# Launch adb_reboot
logging.info(' Launching adb_reboot ...')
device.old_interface.GetAndroidToolStatusAndOutput(
diff --git a/build/android/push_libraries.gypi b/build/android/push_libraries.gypi
index d74fb21..c96e5a5 100644
--- a/build/android/push_libraries.gypi
+++ b/build/android/push_libraries.gypi
@@ -32,6 +32,7 @@
'<(strip_stamp)',
'<(strip_additional_stamp)',
'<(build_device_config_path)',
+ '<(pack_arm_relocations_stamp)',
],
'outputs': [
'<(push_stamp)',
diff --git a/build/android/pylib/chrome_test_server_spawner.py b/build/android/pylib/chrome_test_server_spawner.py
index e1fe7b1..052c2fd 100644
--- a/build/android/pylib/chrome_test_server_spawner.py
+++ b/build/android/pylib/chrome_test_server_spawner.py
@@ -64,17 +64,14 @@
return False
-def _CheckPortStatus(port, expected_status):
- """Returns True if port has expected_status.
+def _CheckPortAvailable(port):
+ """Returns True if |port| is available."""
+ return _WaitUntil(lambda: ports.IsHostPortAvailable(port))
- Args:
- port: the port number.
- expected_status: boolean of expected status.
- Returns:
- Returns True if the status is expected. Otherwise returns False.
- """
- return _WaitUntil(lambda: ports.IsHostPortUsed(port) == expected_status)
+def _CheckPortNotAvailable(port):
+ """Returns True if |port| is not available."""
+ return _WaitUntil(lambda: not ports.IsHostPortAvailable(port))
def _CheckDevicePortStatus(device, port):
@@ -167,7 +164,7 @@
port_json = json.loads(port_json)
if port_json.has_key('port') and isinstance(port_json['port'], int):
self.host_port = port_json['port']
- return _CheckPortStatus(self.host_port, True)
+ return _CheckPortNotAvailable(self.host_port)
logging.error('Failed to get port information from the server data.')
return False
@@ -236,7 +233,7 @@
if self.pipe_out:
self.is_ready = self._WaitToStartAndGetPortFromTestServer()
else:
- self.is_ready = _CheckPortStatus(self.host_port, True)
+ self.is_ready = _CheckPortNotAvailable(self.host_port)
if self.is_ready:
Forwarder.Map([(0, self.host_port)], self.device, self.tool)
# Check whether the forwarder is ready on the device.
@@ -346,7 +343,7 @@
logging.info('Handling request to kill a test server on port: %d.', port)
self.server.test_server_instance.Stop()
# Make sure the status of test server is correct before sending response.
- if _CheckPortStatus(port, False):
+ if _CheckPortAvailable(port):
self._SendResponse(200, 'OK', {}, 'killed')
logging.info('Test server on port %d is killed', port)
else:
diff --git a/build/android/pylib/constants.py b/build/android/pylib/constants.py
index 8b800ab..292ff3b 100644
--- a/build/android/pylib/constants.py
+++ b/build/android/pylib/constants.py
@@ -172,6 +172,10 @@
os.environ['BUILDTYPE'] = build_type
+def SetBuildDirectory(build_directory):
+ os.environ['CHROMIUM_OUT_DIR'] = build_directory
+
+
def GetOutDirectory(build_type=None):
"""Returns the out directory where the output binaries are built.
diff --git a/build/android/pylib/device/commands/__init__.py b/build/android/pylib/device/commands/__init__.py
new file mode 100644
index 0000000..4d6aabb
--- /dev/null
+++ b/build/android/pylib/device/commands/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 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.
diff --git a/build/android/pylib/device/commands/commands.gyp b/build/android/pylib/device/commands/commands.gyp
new file mode 100644
index 0000000..d173e39
--- /dev/null
+++ b/build/android/pylib/device/commands/commands.gyp
@@ -0,0 +1,18 @@
+# Copyright 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'chromium_commands',
+ 'type': 'none',
+ 'variables': {
+ 'java_in_dir': ['java'],
+ },
+ 'includes': [
+ '../../../../../build/java.gypi',
+ ],
+ }
+ ],
+}
diff --git a/build/android/pylib/device/commands/install_commands.py b/build/android/pylib/device/commands/install_commands.py
new file mode 100644
index 0000000..35b11e3
--- /dev/null
+++ b/build/android/pylib/device/commands/install_commands.py
@@ -0,0 +1,51 @@
+# Copyright 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.
+
+import os
+
+from pylib import constants
+
+BIN_DIR = '%s/bin' % constants.TEST_EXECUTABLE_DIR
+_FRAMEWORK_DIR = '%s/framework' % constants.TEST_EXECUTABLE_DIR
+
+_COMMANDS = {
+ 'unzip': 'org.chromium.android.commands.unzip.Unzip',
+}
+
+_SHELL_COMMAND_FORMAT = (
+"""#!/system/bin/sh
+base=%s
+export CLASSPATH=$base/framework/chromium_commands.jar
+exec app_process $base/bin %s $@
+""")
+
+
+def Installed(device):
+ return (all(device.FileExists('%s/%s' % (BIN_DIR, c)) for c in _COMMANDS)
+ and device.FileExists('%s/chromium_commands.jar' % _FRAMEWORK_DIR))
+
+def InstallCommands(device):
+ if device.IsUserBuild():
+ raise Exception('chromium_commands currently requires a userdebug build.')
+
+ chromium_commands_jar_path = os.path.join(
+ constants.GetOutDirectory(), constants.SDK_BUILD_JAVALIB_DIR,
+ 'chromium_commands.dex.jar')
+ if not os.path.exists(chromium_commands_jar_path):
+ raise Exception('%s not found. Please build chromium_commands.'
+ % chromium_commands_jar_path)
+
+ device.RunShellCommand(['mkdir', BIN_DIR, _FRAMEWORK_DIR])
+ for command, main_class in _COMMANDS.iteritems():
+ shell_command = _SHELL_COMMAND_FORMAT % (
+ constants.TEST_EXECUTABLE_DIR, main_class)
+ shell_file = '%s/%s' % (BIN_DIR, command)
+ device.WriteTextFile(shell_file, shell_command)
+ device.RunShellCommand(
+ ['chmod', '755', shell_file], check_return=True)
+
+ device.adb.Push(
+ chromium_commands_jar_path,
+ '%s/chromium_commands.jar' % _FRAMEWORK_DIR)
+
diff --git a/build/android/pylib/device/commands/java/src/org/chromium/android/commands/unzip/Unzip.java b/build/android/pylib/device/commands/java/src/org/chromium/android/commands/unzip/Unzip.java
new file mode 100644
index 0000000..4d2a045
--- /dev/null
+++ b/build/android/pylib/device/commands/java/src/org/chromium/android/commands/unzip/Unzip.java
@@ -0,0 +1,94 @@
+// Copyright 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.
+
+package org.chromium.android.commands.unzip;
+
+import android.util.Log;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * Minimal implementation of the command-line unzip utility for Android.
+ */
+public class Unzip {
+
+ private static final String TAG = "Unzip";
+
+ public static void main(String[] args) {
+ try {
+ (new Unzip()).run(args);
+ } catch (RuntimeException e) {
+ Log.e(TAG, e.toString());
+ System.exit(1);
+ }
+ }
+
+ private void showUsage(PrintStream s) {
+ s.println("Usage:");
+ s.println("unzip [zipfile]");
+ }
+
+ private void unzip(String[] args) {
+ ZipInputStream zis = null;
+ try {
+ String zipfile = args[0];
+ zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipfile)));
+ ZipEntry ze = null;
+
+ byte[] bytes = new byte[1024];
+ while ((ze = zis.getNextEntry()) != null) {
+ File outputFile = new File(ze.getName());
+ if (ze.isDirectory()) {
+ if (!outputFile.exists() && !outputFile.mkdirs()) {
+ throw new RuntimeException(
+ "Failed to create directory: " + outputFile.toString());
+ }
+ } else {
+ File parentDir = outputFile.getParentFile();
+ if (!parentDir.exists() && !parentDir.mkdirs()) {
+ throw new RuntimeException(
+ "Failed to create directory: " + parentDir.toString());
+ }
+ OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFile));
+ int actual_bytes = 0;
+ int total_bytes = 0;
+ while ((actual_bytes = zis.read(bytes)) != -1) {
+ out.write(bytes, 0, actual_bytes);
+ total_bytes += actual_bytes;
+ }
+ out.close();
+ }
+ zis.closeEntry();
+ }
+
+ } catch (IOException e) {
+ throw new RuntimeException("Error while unzipping: " + e.toString());
+ } finally {
+ try {
+ if (zis != null) zis.close();
+ } catch (IOException e) {
+ throw new RuntimeException("Error while closing zip: " + e.toString());
+ }
+ }
+ }
+
+ public void run(String[] args) {
+ if (args.length != 1) {
+ showUsage(System.err);
+ throw new RuntimeException("Incorrect usage.");
+ }
+
+ unzip(args);
+ }
+}
+
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py
index fccdd61..b726cb9 100644
--- a/build/android/pylib/device/device_utils.py
+++ b/build/android/pylib/device/device_utils.py
@@ -8,15 +8,22 @@
"""
# pylint: disable=W0613
+import logging
+import multiprocessing
+import os
import pipes
import sys
+import tempfile
import time
+import zipfile
import pylib.android_commands
from pylib.device import adb_wrapper
from pylib.device import decorators
from pylib.device import device_errors
+from pylib.device.commands import install_commands
from pylib.utils import apk_helper
+from pylib.utils import host_utils
from pylib.utils import parallelizer
_DEFAULT_TIMEOUT = 30
@@ -61,17 +68,23 @@
operation should be retried on failure if no explicit
value is provided.
"""
+ self.adb = None
self.old_interface = None
if isinstance(device, basestring):
+ self.adb = adb_wrapper.AdbWrapper(device)
self.old_interface = pylib.android_commands.AndroidCommands(device)
elif isinstance(device, adb_wrapper.AdbWrapper):
+ self.adb = device
self.old_interface = pylib.android_commands.AndroidCommands(str(device))
elif isinstance(device, pylib.android_commands.AndroidCommands):
+ self.adb = adb_wrapper.AdbWrapper(device.GetDevice())
self.old_interface = device
elif not device:
+ self.adb = adb_wrapper.AdbWrapper('')
self.old_interface = pylib.android_commands.AndroidCommands()
else:
raise ValueError('Unsupported type passed for argument "device"')
+ self._commands_installed = None
self._default_timeout = default_timeout
self._default_retries = default_retries
assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR))
@@ -91,6 +104,9 @@
Raises:
CommandTimeoutError on timeout.
"""
+ return self._IsOnlineImpl()
+
+ def _IsOnlineImpl(self):
return self.old_interface.IsOnline()
@decorators.WithTimeoutAndRetriesFromInstance()
@@ -111,17 +127,6 @@
return self._HasRootImpl()
def _HasRootImpl(self):
- """Implementation of HasRoot.
-
- This is split from HasRoot to allow other DeviceUtils methods to call
- HasRoot without spawning a new timeout thread.
-
- Returns:
- Same as for |HasRoot|.
-
- Raises:
- Same as for |HasRoot|.
- """
return self.old_interface.IsRootEnabled()
@decorators.WithTimeoutAndRetriesFromInstance()
@@ -141,6 +146,24 @@
'Could not enable root.', device=str(self))
@decorators.WithTimeoutAndRetriesFromInstance()
+ def IsUserBuild(self, timeout=None, retries=None):
+ """Checks whether or not the device is running a user build.
+
+ Args:
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Returns:
+ True if the device is running a user build, False otherwise (i.e. if
+ it's running a userdebug build).
+
+ Raises:
+ CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
+ """
+ return self._GetPropImpl('ro.build.type') == 'user'
+
+ @decorators.WithTimeoutAndRetriesFromInstance()
def GetExternalStoragePath(self, timeout=None, retries=None):
"""Get the device's path to its SD card.
@@ -156,6 +179,9 @@
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
+ return self._GetExternalStoragePathImpl()
+
+ def _GetExternalStoragePathImpl(self):
try:
return self.old_interface.GetExternalStorage()
except AssertionError as e:
@@ -183,21 +209,6 @@
self._WaitUntilFullyBootedImpl(wifi=wifi, timeout=timeout)
def _WaitUntilFullyBootedImpl(self, wifi=False, timeout=None):
- """Implementation of WaitUntilFullyBooted.
-
- This is split from WaitUntilFullyBooted to allow other DeviceUtils methods
- to call WaitUntilFullyBooted without spawning a new timeout thread.
-
- TODO(jbudorick) Remove the timeout parameter once this is no longer
- implemented via AndroidCommands.
-
- Args:
- wifi: Same as for |WaitUntilFullyBooted|.
- timeout: timeout in seconds
-
- Raises:
- Same as for |WaitUntilFullyBooted|.
- """
if timeout is None:
timeout = self._default_timeout
self.old_interface.WaitForSystemBootCompleted(timeout)
@@ -281,8 +292,8 @@
str(e), device=str(self)), None, sys.exc_info()[2]
@decorators.WithTimeoutAndRetriesFromInstance()
- def RunShellCommand(self, cmd, check_return=False, as_root=False,
- timeout=None, retries=None):
+ def RunShellCommand(self, cmd, check_return=False, as_root=False, cwd=None,
+ env=None, timeout=None, retries=None):
"""Run an ADB shell command.
TODO(jbudorick) Switch the default value of check_return to True after
@@ -294,6 +305,8 @@
be checked.
as_root: A boolean indicating whether the shell command should be run
with root privileges.
+ cwd: The device directory in which the command should be run.
+ env: The environment variables with which the command should be run.
timeout: timeout in seconds
retries: number of retries
@@ -305,35 +318,23 @@
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- return self._RunShellCommandImpl(cmd, check_return=check_return,
- as_root=as_root, timeout=timeout)
+ return self._RunShellCommandImpl(
+ cmd, check_return=check_return, as_root=as_root, cwd=cwd, env=env,
+ timeout=timeout)
def _RunShellCommandImpl(self, cmd, check_return=False, as_root=False,
- timeout=None):
- """Implementation of RunShellCommand.
-
- This is split from RunShellCommand to allow other DeviceUtils methods to
- call RunShellCommand without spawning a new timeout thread.
-
- TODO(jbudorick) Remove the timeout parameter once this is no longer
- implemented via AndroidCommands.
-
- Args:
- cmd: Same as for |RunShellCommand|.
- check_return: Same as for |RunShellCommand|.
- as_root: Same as for |RunShellCommand|.
- timeout: timeout in seconds
-
- Raises:
- Same as for |RunShellCommand|.
-
- Returns:
- Same as for |RunShellCommand|.
- """
+ cwd=None, env=None, timeout=None):
+ # TODO(jbudorick): Remove the timeout parameter once this is no longer
+ # backed by AndroidCommands.
if isinstance(cmd, list):
cmd = ' '.join(cmd)
- if as_root and not self.HasRoot():
+ if as_root and not self._HasRootImpl():
cmd = 'su -c %s' % cmd
+ if env:
+ cmd = '%s %s' % (
+ ' '.join('%s=%s' % (k, v) for k, v in env.iteritems()), cmd)
+ if cwd:
+ cmd = 'cd %s && %s' % (cwd, cmd)
if check_return:
code, output = self.old_interface.GetShellCommandStatusAndOutput(
cmd, timeout_time=timeout)
@@ -501,15 +502,15 @@
@decorators.WithTimeoutAndRetriesDefaults(
PUSH_CHANGED_FILES_DEFAULT_TIMEOUT,
PUSH_CHANGED_FILES_DEFAULT_RETRIES)
- def PushChangedFiles(self, host_path, device_path, timeout=None,
+ def PushChangedFiles(self, host_device_tuples, timeout=None,
retries=None):
"""Push files to the device, skipping files that don't need updating.
Args:
- host_path: A string containing the absolute path to the file or directory
- on the host that should be minimally pushed to the device.
- device_path: A string containing the absolute path of the destination on
- the device.
+ host_device_tuples: A list of (host_path, device_path) tuples, where
+ |host_path| is an absolute path of a file or directory on the host
+ that should be minimially pushed to the device, and |device_path| is
+ an absolute path of the destination on the device.
timeout: timeout in seconds
retries: number of retries
@@ -518,7 +519,165 @@
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- self.old_interface.PushIfNeeded(host_path, device_path)
+
+ files = []
+ for h, d in host_device_tuples:
+ if os.path.isdir(h):
+ self._RunShellCommandImpl(['mkdir', '-p', '"%s"' % d],
+ check_return=True)
+ files += self._GetChangedFilesImpl(h, d)
+
+ if not files:
+ return
+
+ size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
+ file_count = len(files)
+ dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
+ for h, _ in host_device_tuples)
+ dir_file_count = 0
+ for h, _ in host_device_tuples:
+ if os.path.isdir(h):
+ dir_file_count += sum(len(f) for _r, _d, f in os.walk(h))
+ else:
+ dir_file_count += 1
+
+ push_duration = self._ApproximateDuration(
+ file_count, file_count, size, False)
+ dir_push_duration = self._ApproximateDuration(
+ len(host_device_tuples), dir_file_count, dir_size, False)
+ zip_duration = self._ApproximateDuration(1, 1, size, True)
+
+ self._InstallCommands()
+
+ if dir_push_duration < push_duration and (
+ dir_push_duration < zip_duration or not self._commands_installed):
+ self._PushChangedFilesIndividually(host_device_tuples)
+ elif push_duration < zip_duration or not self._commands_installed:
+ self._PushChangedFilesIndividually(files)
+ else:
+ self._PushChangedFilesZipped(files)
+ self._RunShellCommandImpl(
+ ['chmod', '-R', '777'] + [d for _, d in host_device_tuples],
+ as_root=True)
+
+ def _GetChangedFilesImpl(self, host_path, device_path):
+ real_host_path = os.path.realpath(host_path)
+ try:
+ real_device_path = self._RunShellCommandImpl(
+ ['realpath', device_path], check_return=True)
+ real_device_path = real_device_path[0]
+ except device_errors.CommandFailedError:
+ return [(host_path, device_path)]
+
+ # TODO(jbudorick): Move the md5 logic up into DeviceUtils or base
+ # this function on mtime.
+ # pylint: disable=W0212
+ host_hash_tuples, device_hash_tuples = self.old_interface._RunMd5Sum(
+ real_host_path, real_device_path)
+ # pylint: enable=W0212
+
+ if os.path.isfile(host_path):
+ if (not device_hash_tuples
+ or device_hash_tuples[0].hash != host_hash_tuples[0].hash):
+ return [(host_path, device_path)]
+ else:
+ return []
+ else:
+ device_tuple_dict = dict((d.path, d.hash) for d in device_hash_tuples)
+ to_push = []
+ for host_hash, host_abs_path in (
+ (h.hash, h.path) for h in host_hash_tuples):
+ device_abs_path = '%s/%s' % (
+ real_device_path, os.path.relpath(host_abs_path, real_host_path))
+ if (device_abs_path not in device_tuple_dict
+ or device_tuple_dict[device_abs_path] != host_hash):
+ to_push.append((host_abs_path, device_abs_path))
+ return to_push
+
+ def _InstallCommands(self):
+ if self._commands_installed is None:
+ try:
+ if not install_commands.Installed(self):
+ install_commands.InstallCommands(self)
+ self._commands_installed = True
+ except Exception as e:
+ logging.warning('unzip not available: %s' % str(e))
+ self._commands_installed = False
+
+ @staticmethod
+ def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping):
+ # We approximate the time to push a set of files to a device as:
+ # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where
+ # t: total time (sec)
+ # c1: adb call time delay (sec)
+ # a: number of times adb is called (unitless)
+ # c2: push time delay (sec)
+ # f: number of files pushed via adb (unitless)
+ # c3: zip time delay (sec)
+ # c4: zip rate (bytes/sec)
+ # b: total number of bytes (bytes)
+ # c5: transfer rate (bytes/sec)
+ # c6: compression ratio (unitless)
+
+ # All of these are approximations.
+ ADB_CALL_PENALTY = 0.1 # seconds
+ ADB_PUSH_PENALTY = 0.01 # seconds
+ ZIP_PENALTY = 2.0 # seconds
+ ZIP_RATE = 10000000.0 # bytes / second
+ TRANSFER_RATE = 2000000.0 # bytes / second
+ COMPRESSION_RATIO = 2.0 # unitless
+
+ adb_call_time = ADB_CALL_PENALTY * adb_calls
+ adb_push_setup_time = ADB_PUSH_PENALTY * file_count
+ if is_zipping:
+ zip_time = ZIP_PENALTY + byte_count / ZIP_RATE
+ transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO)
+ else:
+ zip_time = 0
+ transfer_time = byte_count / TRANSFER_RATE
+ return (adb_call_time + adb_push_setup_time + zip_time + transfer_time)
+
+ def _PushChangedFilesIndividually(self, files):
+ for h, d in files:
+ self.adb.Push(h, d)
+
+ def _PushChangedFilesZipped(self, files):
+ if not files:
+ return
+
+ with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file:
+ zip_proc = multiprocessing.Process(
+ target=DeviceUtils._CreateDeviceZip,
+ args=(zip_file.name, files))
+ zip_proc.start()
+ zip_proc.join()
+
+ zip_on_device = '%s/tmp.zip' % self._GetExternalStoragePathImpl()
+ try:
+ self.adb.Push(zip_file.name, zip_on_device)
+ self._RunShellCommandImpl(
+ ['unzip', zip_on_device],
+ as_root=True, check_return=True,
+ env={'PATH': '$PATH:%s' % install_commands.BIN_DIR})
+ finally:
+ if zip_proc.is_alive():
+ zip_proc.terminate()
+ if self._IsOnlineImpl():
+ self._RunShellCommandImpl(['rm', zip_on_device])
+
+ @staticmethod
+ def _CreateDeviceZip(zip_path, host_device_tuples):
+ with zipfile.ZipFile(zip_path, 'w') as zip_file:
+ for host_path, device_path in host_device_tuples:
+ if os.path.isfile(host_path):
+ zip_file.write(host_path, device_path, zipfile.ZIP_DEFLATED)
+ else:
+ for hd, _, files in os.walk(host_path):
+ dd = '%s/%s' % (device_path, os.path.relpath(host_path, hd))
+ zip_file.write(hd, dd, zipfile.ZIP_STORED)
+ for f in files:
+ zip_file.write(os.path.join(hd, f), '%s/%s' % (dd, f),
+ zipfile.ZIP_DEFLATED)
@decorators.WithTimeoutAndRetriesFromInstance()
def FileExists(self, device_path, timeout=None, retries=None):
@@ -537,23 +696,6 @@
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- return self._FileExistsImpl(device_path)
-
- def _FileExistsImpl(self, device_path):
- """Implementation of FileExists.
-
- This is split from FileExists to allow other DeviceUtils methods to call
- FileExists without spawning a new timeout thread.
-
- Args:
- device_path: Same as for |FileExists|.
-
- Returns:
- True if the file exists on the device, False otherwise.
-
- Raises:
- Same as for |FileExists|.
- """
return self.old_interface.FileExistsOnDevice(device_path)
@decorators.WithTimeoutAndRetriesFromInstance()
@@ -598,7 +740,7 @@
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- # TODO(jbudorick) Evaluate whether we awant to return a list of lines after
+ # TODO(jbudorick) Evaluate whether we want to return a list of lines after
# the implementation switch, and if file not found should raise exception.
if as_root:
if not self.old_interface.CanAccessProtectedFileContents():
@@ -714,6 +856,9 @@
Raises:
CommandTimeoutError on timeout.
"""
+ return self._GetPropImpl(property_name)
+
+ def _GetPropImpl(self, property_name):
return self.old_interface.system_properties[property_name]
@decorators.WithTimeoutAndRetriesFromInstance()
@@ -734,6 +879,22 @@
self.old_interface.system_properties[property_name] = value
@decorators.WithTimeoutAndRetriesFromInstance()
+ def GetABI(self, timeout=None, retries=None):
+ """Gets the device main ABI.
+
+ Args:
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Returns:
+ The device's main ABI name.
+
+ Raises:
+ CommandTimeoutError on timeout.
+ """
+ return self.GetProp('ro.product.cpu.abi')
+
+ @decorators.WithTimeoutAndRetriesFromInstance()
def GetPids(self, process_name, timeout=None, retries=None):
"""Returns the PIDs of processes with the given name.
@@ -755,21 +916,6 @@
return self._GetPidsImpl(process_name)
def _GetPidsImpl(self, process_name):
- """Implementation of GetPids.
-
- This is split from GetPids to allow other DeviceUtils methods to call
- GetPids without spawning a new timeout thread.
-
- Args:
- process_name: A string containing the process name to get the PIDs for.
-
- Returns:
- A dict mapping process name to PID for each process that contained the
- provided |process_name|.
-
- Raises:
- DeviceUnreachableError on missing device.
- """
procs_pids = {}
for line in self._RunShellCommandImpl('ps'):
try:
@@ -869,4 +1015,3 @@
return parallelizer_type([
d if isinstance(d, DeviceUtils) else DeviceUtils(d)
for d in devices])
-
diff --git a/build/android/pylib/device/device_utils_test.py b/build/android/pylib/device/device_utils_test.py
index 42dc5b2..513b538 100755
--- a/build/android/pylib/device/device_utils_test.py
+++ b/build/android/pylib/device/device_utils_test.py
@@ -85,6 +85,7 @@
st_size, st_atime, st_mtime, st_ctime)
MOCKED_FUNCTIONS = [
+ ('os.listdir', []),
('os.path.abspath', ''),
('os.path.dirname', ''),
('os.path.exists', False),
@@ -117,14 +118,23 @@
def addMockDirectory(self, path, **kw):
self._addMockThing(path, True, **kw)
- def _addMockThing(self, path, is_dir, size=0, stat=None, walk=None):
+ def _addMockThing(self, path, is_dir, listdir=None, size=0, stat=None,
+ walk=None):
+ if listdir is None:
+ listdir = []
if stat is None:
stat = self.osStatResult()
if walk is None:
walk = []
+
+ dirname = os.sep.join(path.rstrip(os.sep).split(os.sep)[:-1])
+ if dirname and not dirname in self.mock_file_info:
+ self._addMockThing(dirname, True)
+
self.mock_file_info[path] = {
+ 'os.listdir': listdir,
'os.path.abspath': path,
- 'os.path.dirname': '/' + '/'.join(path.strip('/').split('/')[:-1]),
+ 'os.path.dirname': dirname,
'os.path.exists': True,
'os.path.isdir': is_dir,
'os.path.getsize': size,
@@ -208,6 +218,24 @@
'0123456789abcdef', default_timeout=1, default_retries=0)
+class DeviceUtilsNewImplTest(unittest.TestCase):
+
+ def setUp(self):
+ test_serial = '0123456789abcdef'
+ self.adb = mock.Mock(spec=adb_wrapper.AdbWrapper)
+ self.adb.__str__ = mock.Mock(return_value=test_serial)
+ self.adb.GetDeviceSerial.return_value = test_serial
+ self.device = device_utils.DeviceUtils(
+ self.adb, default_timeout=1, default_retries=0)
+
+
+class DeviceUtilsHybridImplTest(DeviceUtilsOldImplTest):
+
+ def setUp(self):
+ super(DeviceUtilsHybridImplTest, self).setUp()
+ self.device.adb = self.adb = mock.Mock(spec=adb_wrapper.AdbWrapper)
+
+
class DeviceUtilsIsOnlineTest(DeviceUtilsOldImplTest):
def testIsOnline_true(self):
@@ -260,6 +288,21 @@
self.device.EnableRoot()
+class DeviceUtilsIsUserBuildTest(DeviceUtilsOldImplTest):
+
+ def testIsUserBuild_yes(self):
+ with self.assertCalls(
+ 'adb -s 0123456789abcdef shell getprop ro.build.type',
+ 'user\r\n'):
+ self.assertTrue(self.device.IsUserBuild())
+
+ def testIsUserBuild_no(self):
+ with self.assertCalls(
+ 'adb -s 0123456789abcdef shell getprop ro.build.type',
+ 'userdebug\r\n'):
+ self.assertFalse(self.device.IsUserBuild())
+
+
class DeviceUtilsGetExternalStoragePathTest(DeviceUtilsOldImplTest):
def testGetExternalStoragePath_succeeds(self):
@@ -843,132 +886,95 @@
self.device.SendKeyEvent(66)
-class DeviceUtilsPushChangedFilesTest(DeviceUtilsOldImplTest):
+class DeviceUtilsPushChangedFilesIndividuallyTest(DeviceUtilsNewImplTest):
+
+ def testPushChangedFilesIndividually_empty(self):
+ test_files = []
+ self.device._PushChangedFilesIndividually(test_files)
+ self.assertEqual(0, self.adb.Push.call_count)
+
+ def testPushChangedFilesIndividually_single(self):
+ test_files = [('/test/host/path', '/test/device/path')]
+ self.device._PushChangedFilesIndividually(test_files)
+ self.adb.Push.assert_called_once_with(
+ '/test/host/path', '/test/device/path')
+
+ def testPushChangedFilesIndividually_multiple(self):
+ test_files = [
+ ('/test/host/path/file1', '/test/device/path/file1'),
+ ('/test/host/path/file2', '/test/device/path/file2')]
+ self.device._PushChangedFilesIndividually(test_files)
+ self.assertEqual(2, self.adb.Push.call_count)
+ self.adb.Push.assert_any_call(
+ '/test/host/path/file1', '/test/device/path/file1')
+ self.adb.Push.assert_any_call(
+ '/test/host/path/file2', '/test/device/path/file2')
- def testPushChangedFiles_noHostPath(self):
- with mock.patch('os.path.exists', return_value=False):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.PushChangedFiles('/test/host/path', '/test/device/path')
+@mock.patch('pylib.device.commands.install_commands.Installed', new=None)
+@mock.patch('pylib.device.commands.install_commands.InstallCommands', new=None)
+class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsHybridImplTest):
- def testPushChangedFiles_file_noChange(self):
- self.device.old_interface._push_if_needed_cache = {}
+ def setUp(self):
+ super(DeviceUtilsPushChangedFilesZippedTest, self).setUp()
- host_file_path = '/test/host/path'
- device_file_path = '/test/device/path'
+ def testPushChangedFilesZipped_empty(self):
+ test_files = []
+ self.device._PushChangedFilesZipped(test_files)
+ self.assertEqual(0, self.adb.Push.call_count)
- mock_fs = MockFileSystem()
- mock_fs.addMockFile(host_file_path, size=100)
+ def testPushChangedFilesZipped_single(self):
+ test_files = [('/test/host/path/file1', '/test/device/path/file1')]
- self.device.old_interface.GetFilesChanged = mock.Mock(return_value=[])
+ self.device._GetExternalStoragePathImpl = mock.Mock(
+ return_value='/test/device/external_dir')
+ self.device._IsOnlineImpl = mock.Mock(return_value=True)
+ self.device._RunShellCommandImpl = mock.Mock()
+ mock_zip_temp = mock.mock_open()
+ mock_zip_temp.return_value.name = '/test/temp/file/tmp.zip'
+ with mock.patch('multiprocessing.Process') as mock_zip_proc, (
+ mock.patch('tempfile.NamedTemporaryFile', mock_zip_temp)):
+ self.device._PushChangedFilesZipped(test_files)
- with mock_fs:
- # GetFilesChanged is mocked, so its adb calls are omitted.
- with self.assertNoAdbCalls():
- self.device.PushChangedFiles(host_file_path, device_file_path)
+ mock_zip_proc.assert_called_once_with(
+ target=device_utils.DeviceUtils._CreateDeviceZip,
+ args=('/test/temp/file/tmp.zip', test_files))
+ self.adb.Push.assert_called_once_with(
+ '/test/temp/file/tmp.zip', '/test/device/external_dir/tmp.zip')
+ self.assertEqual(2, self.device._RunShellCommandImpl.call_count)
+ self.device._RunShellCommandImpl.assert_any_call(
+ ['unzip', '/test/device/external_dir/tmp.zip'],
+ as_root=True, check_return=True,
+ env={'PATH': '$PATH:/data/local/tmp/bin'})
+ self.device._RunShellCommandImpl.assert_any_call(
+ ['rm', '/test/device/external_dir/tmp.zip'])
- def testPushChangedFiles_file_changed(self):
- self.device.old_interface._push_if_needed_cache = {}
+ def testPushChangedFilesZipped_multiple(self):
+ test_files = [('/test/host/path/file1', '/test/device/path/file1'),
+ ('/test/host/path/file2', '/test/device/path/file2')]
- host_file_path = '/test/host/path'
- device_file_path = '/test/device/path'
+ self.device._GetExternalStoragePathImpl = mock.Mock(
+ return_value='/test/device/external_dir')
+ self.device._IsOnlineImpl = mock.Mock(return_value=True)
+ self.device._RunShellCommandImpl = mock.Mock()
+ mock_zip_temp = mock.mock_open()
+ mock_zip_temp.return_value.name = '/test/temp/file/tmp.zip'
+ with mock.patch('multiprocessing.Process') as mock_zip_proc, (
+ mock.patch('tempfile.NamedTemporaryFile', mock_zip_temp)):
+ self.device._PushChangedFilesZipped(test_files)
- mock_fs = MockFileSystem()
- mock_fs.addMockFile(
- host_file_path, size=100,
- stat=MockFileSystem.osStatResult(st_mtime=1000000000))
-
- self.device.old_interface.GetFilesChanged = mock.Mock(
- return_value=[('/test/host/path', '/test/device/path')])
-
- with mock_fs:
- with self.assertCalls('adb -s 0123456789abcdef push '
- '/test/host/path /test/device/path', '100 B/s (100 B in 1.000s)\r\n'):
- self.device.PushChangedFiles(host_file_path, device_file_path)
-
- def testPushChangedFiles_directory_nothingChanged(self):
- self.device.old_interface._push_if_needed_cache = {}
-
- host_file_path = '/test/host/path'
- device_file_path = '/test/device/path'
-
- mock_fs = MockFileSystem()
- mock_fs.addMockDirectory(
- host_file_path, size=256,
- stat=MockFileSystem.osStatResult(st_mtime=1000000000))
- mock_fs.addMockFile(
- host_file_path + '/file1', size=251,
- stat=MockFileSystem.osStatResult(st_mtime=1000000001))
- mock_fs.addMockFile(
- host_file_path + '/file2', size=252,
- stat=MockFileSystem.osStatResult(st_mtime=1000000002))
-
- self.device.old_interface.GetFilesChanged = mock.Mock(return_value=[])
-
- with mock_fs:
- with self.assertCallsSequence([
- ("adb -s 0123456789abcdef shell 'mkdir -p \"/test/device/path\"'",
- '')]):
- self.device.PushChangedFiles(host_file_path, device_file_path)
-
- def testPushChangedFiles_directory_somethingChanged(self):
- self.device.old_interface._push_if_needed_cache = {}
-
- host_file_path = '/test/host/path'
- device_file_path = '/test/device/path'
-
- mock_fs = MockFileSystem()
- mock_fs.addMockDirectory(
- host_file_path, size=256,
- stat=MockFileSystem.osStatResult(st_mtime=1000000000),
- walk=[('/test/host/path', [], ['file1', 'file2'])])
- mock_fs.addMockFile(
- host_file_path + '/file1', size=256,
- stat=MockFileSystem.osStatResult(st_mtime=1000000001))
- mock_fs.addMockFile(
- host_file_path + '/file2', size=256,
- stat=MockFileSystem.osStatResult(st_mtime=1000000002))
-
- self.device.old_interface.GetFilesChanged = mock.Mock(
- return_value=[('/test/host/path/file1', '/test/device/path/file1')])
-
- with mock_fs:
- with self.assertCallsSequence([
- ("adb -s 0123456789abcdef shell 'mkdir -p \"/test/device/path\"'",
- ''),
- ('adb -s 0123456789abcdef push '
- '/test/host/path/file1 /test/device/path/file1',
- '256 B/s (256 B in 1.000s)\r\n')]):
- self.device.PushChangedFiles(host_file_path, device_file_path)
-
- def testPushChangedFiles_directory_everythingChanged(self):
- self.device.old_interface._push_if_needed_cache = {}
-
- host_file_path = '/test/host/path'
- device_file_path = '/test/device/path'
-
- mock_fs = MockFileSystem()
- mock_fs.addMockDirectory(
- host_file_path, size=256,
- stat=MockFileSystem.osStatResult(st_mtime=1000000000))
- mock_fs.addMockFile(
- host_file_path + '/file1', size=256,
- stat=MockFileSystem.osStatResult(st_mtime=1000000001))
- mock_fs.addMockFile(
- host_file_path + '/file2', size=256,
- stat=MockFileSystem.osStatResult(st_mtime=1000000002))
-
- self.device.old_interface.GetFilesChanged = mock.Mock(
- return_value=[('/test/host/path/file1', '/test/device/path/file1'),
- ('/test/host/path/file2', '/test/device/path/file2')])
-
- with mock_fs:
- with self.assertCallsSequence([
- ("adb -s 0123456789abcdef shell 'mkdir -p \"/test/device/path\"'",
- ''),
- ('adb -s 0123456789abcdef push /test/host/path /test/device/path',
- '768 B/s (768 B in 1.000s)\r\n')]):
- self.device.PushChangedFiles(host_file_path, device_file_path)
+ mock_zip_proc.assert_called_once_with(
+ target=device_utils.DeviceUtils._CreateDeviceZip,
+ args=('/test/temp/file/tmp.zip', test_files))
+ self.adb.Push.assert_called_once_with(
+ '/test/temp/file/tmp.zip', '/test/device/external_dir/tmp.zip')
+ self.assertEqual(2, self.device._RunShellCommandImpl.call_count)
+ self.device._RunShellCommandImpl.assert_any_call(
+ ['unzip', '/test/device/external_dir/tmp.zip'],
+ as_root=True, check_return=True,
+ env={'PATH': '$PATH:/data/local/tmp/bin'})
+ self.device._RunShellCommandImpl.assert_any_call(
+ ['rm', '/test/device/external_dir/tmp.zip'])
class DeviceUtilsFileExistsTest(DeviceUtilsOldImplTest):
diff --git a/build/android/pylib/forwarder.py b/build/android/pylib/forwarder.py
index db6ea03..5e45043 100644
--- a/build/android/pylib/forwarder.py
+++ b/build/android/pylib/forwarder.py
@@ -288,9 +288,9 @@
if device_serial in self._initialized_devices:
return
Forwarder._KillDeviceLocked(device, tool)
- device.PushChangedFiles(
+ device.PushChangedFiles([(
self._device_forwarder_path_on_host,
- Forwarder._DEVICE_FORWARDER_FOLDER)
+ Forwarder._DEVICE_FORWARDER_FOLDER)])
cmd = '%s %s' % (tool.GetUtilWrapper(), Forwarder._DEVICE_FORWARDER_PATH)
(exit_code, output) = device.old_interface.GetAndroidToolStatusAndOutput(
cmd, lib_path=Forwarder._DEVICE_FORWARDER_FOLDER)
diff --git a/build/android/pylib/gtest/setup.py b/build/android/pylib/gtest/setup.py
index 1e52d3b..61a8539 100644
--- a/build/android/pylib/gtest/setup.py
+++ b/build/android/pylib/gtest/setup.py
@@ -14,9 +14,11 @@
from pylib import cmd_helper
from pylib import constants
+from pylib import valgrind_tools
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
@@ -287,6 +289,19 @@
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.
@@ -314,6 +329,9 @@
_GenerateDepsDirUsingIsolate(test_options.suite_name,
test_options.isolate_file_path)
+ device_utils.DeviceUtils.parallel(devices).pMap(
+ PushDataDeps, test_options, test_package)
+
tests = _GetTests(test_options, test_package, devices)
# Constructs a new TestRunner with the current options.
diff --git a/build/android/pylib/gtest/test_package_apk.py b/build/android/pylib/gtest/test_package_apk.py
index 429cd2b..4a91278 100644
--- a/build/android/pylib/gtest/test_package_apk.py
+++ b/build/android/pylib/gtest/test_package_apk.py
@@ -44,9 +44,9 @@
# GTest expects argv[0] to be the executable path.
command_line_file.write(self.suite_name + ' ' + options)
command_line_file.flush()
- device.PushChangedFiles(
+ device.PushChangedFiles([(
command_line_file.name,
- self._package_info.cmdline_file)
+ self._package_info.cmdline_file)])
def _GetFifo(self):
# The test.fifo path is determined by:
@@ -131,5 +131,5 @@
#override
def Install(self, device):
- self.tool.CopyFiles()
+ self.tool.CopyFiles(device)
device.Install(self.suite_path)
diff --git a/build/android/pylib/gtest/test_package_exe.py b/build/android/pylib/gtest/test_package_exe.py
index 5f82aad..b0be35c 100644
--- a/build/android/pylib/gtest/test_package_exe.py
+++ b/build/android/pylib/gtest/test_package_exe.py
@@ -105,9 +105,9 @@
TestPackageExecutable._TEST_RUNNER_RET_VAL_FILE))
sh_script_file.flush()
cmd_helper.RunCmd(['chmod', '+x', sh_script_file.name])
- device.PushChangedFiles(
+ device.PushChangedFiles([(
sh_script_file.name,
- constants.TEST_EXECUTABLE_DIR + '/chrome_test_runner.sh')
+ constants.TEST_EXECUTABLE_DIR + '/chrome_test_runner.sh')])
logging.info('Conents of the test runner script: ')
for line in open(sh_script_file.name).readlines():
logging.info(' ' + line.rstrip())
@@ -148,4 +148,4 @@
self.suite_name + '_stripped'))
test_binary = constants.TEST_EXECUTABLE_DIR + '/' + self.suite_name
- device.PushChangedFiles(target_name, test_binary)
+ device.PushChangedFiles([(target_name, test_binary)])
diff --git a/build/android/pylib/gtest/test_runner.py b/build/android/pylib/gtest/test_runner.py
index faffe8f..9f89beb 100644
--- a/build/android/pylib/gtest/test_runner.py
+++ b/build/android/pylib/gtest/test_runner.py
@@ -6,7 +6,6 @@
import os
import re
-from pylib import constants
from pylib import pexpect
from pylib.base import base_test_result
from pylib.base import base_test_runner
@@ -59,22 +58,6 @@
def InstallTestPackage(self):
self.test_package.Install(self.device)
- #override
- def PushDataDeps(self):
- self.device.WaitUntilFullyBooted(timeout=20)
- self.tool.CopyFiles()
- if os.path.exists(constants.ISOLATE_DEPS_DIR):
- # TODO(frankf): linux_dumper_unittest_helper needs to be in the same dir
- # as breakpad_unittests exe. Find a better way to do this.
- if self.test_package.suite_name == 'breakpad_unittests':
- device_dir = constants.TEST_EXECUTABLE_DIR
- else:
- device_dir = self.device.GetExternalStoragePath()
- for p in os.listdir(constants.ISOLATE_DEPS_DIR):
- self.device.PushChangedFiles(
- os.path.join(constants.ISOLATE_DEPS_DIR, p),
- os.path.join(device_dir, p))
-
def _ParseTestOutput(self, p):
"""Process the test output.
diff --git a/build/android/pylib/instrumentation/test_runner.py b/build/android/pylib/instrumentation/test_runner.py
index 4f8cdcf..0e6c168 100644
--- a/build/android/pylib/instrumentation/test_runner.py
+++ b/build/android/pylib/instrumentation/test_runner.py
@@ -99,14 +99,15 @@
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)
- for p in test_data:
- self.device.PushChangedFiles(
- os.path.join(constants.DIR_SOURCE_ROOT, p),
- os.path.join(self.device.GetExternalStoragePath(), p))
+ 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.
@@ -117,13 +118,15 @@
host_test_files_path = os.path.join(constants.DIR_SOURCE_ROOT,
host_src)
if os.path.exists(host_test_files_path):
- self.device.PushChangedFiles(
+ host_device_file_tuples += [(
host_test_files_path,
'%s/%s/%s' % (
self.device.GetExternalStoragePath(),
TestRunner._DEVICE_DATA_DIR,
- dst_layer))
- self.tool.CopyFiles()
+ 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):
diff --git a/build/android/pylib/ports.py b/build/android/pylib/ports.py
index 34efb52..578152c 100644
--- a/build/android/pylib/ports.py
+++ b/build/android/pylib/ports.py
@@ -9,11 +9,9 @@
import httplib
import logging
import os
-import re
import socket
import traceback
-from pylib import cmd_helper
from pylib import constants
@@ -57,7 +55,7 @@
with open(constants.TEST_SERVER_PORT_FILE, 'r+') as fp:
port = int(fp.read())
ports_tried.append(port)
- while IsHostPortUsed(port):
+ while not IsHostPortAvailable(port):
port += 1
ports_tried.append(port)
if (port > constants.TEST_SERVER_PORT_LAST or
@@ -67,7 +65,7 @@
fp.seek(0, os.SEEK_SET)
fp.write('%d' % (port + 1))
except Exception as e:
- logging.info(e)
+ logging.error(e)
finally:
if fp_lock:
fcntl.flock(fp_lock, fcntl.LOCK_UN)
@@ -80,25 +78,23 @@
return port
-def IsHostPortUsed(host_port):
- """Checks whether the specified host port is used or not.
-
- Uses -n -P to inhibit the conversion of host/port numbers to host/port names.
+def IsHostPortAvailable(host_port):
+ """Checks whether the specified host port is available.
Args:
- host_port: Port on host we want to check.
+ host_port: Port on host to check.
Returns:
- True if the port on host is already used, otherwise returns False.
+ True if the port on host is available, otherwise returns False.
"""
- port_info = '(\*)|(127\.0\.0\.1)|(localhost):%d' % host_port
- # TODO(jnd): Find a better way to filter the port. Note that connecting to the
- # socket and closing it would leave it in the TIME_WAIT state. Setting
- # SO_LINGER on it and then closing it makes the Python HTTP server crash.
- re_port = re.compile(port_info, re.MULTILINE)
- if re_port.search(cmd_helper.GetCmdOutput(['lsof', '-nPi:%d' % host_port])):
+ s = socket.socket()
+ try:
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(('', host_port))
+ s.close()
return True
- return False
+ except socket.error:
+ return False
def IsDevicePortUsed(device, device_port, state=''):
diff --git a/build/android/pylib/uiautomator/test_package.py b/build/android/pylib/uiautomator/test_package.py
index d8558c1..fd9120e 100644
--- a/build/android/pylib/uiautomator/test_package.py
+++ b/build/android/pylib/uiautomator/test_package.py
@@ -24,4 +24,4 @@
# Override.
def Install(self, device):
- device.PushChangedFiles(self._jar_path, constants.TEST_EXECUTABLE_DIR)
+ device.PushChangedFiles([(self._jar_path, constants.TEST_EXECUTABLE_DIR)])
diff --git a/build/android/pylib/valgrind_tools.py b/build/android/pylib/valgrind_tools.py
index 69f351a..46cf9e3 100644
--- a/build/android/pylib/valgrind_tools.py
+++ b/build/android/pylib/valgrind_tools.py
@@ -10,12 +10,12 @@
1. For tests that simply run a native process (i.e. no activity is spawned):
-Call tool.CopyFiles().
+Call tool.CopyFiles(device).
Prepend test command line with tool.GetTestWrapper().
2. For tests that spawn an activity:
-Call tool.CopyFiles().
+Call tool.CopyFiles(device).
Call tool.SetupEnvironment().
Run the test as usual.
Call tool.CleanUpEnvironment().
@@ -62,7 +62,8 @@
"""
return ''
- def CopyFiles(self):
+ @classmethod
+ def CopyFiles(cls, device):
"""Copies tool-specific files to the device, create directories, etc."""
pass
@@ -106,21 +107,21 @@
# This is required because ASan is a compiler-based tool, and md5sum
# includes instrumented code from base.
device.old_interface.SetUtilWrapper(self.GetUtilWrapper())
+
+ @classmethod
+ def CopyFiles(cls, device):
+ """Copies ASan tools to the device."""
libs = glob.glob(os.path.join(DIR_SOURCE_ROOT,
'third_party/llvm-build/Release+Asserts/',
'lib/clang/*/lib/linux/',
'libclang_rt.asan-arm-android.so'))
assert len(libs) == 1
- self._lib = libs[0]
-
- def CopyFiles(self):
- """Copies ASan tools to the device."""
subprocess.call([os.path.join(DIR_SOURCE_ROOT,
'tools/android/asan/asan_device_setup.sh'),
- '--device', str(self._device),
- '--lib', self._lib,
+ '--device', str(device),
+ '--lib', libs[0],
'--extra-options', AddressSanitizerTool.EXTRA_OPTIONS])
- self._device.WaitUntilFullyBooted()
+ device.WaitUntilFullyBooted()
def GetTestWrapper(self):
return AddressSanitizerTool.WRAPPER_NAME
@@ -164,18 +165,19 @@
self._wrap_properties = ['wrap.com.google.android.apps.ch',
'wrap.org.chromium.native_test']
- def CopyFiles(self):
+ @classmethod
+ def CopyFiles(cls, device):
"""Copies Valgrind tools to the device."""
- self._device.RunShellCommand(
+ device.RunShellCommand(
'rm -r %s; mkdir %s' % (ValgrindTool.VG_DIR, ValgrindTool.VG_DIR))
- self._device.RunShellCommand(
+ device.RunShellCommand(
'rm -r %s; mkdir %s' % (ValgrindTool.VGLOGS_DIR,
ValgrindTool.VGLOGS_DIR))
- files = self.GetFilesForTool()
- for f in files:
- self._device.PushChangedFiles(
- os.path.join(DIR_SOURCE_ROOT, f),
+ files = cls.GetFilesForTool()
+ device.PushChangedFiles(
+ [((os.path.join(DIR_SOURCE_ROOT, f),
os.path.join(ValgrindTool.VG_DIR, os.path.basename(f)))
+ for f in files)])
def SetupEnvironment(self):
"""Sets up device environment."""
@@ -192,7 +194,8 @@
self._device.RunShellCommand('setprop %s ""' % (prop,))
SetChromeTimeoutScale(self._device, None)
- def GetFilesForTool(self):
+ @staticmethod
+ def GetFilesForTool():
"""Returns a list of file names for the tool."""
raise NotImplementedError()
@@ -211,7 +214,8 @@
def __init__(self, device):
super(MemcheckTool, self).__init__(device)
- def GetFilesForTool(self):
+ @staticmethod
+ def GetFilesForTool():
"""Returns a list of file names for the tool."""
return ['tools/valgrind/android/vg-chrome-wrapper.sh',
'tools/valgrind/memcheck/suppressions.txt',
@@ -232,7 +236,8 @@
def __init__(self, device):
super(TSanTool, self).__init__(device)
- def GetFilesForTool(self):
+ @staticmethod
+ def GetFilesForTool():
"""Returns a list of file names for the tool."""
return ['tools/valgrind/android/vg-chrome-wrapper-tsan.sh',
'tools/valgrind/tsan/suppressions.txt',
@@ -276,3 +281,22 @@
print 'Unknown tool %s, available tools: %s' % (
tool_name, ', '.join(sorted(TOOL_REGISTRY.keys())))
sys.exit(1)
+
+def PushFilesForTool(tool_name, device):
+ """Pushes the files required for |tool_name| to |device|.
+
+ Args:
+ tool_name: Name of the tool to create.
+ device: A DeviceUtils instance.
+ """
+ if not tool_name:
+ return
+
+ clazz = TOOL_REGISTRY.get(tool_name)
+ if clazz:
+ clazz.CopyFiles(device)
+ else:
+ print 'Unknown tool %s, available tools: %s' % (
+ tool_name, ', '.join(sorted(TOOL_REGISTRY.keys())))
+ sys.exit(1)
+
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index 5303463..92c9798 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -57,6 +57,9 @@
const='Release', dest='build_type',
help=('If set, run test suites under out/Release.'
' Default is env var BUILDTYPE or Debug.'))
+ group.add_option('--build-directory', dest='build_directory',
+ help=('Path to the directory in which build files are'
+ ' located (should not include build type)'))
group.add_option('-c', dest='cleanup_test_files',
help='Cleanup test files on the device after run',
action='store_true')
@@ -93,6 +96,8 @@
"""Processes and handles all common options."""
run_tests_helper.SetLogLevel(options.verbose_count)
constants.SetBuildType(options.build_type)
+ if options.build_directory:
+ constants.SetBuildDirectory(options.build_directory)
def AddGTestOptions(option_parser):
diff --git a/build/android/tests/multiple_proguards/src/dummy/NativeLibraries.java b/build/android/tests/multiple_proguards/src/dummy/NativeLibraries.java
index 56cd734..2f7db71 100644
--- a/build/android/tests/multiple_proguards/src/dummy/NativeLibraries.java
+++ b/build/android/tests/multiple_proguards/src/dummy/NativeLibraries.java
@@ -9,9 +9,9 @@
* NativeLibraries to build, but doesn't include it in its jar file.
*/
public class NativeLibraries {
- public static boolean USE_LINKER = false;
- public static boolean USE_LIBRARY_IN_ZIP_FILE = false;
- public static boolean ENABLE_LINKER_TESTS = false;
+ public static boolean sUseLinker = false;
+ public static boolean sUseLibraryInZipFile = false;
+ public static boolean sEnableLinkerTests = false;
static final String[] LIBRARIES = {};
- static String VERSION_NUMBER = "";
+ static String sVersionNumber = "";
}