diff --git a/testing/geckodriver/src/capabilities.rs b/testing/geckodriver/src/capabilities.rs index edd6b0ea8c6c..ea29e592fe03 100644 --- a/testing/geckodriver/src/capabilities.rs +++ b/testing/geckodriver/src/capabilities.rs @@ -11,9 +11,9 @@ use std::collections::BTreeMap; use std::default::Default; use std::error::Error; use std::fs; +use std::io; use std::io::BufWriter; use std::io::Cursor; -use std::io; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::str::{self, FromStr}; @@ -34,7 +34,6 @@ pub struct FirefoxCapabilities<'a> { version_cache: BTreeMap, } - impl<'a> FirefoxCapabilities<'a> { pub fn new(fallback_binary: Option<&'a PathBuf>) -> FirefoxCapabilities<'a> { FirefoxCapabilities { @@ -69,8 +68,7 @@ impl<'a> FirefoxCapabilities<'a> { }); if let Some(ref version) = rv { debug!("Found version {}", version); - self.version_cache - .insert(binary.clone(), version.clone()); + self.version_cache.insert(binary.clone(), version.clone()); } else { debug!("Failed to get binary version"); } @@ -81,7 +79,8 @@ impl<'a> FirefoxCapabilities<'a> { } fn version_from_binary(&self, binary: &PathBuf) -> Option { - let version_regexp = Regex::new(r#"\d+\.\d+(?:[a-z]\d+)?"#).expect("Error parsing version regexp"); + let version_regexp = + Regex::new(r#"\d+\.\d+(?:[a-z]\d+)?"#).expect("Error parsing version regexp"); let output = Command::new(binary) .args(&["-version"]) .stdout(Stdio::piped()) @@ -90,7 +89,8 @@ impl<'a> FirefoxCapabilities<'a> { .ok(); if let Some(x) = output { - version_regexp.captures(&*x.stdout) + version_regexp + .captures(&*x.stdout) .and_then(|captures| captures.get(0)) .and_then(|m| str::from_utf8(m.as_bytes()).ok()) .map(|x| x.into()) @@ -104,7 +104,8 @@ impl<'a> FirefoxCapabilities<'a> { fn convert_version_error(err: mozversion::Error) -> WebDriverError { WebDriverError::new( ErrorStatus::SessionNotCreated, - err.description().to_string()) + err.description().to_string(), + ) } impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> { @@ -122,8 +123,8 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> { fn platform_name(&mut self, _: &Capabilities) -> WebDriverResult> { Ok(if cfg!(target_os = "windows") { - Some("windows".into()) - } else if cfg!(target_os = "macos") { + Some("windows".into()) + } else if cfg!(target_os = "macos") { Some("mac".into()) } else if cfg!(target_os = "linux") { Some("linux".into()) @@ -145,10 +146,11 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> { Ok(true) } - fn compare_browser_version(&mut self, - version: &str, - comparison: &str) - -> WebDriverResult { + fn compare_browser_version( + &mut self, + version: &str, + comparison: &str, + ) -> WebDriverResult { try!(Version::from_str(version).or_else(|x| Err(convert_version_error(x)))) .matches(comparison) .or_else(|x| Err(convert_version_error(x))) @@ -158,78 +160,100 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> { Ok(true) } - fn validate_custom(&self, name: &str, value: &Value) -> WebDriverResult<()> { + fn validate_custom(&self, name: &str, value: &Value) -> WebDriverResult<()> { if !name.starts_with("moz:") { - return Ok(()) + return Ok(()); } match name { "moz:firefoxOptions" => { - let data = try_opt!(value.as_object(), - ErrorStatus::InvalidArgument, - "moz:firefoxOptions is not an object"); + let data = try_opt!( + value.as_object(), + ErrorStatus::InvalidArgument, + "moz:firefoxOptions is not an object" + ); for (key, value) in data.iter() { match &**key { "binary" => { if !value.is_string() { return Err(WebDriverError::new( ErrorStatus::InvalidArgument, - "binary path is not a string")); + "binary path is not a string", + )); } - }, + } "args" => { - if !try_opt!(value.as_array(), - ErrorStatus::InvalidArgument, - "args is not an array") - .iter() - .all(|value| value.is_string()) { + if !try_opt!( + value.as_array(), + ErrorStatus::InvalidArgument, + "args is not an array" + ).iter() + .all(|value| value.is_string()) + { return Err(WebDriverError::new( ErrorStatus::InvalidArgument, - "args entry is not a string")); - } - }, + "args entry is not a string", + )); + } + } "profile" => { if !value.is_string() { return Err(WebDriverError::new( ErrorStatus::InvalidArgument, - "profile is not a string")); + "profile is not a string", + )); } - }, + } "log" => { - let log_data = try_opt!(value.as_object(), - ErrorStatus::InvalidArgument, - "log value is not an object"); + let log_data = try_opt!( + value.as_object(), + ErrorStatus::InvalidArgument, + "log value is not an object" + ); for (log_key, log_value) in log_data.iter() { match &**log_key { "level" => { - let level = try_opt!(log_value.as_str(), - ErrorStatus::InvalidArgument, - "log level is not a string"); + let level = try_opt!( + log_value.as_str(), + ErrorStatus::InvalidArgument, + "log level is not a string" + ); if Level::from_str(level).is_err() { return Err(WebDriverError::new( ErrorStatus::InvalidArgument, - format!("Not a valid log level: {}", level))) + format!("Not a valid log level: {}", level), + )); } } - x => return Err(WebDriverError::new( - ErrorStatus::InvalidArgument, - format!("Invalid log field {}", x))) + x => { + return Err(WebDriverError::new( + ErrorStatus::InvalidArgument, + format!("Invalid log field {}", x), + )) + } } } - }, - "prefs" => { - let prefs_data = try_opt!(value.as_object(), - ErrorStatus::InvalidArgument, - "prefs value is not an object"); - if !prefs_data.values() - .all(|x| x.is_string() || x.is_i64() || x.is_u64() || x.is_boolean()) { - return Err(WebDriverError::new( - ErrorStatus::InvalidArgument, - "Preference values not all string or integer or boolean")); - } } - x => return Err(WebDriverError::new( - ErrorStatus::InvalidArgument, - format!("Invalid moz:firefoxOptions field {}", x))) + "prefs" => { + let prefs_data = try_opt!( + value.as_object(), + ErrorStatus::InvalidArgument, + "prefs value is not an object" + ); + if !prefs_data.values().all(|x| { + x.is_string() || x.is_i64() || x.is_u64() || x.is_boolean() + }) { + return Err(WebDriverError::new( + ErrorStatus::InvalidArgument, + "Preference values not all string or integer or boolean", + )); + } + } + x => { + return Err(WebDriverError::new( + ErrorStatus::InvalidArgument, + format!("Invalid moz:firefoxOptions field {}", x), + )) + } } } } @@ -237,18 +261,24 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> { if !value.is_boolean() { return Err(WebDriverError::new( ErrorStatus::InvalidArgument, - "moz:useNonSpecCompliantPointerOrigin is not a boolean")); + "moz:useNonSpecCompliantPointerOrigin is not a boolean", + )); } } "moz:webdriverClick" => { if !value.is_boolean() { return Err(WebDriverError::new( ErrorStatus::InvalidArgument, - "moz:webdriverClick is not a boolean")); + "moz:webdriverClick is not a boolean", + )); } } - _ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument, - format!("Unrecognised option {}", name))) + _ => { + return Err(WebDriverError::new( + ErrorStatus::InvalidArgument, + format!("Unrecognised option {}", name), + )) + } } Ok(()) } @@ -278,17 +308,19 @@ impl FirefoxOptions { Default::default() } - pub fn from_capabilities(binary_path: Option, - matched: &mut Capabilities) - -> WebDriverResult { + pub fn from_capabilities( + binary_path: Option, + matched: &mut Capabilities, + ) -> WebDriverResult { let mut rv = FirefoxOptions::new(); rv.binary = binary_path; if let Some(json) = matched.remove("moz:firefoxOptions") { - let options = try!(json.as_object() - .ok_or(WebDriverError::new(ErrorStatus::InvalidArgument, - "'moz:firefoxOptions' \ - capability is not an object"))); + let options = try!(json.as_object().ok_or(WebDriverError::new( + ErrorStatus::InvalidArgument, + "'moz:firefoxOptions' \ + capability is not an object" + ))); rv.profile = try!(FirefoxOptions::load_profile(&options)); rv.args = try!(FirefoxOptions::load_args(&options)); @@ -301,21 +333,22 @@ impl FirefoxOptions { fn load_profile(options: &Capabilities) -> WebDriverResult> { if let Some(profile_json) = options.get("profile") { - let profile_base64 = - try!(profile_json - .as_str() - .ok_or(WebDriverError::new(ErrorStatus::UnknownError, - "Profile is not a string"))); + let profile_base64 = try!(profile_json.as_str().ok_or(WebDriverError::new( + ErrorStatus::UnknownError, + "Profile is not a string" + ))); let profile_zip = &*try!(base64::decode(profile_base64)); // Create an emtpy profile directory let profile = try!(Profile::new(None)); - try!(unzip_buffer(profile_zip, - profile - .temp_dir - .as_ref() - .expect("Profile doesn't have a path") - .path())); + try!(unzip_buffer( + profile_zip, + profile + .temp_dir + .as_ref() + .expect("Profile doesn't have a path") + .path() + )); Ok(Some(profile)) } else { @@ -325,18 +358,22 @@ impl FirefoxOptions { fn load_args(options: &Capabilities) -> WebDriverResult>> { if let Some(args_json) = options.get("args") { - let args_array = try!(args_json - .as_array() - .ok_or(WebDriverError::new(ErrorStatus::UnknownError, - "Arguments were not an \ - array"))); - let args = try!(args_array - .iter() - .map(|x| x.as_str().map(|x| x.to_owned())) - .collect::>>() - .ok_or(WebDriverError::new(ErrorStatus::UnknownError, - "Arguments entries were not all \ - strings"))); + let args_array = try!(args_json.as_array().ok_or(WebDriverError::new( + ErrorStatus::UnknownError, + "Arguments were not an \ + array" + ))); + let args = try!( + args_array + .iter() + .map(|x| x.as_str().map(|x| x.to_owned())) + .collect::>>() + .ok_or(WebDriverError::new( + ErrorStatus::UnknownError, + "Arguments entries were not all \ + strings" + )) + ); Ok(Some(args)) } else { Ok(None) @@ -372,10 +409,10 @@ impl FirefoxOptions { pub fn load_prefs(options: &Capabilities) -> WebDriverResult> { if let Some(prefs_data) = options.get("prefs") { - let prefs = try!(prefs_data - .as_object() - .ok_or(WebDriverError::new(ErrorStatus::UnknownError, - "Prefs were not an object"))); + let prefs = try!(prefs_data.as_object().ok_or(WebDriverError::new( + ErrorStatus::UnknownError, + "Prefs were not an object" + ))); let mut rv = Vec::with_capacity(prefs.len()); for (key, value) in prefs.iter() { rv.push((key.clone(), try!(pref_from_json(value)))); @@ -392,21 +429,25 @@ fn pref_from_json(value: &Value) -> WebDriverResult { &Value::String(ref x) => Ok(Pref::new(x.clone())), &Value::Number(ref x) => Ok(Pref::new(x.as_i64().unwrap())), &Value::Bool(x) => Ok(Pref::new(x)), - _ => Err(WebDriverError::new(ErrorStatus::UnknownError, - "Could not convert pref value to string, boolean, or integer")) + _ => Err(WebDriverError::new( + ErrorStatus::UnknownError, + "Could not convert pref value to string, boolean, or integer", + )), } } fn unzip_buffer(buf: &[u8], dest_dir: &Path) -> WebDriverResult<()> { let reader = Cursor::new(buf); - let mut zip = try!(zip::ZipArchive::new(reader).map_err(|_| { - WebDriverError::new(ErrorStatus::UnknownError, "Failed to unzip profile") - })); + let mut zip = try!( + zip::ZipArchive::new(reader) + .map_err(|_| WebDriverError::new(ErrorStatus::UnknownError, "Failed to unzip profile")) + ); for i in 0..zip.len() { - let mut file = try!(zip.by_index(i).map_err(|_| { - WebDriverError::new(ErrorStatus::UnknownError, "Processing profile zip file failed") - })); + let mut file = try!(zip.by_index(i).map_err(|_| WebDriverError::new( + ErrorStatus::UnknownError, + "Processing profile zip file failed" + ))); let unzip_path = { let name = file.name(); let is_dir = name.ends_with("/"); @@ -486,8 +527,10 @@ mod tests { println!("{:#?}", prefs.prefs); - assert_eq!(prefs.get("startup.homepage_welcome_url"), - Some(&Pref::new("data:text/html,PASS"))); + assert_eq!( + prefs.get("startup.homepage_welcome_url"), + Some(&Pref::new("data:text/html,PASS")) + ); } #[test] diff --git a/testing/geckodriver/src/logging.rs b/testing/geckodriver/src/logging.rs index 4a4faa86923c..a8bda4da5c43 100644 --- a/testing/geckodriver/src/logging.rs +++ b/testing/geckodriver/src/logging.rs @@ -279,7 +279,10 @@ mod tests { ]; for &(lvl, s) in tests.iter() { - let expected = Pref { value: PrefValue::String(s.to_string()), sticky: false }; + let expected = Pref { + value: PrefValue::String(s.to_string()), + sticky: false, + }; assert_eq!(Into::::into(lvl), expected); } } diff --git a/testing/geckodriver/src/main.rs b/testing/geckodriver/src/main.rs index 70a8e8bc6ad4..cc01b46710ee 100644 --- a/testing/geckodriver/src/main.rs +++ b/testing/geckodriver/src/main.rs @@ -14,8 +14,8 @@ extern crate serde; extern crate serde_derive; extern crate serde_json; extern crate uuid; -extern crate zip; extern crate webdriver; +extern crate zip; #[macro_use] extern crate log; @@ -28,25 +28,25 @@ use std::str::FromStr; use clap::{App, Arg}; macro_rules! try_opt { - ($expr:expr, $err_type:expr, $err_msg:expr) => ({ + ($expr:expr, $err_type:expr, $err_msg:expr) => {{ match $expr { Some(x) => x, - None => return Err(WebDriverError::new($err_type, $err_msg)) + None => return Err(WebDriverError::new($err_type, $err_msg)), } - }) + }}; } mod build; -mod logging; -mod prefs; -mod marionette; mod capabilities; +mod logging; +mod marionette; +mod prefs; #[cfg(test)] pub mod test; use build::BuildInfo; -use marionette::{MarionetteHandler, MarionetteSettings, extension_routes}; +use marionette::{extension_routes, MarionetteHandler, MarionetteSettings}; type ProgramResult = std::result::Result<(), (ExitCode, String)>; @@ -69,52 +69,70 @@ fn print_version() { fn app<'a, 'b>() -> App<'a, 'b> { App::new(format!("geckodriver {}", crate_version!())) .about("WebDriver implementation for Firefox.") - .arg(Arg::with_name("webdriver_host") - .long("host") - .value_name("HOST") - .help("Host ip to use for WebDriver server (default: 127.0.0.1)") - .takes_value(true)) - .arg(Arg::with_name("webdriver_port") - .short("p") - .long("port") - .value_name("PORT") - .help("Port to use for WebDriver server (default: 4444)") - .takes_value(true) - .alias("webdriver-port")) - .arg(Arg::with_name("binary") - .short("b") - .long("binary") - .value_name("BINARY") - .help("Path to the Firefox binary") - .takes_value(true)) - .arg(Arg::with_name("marionette_port") - .long("marionette-port") - .value_name("PORT") - .help("Port to use to connect to Gecko (default: random free port)") - .takes_value(true)) - .arg(Arg::with_name("connect_existing") - .long("connect-existing") - .requires("marionette_port") - .help("Connect to an existing Firefox instance")) - .arg(Arg::with_name("jsdebugger") - .long("jsdebugger") - .takes_value(false) - .help("Attach browser toolbox debugger for Firefox")) - .arg(Arg::with_name("verbosity") - .short("v") - .multiple(true) - .conflicts_with("log_level") - .help("Log level verbosity (-v for debug and -vv for trace level)")) - .arg(Arg::with_name("log_level") - .long("log") - .takes_value(true) - .value_name("LEVEL") - .possible_values(&["fatal", "error", "warn", "info", "config", "debug", "trace"]) - .help("Set Gecko log level")) - .arg(Arg::with_name("version") - .short("V") - .long("version") - .help("Prints version and copying information")) + .arg( + Arg::with_name("webdriver_host") + .long("host") + .value_name("HOST") + .help("Host ip to use for WebDriver server (default: 127.0.0.1)") + .takes_value(true), + ) + .arg( + Arg::with_name("webdriver_port") + .short("p") + .long("port") + .value_name("PORT") + .help("Port to use for WebDriver server (default: 4444)") + .takes_value(true) + .alias("webdriver-port"), + ) + .arg( + Arg::with_name("binary") + .short("b") + .long("binary") + .value_name("BINARY") + .help("Path to the Firefox binary") + .takes_value(true), + ) + .arg( + Arg::with_name("marionette_port") + .long("marionette-port") + .value_name("PORT") + .help("Port to use to connect to Gecko (default: random free port)") + .takes_value(true), + ) + .arg( + Arg::with_name("connect_existing") + .long("connect-existing") + .requires("marionette_port") + .help("Connect to an existing Firefox instance"), + ) + .arg( + Arg::with_name("jsdebugger") + .long("jsdebugger") + .takes_value(false) + .help("Attach browser toolbox debugger for Firefox"), + ) + .arg( + Arg::with_name("verbosity") + .short("v") + .multiple(true) + .conflicts_with("log_level") + .help("Log level verbosity (-v for debug and -vv for trace level)"), + ) + .arg( + Arg::with_name("log_level") + .long("log") + .takes_value(true) + .value_name("LEVEL") + .possible_values(&["fatal", "error", "warn", "info", "config", "debug", "trace"]) + .help("Set Gecko log level"), + ) + .arg( + Arg::with_name("version") + .short("V") + .long("version") + .help("Prints version and copying information"), + ) } fn run() -> ProgramResult { @@ -143,12 +161,10 @@ fn run() -> ProgramResult { let binary = matches.value_of("binary").map(|x| PathBuf::from(x)); let marionette_port = match matches.value_of("marionette_port") { - Some(x) => { - match u16::from_str(x) { - Ok(x) => Some(x), - Err(_) => return Err((ExitCode::Usage, "invalid Marionette port".into())), - } - } + Some(x) => match u16::from_str(x) { + Ok(x) => Some(x), + Err(_) => return Err((ExitCode::Usage, "invalid Marionette port".into())), + }, None => None, };