mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Auto merge of #21213 - servo:android-wpt, r=nox+jgraham
Add some support for WPT tests in an Android emulator through WebDriver This succeeds on my machine: `./mach test-wpt --product servodriver --binary etc/run_in_headless_android_emulator.py --binary-arg servo-x86 --binary-arg target/i686-linux-android/release/servo.apk /_mozilla/mozilla/DOMParser.html` <!-- 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/21213) <!-- Reviewable:end -->
This commit is contained in:
commit
37a73d3bc0
6 changed files with 256 additions and 76 deletions
|
@ -931,7 +931,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
||||||
FromCompositorMsg::GetFocusTopLevelBrowsingContext(resp_chan) => {
|
FromCompositorMsg::GetFocusTopLevelBrowsingContext(resp_chan) => {
|
||||||
let focus_browsing_context = self.focus_pipeline_id
|
let focus_browsing_context = self.focus_pipeline_id
|
||||||
.and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
|
.and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
|
||||||
.map(|pipeline| pipeline.top_level_browsing_context_id);
|
.map(|pipeline| pipeline.top_level_browsing_context_id)
|
||||||
|
.filter(|&top_level_browsing_context_id| {
|
||||||
|
let browsing_context_id =
|
||||||
|
BrowsingContextId::from(top_level_browsing_context_id);
|
||||||
|
self.browsing_contexts.contains_key(&browsing_context_id)
|
||||||
|
});
|
||||||
let _ = resp_chan.send(focus_browsing_context);
|
let _ = resp_chan.send(focus_browsing_context);
|
||||||
}
|
}
|
||||||
FromCompositorMsg::KeyEvent(ch, key, state, modifiers) => {
|
FromCompositorMsg::KeyEvent(ch, key, state, modifiers) => {
|
||||||
|
|
|
@ -107,14 +107,14 @@ struct WebDriverSession {
|
||||||
|
|
||||||
/// Time to wait for injected scripts to run before interrupting them. A [`None`] value
|
/// Time to wait for injected scripts to run before interrupting them. A [`None`] value
|
||||||
/// specifies that the script should run indefinitely.
|
/// specifies that the script should run indefinitely.
|
||||||
script_timeout: Option<u64>,
|
script_timeout: u64,
|
||||||
|
|
||||||
/// Time to wait for a page to finish loading upon navigation.
|
/// Time to wait for a page to finish loading upon navigation.
|
||||||
load_timeout: Option<u64>,
|
load_timeout: u64,
|
||||||
|
|
||||||
/// Time to wait for the element location strategy when retrieving elements, and when
|
/// Time to wait for the element location strategy when retrieving elements, and when
|
||||||
/// waiting for an element to become interactable.
|
/// waiting for an element to become interactable.
|
||||||
implicit_wait_timeout: Option<u64>,
|
implicit_wait_timeout: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebDriverSession {
|
impl WebDriverSession {
|
||||||
|
@ -127,9 +127,9 @@ impl WebDriverSession {
|
||||||
browsing_context_id: browsing_context_id,
|
browsing_context_id: browsing_context_id,
|
||||||
top_level_browsing_context_id: top_level_browsing_context_id,
|
top_level_browsing_context_id: top_level_browsing_context_id,
|
||||||
|
|
||||||
script_timeout: Some(30_000),
|
script_timeout: 30_000,
|
||||||
load_timeout: Some(300_000),
|
load_timeout: 300_000,
|
||||||
implicit_wait_timeout: Some(0),
|
implicit_wait_timeout: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ struct Handler {
|
||||||
resize_timeout: u32,
|
resize_timeout: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
enum ServoExtensionRoute {
|
enum ServoExtensionRoute {
|
||||||
GetPrefs,
|
GetPrefs,
|
||||||
SetPrefs,
|
SetPrefs,
|
||||||
|
@ -171,7 +171,7 @@ impl WebDriverExtensionRoute for ServoExtensionRoute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
enum ServoExtensionCommand {
|
enum ServoExtensionCommand {
|
||||||
GetPrefs(GetPrefsParameters),
|
GetPrefs(GetPrefsParameters),
|
||||||
SetPrefs(SetPrefsParameters),
|
SetPrefs(SetPrefsParameters),
|
||||||
|
@ -188,7 +188,7 @@ impl WebDriverExtensionCommand for ServoExtensionCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
struct GetPrefsParameters {
|
struct GetPrefsParameters {
|
||||||
prefs: Vec<String>
|
prefs: Vec<String>
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,7 @@ impl ToJson for GetPrefsParameters {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
struct SetPrefsParameters {
|
struct SetPrefsParameters {
|
||||||
prefs: Vec<(String, PrefValue)>
|
prefs: Vec<(String, PrefValue)>
|
||||||
}
|
}
|
||||||
|
@ -371,7 +371,7 @@ impl Handler {
|
||||||
-> WebDriverResult<WebDriverResponse> {
|
-> WebDriverResult<WebDriverResponse> {
|
||||||
let timeout = self.session()?.load_timeout;
|
let timeout = self.session()?.load_timeout;
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
thread::sleep(Duration::from_millis(timeout.unwrap()));
|
thread::sleep(Duration::from_millis(timeout));
|
||||||
let _ = sender.send(LoadStatus::LoadTimeout);
|
let _ = sender.send(LoadStatus::LoadTimeout);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -720,9 +720,15 @@ impl Handler {
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or(WebDriverError::new(ErrorStatus::SessionNotCreated, ""))?;
|
.ok_or(WebDriverError::new(ErrorStatus::SessionNotCreated, ""))?;
|
||||||
|
|
||||||
session.script_timeout = parameters.script;
|
if let Some(timeout) = parameters.script {
|
||||||
session.load_timeout = parameters.page_load;
|
session.script_timeout = timeout
|
||||||
session.implicit_wait_timeout = parameters.implicit;
|
}
|
||||||
|
if let Some(timeout) = parameters.page_load {
|
||||||
|
session.load_timeout = timeout
|
||||||
|
}
|
||||||
|
if let Some(timeout) = parameters.implicit {
|
||||||
|
session.implicit_wait_timeout = timeout
|
||||||
|
}
|
||||||
|
|
||||||
Ok(WebDriverResponse::Void)
|
Ok(WebDriverResponse::Void)
|
||||||
}
|
}
|
||||||
|
@ -750,15 +756,10 @@ impl Handler {
|
||||||
let func_body = ¶meters.script;
|
let func_body = ¶meters.script;
|
||||||
let args_string = "window.webdriverCallback";
|
let args_string = "window.webdriverCallback";
|
||||||
|
|
||||||
let script = match self.session()?.script_timeout {
|
let script = format!("setTimeout(webdriverTimeout, {}); (function(callback) {{ {} }})({})",
|
||||||
Some(timeout) => {
|
self.session()?.script_timeout,
|
||||||
format!("setTimeout(webdriverTimeout, {}); (function(callback) {{ {} }})({})",
|
func_body,
|
||||||
timeout,
|
args_string);
|
||||||
func_body,
|
|
||||||
args_string)
|
|
||||||
}
|
|
||||||
None => format!("(function(callback) {{ {} }})({})", func_body, args_string),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
let command = WebDriverScriptCommand::ExecuteAsyncScript(script, sender);
|
let command = WebDriverScriptCommand::ExecuteAsyncScript(script, sender);
|
||||||
|
|
|
@ -48,33 +48,30 @@ def main(avd_name, apk_path, *args):
|
||||||
|
|
||||||
# Now `adb shell` will work, but `adb install` needs a system service
|
# 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.
|
# that might still be in the midle of starting and not be responsive yet.
|
||||||
|
wait_for_boot(adb)
|
||||||
|
|
||||||
# https://stackoverflow.com/a/38896494/1162888
|
# These steps should happen before application start
|
||||||
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])
|
check_call(adb + ["install", "-r", apk_path])
|
||||||
|
args = list(args)
|
||||||
data_dir = "/sdcard/Android/data/com.mozilla.servo/files"
|
write_user_stylesheets(adb, args)
|
||||||
params_file = data_dir + "/android_params"
|
write_args(adb, args)
|
||||||
|
|
||||||
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"],
|
check_call(adb + ["shell", "am start com.mozilla.servo/com.mozilla.servo.MainActivity"],
|
||||||
stdout=sys.stderr)
|
stdout=sys.stderr)
|
||||||
|
|
||||||
logcat_args = ["RustAndroidGlueStdouterr:D", "*:S", "-v", "raw"]
|
# Start showing logs as soon as the application starts,
|
||||||
|
# in case they say something useful while we wait in subsequent steps.
|
||||||
|
logcat_args = [
|
||||||
|
"--format=raw", # Print no metadata, only log messages
|
||||||
|
"RustAndroidGlueStdouterr:D", # Show (debug level) Rust stdio
|
||||||
|
"*:S", # Hide everything else
|
||||||
|
]
|
||||||
with terminate_on_exit(adb + ["logcat"] + logcat_args) as logcat:
|
with terminate_on_exit(adb + ["logcat"] + logcat_args) as logcat:
|
||||||
|
|
||||||
|
# This step needs to happen after application start
|
||||||
|
forward_webdriver(adb, args)
|
||||||
|
|
||||||
|
# logcat normally won't exit on its own, wait until we get a SIGTERM signal.
|
||||||
logcat.wait()
|
logcat.wait()
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,11 +100,90 @@ def terminate_on_exit(*args, **kwargs):
|
||||||
process.terminate()
|
process.terminate()
|
||||||
|
|
||||||
|
|
||||||
def check_call(*args, **kwargs):
|
# https://stackoverflow.com/a/38896494/1162888
|
||||||
|
def wait_for_boot(adb):
|
||||||
|
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:
|
||||||
|
return
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
def call(*args, **kwargs):
|
||||||
with terminate_on_exit(*args, **kwargs) as process:
|
with terminate_on_exit(*args, **kwargs) as process:
|
||||||
exit_code = process.wait()
|
return process.wait()
|
||||||
if exit_code != 0:
|
|
||||||
sys.exit(exit_code)
|
|
||||||
|
def check_call(*args, **kwargs):
|
||||||
|
exit_code = call(*args, **kwargs)
|
||||||
|
if exit_code != 0:
|
||||||
|
sys.exit(exit_code)
|
||||||
|
|
||||||
|
|
||||||
|
def write_args(adb, args):
|
||||||
|
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)])
|
||||||
|
|
||||||
|
|
||||||
|
def write_user_stylesheets(adb, args):
|
||||||
|
data_dir = "/sdcard/Android/data/com.mozilla.servo/files"
|
||||||
|
check_call(adb + ["shell", "mkdir -p %s" % data_dir])
|
||||||
|
for i, (pos, path) in enumerate(extract_args("--user-stylesheet", args)):
|
||||||
|
remote_path = "%s/user%s.css" % (data_dir, i)
|
||||||
|
args[pos] = remote_path
|
||||||
|
check_call(adb + ["push", path, remote_path], stdout=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def forward_webdriver(adb, args):
|
||||||
|
webdriver_port = extract_arg("--webdriver", args)
|
||||||
|
if webdriver_port is not None:
|
||||||
|
# `adb forward` will start accepting TCP connections even if the other side does not.
|
||||||
|
# (If the remote side refuses the connection,
|
||||||
|
# adb will close the local side after accepting it.)
|
||||||
|
# This is incompatible with wptrunner which relies on TCP connection acceptance
|
||||||
|
# to figure out when it can start sending WebDriver requests.
|
||||||
|
#
|
||||||
|
# So wait until the remote side starts listening before setting up the forwarding.
|
||||||
|
wait_for_tcp_server(adb, webdriver_port)
|
||||||
|
|
||||||
|
port = "tcp:%s" % webdriver_port
|
||||||
|
check_call(adb + ["forward", port, port])
|
||||||
|
sys.stderr.write("Forwarding WebDriver port %s to the emulator\n" % webdriver_port)
|
||||||
|
|
||||||
|
split = os.environ.get("EMULATOR_REVERSE_FORWARD_PORTS", "").split(",")
|
||||||
|
ports = [int(part) for part in split if part]
|
||||||
|
for port in ports:
|
||||||
|
port = "tcp:%s" % port
|
||||||
|
check_call(adb + ["reverse", port, port])
|
||||||
|
if ports:
|
||||||
|
sys.stderr.write("Reverse-forwarding ports %s\n" % ", ".join(map(str, ports)))
|
||||||
|
|
||||||
|
|
||||||
|
def extract_arg(name, args):
|
||||||
|
for _, arg in extract_args(name, args):
|
||||||
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
def extract_args(name, args):
|
||||||
|
previous_arg_matches = False
|
||||||
|
for i, arg in enumerate(args):
|
||||||
|
if previous_arg_matches:
|
||||||
|
yield i, arg
|
||||||
|
previous_arg_matches = arg == name
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_tcp_server(adb, port):
|
||||||
|
while call(adb + ["shell", "nc -z 127.0.0.1 %s" % port], stdout=sys.stderr) != 0:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
# Copied from Python 3.3+'s shlex.quote()
|
# Copied from Python 3.3+'s shlex.quote()
|
||||||
|
|
|
@ -367,6 +367,21 @@ class MachCommands(CommandBase):
|
||||||
else:
|
else:
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@Command('test-wpt-android',
|
||||||
|
description='Run the web platform test suite in an Android emulator',
|
||||||
|
category='testing',
|
||||||
|
parser=create_parser_wpt)
|
||||||
|
def test_wpt_android(self, release=False, dev=False, binary_args=None, **kwargs):
|
||||||
|
kwargs.update(
|
||||||
|
release=release,
|
||||||
|
dev=dev,
|
||||||
|
product="servodriver",
|
||||||
|
processes=1,
|
||||||
|
binary_args=self.in_android_emulator(release, dev) + (binary_args or []),
|
||||||
|
binary=sys.executable,
|
||||||
|
)
|
||||||
|
return self._test_wpt(**kwargs)
|
||||||
|
|
||||||
def _test_wpt(self, **kwargs):
|
def _test_wpt(self, **kwargs):
|
||||||
hosts_file_path = path.join(self.context.topdir, 'tests', 'wpt', 'hosts')
|
hosts_file_path = path.join(self.context.topdir, 'tests', 'wpt', 'hosts')
|
||||||
os.environ["hosts_file_path"] = hosts_file_path
|
os.environ["hosts_file_path"] = hosts_file_path
|
||||||
|
@ -559,28 +574,15 @@ class MachCommands(CommandBase):
|
||||||
@CommandArgument('--dev', '-d', action='store_true',
|
@CommandArgument('--dev', '-d', action='store_true',
|
||||||
help='Run the dev build')
|
help='Run the dev build')
|
||||||
def test_android_startup(self, release, dev):
|
def test_android_startup(self, release, dev):
|
||||||
if (release and dev) or not (release or dev):
|
|
||||||
print("Please specify one of --dev or --release.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
avd = "servo-x86"
|
|
||||||
target = "i686-linux-android"
|
|
||||||
print("Assuming --target " + target)
|
|
||||||
|
|
||||||
env = self.build_env(target=target)
|
|
||||||
assert self.handle_android_target(target)
|
|
||||||
binary_path = self.get_binary_path(release, dev, android=True)
|
|
||||||
apk = binary_path + ".apk"
|
|
||||||
|
|
||||||
html = """
|
html = """
|
||||||
<script>
|
<script>
|
||||||
console.log("JavaScript is running!")
|
console.log("JavaScript is running!")
|
||||||
</script>
|
</script>
|
||||||
"""
|
"""
|
||||||
url = "data:text/html;base64," + html.encode("base64").replace("\n", "")
|
url = "data:text/html;base64," + html.encode("base64").replace("\n", "")
|
||||||
py = path.join(self.context.topdir, "etc", "run_in_headless_android_emulator.py")
|
args = self.in_android_emulator(release, dev)
|
||||||
args = [sys.executable, py, avd, apk, url]
|
args = [sys.executable] + args + [url]
|
||||||
process = subprocess.Popen(args, stdout=subprocess.PIPE, env=env)
|
process = subprocess.Popen(args, stdout=subprocess.PIPE)
|
||||||
try:
|
try:
|
||||||
while 1:
|
while 1:
|
||||||
line = process.stdout.readline()
|
line = process.stdout.readline()
|
||||||
|
@ -593,6 +595,24 @@ class MachCommands(CommandBase):
|
||||||
finally:
|
finally:
|
||||||
process.terminate()
|
process.terminate()
|
||||||
|
|
||||||
|
def in_android_emulator(self, release, dev):
|
||||||
|
if (release and dev) or not (release or dev):
|
||||||
|
print("Please specify one of --dev or --release.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
avd = "servo-x86"
|
||||||
|
target = "i686-linux-android"
|
||||||
|
print("Assuming --target " + target)
|
||||||
|
|
||||||
|
env = self.build_env(target=target)
|
||||||
|
os.environ["PATH"] = env["PATH"]
|
||||||
|
assert self.handle_android_target(target)
|
||||||
|
binary_path = self.get_binary_path(release, dev, android=True)
|
||||||
|
apk = binary_path + ".apk"
|
||||||
|
|
||||||
|
py = path.join(self.context.topdir, "etc", "run_in_headless_android_emulator.py")
|
||||||
|
return [py, avd, apk]
|
||||||
|
|
||||||
@Command('test-jquery',
|
@Command('test-jquery',
|
||||||
description='Run the jQuery test suite',
|
description='Run the jQuery test suite',
|
||||||
category='testing')
|
category='testing')
|
||||||
|
|
|
@ -37,7 +37,9 @@ def check_args(**kwargs):
|
||||||
def browser_kwargs(test_type, run_info_data, **kwargs):
|
def browser_kwargs(test_type, run_info_data, **kwargs):
|
||||||
return {
|
return {
|
||||||
"binary": kwargs["binary"],
|
"binary": kwargs["binary"],
|
||||||
|
"binary_args": kwargs["binary_args"],
|
||||||
"debug_info": kwargs["debug_info"],
|
"debug_info": kwargs["debug_info"],
|
||||||
|
"server_config": kwargs["config"],
|
||||||
"user_stylesheets": kwargs.get("user_stylesheets"),
|
"user_stylesheets": kwargs.get("user_stylesheets"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,16 +73,19 @@ def write_hosts_file(config):
|
||||||
|
|
||||||
class ServoWebDriverBrowser(Browser):
|
class ServoWebDriverBrowser(Browser):
|
||||||
used_ports = set()
|
used_ports = set()
|
||||||
|
init_timeout = 300 # Large timeout for cases where we're booting an Android emulator
|
||||||
|
|
||||||
def __init__(self, logger, binary, debug_info=None, webdriver_host="127.0.0.1",
|
def __init__(self, logger, binary, debug_info=None, webdriver_host="127.0.0.1",
|
||||||
user_stylesheets=None):
|
server_config=None, binary_args=None, user_stylesheets=None):
|
||||||
Browser.__init__(self, logger)
|
Browser.__init__(self, logger)
|
||||||
self.binary = binary
|
self.binary = binary
|
||||||
|
self.binary_args = binary_args or []
|
||||||
self.webdriver_host = webdriver_host
|
self.webdriver_host = webdriver_host
|
||||||
self.webdriver_port = None
|
self.webdriver_port = None
|
||||||
self.proc = None
|
self.proc = None
|
||||||
self.debug_info = debug_info
|
self.debug_info = debug_info
|
||||||
self.hosts_path = write_hosts_file()
|
self.hosts_path = write_hosts_file(server_config)
|
||||||
|
self.server_ports = server_config.ports if server_config else {}
|
||||||
self.command = None
|
self.command = None
|
||||||
self.user_stylesheets = user_stylesheets if user_stylesheets else []
|
self.user_stylesheets = user_stylesheets if user_stylesheets else []
|
||||||
|
|
||||||
|
@ -91,10 +96,16 @@ class ServoWebDriverBrowser(Browser):
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["HOST_FILE"] = self.hosts_path
|
env["HOST_FILE"] = self.hosts_path
|
||||||
env["RUST_BACKTRACE"] = "1"
|
env["RUST_BACKTRACE"] = "1"
|
||||||
|
env["EMULATOR_REVERSE_FORWARD_PORTS"] = ",".join(
|
||||||
|
str(port)
|
||||||
|
for _protocol, ports in self.server_ports.items()
|
||||||
|
for port in ports
|
||||||
|
if port
|
||||||
|
)
|
||||||
|
|
||||||
debug_args, command = browser_command(
|
debug_args, command = browser_command(
|
||||||
self.binary,
|
self.binary,
|
||||||
[
|
self.binary_args + [
|
||||||
"--hard-fail",
|
"--hard-fail",
|
||||||
"--webdriver", str(self.webdriver_port),
|
"--webdriver", str(self.webdriver_port),
|
||||||
"about:blank",
|
"about:blank",
|
||||||
|
@ -151,9 +162,10 @@ class ServoWebDriverBrowser(Browser):
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
self.stop()
|
self.stop()
|
||||||
shutil.rmtree(os.path.dirname(self.hosts_file))
|
os.remove(self.hosts_path)
|
||||||
|
|
||||||
def executor_browser(self):
|
def executor_browser(self):
|
||||||
assert self.webdriver_port is not None
|
assert self.webdriver_port is not None
|
||||||
return ExecutorBrowser, {"webdriver_host": self.webdriver_host,
|
return ExecutorBrowser, {"webdriver_host": self.webdriver_host,
|
||||||
"webdriver_port": self.webdriver_port}
|
"webdriver_port": self.webdriver_port,
|
||||||
|
"init_timeout": self.init_timeout}
|
||||||
|
|
|
@ -6,13 +6,16 @@ import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from .base import (Protocol,
|
from .base import (Protocol,
|
||||||
|
BaseProtocolPart,
|
||||||
RefTestExecutor,
|
RefTestExecutor,
|
||||||
RefTestImplementation,
|
RefTestImplementation,
|
||||||
TestharnessExecutor,
|
TestharnessExecutor,
|
||||||
strip_server)
|
strip_server)
|
||||||
from ..testrunner import Stop
|
from ..testrunner import Stop
|
||||||
|
from ..webdriver_server import wait_for_service
|
||||||
|
|
||||||
webdriver = None
|
webdriver = None
|
||||||
|
ServoCommandExtensions = None
|
||||||
|
|
||||||
here = os.path.join(os.path.split(__file__)[0])
|
here = os.path.join(os.path.split(__file__)[0])
|
||||||
|
|
||||||
|
@ -23,20 +26,76 @@ def do_delayed_imports():
|
||||||
global webdriver
|
global webdriver
|
||||||
import webdriver
|
import webdriver
|
||||||
|
|
||||||
|
global ServoCommandExtensions
|
||||||
|
class ServoCommandExtensions(object):
|
||||||
|
def __init__(self, session):
|
||||||
|
self.session = session
|
||||||
|
|
||||||
|
@webdriver.client.command
|
||||||
|
def get_prefs(self, *prefs):
|
||||||
|
body = {"prefs": list(prefs)}
|
||||||
|
return self.session.send_session_command("POST", "servo/prefs/get", body)
|
||||||
|
|
||||||
|
@webdriver.client.command
|
||||||
|
def set_prefs(self, prefs):
|
||||||
|
body = {"prefs": prefs}
|
||||||
|
return self.session.send_session_command("POST", "servo/prefs/set", body)
|
||||||
|
|
||||||
|
@webdriver.client.command
|
||||||
|
def reset_prefs(self, *prefs):
|
||||||
|
body = {"prefs": list(prefs)}
|
||||||
|
return self.session.send_session_command("POST", "servo/prefs/reset", body)
|
||||||
|
|
||||||
|
def change_prefs(self, old_prefs, new_prefs):
|
||||||
|
# Servo interprets reset with an empty list as reset everything
|
||||||
|
if old_prefs:
|
||||||
|
self.reset_prefs(*old_prefs.keys())
|
||||||
|
self.set_prefs({k: parse_pref_value(v) for k, v in new_prefs.items()})
|
||||||
|
|
||||||
|
|
||||||
|
# See parse_pref_from_command_line() in components/config/opts.rs
|
||||||
|
def parse_pref_value(value):
|
||||||
|
if value == "true":
|
||||||
|
return True
|
||||||
|
if value == "false":
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
return float(value)
|
||||||
|
except ValueError:
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class ServoBaseProtocolPart(BaseProtocolPart):
|
||||||
|
def execute_script(self, script, async=False):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_timeout(self, timeout):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_window(self, handle):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ServoWebDriverProtocol(Protocol):
|
class ServoWebDriverProtocol(Protocol):
|
||||||
|
implements = [ServoBaseProtocolPart]
|
||||||
|
|
||||||
def __init__(self, executor, browser, capabilities, **kwargs):
|
def __init__(self, executor, browser, capabilities, **kwargs):
|
||||||
do_delayed_imports()
|
do_delayed_imports()
|
||||||
Protocol.__init__(self, executor, browser)
|
Protocol.__init__(self, executor, browser)
|
||||||
self.capabilities = capabilities
|
self.capabilities = capabilities
|
||||||
self.host = browser.webdriver_host
|
self.host = browser.webdriver_host
|
||||||
self.port = browser.webdriver_port
|
self.port = browser.webdriver_port
|
||||||
|
self.init_timeout = browser.init_timeout
|
||||||
self.session = None
|
self.session = None
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
"""Connect to browser via WebDriver."""
|
"""Connect to browser via WebDriver."""
|
||||||
self.session = webdriver.Session(self.host, self.port,
|
wait_for_service((self.host, self.port), timeout=self.init_timeout)
|
||||||
extension=webdriver.servo.ServoCommandExtensions)
|
|
||||||
|
self.session = webdriver.Session(self.host, self.port, extension=ServoCommandExtensions)
|
||||||
self.session.start()
|
self.session.start()
|
||||||
|
|
||||||
def after_connect(self):
|
def after_connect(self):
|
||||||
|
@ -70,11 +129,6 @@ class ServoWebDriverProtocol(Protocol):
|
||||||
self.logger.error(traceback.format_exc(e))
|
self.logger.error(traceback.format_exc(e))
|
||||||
break
|
break
|
||||||
|
|
||||||
def on_environment_change(self, old_environment, new_environment):
|
|
||||||
#Unset all the old prefs
|
|
||||||
self.session.extension.reset_prefs(*old_environment.get("prefs", {}).keys())
|
|
||||||
self.session.extension.set_prefs(new_environment.get("prefs", {}))
|
|
||||||
|
|
||||||
|
|
||||||
class ServoWebDriverRun(object):
|
class ServoWebDriverRun(object):
|
||||||
def __init__(self, func, session, url, timeout, current_timeout=None):
|
def __init__(self, func, session, url, timeout, current_timeout=None):
|
||||||
|
@ -174,6 +228,12 @@ class ServoWebDriverTestharnessExecutor(TestharnessExecutor):
|
||||||
session.back()
|
session.back()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def on_environment_change(self, new_environment):
|
||||||
|
self.protocol.session.extension.change_prefs(
|
||||||
|
self.last_environment.get("prefs", {}),
|
||||||
|
new_environment.get("prefs", {})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TimeoutError(Exception):
|
class TimeoutError(Exception):
|
||||||
pass
|
pass
|
||||||
|
@ -240,3 +300,9 @@ class ServoWebDriverRefTestExecutor(RefTestExecutor):
|
||||||
session.url = url
|
session.url = url
|
||||||
session.execute_async_script(self.wait_script)
|
session.execute_async_script(self.wait_script)
|
||||||
return session.screenshot()
|
return session.screenshot()
|
||||||
|
|
||||||
|
def on_environment_change(self, new_environment):
|
||||||
|
self.protocol.session.extension.change_prefs(
|
||||||
|
self.last_environment.get("prefs", {}),
|
||||||
|
new_environment.get("prefs", {})
|
||||||
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue