summaryrefslogtreecommitdiff
path: root/deps/v8/tools/testrunner/local
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/tools/testrunner/local')
-rw-r--r--deps/v8/tools/testrunner/local/android.py207
-rw-r--r--deps/v8/tools/testrunner/local/command.py113
-rw-r--r--deps/v8/tools/testrunner/local/statusfile.py2
3 files changed, 311 insertions, 11 deletions
diff --git a/deps/v8/tools/testrunner/local/android.py b/deps/v8/tools/testrunner/local/android.py
new file mode 100644
index 0000000000..fb25bb5a17
--- /dev/null
+++ b/deps/v8/tools/testrunner/local/android.py
@@ -0,0 +1,207 @@
+# Copyright 2018 the V8 project authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Wrapper around the Android device abstraction from src/build/android.
+"""
+
+import logging
+import os
+import sys
+
+
+BASE_DIR = os.path.normpath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..'))
+ANDROID_DIR = os.path.join(BASE_DIR, 'build', 'android')
+DEVICE_DIR = '/data/local/tmp/v8/'
+
+
+class TimeoutException(Exception):
+ def __init__(self, timeout):
+ self.timeout = timeout
+
+
+class CommandFailedException(Exception):
+ def __init__(self, status, output):
+ self.status = status
+ self.output = output
+
+
+class _Driver(object):
+ """Helper class to execute shell commands on an Android device."""
+ def __init__(self, device=None):
+ assert os.path.exists(ANDROID_DIR)
+ sys.path.insert(0, ANDROID_DIR)
+
+ # We import the dependencies only on demand, so that this file can be
+ # imported unconditionally.
+ import devil_chromium
+ from devil.android import device_errors # pylint: disable=import-error
+ from devil.android import device_utils # pylint: disable=import-error
+ from devil.android.perf import cache_control # pylint: disable=import-error
+ from devil.android.perf import perf_control # pylint: disable=import-error
+ from devil.android.sdk import adb_wrapper # pylint: disable=import-error
+ global cache_control
+ global device_errors
+ global perf_control
+
+ devil_chromium.Initialize()
+
+ if not device:
+ # Detect attached device if not specified.
+ devices = adb_wrapper.AdbWrapper.Devices()
+ assert devices, 'No devices detected'
+ assert len(devices) == 1, 'Multiple devices detected.'
+ device = str(devices[0])
+ self.adb_wrapper = adb_wrapper.AdbWrapper(device)
+ self.device = device_utils.DeviceUtils(self.adb_wrapper)
+
+ # This remembers what we have already pushed to the device.
+ self.pushed = set()
+
+ def tear_down(self):
+ """Clean up files after running all tests."""
+ self.device.RemovePath(DEVICE_DIR, force=True, recursive=True)
+
+ def push_file(self, host_dir, file_name, target_rel='.',
+ skip_if_missing=False):
+ """Push a single file to the device (cached).
+
+ Args:
+ host_dir: Absolute parent directory of the file to push.
+ file_name: Name of the file to push.
+ target_rel: Parent directory of the target location on the device
+ (relative to the device's base dir for testing).
+ skip_if_missing: Keeps silent about missing files when set. Otherwise logs
+ error.
+ """
+ file_on_host = os.path.join(host_dir, file_name)
+
+ # Only push files not yet pushed in one execution.
+ if file_on_host in self.pushed:
+ return
+
+ file_on_device_tmp = os.path.join(DEVICE_DIR, '_tmp_', file_name)
+ file_on_device = os.path.join(DEVICE_DIR, target_rel, file_name)
+ folder_on_device = os.path.dirname(file_on_device)
+
+ # Only attempt to push files that exist.
+ if not os.path.exists(file_on_host):
+ if not skip_if_missing:
+ logging.critical('Missing file on host: %s' % file_on_host)
+ return
+
+ # Work-around for 'text file busy' errors. Push the files to a temporary
+ # location and then copy them with a shell command.
+ output = self.adb_wrapper.Push(file_on_host, file_on_device_tmp)
+ # Success looks like this: '3035 KB/s (12512056 bytes in 4.025s)'.
+ # Errors look like this: 'failed to copy ... '.
+ if output and not re.search('^[0-9]', output.splitlines()[-1]):
+ logging.critical('PUSH FAILED: ' + output)
+ self.adb_wrapper.Shell('mkdir -p %s' % folder_on_device)
+ self.adb_wrapper.Shell('cp %s %s' % (file_on_device_tmp, file_on_device))
+ self.pushed.add(file_on_host)
+
+ def push_executable(self, shell_dir, target_dir, binary):
+ """Push files required to run a V8 executable.
+
+ Args:
+ shell_dir: Absolute parent directory of the executable on the host.
+ target_dir: Parent directory of the executable on the device (relative to
+ devices' base dir for testing).
+ binary: Name of the binary to push.
+ """
+ self.push_file(shell_dir, binary, target_dir)
+
+ # Push external startup data. Backwards compatible for revisions where
+ # these files didn't exist. Or for bots that don't produce these files.
+ self.push_file(
+ shell_dir,
+ 'natives_blob.bin',
+ target_dir,
+ skip_if_missing=True,
+ )
+ self.push_file(
+ shell_dir,
+ 'snapshot_blob.bin',
+ target_dir,
+ skip_if_missing=True,
+ )
+ self.push_file(
+ shell_dir,
+ 'snapshot_blob_trusted.bin',
+ target_dir,
+ skip_if_missing=True,
+ )
+ self.push_file(
+ shell_dir,
+ 'icudtl.dat',
+ target_dir,
+ skip_if_missing=True,
+ )
+
+ def run(self, target_dir, binary, args, rel_path, timeout, env=None,
+ logcat_file=False):
+ """Execute a command on the device's shell.
+
+ Args:
+ target_dir: Parent directory of the executable on the device (relative to
+ devices' base dir for testing).
+ binary: Name of the binary.
+ args: List of arguments to pass to the binary.
+ rel_path: Relative path on device to use as CWD.
+ timeout: Timeout in seconds.
+ env: The environment variables with which the command should be run.
+ logcat_file: File into which to stream adb logcat log.
+ """
+ binary_on_device = os.path.join(DEVICE_DIR, target_dir, binary)
+ cmd = [binary_on_device] + args
+ def run_inner():
+ try:
+ output = self.device.RunShellCommand(
+ cmd,
+ cwd=os.path.join(DEVICE_DIR, rel_path),
+ check_return=True,
+ env=env,
+ timeout=timeout,
+ retries=0,
+ )
+ return '\n'.join(output)
+ except device_errors.AdbCommandFailedError as e:
+ raise CommandFailedException(e.status, e.output)
+ except device_errors.CommandTimeoutError:
+ raise TimeoutException(timeout)
+
+
+ if logcat_file:
+ with self.device.GetLogcatMonitor(output_file=logcat_file) as logmon:
+ result = run_inner()
+ logmon.Close()
+ return result
+ else:
+ return run_inner()
+
+ def drop_ram_caches(self):
+ """Drop ran caches on device."""
+ cache = cache_control.CacheControl(self.device)
+ cache.DropRamCaches()
+
+ def set_high_perf_mode(self):
+ """Set device into high performance mode."""
+ perf = perf_control.PerfControl(self.device)
+ perf.SetHighPerfMode()
+
+ def set_default_perf_mode(self):
+ """Set device into default performance mode."""
+ perf = perf_control.PerfControl(self.device)
+ perf.SetDefaultPerfMode()
+
+
+_ANDROID_DRIVER = None
+def android_driver(device=None):
+ """Singleton access method to the driver class."""
+ global _ANDROID_DRIVER
+ if not _ANDROID_DRIVER:
+ _ANDROID_DRIVER = _Driver(device)
+ return _ANDROID_DRIVER
diff --git a/deps/v8/tools/testrunner/local/command.py b/deps/v8/tools/testrunner/local/command.py
index adc9c2e452..302d568e87 100644
--- a/deps/v8/tools/testrunner/local/command.py
+++ b/deps/v8/tools/testrunner/local/command.py
@@ -4,16 +4,22 @@
import os
+import re
import signal
import subprocess
import sys
import threading
import time
+from ..local.android import (
+ android_driver, CommandFailedException, TimeoutException)
from ..local import utils
from ..objects import output
+BASE_DIR = os.path.normpath(
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), '..' , '..', '..'))
+
SEM_INVALID_VALUE = -1
SEM_NOGPFAULTERRORBOX = 0x0002 # Microsoft Platform SDK WinBase.h
@@ -33,7 +39,18 @@ class AbortException(Exception):
class BaseCommand(object):
def __init__(self, shell, args=None, cmd_prefix=None, timeout=60, env=None,
- verbose=False):
+ verbose=False, resources_func=None):
+ """Initialize the command.
+
+ Args:
+ shell: The name of the executable (e.g. d8).
+ args: List of args to pass to the executable.
+ cmd_prefix: Prefix of command (e.g. a wrapper script).
+ timeout: Timeout in seconds.
+ env: Environment dict for execution.
+ verbose: Print additional output.
+ resources_func: Callable, returning all test files needed by this command.
+ """
assert(timeout > 0)
self.shell = shell
@@ -43,11 +60,11 @@ class BaseCommand(object):
self.env = env or {}
self.verbose = verbose
- def execute(self, **additional_popen_kwargs):
+ def execute(self):
if self.verbose:
print '# %s' % self
- process = self._start_process(**additional_popen_kwargs)
+ process = self._start_process()
# Variable to communicate with the signal handler.
abort_occured = [False]
@@ -79,14 +96,13 @@ class BaseCommand(object):
duration
)
- def _start_process(self, **additional_popen_kwargs):
+ def _start_process(self):
try:
return subprocess.Popen(
args=self._get_popen_args(),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=self._get_env(),
- **additional_popen_kwargs
)
except Exception as e:
sys.stderr.write('Error executing: %s\n' % self)
@@ -187,8 +203,85 @@ class WindowsCommand(BaseCommand):
sys.stdout.flush()
-# Set the Command class to the OS-specific version.
-if utils.IsWindows():
- Command = WindowsCommand
-else:
- Command = PosixCommand
+class AndroidCommand(BaseCommand):
+ def __init__(self, shell, args=None, cmd_prefix=None, timeout=60, env=None,
+ verbose=False, resources_func=None):
+ """Initialize the command and all files that need to be pushed to the
+ Android device.
+ """
+ self.shell_name = os.path.basename(shell)
+ self.shell_dir = os.path.dirname(shell)
+ self.files_to_push = resources_func()
+
+ # Make all paths in arguments relative and also prepare files from arguments
+ # for pushing to the device.
+ rel_args = []
+ find_path_re = re.compile(r'.*(%s/[^\'"]+).*' % re.escape(BASE_DIR))
+ for arg in (args or []):
+ match = find_path_re.match(arg)
+ if match:
+ self.files_to_push.append(match.group(1))
+ rel_args.append(
+ re.sub(r'(.*)%s/(.*)' % re.escape(BASE_DIR), r'\1\2', arg))
+
+ super(AndroidCommand, self).__init__(
+ shell, args=rel_args, cmd_prefix=cmd_prefix, timeout=timeout, env=env,
+ verbose=verbose)
+
+ def execute(self, **additional_popen_kwargs):
+ """Execute the command on the device.
+
+ This pushes all required files to the device and then runs the command.
+ """
+ if self.verbose:
+ print '# %s' % self
+
+ android_driver().push_executable(self.shell_dir, 'bin', self.shell_name)
+
+ for abs_file in self.files_to_push:
+ abs_dir = os.path.dirname(abs_file)
+ file_name = os.path.basename(abs_file)
+ rel_dir = os.path.relpath(abs_dir, BASE_DIR)
+ android_driver().push_file(abs_dir, file_name, rel_dir)
+
+ start_time = time.time()
+ return_code = 0
+ timed_out = False
+ try:
+ stdout = android_driver().run(
+ 'bin', self.shell_name, self.args, '.', self.timeout, self.env)
+ except CommandFailedException as e:
+ return_code = e.status
+ stdout = e.output
+ except TimeoutException as e:
+ return_code = 1
+ timed_out = True
+ # Sadly the Android driver doesn't provide output on timeout.
+ stdout = ''
+
+ duration = time.time() - start_time
+ return output.Output(
+ return_code,
+ timed_out,
+ stdout,
+ '', # No stderr available.
+ -1, # No pid available.
+ duration,
+ )
+
+
+Command = None
+def setup(target_os):
+ """Set the Command class to the OS-specific version."""
+ global Command
+ if target_os == 'android':
+ Command = AndroidCommand
+ elif target_os == 'windows':
+ Command = WindowsCommand
+ else:
+ Command = PosixCommand
+
+def tear_down():
+ """Clean up after using commands."""
+ if Command == AndroidCommand:
+ android_driver().tear_down()
diff --git a/deps/v8/tools/testrunner/local/statusfile.py b/deps/v8/tools/testrunner/local/statusfile.py
index ecfbf008a2..4742e84caf 100644
--- a/deps/v8/tools/testrunner/local/statusfile.py
+++ b/deps/v8/tools/testrunner/local/statusfile.py
@@ -55,7 +55,7 @@ for key in [SKIP, FAIL, PASS, CRASH, SLOW, FAIL_OK, NO_VARIANTS, FAIL_SLOPPY,
# Support arches, modes to be written as keywords instead of strings.
VARIABLES = {ALWAYS: True}
-for var in ["debug", "release", "big", "little",
+for var in ["debug", "release", "big", "little", "android",
"android_arm", "android_arm64", "android_ia32", "android_x64",
"arm", "arm64", "ia32", "mips", "mipsel", "mips64", "mips64el",
"x64", "ppc", "ppc64", "s390", "s390x", "macos", "windows",