Skip to content

Commit 8099793

Browse files
committed
Support launching multiple Chrome DriverSessions
1 parent c5a4a67 commit 8099793

4 files changed

Lines changed: 59 additions & 17 deletions

File tree

src/chrome.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::*;
22

33
use std::process::{Command, Child, Stdio};
4+
use std::sync::Arc;
45
use std::thread;
56
use std::time::Duration;
67

@@ -26,7 +27,7 @@ impl ChromeDriverBuilder {
2627
self.kill_on_drop = kill;
2728
self
2829
}
29-
pub fn spawn(self) -> Result<ChromeDriver, Error> {
30+
pub fn spawn(self) -> Result<Arc<ChromeDriver>, Error> {
3031
let port = util::check_tcp_port(self.port)?;
3132

3233
let child = Command::new("chromedriver")
@@ -38,11 +39,11 @@ impl ChromeDriverBuilder {
3839

3940
// TODO: parameterize this
4041
thread::sleep(Duration::new(1, 500));
41-
Ok(ChromeDriver {
42+
Ok(Arc::new(ChromeDriver {
4243
child: child,
4344
url: format!("http://localhost:{}", port),
4445
kill_on_drop: self.kill_on_drop,
45-
})
46+
}))
4647
}
4748
}
4849

@@ -54,7 +55,7 @@ pub struct ChromeDriver {
5455
}
5556

5657
impl ChromeDriver {
57-
pub fn spawn() -> Result<Self, Error> {
58+
pub fn spawn() -> Result<Arc<Self>, Error> {
5859
ChromeDriverBuilder::new().spawn()
5960
}
6061
pub fn build() -> ChromeDriverBuilder {
@@ -70,8 +71,15 @@ impl Drop for ChromeDriver {
7071
}
7172
}
7273

73-
impl Driver for ChromeDriver {
74+
// ChromeDriver supports launching multiple browsers from the same chromedriver process.
75+
impl Driver for Arc<ChromeDriver> {
7476
fn url(&self) -> &str {
7577
&self.url
7678
}
7779
}
80+
81+
impl MultiSessionDriver for Arc<ChromeDriver> {
82+
fn session(&self) -> Result<DriverSession, Error> {
83+
DriverSession::create_session(self.url(), self.clone())
84+
}
85+
}

src/firefox.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ impl GeckoDriver {
6969
pub fn build() -> GeckoDriverBuilder {
7070
GeckoDriverBuilder::new()
7171
}
72+
73+
/// Start a session for this driver
74+
pub fn session(self) -> Result<DriverSession, Error> where Self : Sized + 'static {
75+
DriverSession::create_session(&self.url().to_owned(), self)
76+
}
7277
}
7378

7479
impl Drop for GeckoDriver {
@@ -84,4 +89,3 @@ impl Driver for GeckoDriver {
8489
&self.url
8590
}
8691
}
87-

src/lib.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::convert::From;
77
use std::io::Read;
88
use std::io;
99
use std::fmt::{self, Debug};
10+
use std::sync::Weak;
1011

1112
extern crate hyper;
1213
use hyper::client::*;
@@ -83,10 +84,11 @@ impl From<serde_json::Error> for Error {
8384
pub trait Driver {
8485
/// The url used to connect to this driver
8586
fn url(&self) -> &str;
87+
}
88+
89+
pub trait MultiSessionDriver: Driver {
8690
/// Start a session for this driver
87-
fn session(self) -> Result<DriverSession, Error> where Self : Sized + 'static {
88-
DriverSession::create_session(Box::new(self))
89-
}
91+
fn session(&self) -> Result<DriverSession, Error>;
9092
}
9193

9294
/// A driver using a pre-existing WebDriver HTTP URL.
@@ -101,6 +103,17 @@ impl Driver for HttpDriver {
101103
fn url(&self) -> &str { &self.url }
102104
}
103105

106+
impl MultiSessionDriver for HttpDriver {
107+
fn session(&self) -> Result<DriverSession, Error> {
108+
// We need to pass something which implements Drop here,
109+
// and a Weak is a cheap something.
110+
// Ideally, create_session would take an Option<impl Drop>,
111+
// and we could just pass None, but impl Trait isn't a stable thing yet.
112+
let r: Weak<()> = Weak::new();
113+
DriverSession::create_session(&self.url(), r)
114+
}
115+
}
116+
104117
/// Wrapper around the hyper Client, that handles Json encoding and URL construction
105118
struct HttpClient {
106119
baseurl: Url,
@@ -166,25 +179,25 @@ impl HttpClient {
166179
///
167180
/// By default the session is removed on `Drop`
168181
pub struct DriverSession {
169-
driver: Box<Driver>,
182+
driver: Box<Drop>,
170183
client: HttpClient,
171184
session_id: String,
172185
drop_session: bool,
173186
}
174187

175188
impl DriverSession {
176189
/// Create a new session with the driver.
177-
pub fn create_session(driver: Box<Driver>)
190+
pub fn create_session<D: Drop + Sized + 'static>(url: &str, driver: D)
178191
-> Result<DriverSession, Error>
179192
{
180-
let baseurl = Url::parse(driver.url())
193+
let baseurl = Url::parse(url)
181194
.map_err(|_| Error::InvalidUrl)?;
182195
let client = HttpClient::new(baseurl);
183196
info!("Creating session at {}", client.baseurl);
184197
let sess = try!(Self::new_session(&client, &NewSessionCmd::new()));
185198
info!("Session {} created", sess.sessionId);
186199
Ok(DriverSession {
187-
driver: driver,
200+
driver: Box::new(driver),
188201
client: client,
189202
session_id: sess.sessionId,
190203
drop_session: true,
@@ -193,10 +206,8 @@ impl DriverSession {
193206

194207
/// Use an existing session
195208
pub fn attach(url: &str, session_id: &str) -> Result<DriverSession, Error> {
196-
let driver = Box::new(HttpDriver {
197-
url: url.to_owned(),
198-
});
199209
let baseurl = try!(Url::parse(url).map_err(|_| Error::InvalidUrl));
210+
let driver: Box<Weak<()>> = Box::new(Weak::new());
200211
let mut s = DriverSession {
201212
driver: driver,
202213
client: HttpClient::new(baseurl),

tests/webdriver_client_integration.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ use std::path::PathBuf;
1515
use std::sync::{Once, ONCE_INIT};
1616
use std::thread::sleep;
1717
use std::time::Duration;
18-
use webdriver_client::{Driver, DriverSession, HttpDriverBuilder, LocationStrategy};
18+
use webdriver_client::{Driver, DriverSession, HttpDriverBuilder, LocationStrategy, MultiSessionDriver};
19+
use webdriver_client::chrome::ChromeDriver;
1920
use webdriver_client::firefox::GeckoDriver;
2021
use webdriver_client::messages::ExecuteCmd;
2122

@@ -335,6 +336,24 @@ fn test_http_driver() {
335336
assert_eq!(url, test_url);
336337
}
337338

339+
#[test]
340+
fn launch_multiple_chromes() {
341+
ensure_logging_init();
342+
343+
let server = FileServer::new();
344+
let chromedriver = ChromeDriver::build().spawn().unwrap();
345+
let chrome1 = chromedriver.session().expect("Error starting first browser");
346+
let chrome2 = chromedriver.session().expect("Error starting second browser");
347+
348+
let page1 = server.url("/page1.html");
349+
let page2 = server.url("/page2.html");
350+
chrome1.go(&page1).expect("Error getting page1");
351+
chrome2.go(&page2).expect("Error getting page2");
352+
353+
assert_eq!(&chrome1.get_current_url().expect("Error getting url 1"), &page1);
354+
assert_eq!(&chrome2.get_current_url().expect("Error getting url 2"), &page2);
355+
}
356+
338357
fn ensure_logging_init() {
339358
static DONE: Once = ONCE_INIT;
340359
DONE.call_once(|| init_logging());

0 commit comments

Comments
 (0)