mirror of
https://github.com/servo/servo.git
synced 2025-07-28 01:30:32 +01:00
Auto merge of #21200 - servo:run_in_headless_android, r=nox
Refactor run_in_headless_android_emulator.py out of mach test-android-startup … and make it more careful about not leaving zombie processes behind. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21200) <!-- Reviewable:end -->
This commit is contained in:
commit
cc2fe91041
2 changed files with 158 additions and 59 deletions
138
etc/run_in_headless_android_emulator.py
Executable file
138
etc/run_in_headless_android_emulator.py
Executable file
|
@ -0,0 +1,138 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# Copyright 2018 The Servo Project Developers. See the COPYRIGHT
|
||||||
|
# file at the top-level directory of this distribution.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
# option. This file may not be copied, modified, or distributed
|
||||||
|
# except according to those terms.
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def main(avd_name, apk_path, *args):
|
||||||
|
emulator_port = "5580"
|
||||||
|
emulator_args = [
|
||||||
|
tool_path("emulator", "emulator"),
|
||||||
|
"@" + avd_name,
|
||||||
|
"-wipe-data",
|
||||||
|
"-no-window",
|
||||||
|
"-no-snapshot",
|
||||||
|
"-no-snapstorage",
|
||||||
|
"-gpu", "guest",
|
||||||
|
"-port", emulator_port,
|
||||||
|
]
|
||||||
|
with terminate_on_exit(emulator_args, stdout=sys.stderr) as emulator_process:
|
||||||
|
# This is hopefully enough time for the emulator to exit
|
||||||
|
# if it cannot start because of a configuration problem,
|
||||||
|
# and probably more time than it needs to boot anyway
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
if emulator_process.poll() is not None:
|
||||||
|
# The emulator process has terminated already,
|
||||||
|
# wait-for-device would block indefinitely
|
||||||
|
print("Emulator did not start")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
adb = [tool_path("platform-tools", "adb"), "-s", "emulator-" + emulator_port]
|
||||||
|
|
||||||
|
with terminate_on_exit(adb + ["wait-for-device"]) as wait_for_device:
|
||||||
|
wait_for_device.wait()
|
||||||
|
|
||||||
|
# Now `adb shell` will work, but `adb install` needs a system service
|
||||||
|
# that might still be in the midle of starting and not be responsive yet.
|
||||||
|
|
||||||
|
# https://stackoverflow.com/a/38896494/1162888
|
||||||
|
while 1:
|
||||||
|
with terminate_on_exit(
|
||||||
|
adb + ["shell", "getprop", "sys.boot_completed"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
) as getprop:
|
||||||
|
stdout, stderr = getprop.communicate()
|
||||||
|
if "1" in stdout:
|
||||||
|
break
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
check_call(adb + ["install", "-r", apk_path])
|
||||||
|
|
||||||
|
data_dir = "/sdcard/Android/data/com.mozilla.servo/files"
|
||||||
|
params_file = data_dir + "/android_params"
|
||||||
|
|
||||||
|
check_call(adb + ["shell", "mkdir -p %s" % data_dir])
|
||||||
|
check_call(adb + ["shell", "echo 'servo' > %s" % params_file])
|
||||||
|
for arg in args:
|
||||||
|
check_call(adb + ["shell", "echo %s >> %s" % (shell_quote(arg), params_file)])
|
||||||
|
|
||||||
|
check_call(adb + ["shell", "am start com.mozilla.servo/com.mozilla.servo.MainActivity"],
|
||||||
|
stdout=sys.stderr)
|
||||||
|
|
||||||
|
logcat_args = ["RustAndroidGlueStdouterr:D", "*:S", "-v", "raw"]
|
||||||
|
with terminate_on_exit(adb + ["logcat"] + logcat_args) as logcat:
|
||||||
|
logcat.wait()
|
||||||
|
|
||||||
|
|
||||||
|
def tool_path(directory, bin_name):
|
||||||
|
if "ANDROID_SDK" in os.environ:
|
||||||
|
path = os.path.join(os.environ["ANDROID_SDK"], directory, bin_name)
|
||||||
|
if os.path.exists(path):
|
||||||
|
return path
|
||||||
|
|
||||||
|
path = os.path.join(os.path.dirname(__file__), "..", "android-toolchains", "sdk",
|
||||||
|
directory, bin_name)
|
||||||
|
if os.path.exists(path):
|
||||||
|
return path
|
||||||
|
|
||||||
|
return bin_name
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def terminate_on_exit(*args, **kwargs):
|
||||||
|
process = subprocess.Popen(*args, **kwargs)
|
||||||
|
try:
|
||||||
|
yield process
|
||||||
|
finally:
|
||||||
|
if process.poll() is None:
|
||||||
|
# The process seems to be still running
|
||||||
|
process.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
def check_call(*args, **kwargs):
|
||||||
|
with terminate_on_exit(*args, **kwargs) as process:
|
||||||
|
exit_code = process.wait()
|
||||||
|
if exit_code != 0:
|
||||||
|
sys.exit(exit_code)
|
||||||
|
|
||||||
|
|
||||||
|
# Copied from Python 3.3+'s shlex.quote()
|
||||||
|
def shell_quote(arg):
|
||||||
|
# use single quotes, and put single quotes into double quotes
|
||||||
|
# the string $'b is then quoted as '$'"'"'b'
|
||||||
|
return "'" + arg.replace("'", "'\"'\"'") + "'"
|
||||||
|
|
||||||
|
|
||||||
|
def interrupt(_signum, _frame):
|
||||||
|
raise KeyboardInterrupt
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print("Usage: %s avd_name apk_path [servo args...]" % sys.argv[0])
|
||||||
|
print("Example: %s servo-x86 target/i686-linux-android/release/servo.apk https://servo.org"
|
||||||
|
% sys.argv[0])
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# When `./mach test-android-startup` runs `Popen.terminate()` on this process,
|
||||||
|
# raise an exception in order to make `finally:` blocks run
|
||||||
|
# and also terminate sub-subprocesses.
|
||||||
|
signal.signal(signal.SIGTERM, interrupt)
|
||||||
|
sys.exit(main(*sys.argv[1:]))
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(1)
|
|
@ -562,75 +562,36 @@ class MachCommands(CommandBase):
|
||||||
if (release and dev) or not (release or dev):
|
if (release and dev) or not (release or dev):
|
||||||
print("Please specify one of --dev or --release.")
|
print("Please specify one of --dev or --release.")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
avd = "servo-x86"
|
||||||
target = "i686-linux-android"
|
target = "i686-linux-android"
|
||||||
print("Assuming --target " + target)
|
print("Assuming --target " + target)
|
||||||
|
|
||||||
env = self.build_env(target=target)
|
env = self.build_env(target=target)
|
||||||
assert self.handle_android_target(target)
|
assert self.handle_android_target(target)
|
||||||
|
binary_path = self.get_binary_path(release, dev, android=True)
|
||||||
|
apk = binary_path + ".apk"
|
||||||
|
|
||||||
emulator_port = "5580"
|
html = """
|
||||||
adb = [self.android_adb_path(env), "-s", "emulator-" + emulator_port]
|
<script>
|
||||||
emulator_process = subprocess.Popen([
|
console.log("JavaScript is running!")
|
||||||
self.android_emulator_path(env),
|
</script>
|
||||||
"@servo-x86",
|
"""
|
||||||
"-no-window",
|
url = "data:text/html;base64," + html.encode("base64").replace("\n", "")
|
||||||
"-gpu", "guest",
|
py = path.join(self.context.topdir, "etc", "run_in_headless_android_emulator.py")
|
||||||
"-port", emulator_port,
|
args = [sys.executable, py, avd, apk, url]
|
||||||
])
|
process = subprocess.Popen(args, stdout=subprocess.PIPE, env=env)
|
||||||
try:
|
try:
|
||||||
# This is hopefully enough time for the emulator to exit
|
|
||||||
# if it cannot start because of a configuration problem,
|
|
||||||
# and probably more time than it needs to boot anyway
|
|
||||||
time.sleep(1)
|
|
||||||
if emulator_process.poll() is not None:
|
|
||||||
# The process has terminated already, wait-for-device would block indefinitely
|
|
||||||
return 1
|
|
||||||
|
|
||||||
subprocess.call(adb + ["wait-for-device"])
|
|
||||||
|
|
||||||
# https://stackoverflow.com/a/38896494/1162888
|
|
||||||
while 1:
|
while 1:
|
||||||
stdout, stderr = subprocess.Popen(
|
line = process.stdout.readline()
|
||||||
adb + ["shell", "getprop", "sys.boot_completed"],
|
if len(line) == 0:
|
||||||
stdout=subprocess.PIPE,
|
print("EOF without finding the expected line")
|
||||||
).communicate()
|
return 1
|
||||||
if "1" in stdout:
|
print(line.rstrip())
|
||||||
break
|
|
||||||
print("Waiting for the emulator to boot")
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
binary_path = self.get_binary_path(release, dev, android=True)
|
|
||||||
result = subprocess.call(adb + ["install", "-r", binary_path + ".apk"])
|
|
||||||
if result != 0:
|
|
||||||
return result
|
|
||||||
|
|
||||||
html = """
|
|
||||||
<script>
|
|
||||||
console.log("JavaScript is running!")
|
|
||||||
</script>
|
|
||||||
"""
|
|
||||||
url = "data:text/html;base64," + html.encode("base64").replace("\n", "")
|
|
||||||
result = subprocess.call(adb + ["shell", """
|
|
||||||
mkdir -p /sdcard/Android/data/com.mozilla.servo/files/
|
|
||||||
echo 'servo' > /sdcard/Android/data/com.mozilla.servo/files/android_params
|
|
||||||
echo '%s' >> /sdcard/Android/data/com.mozilla.servo/files/android_params
|
|
||||||
am start com.mozilla.servo/com.mozilla.servo.MainActivity
|
|
||||||
""" % url])
|
|
||||||
if result != 0:
|
|
||||||
return result
|
|
||||||
|
|
||||||
logcat = adb + ["logcat", "RustAndroidGlueStdouterr:D", "*:S", "-v", "raw"]
|
|
||||||
logcat_process = subprocess.Popen(logcat, stdout=subprocess.PIPE)
|
|
||||||
while 1:
|
|
||||||
line = logcat_process.stdout.readline()
|
|
||||||
if "JavaScript is running!" in line:
|
if "JavaScript is running!" in line:
|
||||||
print(line)
|
|
||||||
break
|
break
|
||||||
logcat_process.kill()
|
|
||||||
finally:
|
finally:
|
||||||
try:
|
process.terminate()
|
||||||
emulator_process.kill()
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@Command('test-jquery',
|
@Command('test-jquery',
|
||||||
description='Run the jQuery test suite',
|
description='Run the jQuery test suite',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue