Skip to content

Commit

Permalink
[chromedriver] Add useExistingBrowser capability for connecting to ex…
Browse files Browse the repository at this point in the history
…isting instance of Chrome.

Capability is expected to formatted as hostname:port, but hostname is ignored for now.
BUG=205
R=kkania@chromium.org

Review URL: https://codereview.chromium.org/23267004

Patch from Binod Pant <binod.pant@mathworks.com>.

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@219619 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
kkania@chromium.org committed Aug 26, 2013
1 parent c3eb117 commit af898a5
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 2 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,5 @@ Google Inc. <*@google.com>
NVIDIA Corporation <*@nvidia.com>
Opera Software ASA <*@opera.com>
The Chromium Authors <*@chromium.org>
The MathWorks, Inc. <binod.pant@mathworks.com>
Torchmobile Inc.
2 changes: 2 additions & 0 deletions chrome/chrome_tests.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,8 @@
'test/chromedriver/chrome/chrome_android_impl.h',
'test/chromedriver/chrome/chrome_desktop_impl.cc',
'test/chromedriver/chrome/chrome_desktop_impl.h',
'test/chromedriver/chrome/chrome_existing_impl.cc',
'test/chromedriver/chrome/chrome_existing_impl.h',
'test/chromedriver/chrome/chrome_finder.cc',
'test/chromedriver/chrome/chrome_finder.h',
'test/chromedriver/chrome/chrome_finder_mac.mm',
Expand Down
52 changes: 52 additions & 0 deletions chrome/test/chromedriver/capabilities.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

#include "base/bind.h"
#include "base/callback.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
Expand Down Expand Up @@ -289,6 +292,45 @@ Status ParseAndroidChromeCapabilities(const base::DictionaryValue& desired_caps,
return Status(kOk);
}

Status ParseExistingBrowserCapabilities(
const base::DictionaryValue& desired_caps,
Capabilities* capabilities) {
const base::Value* chrome_options = NULL;
if (desired_caps.Get("chromeOptions", &chrome_options)) {
const base::DictionaryValue* chrome_options_dict = NULL;
if (!chrome_options->GetAsDictionary(&chrome_options_dict))
return Status(kUnknownError, "'chromeOptions' must be a dictionary");

// UseExistingBrowser will be formatted as host:port.
const base::Value* existing_browser_value;
if (!chrome_options_dict->Get("useExistingBrowser",
&existing_browser_value)) {
return Status(kOk);
}

std::string existing_pair;
if (!existing_browser_value->GetAsString(&existing_pair)) {
return Status(kUnknownError,
"'useExistingBrowser' must be host:port");
}

std::vector<std::string> values;
base::SplitString(existing_pair, ':', &values);
if (values.size() != 2) {
return Status(kUnknownError,
"'useExistingBrowser' must be formatted as host:port");
}

// Ignoring host input for now, hardcoding to 127.0.0.1.
base::StringToInt(values[1], &capabilities->existing_browser_port);
if (capabilities->existing_browser_port <= 0) {
return Status(kUnknownError,
"existing browser port must be greater than 0");
}
}
return Status(kOk);
}

Status ParseLoggingPrefs(const base::DictionaryValue& desired_caps,
Capabilities* capabilities) {
const base::Value* logging_prefs = NULL;
Expand All @@ -308,6 +350,7 @@ Status ParseLoggingPrefs(const base::DictionaryValue& desired_caps,

Capabilities::Capabilities()
: detach(false),
existing_browser_port(0),
command(CommandLine::NO_PROGRAM) {}

Capabilities::~Capabilities() {}
Expand All @@ -316,6 +359,10 @@ bool Capabilities::IsAndroid() const {
return !android_package.empty();
}

bool Capabilities::IsExistingBrowser() const {
return existing_browser_port > 0;
}

Status Capabilities::Parse(
const base::DictionaryValue& desired_caps,
Log* log) {
Expand All @@ -327,6 +374,11 @@ Status Capabilities::Parse(
return status;
if (IsAndroid())
return Status(kOk);
status = ParseExistingBrowserCapabilities(desired_caps, this);
if (status.IsError())
return status;
if (IsExistingBrowser())
return Status(kOk);

std::map<std::string, Parser> parser_map;
parser_map["proxy"] = base::Bind(&ParseProxy);
Expand Down
6 changes: 6 additions & 0 deletions chrome/test/chromedriver/capabilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ struct Capabilities {
Capabilities();
~Capabilities();

// Return true if existing host:port session is to be used.
bool IsExistingBrowser() const;

// Return true if android package is specified.
bool IsAndroid() const;

Expand All @@ -34,6 +37,9 @@ struct Capabilities {
// ChromeDriver dies.
bool detach;

// If provided, the remote debugging port on 127.0.0.1 to connect to.
int existing_browser_port;

std::string android_package;
std::string android_activity;
std::string android_process;
Expand Down
11 changes: 11 additions & 0 deletions chrome/test/chromedriver/capabilities_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,14 @@ TEST(ParseCapabilities, ExcludeSwitches) {
ASSERT_TRUE(switches.find("switch1") != switches.end());
ASSERT_TRUE(switches.find("switch2") != switches.end());
}

TEST(ParseCapabilities, UseExistingBrowser) {
Capabilities capabilities;
base::DictionaryValue caps;
caps.SetString("chromeOptions.useExistingBrowser", "abc:123");
Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.IsExistingBrowser());
ASSERT_EQ(123, capabilities.existing_browser_port);
}
24 changes: 24 additions & 0 deletions chrome/test/chromedriver/chrome/chrome_existing_impl.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/test/chromedriver/chrome/chrome_existing_impl.h"
#include "chrome/test/chromedriver/chrome/devtools_http_client.h"
#include "chrome/test/chromedriver/chrome/status.h"

ChromeExistingImpl::ChromeExistingImpl(
scoped_ptr<DevToolsHttpClient> client,
ScopedVector<DevToolsEventListener>& devtools_event_listeners,
Log* log)
: ChromeImpl(client.Pass(), devtools_event_listeners, log) {}

ChromeExistingImpl::~ChromeExistingImpl() {}

std::string ChromeExistingImpl::GetOperatingSystemName() {
return std::string();
}

Status ChromeExistingImpl::Quit() {
return Status(kOk);
}

29 changes: 29 additions & 0 deletions chrome/test/chromedriver/chrome/chrome_existing_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_EXISTING_IMPL_H_
#define CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_EXISTING_IMPL_H_

#include <string>

#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/test/chromedriver/chrome/chrome_impl.h"

class DevToolsHttpClient;

class ChromeExistingImpl : public ChromeImpl {
public:
ChromeExistingImpl(
scoped_ptr<DevToolsHttpClient> client,
ScopedVector<DevToolsEventListener>& devtools_event_listeners,
Log* log);
virtual ~ChromeExistingImpl();

// Overridden from Chrome.
virtual std::string GetOperatingSystemName() OVERRIDE;
virtual Status Quit() OVERRIDE;
};

#endif // CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_EXISTING_IMPL_H_
29 changes: 28 additions & 1 deletion chrome/test/chromedriver/chrome_launcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "base/values.h"
#include "chrome/test/chromedriver/chrome/chrome_android_impl.h"
#include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
#include "chrome/test/chromedriver/chrome/chrome_existing_impl.h"
#include "chrome/test/chromedriver/chrome/chrome_finder.h"
#include "chrome/test/chromedriver/chrome/device_manager.h"
#include "chrome/test/chromedriver/chrome/devtools_http_client.h"
Expand Down Expand Up @@ -172,6 +173,27 @@ Status WaitForDevToolsAndCheckVersion(
return Status(kUnknownError, "unable to discover open pages");
}

Status LaunchExistingChromeSession(
URLRequestContextGetter* context_getter,
int port,
const SyncWebSocketFactory& socket_factory,
Log* log,
const Capabilities& capabilities,
ScopedVector<DevToolsEventListener>& devtools_event_listeners,
scoped_ptr<Chrome>* chrome) {
Status status(kOk);
scoped_ptr<DevToolsHttpClient> devtools_client;
status = WaitForDevToolsAndCheckVersion(
port, context_getter, socket_factory, log, &devtools_client);
if (status.IsError()) {
return Status(kUnknownError, "Failed to connect to existing chrome");
}
chrome->reset(new ChromeExistingImpl(devtools_client.Pass(),
devtools_event_listeners,
log));
return Status(kOk);
}

Status LaunchDesktopChrome(
URLRequestContextGetter* context_getter,
int port,
Expand Down Expand Up @@ -318,7 +340,12 @@ Status LaunchChrome(
const Capabilities& capabilities,
ScopedVector<DevToolsEventListener>& devtools_event_listeners,
scoped_ptr<Chrome>* chrome) {
if (capabilities.IsAndroid()) {

if (capabilities.IsExistingBrowser()) {
return LaunchExistingChromeSession(
context_getter, capabilities.existing_browser_port, socket_factory,
log, capabilities, devtools_event_listeners, chrome);
} else if (capabilities.IsAndroid()) {
return LaunchAndroidChrome(
context_getter, port, socket_factory, log, capabilities,
devtools_event_listeners, device_manager, chrome);
Expand Down
6 changes: 5 additions & 1 deletion chrome/test/chromedriver/client/chromedriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class ChromeDriver(object):

def __init__(self, server_url, chrome_binary=None, android_package=None,
chrome_switches=None, chrome_extensions=None,
chrome_log_path=None):
chrome_log_path=None, chrome_existing_browser=None):
self._executor = command_executor.CommandExecutor(server_url)

options = {}
Expand All @@ -83,6 +83,10 @@ def __init__(self, server_url, chrome_binary=None, android_package=None,
assert type(chrome_log_path) is str
options['logPath'] = chrome_log_path

if chrome_existing_browser:
assert type(chrome_existing_browser) is str
options['useExistingBrowser'] = chrome_existing_browser

params = {
'desiredCapabilities': {
'chromeOptions': options
Expand Down
33 changes: 33 additions & 0 deletions chrome/test/chromedriver/test/run_py_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

import base64
import optparse
import subprocess
import os
import sys
import socket
import tempfile
import time
import unittest
Expand Down Expand Up @@ -104,6 +106,7 @@ def _GetDesktopNegativeFilter(version_name):
'ChromeDriverTest.testWindowSize',
'ChromeDriverTest.testWindowMaximize',
'ChromeLogPathCapabilityTest.testChromeLogPath',
'ExistingBrowserTest.*',
# Don't enable perf testing on Android yet.
'PerfTest.testSessionStartTime',
'PerfTest.testSessionStopTime',
Expand Down Expand Up @@ -670,6 +673,36 @@ def testQuitASessionMoreThanOnce(self):
driver.Quit()


class ExistingBrowserTest(ChromeDriverBaseTest):
"""Tests for ChromeDriver existing browser capability."""
def setUp(self):
self.assertTrue(_CHROME_BINARY is not None,
'must supply a chrome binary arg')

def testConnectToExistingBrowser(self):
port = self.FindFreePort()
temp_dir = util.MakeTempDir()
process = subprocess.Popen([_CHROME_BINARY,
'--remote-debugging-port=%d' % port,
'--user-data-dir=%s' % temp_dir])
if process is None:
raise RuntimeError('Chrome could not be started with debugging port')
try:
hostAndPort = '127.0.0.1:%d' % port
driver = self.CreateDriver(chrome_existing_browser=hostAndPort)
driver.ExecuteScript('console.info("%s")' % 'connecting at %d!' % port)
driver.Quit()
finally:
process.terminate()

def FindFreePort(self):
for port in range(10000, 10100):
try:
socket.create_connection(('127.0.0.1', port), 0.2).close()
except socket.error:
return port
raise RuntimeError('Cannot find open port')

class PerfTest(ChromeDriverBaseTest):
"""Tests for ChromeDriver perf."""
def setUp(self):
Expand Down

0 comments on commit af898a5

Please sign in to comment.