android: Run tests by running run_tests.py on the build host

Bug: crashpad:30
Change-Id: Ie432c58c4a2505b6434861276512a5011fd285d4
Reviewed-on: https://chromium-review.googlesource.com/811891
Commit-Queue: Mark Mentovai <mark@chromium.org>
Reviewed-by: Scott Graham <scottmg@chromium.org>
This commit is contained in:
Mark Mentovai 2017-12-07 16:57:46 -05:00 committed by Commit Bot
parent 914b0d6755
commit 659420bc7d
2 changed files with 148 additions and 38 deletions

View File

@ -19,6 +19,8 @@ from __future__ import print_function
import os import os
import pipes import pipes
import posixpath
import re
import subprocess import subprocess
import sys import sys
import uuid import uuid
@ -28,18 +30,120 @@ CRASHPAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
IS_WINDOWS_HOST = sys.platform.startswith('win') IS_WINDOWS_HOST = sys.platform.startswith('win')
def _GetFuchsiaSDKRoot(): def _BinaryDirTargetOS(binary_dir):
arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64' """Returns the apparent target OS of binary_dir, or None if none appear to be
return os.path.join(CRASHPAD_DIR, 'third_party', 'fuchsia', 'sdk', arch) explicitly specified."""
# Look for a GN “target_os”.
def _BinaryDirLooksLikeFuchsiaBuild(binary_dir):
"""Checks whether the provided output directory targets Fuchsia."""
popen = subprocess.Popen( popen = subprocess.Popen(
['gn', 'args', binary_dir, '--list=target_os', '--short'], ['gn', 'args', binary_dir, '--list=target_os', '--short'],
shell=IS_WINDOWS_HOST, stdout=subprocess.PIPE, stderr=open(os.devnull)) shell=IS_WINDOWS_HOST, stdout=subprocess.PIPE, stderr=open(os.devnull))
value = popen.communicate()[0] value = popen.communicate()[0]
return popen.returncode == 0 and 'target_os = "fuchsia"' in value if popen.returncode == 0:
match = re.match('target_os = "(.*)"$', value.decode('utf-8'))
if match:
return match.group(1)
# For GYP with Ninja, look for the appearance of “linux-android” in the path
# to ar. This path is configured by gyp_crashpad_android.py.
try:
with open(os.path.join(binary_dir, 'build.ninja')) as build_ninja_file:
build_ninja_content = build_ninja_file.read()
match = re.search('^ar = .+-linux-android(eabi)?-ar$',
build_ninja_content,
re.MULTILINE)
if match:
return 'android'
except FileNotFoundError:
# Ninja may not be in use. Assume the best.
pass
return None
def _RunOnAndroidTarget(binary_dir, test, android_device):
local_test_path = os.path.join(binary_dir, test)
MAYBE_UNSUPPORTED_TESTS = (
'crashpad_client_test',
'crashpad_handler_test',
'crashpad_minidump_test',
'crashpad_snapshot_test',
)
if not os.path.exists(local_test_path) and test in MAYBE_UNSUPPORTED_TESTS:
print(test, 'is not present and may not be supported, skipping')
return
device_temp_dir = subprocess.check_output(
['adb', '-s', android_device, 'shell',
'mktemp', '-d', '/data/local/tmp/%s.XXXXXXXX' % test],
shell=IS_WINDOWS_HOST).decode('utf-8').rstrip()
try:
# Specify test dependencies that must be pushed to the device. This could be
# determined automatically in a GN build, following the example used for
# Fuchsia. Since nothing like that exists for GYP, hard-code it for
# supported tests.
test_build_artifacts = [test]
test_data = ['test/test_paths_test_data_root.txt']
if test == 'crashpad_test_test':
test_build_artifacts.append(
'crashpad_test_test_multiprocess_exec_test_child')
elif test == 'crashpad_util_test':
test_data.append('util/net/testdata/')
def _adb(*args):
# Flush all of this scripts own buffered stdout output before running
# adb, which will likely produce its own output on stdout.
sys.stdout.flush()
adb_command = ['adb', '-s', android_device]
adb_command.extend(args)
subprocess.check_call(adb_command, shell=IS_WINDOWS_HOST)
# Establish the directory structure on the device.
device_out_dir = posixpath.join(device_temp_dir, 'out')
device_mkdirs = [device_out_dir]
for source_path in test_data:
# A trailing slash could reasonably mean to copy an entire directory, but
# will interfere with whats needed from the path split. All parent
# directories of any source_path need to be be represented in
# device_mkdirs, but its important that no source_path itself wind up in
# device_mkdirs, even if source_path names a directory, because that would
# cause the “adb push” of the directory below to behave incorrectly.
if source_path.endswith(posixpath.sep):
source_path = source_path[:-1]
device_source_path = posixpath.join(device_temp_dir, source_path)
device_mkdir = posixpath.split(device_source_path)[0]
if device_mkdir not in device_mkdirs:
device_mkdirs.append(device_mkdir)
adb_mkdir_command = ['shell', 'mkdir', '-p']
adb_mkdir_command.extend(device_mkdirs)
_adb(*adb_mkdir_command)
# Push the test binary and any other build output to the device.
adb_push_command = ['push']
for artifact in test_build_artifacts:
adb_push_command.append(os.path.join(binary_dir, artifact))
adb_push_command.append(device_out_dir)
_adb(*adb_push_command)
# Push test data to the device.
for source_path in test_data:
_adb('push',
os.path.join(CRASHPAD_DIR, source_path),
posixpath.join(device_temp_dir, source_path))
# Run the test on the device.
_adb('shell', 'env', 'CRASHPAD_TEST_DATA_ROOT=' + device_temp_dir,
posixpath.join(device_out_dir, test))
finally:
_adb('shell', 'rm', '-rf', device_temp_dir)
def _GetFuchsiaSDKRoot():
arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64'
return os.path.join(CRASHPAD_DIR, 'third_party', 'fuchsia', 'sdk', arch)
def _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests): def _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests):
@ -170,7 +274,9 @@ def main(args):
if os.path.isdir(binary_dir_32): if os.path.isdir(binary_dir_32):
os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32 os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32
is_fuchsia = _BinaryDirLooksLikeFuchsiaBuild(binary_dir) target_os = _BinaryDirTargetOS(binary_dir)
is_android = target_os == 'android'
is_fuchsia = target_os == 'fuchsia'
tests = [ tests = [
'crashpad_client_test', 'crashpad_client_test',
@ -181,7 +287,26 @@ def main(args):
'crashpad_util_test', 'crashpad_util_test',
] ]
if is_fuchsia: if is_android:
android_device = os.environ.get('ANDROID_DEVICE')
if not android_device:
adb_devices = subprocess.check_output(['adb', 'devices'],
shell=IS_WINDOWS_HOST)
devices = []
for line in adb_devices.splitlines():
line = line.decode('utf-8')
if (line == 'List of devices attached' or
re.match('^\* daemon .+ \*$', line) or
line == ''):
continue
(device, ignore) = line.split('\t')
devices.append(device)
if len(devices) != 1:
print("Please set ANDROID_DEVICE to your device's id", file=sys.stderr)
return 2
android_device = devices[0]
print('Using autodetected Android device:', android_device)
elif is_fuchsia:
zircon_nodename = os.environ.get('ZIRCON_NODENAME') zircon_nodename = os.environ.get('ZIRCON_NODENAME')
if not zircon_nodename: if not zircon_nodename:
netls = os.path.join(_GetFuchsiaSDKRoot(), 'tools', 'netls') netls = os.path.join(_GetFuchsiaSDKRoot(), 'tools', 'netls')
@ -212,7 +337,9 @@ def main(args):
subprocess.check_call( subprocess.check_call(
[sys.executable, os.path.join(CRASHPAD_DIR, test), binary_dir]) [sys.executable, os.path.join(CRASHPAD_DIR, test), binary_dir])
else: else:
if is_fuchsia: if is_android:
_RunOnAndroidTarget(binary_dir, test, android_device)
elif is_fuchsia:
_RunOnFuchsiaTarget(binary_dir, test, zircon_nodename) _RunOnFuchsiaTarget(binary_dir, test, zircon_nodename)
else: else:
subprocess.check_call(os.path.join(binary_dir, test)) subprocess.check_call(os.path.join(binary_dir, test))

View File

@ -210,35 +210,18 @@ Software Development Kit.
### Android ### Android
To test on Android, use [ADB (Android Debug To test on Android, [ADB (Android Debug
Bridge)](https://developer.android.com/studio/command-line/adb.html) to `adb Bridge)](https://developer.android.com/studio/command-line/adb.html) from the
push` test executables and test data to a device or emulator, then use `adb [Android SDK](https://developer.android.com/sdk/) must be in the `PATH`. Note
shell` to get a shell to run the test executables from. ADB is part of the that it is sufficient to install just the command-line tools from the Android
[Android SDK](https://developer.android.com/sdk/). Note that it is sufficient to SDK. The entire Android Studio IDE is not necessary to obtain ADB.
install just the command-line tools. The entire Android Studio IDE is not
necessary to obtain ADB.
This example runs `crashpad_test_test` on a device. This test executable has a When asked to test an Android build directory, `run_tests.py` will detect a
run-time dependency on a second executable and a test data file, which are also single connected Android device (including an emulator). If multiple devices are
transferred to the device prior to running the test. connected, one may be chosen explicitly with the `ANDROID_DEVICE` environment
variable. `run_tests.py` will upload test executables and data to a temporary
``` location on the detected or selected device, run them, and clean up after itself
$ cd ~/crashpad/crashpad when done.
$ adb push out/android_arm64_api21/out/Debug/crashpad_test_test /data/local/tmp/
[100%] /data/local/tmp/crashpad_test_test
$ adb push \
out/android_arm64_api21/out/Debug/crashpad_test_test_multiprocess_exec_test_child \
/data/local/tmp/
[100%] /data/local/tmp/crashpad_test_test_multiprocess_exec_test_child
$ adb shell mkdir -p /data/local/tmp/crashpad_test_data_root/test
$ adb push test/test_paths_test_data_root.txt \
/data/local/tmp/crashpad_test_data_root/test/
[100%] /data/local/tmp/crashpad_test_data_root/test/test_paths_test_data_root.txt
$ adb shell
device:/ $ cd /data/local/tmp
device:/data/local/tmp $ CRASHPAD_TEST_DATA_ROOT=crashpad_test_data_root \
./crashpad_test_test
```
## Contributing ## Contributing