diff --git a/testing/geckodriver/src/main.rs b/testing/geckodriver/src/main.rs index 5dd1201f6c40..3fbf1c56f304 100644 --- a/testing/geckodriver/src/main.rs +++ b/testing/geckodriver/src/main.rs @@ -19,7 +19,7 @@ use std::net::{SocketAddr, IpAddr}; use std::path::Path; use std::str::FromStr; -use argparse::{ArgumentParser, IncrBy, StoreTrue, Store}; +use argparse::{ArgumentParser, IncrBy, StoreTrue, Store, StoreOption}; use webdriver::server::start; use marionette::{MarionetteHandler, BrowserLauncher, LogLevel, MarionetteSettings, extension_routes}; @@ -46,7 +46,7 @@ struct Options { binary: String, webdriver_host: String, webdriver_port: u16, - marionette_port: u16, + marionette_port: Option, connect_existing: bool, e10s: bool, log_level: String, @@ -59,7 +59,7 @@ fn parse_args() -> Options { binary: "".to_owned(), webdriver_host: "127.0.0.1".to_owned(), webdriver_port: 4444u16, - marionette_port: 2828u16, + marionette_port: None, connect_existing: false, e10s: false, log_level: "".to_owned(), @@ -81,7 +81,7 @@ fn parse_args() -> Options { .add_option(&["--webdriver-port"], Store, "Port to run webdriver on"); parser.refer(&mut opts.marionette_port) - .add_option(&["--marionette-port"], Store, + .add_option(&["--marionette-port"], StoreOption, "Port to run marionette on"); parser.refer(&mut opts.connect_existing) .add_option(&["--connect-existing"], StoreTrue, @@ -205,6 +205,8 @@ mod tests { use mozprofile::preferences::Pref; use std::io::Read; + const MARIONETTE_DEFAULT_PORT: u16 = 2828; + #[test] fn test_profile() { let mut profile_data = Vec::with_capacity(1024); @@ -227,7 +229,7 @@ mod tests { }; let settings = MarionetteSettings { - port: 2828, + port: None, launcher: BrowserLauncher::None, e10s: false, log_level: None, @@ -235,7 +237,7 @@ mod tests { let handler = MarionetteHandler::new(settings); let mut gecko_profile = handler.load_profile(&capabilities).unwrap().unwrap(); - handler.set_prefs(&mut gecko_profile, true).unwrap(); + handler.set_prefs(MARIONETTE_DEFAULT_PORT, &mut gecko_profile, true).unwrap(); let prefs = gecko_profile.user_prefs().unwrap(); diff --git a/testing/geckodriver/src/marionette.rs b/testing/geckodriver/src/marionette.rs index f5d849bfd5c4..91d53f8d1afe 100644 --- a/testing/geckodriver/src/marionette.rs +++ b/testing/geckodriver/src/marionette.rs @@ -16,7 +16,7 @@ use std::io::ErrorKind; use std::io::Result as IoResult; use std::io::prelude::*; use std::io; -use std::net::TcpStream; +use std::net::{TcpListener, TcpStream}; use std::path::{Path, PathBuf}; use std::sync::Mutex; use std::thread::sleep; @@ -277,7 +277,7 @@ impl FromStr for LogLevel { } pub struct MarionetteSettings { - pub port: u16, + pub port: Option, pub launcher: BrowserLauncher, /// Enable or disable Electrolysis, or multi-processing, in Gecko. @@ -293,7 +293,7 @@ pub struct MarionetteHandler { connection: Mutex>, launcher: BrowserLauncher, browser: Option, - port: u16, + port: Option, e10s: bool, log_level: Option, } @@ -315,7 +315,11 @@ impl MarionetteHandler { debug!("create_connection"); let profile = try!(self.load_profile(capabilities)); let args = try!(self.load_browser_args(capabilities)); - match self.start_browser(profile, args) { + let port = match self.port { + Some(x) => x, + None => try!(get_free_port()) + }; + match self.start_browser(port, profile, args) { Err(e) => { return Err(WebDriverError::new(ErrorStatus::UnknownError, e.description().to_owned())); @@ -323,7 +327,7 @@ impl MarionetteHandler { Ok(_) => {} } debug!("Creating connection"); - let mut connection = MarionetteConnection::new(self.port, session_id.clone()); + let mut connection = MarionetteConnection::new(port, session_id.clone()); debug!("Starting marionette connection"); try!(connection.connect()); debug!("Marionette connection started"); @@ -331,7 +335,7 @@ impl MarionetteHandler { Ok(()) } - fn start_browser(&mut self, profile: Option, args: Option>) -> Result<(), RunnerError> { + fn start_browser(&mut self, port: u16, profile: Option, args: Option>) -> Result<(), RunnerError> { let custom_profile = profile.is_some(); match self.launcher { @@ -340,7 +344,7 @@ impl MarionetteHandler { if let Some(cmd_args) = args { runner.args().extend(cmd_args); }; - try!(self.set_prefs(&mut runner.profile, custom_profile)); + try!(self.set_prefs(port, &mut runner.profile, custom_profile)); try!(runner.start()); self.browser = Some(runner); @@ -353,10 +357,10 @@ impl MarionetteHandler { Ok(()) } - pub fn set_prefs(&self, profile: &mut Profile, custom_profile: bool) + pub fn set_prefs(&self, port: u16, profile: &mut Profile, custom_profile: bool) -> Result<(), RunnerError> { let prefs = try!(profile.user_prefs()); - prefs.insert("marionette.defaultPrefs.port", Pref::new(self.port as i64)); + prefs.insert("marionette.defaultPrefs.port", Pref::new(port as i64)); prefs.insert_slice(&FIREFOX_REQUIRED_PREFERENCES[..]); if !custom_profile { @@ -1085,6 +1089,12 @@ impl ToJson for MarionetteError { } } +fn get_free_port() -> IoResult { + TcpListener::bind(&("localhost", 0)) + .and_then(|stream| stream.local_addr()) + .map(|x| x.port()) +} + pub struct MarionetteConnection { port: u16, stream: Option, @@ -1105,6 +1115,7 @@ impl MarionetteConnection { let poll_interval = 100; // ms let poll_attempts = timeout / poll_interval; let mut poll_attempt = 0; + info!("Connecting to Marionette on localhost:{}", self.port); loop { match TcpStream::connect(&("localhost", self.port)) { Ok(stream) => {