summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-03 21:49:03 +0000
committerkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-03 21:49:03 +0000
commit2a28e0b2f08036c00598eadfd5bf6bb7fbe0d339 (patch)
tree79d96f98e81ea8942c876b52a7f78baa0a5a179d
parent9e1bdd3f9785ed8a84a137c98b781ddaec41b080 (diff)
downloadchromium_src-2a28e0b2f08036c00598eadfd5bf6bb7fbe0d339.zip
chromium_src-2a28e0b2f08036c00598eadfd5bf6bb7fbe0d339.tar.gz
chromium_src-2a28e0b2f08036c00598eadfd5bf6bb7fbe0d339.tar.bz2
Break Session off into a separate Automation class, which is just focused on controlling Chrome. Have Session create its own thread which automation messages are executed on.
BUG=none TEST=none Review URL: http://codereview.chromium.org/6306013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@73670 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/chrome_tests.gypi4
-rw-r--r--chrome/test/webdriver/automation.cc95
-rw-r--r--chrome/test/webdriver/automation.h61
-rw-r--r--chrome/test/webdriver/chromedriver_launcher.py108
-rw-r--r--chrome/test/webdriver/commands/create_session.cc19
-rw-r--r--chrome/test/webdriver/commands/navigate_commands.cc7
-rw-r--r--chrome/test/webdriver/commands/response.h1
-rw-r--r--chrome/test/webdriver/commands/source_command.cc11
-rw-r--r--chrome/test/webdriver/commands/title_command.cc8
-rw-r--r--chrome/test/webdriver/commands/url_command.cc11
-rw-r--r--chrome/test/webdriver/commands/webdriver_command.cc11
-rw-r--r--chrome/test/webdriver/commands/webdriver_command.h11
-rw-r--r--chrome/test/webdriver/commands/webelement_command.cc3
-rw-r--r--chrome/test/webdriver/commands/webelement_command.h3
-rw-r--r--chrome/test/webdriver/dispatch.cc114
-rw-r--r--chrome/test/webdriver/dispatch.h71
-rw-r--r--chrome/test/webdriver/server.cc5
-rw-r--r--chrome/test/webdriver/session.cc248
-rw-r--r--chrome/test/webdriver/session.h51
-rw-r--r--chrome/test/webdriver/session_manager.cc58
-rw-r--r--chrome/test/webdriver/session_manager.h6
21 files changed, 501 insertions, 405 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 2e34646..0d22801 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -710,6 +710,8 @@
'../third_party/mongoose/mongoose.h',
'../third_party/mongoose/mongoose.c',
'../third_party/webdriver/atoms.h',
+ 'test/webdriver/automation.h',
+ 'test/webdriver/automation.cc',
'test/webdriver/dispatch.h',
'test/webdriver/dispatch.cc',
'test/webdriver/error_codes.h',
@@ -747,6 +749,8 @@
'test/webdriver/commands/url_command.cc',
'test/webdriver/commands/webdriver_command.h',
'test/webdriver/commands/webdriver_command.cc',
+ 'test/webdriver/commands/webelement_command.h',
+ 'test/webdriver/commands/webelement_command.cc',
],
'conditions': [
['OS=="linux"', {
diff --git a/chrome/test/webdriver/automation.cc b/chrome/test/webdriver/automation.cc
new file mode 100644
index 0000000..e0cd968
--- /dev/null
+++ b/chrome/test/webdriver/automation.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2011 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/webdriver/automation.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/test_launcher_utils.h"
+#include "googleurl/src/gurl.h"
+
+namespace webdriver {
+
+void Automation::Init(bool* success) {
+ *success = false;
+
+ // Create a temp directory for the new profile.
+ if (!profile_dir_.CreateUniqueTempDir()) {
+ LOG(ERROR) << "Could not make a temp profile directory";
+ return;
+ }
+ // TODO(kkania): See if these are still needed.
+ test_launcher_utils::PrepareBrowserCommandLineForTests(&launch_arguments_);
+ launch_arguments_.AppendSwitch(switches::kDomAutomationController);
+ launch_arguments_.AppendSwitch(switches::kFullMemoryCrashReport);
+
+ launch_arguments_.AppendSwitchPath(switches::kUserDataDir,
+ profile_dir_.path());
+ UITestBase::SetUp();
+ browser_ = automation()->GetBrowserWindow(0);
+ if (!browser_.get()) {
+ Terminate();
+ return;
+ }
+ tab_ = browser_->GetActiveTab();
+ if (!tab_.get()) {
+ Terminate();
+ return;
+ }
+ *success = true;
+}
+
+void Automation::Terminate() {
+ QuitBrowser();
+}
+
+void Automation::ExecuteScript(const std::string& frame_xpath,
+ const std::string& script,
+ std::string* result,
+ bool* success) {
+ std::wstring wide_xpath = UTF8ToWide(frame_xpath);
+ std::wstring wide_script = UTF8ToWide(script);
+ std::wstring wide_result;
+ *success = tab_->ExecuteAndExtractString(
+ wide_xpath, wide_script, &wide_result);
+ if (*success)
+ *result = WideToUTF8(wide_result);
+}
+
+void Automation::NavigateToURL(const std::string& url,
+ bool* success) {
+ *success = tab_->NavigateToURL(GURL(url));
+}
+
+void Automation::GoForward(bool* success) {
+ *success = tab_->GoForward();
+}
+
+void Automation::GoBack(bool* success) {
+ *success = tab_->GoBack();
+}
+
+void Automation::Reload(bool* success) {
+ *success = tab_->Reload();
+}
+
+void Automation::GetURL(std::string* url,
+ bool* success) {
+ GURL gurl;
+ *success = tab_->GetCurrentURL(&gurl);
+ if (*success)
+ *url = gurl.possibly_invalid_spec();
+}
+
+void Automation::GetTabTitle(std::string* tab_title,
+ bool* success) {
+ std::wstring wide_title;
+ *success = tab_->GetTabTitle(&wide_title);
+ if (*success)
+ *tab_title = WideToUTF8(wide_title);
+}
+
+} // namespace webdriver
diff --git a/chrome/test/webdriver/automation.h b/chrome/test/webdriver/automation.h
new file mode 100644
index 0000000..4bf8758
--- /dev/null
+++ b/chrome/test/webdriver/automation.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2011 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_WEBDRIVER_AUTOMATION_H_
+#define CHROME_TEST_WEBDRIVER_AUTOMATION_H_
+
+#include <string>
+
+#include "base/task.h"
+#include "base/ref_counted.h"
+#include "base/scoped_temp_dir.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+
+namespace webdriver {
+
+// Creates and controls the Chrome instance.
+// This class should be created and accessed on a single thread.
+// TODO(phajdan.jr): Abstract UITestBase classes, see:
+// http://code.google.com/p/chromium/issues/detail?id=56865
+class Automation : private UITestBase {
+ public:
+ Automation() {}
+
+ // Creates a browser.
+ void Init(bool* success);
+
+ // Terminates this session and disconnects its automation proxy. After
+ // invoking this method, the Automation can safely be deleted.
+ void Terminate();
+
+ // Executes the given |script| in the specified frame of the current
+ // tab. |result| will be set to the JSON result. Returns true on success.
+ void ExecuteScript(const std::string& frame_xpath,
+ const std::string& script,
+ std::string* result,
+ bool* success);
+
+ void NavigateToURL(const std::string& url, bool* success);
+ void GoForward(bool* success);
+ void GoBack(bool* success);
+ void Reload(bool* success);
+ void GetURL(std::string* url, bool* success);
+ void GetTabTitle(std::string* tab_title, bool* success);
+
+ private:
+ scoped_refptr<BrowserProxy> browser_;
+ scoped_refptr<TabProxy> tab_;
+
+ ScopedTempDir profile_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(Automation);
+};
+
+} // namespace webdriver
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(webdriver::Automation);
+
+#endif // CHROME_TEST_WEBDRIVER_AUTOMATION_H_
diff --git a/chrome/test/webdriver/chromedriver_launcher.py b/chrome/test/webdriver/chromedriver_launcher.py
index 4c91a35..1777525 100644
--- a/chrome/test/webdriver/chromedriver_launcher.py
+++ b/chrome/test/webdriver/chromedriver_launcher.py
@@ -12,10 +12,15 @@ For ChromeDriver documentation, refer to:
import logging
import os
import platform
-import signal
import subprocess
import sys
-import threading
+
+if sys.version_info < (2,6):
+ # Subprocess.Popen.kill is not available prior to 2.6.
+ if platform.system() == 'Windows':
+ import win32api
+ else:
+ import signal
class ChromeDriverLauncher:
@@ -34,10 +39,6 @@ class ChromeDriverLauncher:
self._port = port
if self._exe_path is None:
self._exe_path = ChromeDriverLauncher.LocateExe()
- if self._exe_path is None:
- raise RuntimeError('ChromeDriver exe could not be found in its default '
- 'location. Searched in following directories: ' +
- ', '.join(self.DefaultExeLocations()))
if self._root_path is None:
self._root_path = '.'
if self._port is None:
@@ -47,20 +48,26 @@ class ChromeDriverLauncher:
if not os.path.exists(self._exe_path):
raise RuntimeError('ChromeDriver exe not found at: ' + self._exe_path)
-
- os.environ['PATH'] = os.path.dirname(self._exe_path) + os.environ['PATH']
self.Start()
@staticmethod
- def DefaultExeLocations():
- """Returns the paths that are used to find the ChromeDriver executable.
+ def LocateExe():
+ """Attempts to locate the ChromeDriver executable.
+
+ This searches the current directory, then checks the appropriate build
+ locations according to platform.
Returns:
- a list of directories that would be searched for the executable
+ absolute path to the ChromeDriver executable, or None if not found
"""
+ exe_name = 'chromedriver'
+ if platform.system() == 'Windows':
+ exe_name += '.exe'
+ if os.path.exists(exe_name):
+ return os.path.abspath(exe_name)
+
script_dir = os.path.dirname(__file__)
- chrome_src = os.path.abspath(os.path.join(
- script_dir, os.pardir, os.pardir, os.pardir))
+ chrome_src = os.path.join(script_dir, os.pardir, os.pardir, os.pardir)
bin_dirs = {
'linux2': [ os.path.join(chrome_src, 'out', 'Debug'),
os.path.join(chrome_src, 'sconsbuild', 'Debug'),
@@ -73,23 +80,7 @@ class ChromeDriverLauncher:
os.path.join(chrome_src, 'chrome', 'Release'),
os.path.join(chrome_src, 'build', 'Release')],
}
- return [os.getcwd()] + bin_dirs.get(sys.platform, [])
-
- @staticmethod
- def LocateExe():
- """Attempts to locate the ChromeDriver executable.
-
- This searches the current directory, then checks the appropriate build
- locations according to platform.
-
- Returns:
- absolute path to the ChromeDriver executable, or None if not found
- """
- exe_name = 'chromedriver'
- if platform.system() == 'Windows':
- exe_name += '.exe'
-
- for dir in ChromeDriverLauncher.DefaultExeLocations():
+ for dir in bin_dirs.get(sys.platform, []):
path = os.path.join(dir, exe_name)
if os.path.exists(path):
return os.path.abspath(path)
@@ -99,64 +90,33 @@ class ChromeDriverLauncher:
"""Starts a new ChromeDriver process.
Kills a previous one if it is still running.
-
- Raises:
- RuntimeError if ChromeDriver does not start
"""
- def _WaitForLaunchResult(stdout, started_event, launch_result):
- """Reads from the stdout of ChromeDriver and parses the launch result.
-
- Args:
- stdout: handle to ChromeDriver's standard output
- started_event: condition variable to notify when the launch result
- has been parsed
- launch_result: dictionary to add the result of this launch to
- """
- status_line = stdout.readline()
- started_event.acquire()
- launch_result['success'] = status_line.startswith('Started')
- launch_result['status_line'] = status_line
- started_event.notify()
- started_event.release()
-
if self._process is not None:
self.Kill()
-
proc = subprocess.Popen([self._exe_path,
'--port=%d' % self._port,
- '--root=%s' % self._root_path],
- stdout=subprocess.PIPE)
+ '--root="%s"' % self._root_path])
if proc is None:
raise RuntimeError('ChromeDriver cannot be started')
+ logging.info('Started chromedriver at port %s' % self._port)
self._process = proc
- # Wait for ChromeDriver to be initialized before returning.
- launch_result = {}
- started_event = threading.Condition()
- started_event.acquire()
- spawn_thread = threading.Thread(
- target=_WaitForLaunchResult,
- args=(proc.stdout, started_event, launch_result))
- spawn_thread.start()
- started_event.wait(20)
- timed_out = 'success' not in launch_result
- started_event.release()
- if timed_out:
- raise RuntimeError('ChromeDriver did not respond')
- elif not launch_result['success']:
- raise RuntimeError('ChromeDriver failed to launch: ' +
- launch_result['status_line'])
- logging.info('ChromeDriver running on port %s' % self._port)
-
def Kill(self):
"""Kills a currently running ChromeDriver process, if it is running."""
if self._process is None:
return
- pid = self._process.pid
- if platform.system() == 'Windows':
- subprocess.call(['taskkill.exe', '/T', '/F', '/PID', str(pid)])
+ if sys.version_info < (2,6):
+ # From http://stackoverflow.com/questions/1064335
+ if platform.system() == 'Windows':
+ PROCESS_TERMINATE = 1
+ handle = win32api.OpenProcess(PROCESS_TERMINATE, False,
+ self._process.pid)
+ win32api.TerminateProcess(handle, -1)
+ win32api.CloseHandle(handle)
+ else:
+ os.kill(self._process.pid, signal.SIGKILL)
else:
- os.kill(pid, signal.SIGTERM)
+ self._process.kill()
self._process = None
def __del__(self):
diff --git a/chrome/test/webdriver/commands/create_session.cc b/chrome/test/webdriver/commands/create_session.cc
index 3272a43..dfb1838 100644
--- a/chrome/test/webdriver/commands/create_session.cc
+++ b/chrome/test/webdriver/commands/create_session.cc
@@ -10,16 +10,27 @@
#include "base/values.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/common/chrome_constants.h"
+#include "chrome/test/webdriver/session.h"
+#include "chrome/test/webdriver/session_manager.h"
namespace webdriver {
void CreateSession::ExecutePost(Response* const response) {
SessionManager* session_manager = SessionManager::GetInstance();
- std::string session_id;
+ Session* session = session_manager->Create();
+ if (!session) {
+ SET_WEBDRIVER_ERROR(response,
+ "Failed to create session",
+ kInternalServerError);
+ return;
+ }
- if (!session_manager->Create(&session_id)) {
- response->set_status(kUnknownError);
- response->set_value(Value::CreateStringValue("Failed to create session"));
+ std::string session_id = session->id();
+ if (!session->Init()) {
+ session_manager->Delete(session_id);
+ SET_WEBDRIVER_ERROR(response,
+ "Failed to initialize session",
+ kInternalServerError);
return;
}
diff --git a/chrome/test/webdriver/commands/navigate_commands.cc b/chrome/test/webdriver/commands/navigate_commands.cc
index 1e02bce..ab6b16c 100644
--- a/chrome/test/webdriver/commands/navigate_commands.cc
+++ b/chrome/test/webdriver/commands/navigate_commands.cc
@@ -7,7 +7,7 @@
namespace webdriver {
void ForwardCommand::ExecutePost(Response* const response) {
- if (!tab_->GoForward()) {
+ if (!session_->GoForward()) {
SET_WEBDRIVER_ERROR(response, "GoForward failed", kInternalServerError);
return;
}
@@ -17,7 +17,7 @@ void ForwardCommand::ExecutePost(Response* const response) {
}
void BackCommand::ExecutePost(Response* const response) {
- if (!tab_->GoBack()) {
+ if (!session_->GoBack()) {
SET_WEBDRIVER_ERROR(response, "GoBack failed", kInternalServerError);
return;
}
@@ -27,7 +27,7 @@ void BackCommand::ExecutePost(Response* const response) {
}
void RefreshCommand::ExecutePost(Response* const response) {
- if (!tab_->Reload()) {
+ if (!session_->Reload()) {
SET_WEBDRIVER_ERROR(response, "Reload failed", kInternalServerError);
return;
}
@@ -37,4 +37,3 @@ void RefreshCommand::ExecutePost(Response* const response) {
}
} // namespace webdriver
-
diff --git a/chrome/test/webdriver/commands/response.h b/chrome/test/webdriver/commands/response.h
index 645ded2..3b39302 100644
--- a/chrome/test/webdriver/commands/response.h
+++ b/chrome/test/webdriver/commands/response.h
@@ -9,6 +9,7 @@
#include <string>
#include "base/basictypes.h"
+#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/values.h"
#include "chrome/test/webdriver/error_codes.h"
diff --git a/chrome/test/webdriver/commands/source_command.cc b/chrome/test/webdriver/commands/source_command.cc
index 54cb8b1..0192d4f 100644
--- a/chrome/test/webdriver/commands/source_command.cc
+++ b/chrome/test/webdriver/commands/source_command.cc
@@ -18,11 +18,10 @@ const wchar_t* const kSource[] = {
void SourceCommand::ExecuteGet(Response* const response) {
const std::wstring jscript = build_atom(kSource, sizeof kSource);
- // Get the source code for the current frame only.
- std::wstring xpath = session_->current_frame_xpath();
- std::wstring result = L"";
+ Value* result = NULL;
- if (!tab_->ExecuteAndExtractString(xpath, jscript, &result)) {
+ scoped_ptr<ListValue> list(new ListValue());
+ if (!session_->ExecuteScript(jscript, list.get(), &result)) {
LOG(ERROR) << "Could not execute JavaScript to find source. JavaScript"
<< " used was:\n" << kSource;
LOG(ERROR) << "ExecuteAndExtractString's results was: "
@@ -31,10 +30,8 @@ void SourceCommand::ExecuteGet(Response* const response) {
kInternalServerError);
return;
}
-
- response->set_value(new StringValue(WideToUTF16(result)));
+ response->set_value(result);
response->set_status(kSuccess);
}
} // namespace webdriver
-
diff --git a/chrome/test/webdriver/commands/title_command.cc b/chrome/test/webdriver/commands/title_command.cc
index 544aaa6..1213895 100644
--- a/chrome/test/webdriver/commands/title_command.cc
+++ b/chrome/test/webdriver/commands/title_command.cc
@@ -4,21 +4,19 @@
#include <string>
-#include "base/utf_string_conversions.h"
#include "chrome/test/webdriver/commands/title_command.h"
namespace webdriver {
void TitleCommand::ExecuteGet(Response* const response) {
- std::wstring title;
- if (!tab_->GetTabTitle(&title)) {
+ std::string title;
+ if (!session_->GetTabTitle(&title)) {
SET_WEBDRIVER_ERROR(response, "GetTabTitle failed", kInternalServerError);
return;
}
- response->set_value(new StringValue(WideToUTF16(title)));
+ response->set_value(new StringValue(title));
response->set_status(kSuccess);
}
} // namespace webdriver
-
diff --git a/chrome/test/webdriver/commands/url_command.cc b/chrome/test/webdriver/commands/url_command.cc
index 4b716a8..87c5127 100644
--- a/chrome/test/webdriver/commands/url_command.cc
+++ b/chrome/test/webdriver/commands/url_command.cc
@@ -5,18 +5,17 @@
#include <string>
#include "chrome/test/webdriver/commands/url_command.h"
-#include "googleurl/src/gurl.h"
namespace webdriver {
void URLCommand::ExecuteGet(Response* const response) {
- GURL url;
- if (!tab_->GetCurrentURL(&url)) {
+ std::string url;
+ if (!session_->GetURL(&url)) {
SET_WEBDRIVER_ERROR(response, "GetCurrentURL failed", kInternalServerError);
return;
}
- response->set_value(new StringValue(url.spec()));
+ response->set_value(new StringValue(url));
response->set_status(kSuccess);
}
@@ -28,8 +27,7 @@ void URLCommand::ExecutePost(Response* const response) {
return;
}
// TODO(jmikhail): sniff for meta-redirects.
- GURL rgurl(url);
- if (!tab_->NavigateToURL(rgurl)) {
+ if (!session_->NavigateToURL(url)) {
SET_WEBDRIVER_ERROR(response, "NavigateToURL failed",
kInternalServerError);
return;
@@ -41,4 +39,3 @@ void URLCommand::ExecutePost(Response* const response) {
}
} // namespace webdriver
-
diff --git a/chrome/test/webdriver/commands/webdriver_command.cc b/chrome/test/webdriver/commands/webdriver_command.cc
index 276fa65..f1931c0 100644
--- a/chrome/test/webdriver/commands/webdriver_command.cc
+++ b/chrome/test/webdriver/commands/webdriver_command.cc
@@ -59,17 +59,6 @@ bool WebDriverCommand::Init(Response* const response) {
}
response->SetField("sessionId", Value::CreateStringValue(session_id));
- return !RequiresValidTab() || VerifyTabIsValid(response);
-}
-
-bool WebDriverCommand::VerifyTabIsValid(Response* response) {
- tab_ = session_->ActiveTab();
- if (!tab_.get()) {
- response->set_value(Value::CreateStringValue(
- "Lost session window handle; did you close the window?"));
- response->set_status(kNoSuchWindow);
- return false;
- }
return true;
}
diff --git a/chrome/test/webdriver/commands/webdriver_command.h b/chrome/test/webdriver/commands/webdriver_command.h
index 52500cf..4446b05 100644
--- a/chrome/test/webdriver/commands/webdriver_command.h
+++ b/chrome/test/webdriver/commands/webdriver_command.h
@@ -43,17 +43,7 @@ class WebDriverCommand : public Command {
// failure.
DictionaryValue* GetElementIdAsDictionaryValue(const std::string& element_id);
- // Returns whether this command requires a valid TabProxy upon
- // initialization.
- virtual bool RequiresValidTab() { return true; }
-
- // Returns whether this command has a valid TabProxy. Returns true on
- // success. Otherwise, returns false and populates the |resposne| with the
- // necessary information to return to the client.
- bool VerifyTabIsValid(Response* response);
-
Session* session_;
- scoped_refptr<TabProxy> tab_;
DISALLOW_COPY_AND_ASSIGN(WebDriverCommand);
};
@@ -61,4 +51,3 @@ class WebDriverCommand : public Command {
} // namespace webdriver
#endif // CHROME_TEST_WEBDRIVER_COMMANDS_WEBDRIVER_COMMAND_H_
-
diff --git a/chrome/test/webdriver/commands/webelement_command.cc b/chrome/test/webdriver/commands/webelement_command.cc
index 9486c7b..f40fd6a 100644
--- a/chrome/test/webdriver/commands/webelement_command.cc
+++ b/chrome/test/webdriver/commands/webelement_command.cc
@@ -10,7 +10,7 @@
namespace webdriver {
-bool WebElementCommand::Init(Response* response) {
+bool WebElementCommand::Init(Response* const response) {
if (WebDriverCommand::Init(response)) {
SET_WEBDRIVER_ERROR(response, "Failure on Init for web element command",
kInternalServerError);
@@ -83,4 +83,3 @@ bool WebElementCommand::GetElementSize(int* width, int* height) {
}
} // namespace webdriver
-
diff --git a/chrome/test/webdriver/commands/webelement_command.h b/chrome/test/webdriver/commands/webelement_command.h
index a093b05..79e3a05 100644
--- a/chrome/test/webdriver/commands/webelement_command.h
+++ b/chrome/test/webdriver/commands/webelement_command.h
@@ -22,7 +22,7 @@ class WebElementCommand : public WebDriverCommand {
path_segments_(path_segments) {}
virtual ~WebElementCommand() {}
- virtual bool Init(Response* response);
+ virtual bool Init(Response* const response);
protected:
bool GetElementLocation(bool in_view, int* x, int* y);
@@ -40,4 +40,3 @@ class WebElementCommand : public WebDriverCommand {
} // namespace webdriver
#endif // CHROME_TEST_WEBDRIVER_COMMANDS_WEBELEMENT_COMMAND_H_
-
diff --git a/chrome/test/webdriver/dispatch.cc b/chrome/test/webdriver/dispatch.cc
index a24b43e..aa83a2b 100644
--- a/chrome/test/webdriver/dispatch.cc
+++ b/chrome/test/webdriver/dispatch.cc
@@ -1,16 +1,27 @@
// Copyright (c) 2010 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/webdriver/dispatch.h"
#include <sstream>
#include <string>
-
-#include "base/json/json_writer.h"
+#include <vector>
+
+#include "base/logging.h"
+#include "base/message_loop_proxy.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
#include "chrome/test/webdriver/commands/command.h"
+#include "chrome/test/webdriver/session_manager.h"
+#include "chrome/test/webdriver/utility_functions.h"
namespace webdriver {
+namespace {
+
// The standard HTTP Status codes are implemented below. Chrome uses
// OK, See Other, Not Found, Method Not Allowed, and Internal Error.
// Internal Error, HTTP 500, is used as a catch all for any issue
@@ -126,35 +137,27 @@ void SendHttpInternalError(struct mg_connection* const connection,
mg_printf(connection, "%s", out.str().c_str());
}
-// For every HTTP request from the mongoode server DispatchCommand will
-// inspect the HTTP method call requested and execute the proper function
-// mapped from the class Command.
-void DispatchCommand(Command* const command, const std::string& method,
+void DispatchCommand(Command* const command,
+ const std::string& method,
Response* response) {
- if (command->DoesPost() && method == "POST") {
- if (command->Init(response))
- command->ExecutePost(response);
- } else if (command->DoesGet() && method == "GET") {
- if (command->Init(response))
- command->ExecuteGet(response);
- } else if (command->DoesDelete() && method == "DELETE") {
- if (command->Init(response))
- command->ExecuteDelete(response);
+ if (!command->Init(response))
+ return;
+
+ if (method == "POST") {
+ command->ExecutePost(response);
+ } else if (method == "GET") {
+ command->ExecuteGet(response);
+ } else if (method == "DELETE") {
+ command->ExecuteDelete(response);
} else {
- ListValue* methods = new ListValue;
- if (command->DoesPost())
- methods->Append(Value::CreateStringValue("POST"));
- if (command->DoesGet()) {
- methods->Append(Value::CreateStringValue("GET"));
- methods->Append(Value::CreateStringValue("HEAD"));
- }
- if (command->DoesDelete())
- methods->Append(Value::CreateStringValue("DELETE"));
- response->set_status(kMethodNotAllowed);
- response->set_value(methods); // Assumes ownership.
+ NOTREACHED();
}
}
+} // namespace
+
+namespace internal {
+
void SendResponse(struct mg_connection* const connection,
const struct mg_request_info* const request_info,
const Response& response) {
@@ -188,5 +191,62 @@ void SendResponse(struct mg_connection* const connection,
}
}
-} // namespace webdriver
+bool ParseRequestInfo(const struct mg_request_info* const request_info,
+ std::string* method,
+ std::vector<std::string>* path_segments,
+ DictionaryValue** parameters,
+ Response* const response) {
+ *method = request_info->request_method;
+ if (*method == "HEAD")
+ *method = "GET";
+ else if (*method == "PUT")
+ *method = "POST";
+
+ std::string uri(request_info->uri);
+ base::SplitString(uri, '/', path_segments);
+
+ if (*method == "POST" && request_info->post_data_len > 0) {
+ VLOG(1) << "...parsing request body";
+ std::string json(request_info->post_data, request_info->post_data_len);
+ std::string error;
+ if (!ParseJSONDictionary(json, parameters, &error)) {
+ response->set_value(Value::CreateStringValue(
+ "Failed to parse command data: " + error + "\n Data: " + json));
+ response->set_status(kBadRequest);
+ return false;
+ }
+ }
+ VLOG(1) << "Parsed " << method << " " << uri
+ << std::string(request_info->post_data, request_info->post_data_len);
+ return true;
+}
+
+void DispatchHelper(Command* command_ptr,
+ const std::string& method,
+ Response* response) {
+ CHECK(method == "GET" || method == "POST" || method == "DELETE");
+ scoped_ptr<Command> command(command_ptr);
+
+ if ((method == "GET" && !command->DoesGet()) ||
+ (method == "POST" && !command->DoesPost()) ||
+ (method == "DELETE" && !command->DoesDelete())) {
+ ListValue* methods = new ListValue;
+ if (command->DoesPost())
+ methods->Append(Value::CreateStringValue("POST"));
+ if (command->DoesGet()) {
+ methods->Append(Value::CreateStringValue("GET"));
+ methods->Append(Value::CreateStringValue("HEAD"));
+ }
+ if (command->DoesDelete())
+ methods->Append(Value::CreateStringValue("DELETE"));
+ response->set_status(kMethodNotAllowed);
+ response->set_value(methods);
+ return;
+ }
+ DispatchCommand(command.get(), method, response);
+}
+
+} // namespace internal
+
+} // namespace webdriver
diff --git a/chrome/test/webdriver/dispatch.h b/chrome/test/webdriver/dispatch.h
index 5add729..817dd7c 100644
--- a/chrome/test/webdriver/dispatch.h
+++ b/chrome/test/webdriver/dispatch.h
@@ -5,23 +5,20 @@
#ifndef CHROME_TEST_WEBDRIVER_DISPATCH_H_
#define CHROME_TEST_WEBDRIVER_DISPATCH_H_
-#include <sstream>
#include <string>
#include <vector>
-#include "base/logging.h"
-#include "base/scoped_ptr.h"
-#include "base/string_split.h"
-#include "base/string_util.h"
-#include "chrome/test/webdriver/utility_functions.h"
-#include "chrome/test/webdriver/commands/command.h"
-
+#include "chrome/test/webdriver/commands/response.h"
#include "third_party/mongoose/mongoose.h"
+class DictionaryValue;
+
namespace webdriver {
class Command;
+namespace internal {
+
// Sends a |response| to a WebDriver command back to the client.
// |connection| is the communication pipe to the HTTP server and
// |request_info| contains any data sent by the user.
@@ -29,13 +26,21 @@ void SendResponse(struct mg_connection* const connection,
const struct mg_request_info* const request_info,
const Response& response);
+// Parses the request info and returns whether parsing was successful. If not,
+// |response| has been modified with the error.
+bool ParseRequestInfo(const struct mg_request_info* const request_info,
+ std::string* method,
+ std::vector<std::string>* path_segments,
+ DictionaryValue** parameters,
+ Response* const response);
-// Serves as a link to the mongoose server to find if the user request
-// is an HTTP POST, GET, or DELETE and then executes the proper function
-// calls for the class that inherts from Command. An HTTP CREATE is not
-// handled and is reserved only for the establishment of a session.
-void DispatchCommand(Command* const command, const std::string& method,
- Response* response);
+// Allows the bulk of the implementation of |Dispatch| to be moved out of this
+// header file. Takes ownership of |command|.
+void DispatchHelper(Command* const command,
+ const std::string& method,
+ Response* const response);
+
+} // namespace internal
// Template function for dispatching commands sent to the WebDriver REST
// service. |CommandType| must be a subtype of |webdriver::Command|.
@@ -43,37 +48,23 @@ template<typename CommandType>
void Dispatch(struct mg_connection* connection,
const struct mg_request_info* request_info,
void* user_data) {
- Response response;
-
- std::string method(request_info->request_method);
-
+ std::string method;
std::vector<std::string> path_segments;
- std::string uri(request_info->uri);
- base::SplitString(uri, '/', &path_segments);
-
DictionaryValue* parameters = NULL;
- if ((method == "POST" || method == "PUT") &&
- request_info->post_data_len > 0) {
- VLOG(1) << "...parsing request body";
- std::string json(request_info->post_data, request_info->post_data_len);
- std::string error;
- if (!ParseJSONDictionary(json, &parameters, &error)) {
- response.set_value(Value::CreateStringValue(
- "Failed to parse command data: " + error + "\n Data: " + json));
- response.set_status(kBadRequest);
- SendResponse(connection, request_info, response);
- return;
- }
+ Response response;
+ if (internal::ParseRequestInfo(request_info,
+ &method,
+ &path_segments,
+ &parameters,
+ &response)) {
+ internal::DispatchHelper(
+ new CommandType(path_segments, parameters),
+ method,
+ &response);
}
-
- VLOG(1) << "Dispatching " << method << " " << uri
- << std::string(request_info->post_data, request_info->post_data_len);
- scoped_ptr<CommandType> ptr(new CommandType(path_segments, parameters));
- DispatchCommand(ptr.get(), method, &response);
- SendResponse(connection, request_info, response);
+ internal::SendResponse(connection, request_info, response);
}
} // namespace webdriver
#endif // CHROME_TEST_WEBDRIVER_DISPATCH_H_
-
diff --git a/chrome/test/webdriver/server.cc b/chrome/test/webdriver/server.cc
index edf0098..6409524 100644
--- a/chrome/test/webdriver/server.cc
+++ b/chrome/test/webdriver/server.cc
@@ -55,7 +55,6 @@ signal_handler(int sig_num) {
}
namespace webdriver {
-
template <typename CommandType>
void SetCallback(struct mg_context* ctx, const char* pattern) {
mg_set_uri_callback(ctx, pattern, &Dispatch<CommandType>, NULL);
@@ -116,6 +115,7 @@ int main(int argc, char *argv[]) {
port = cmd_line.GetSwitchValueASCII(std::string("port"));
}
+ VLOG(1) << "Using port: " << port;
webdriver::SessionManager* session = webdriver::SessionManager::GetInstance();
session->SetIPAddress(port);
@@ -127,7 +127,7 @@ int main(int argc, char *argv[]) {
webdriver::InitCallbacks(ctx);
- std::cout << "Started: port=" << port << std::endl;
+ std::cout << "Starting server on port: " << port << std::endl;
// The default behavior is to run this service forever.
while (true)
base::PlatformThread::Sleep(3600);
@@ -138,3 +138,4 @@ int main(int argc, char *argv[]) {
mg_stop(ctx);
return (EXIT_SUCCESS);
}
+
diff --git a/chrome/test/webdriver/session.cc b/chrome/test/webdriver/session.cc
index fadc41e..96fa461 100644
--- a/chrome/test/webdriver/session.cc
+++ b/chrome/test/webdriver/session.cc
@@ -4,178 +4,55 @@
#include "chrome/test/webdriver/session_manager.h"
-#ifdef OS_POSIX
-#include <dirent.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#endif
-#ifdef OS_WIN
-#include <windows.h>
-#include <shellapi.h>
-#endif
-
-#include <stdlib.h>
-#ifdef OS_POSIX
-#include <algorithm>
-#endif
#include <vector>
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/logging.h"
+#include "base/message_loop_proxy.h"
#include "base/process.h"
#include "base/process_util.h"
#include "base/string_util.h"
-#include "base/test/test_timeouts.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
+#include "base/test/test_timeouts.h"
#include "base/utf_string_conversions.h"
-
#include "chrome/app/chrome_command_ids.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/test_launcher_utils.h"
#include "chrome/test/webdriver/utility_functions.h"
-
#include "third_party/webdriver/atoms.h"
namespace webdriver {
-#ifdef OS_WIN
-namespace {
-std::string GetTempPath() {
- DWORD result = ::GetTempPath(0, L"");
- if (result == 0)
- LOG(ERROR) << "Could not get system temp path";
-
- std::vector<TCHAR> tempPath(result + 1);
- result = ::GetTempPath(static_cast<DWORD>(tempPath.size()), &tempPath[0]);
- if ((result == 0) || (result >= tempPath.size())) {
- LOG(ERROR) << "Could not get system temp path";
- NOTREACHED();
- }
- return std::string(tempPath.begin(),
- tempPath.begin() + static_cast<std::size_t>(result));
-}
-} // namespace
-#endif
-
Session::Session(const std::string& id)
- : UITestBase(), id_(id), window_num_(0), implicit_wait_(0),
+ : thread_(id.c_str()),
+ id_(id),
+ window_num_(0),
+ implicit_wait_(0),
current_frame_xpath_(L"") {
}
bool Session::Init() {
- // Create a temp directory for the new profile.
- if (!CreateTemporaryProfileDirectory()) {
- LOG(ERROR) << "Could not make a temp profile directory, "
- << tmp_profile_dir()
- << "\nNeed to quit, the issue must be fixed";
- exit(-1);
- }
-
- SetupCommandLine();
- LaunchBrowserAndServer();
- return LoadProxies();
-}
-
-scoped_refptr<TabProxy> Session::ActiveTab() {
- int tab_index;
- if (!tab_->GetTabIndex(&tab_index) ||
- !browser_->ActivateTab(tab_index)) {
- LOG(ERROR) << "Failed to session tab";
- return NULL;
- }
- return tab_;
-}
-
-bool Session::LoadProxies() {
- AutomationProxy* proxy = automation();
- scoped_refptr<BrowserProxy> browser = proxy->GetBrowserWindow(0);
- if (!browser.get()) {
- LOG(WARNING) << "Failed to get browser window.";
+ if (!thread_.Start()) {
+ LOG(ERROR) << "Cannot start session thread";
return false;
}
-
- scoped_refptr<TabProxy> tab = browser->GetActiveTab();
- if (!tab.get()) {
- LOG(ERROR) << "Could not load tab";
- return false;
- }
-
- SetBrowserAndTab(0, browser, tab);
- return true;
-}
-
-void Session::SetupCommandLine() {
- test_launcher_utils::PrepareBrowserCommandLineForTests(&launch_arguments_);
- launch_arguments_.AppendSwitch(switches::kDomAutomationController);
- launch_arguments_.AppendSwitch(switches::kFullMemoryCrashReport);
-
- launch_arguments_.AppendSwitchASCII(switches::kUserDataDir,
- tmp_profile_dir());
-}
-
-void Session::SetBrowserAndTab(const int window_num,
- const scoped_refptr<BrowserProxy>& browser,
- const scoped_refptr<TabProxy>& tab) {
- window_num_ = window_num;
- browser_ = browser;
- tab_ = tab;
- current_frame_xpath_ = L"";
-
- int tab_num;
- LOG_IF(WARNING, !tab->GetTabIndex(&tab_num) || !browser->ActivateTab(tab_num))
- << "Failed to activate tab";
-}
-
-bool Session::CreateTemporaryProfileDirectory() {
- memset(tmp_profile_dir_, 0, sizeof tmp_profile_dir_);
-#ifdef OS_POSIX
- strncat(tmp_profile_dir_, "/tmp/webdriverXXXXXX", sizeof tmp_profile_dir_);
- if (mkdtemp(tmp_profile_dir_) == NULL) {
- LOG(ERROR) << "mkdtemp failed";
- return false;
- }
-#elif OS_WIN
- DWORD ret;
- ProfileDir temp_dir;
-
- ret = GetTempPathA(sizeof temp_dir, temp_dir);
- if (ret == 0 || ret > sizeof temp_dir) {
- LOG(ERROR) << "Could not find the temp directory";
- return false;
- }
-
- ret = GetTempFileNameA(temp_dir, // Directory for tmp files.
- "webdriver", // Temp file name prefix.
- static_cast<int>(time(NULL)) % 65535 + 1,
- tmp_profile_dir_); // Buffer for name.
-
- if (ret ==0) {
- LOG(ERROR) << "Could not generate temp directory name";
- return false;
- }
-
- if (!CreateDirectoryA(tmp_profile_dir_, NULL)) {
- DWORD dw = GetLastError();
- LOG(ERROR) << "Error code: " << dw;
- return false;
- }
-#endif
- VLOG(1) << "Using temporary profile directory: " << tmp_profile_dir_;
- return true;
+ automation_.reset(new Automation());
+
+ bool success = false;
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::Init,
+ &success));
+ return success;
}
void Session::Terminate() {
- QuitBrowser();
-#ifdef OS_POSIX
- FilePath del_dir = FilePath(tmp_profile_dir());
-#elif OS_WIN
- FilePath del_dir = FilePath(ASCIIToWide(tmp_profile_dir()));
-#endif
- if (file_util::PathExists(del_dir) && !file_util::Delete(del_dir, true))
- LOG(ERROR) << "Could not clean up temp directory: " << tmp_profile_dir();
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::Terminate));
}
ErrorCode Session::ExecuteScript(const std::wstring& script,
@@ -199,12 +76,21 @@ ErrorCode Session::ExecuteScript(const std::wstring& script,
VLOG(1) << "Executing script in frame: " << current_frame_xpath_;
std::wstring result;
- scoped_refptr<TabProxy> tab = ActiveTab();
- if (!tab->ExecuteAndExtractString(current_frame_xpath_, jscript, &result)) {
+ bool success;
+ std::string result_utf8;
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::ExecuteScript,
+ WideToUTF8(current_frame_xpath_),
+ WideToUTF8(jscript),
+ &result_utf8,
+ &success));
+ if (!success) {
*value = Value::CreateStringValue(
"Unknown internal script execution failure");
return kUnknownError;
}
+ result = UTF8ToWide(result_utf8);
VLOG(1) << "...script result: " << result;
std::string temp = WideToASCII(result);
@@ -243,4 +129,78 @@ ErrorCode Session::ExecuteScript(const std::wstring& script,
return static_cast<ErrorCode>(status);
}
+bool Session::NavigateToURL(const std::string& url) {
+ bool success = false;
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::NavigateToURL,
+ url,
+ &success));
+ return success;
+}
+
+bool Session::GoForward() {
+ bool success = false;
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::GoForward,
+ &success));
+ return success;
+}
+
+bool Session::GoBack() {
+ bool success = false;
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::GoBack,
+ &success));
+ return success;
+}
+
+bool Session::Reload() {
+ bool success = false;
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::Reload,
+ &success));
+ return success;
+}
+
+bool Session::GetURL(std::string* url) {
+ bool success = false;
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::GetURL,
+ url,
+ &success));
+ return success;
+}
+
+bool Session::GetTabTitle(std::string* tab_title) {
+ bool success = false;
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::GetTabTitle,
+ tab_title,
+ &success));
+ return success;
+}
+
+void Session::RunSessionTask(Task* task) {
+ base::WaitableEvent done_event(false, false);
+ thread_.message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod(
+ this,
+ &Session::RunSessionTaskOnSessionThread,
+ task,
+ &done_event));
+ done_event.Wait();
+}
+
+void Session::RunSessionTaskOnSessionThread(Task* task,
+ base::WaitableEvent* done_event) {
+ task->Run();
+ delete task;
+ done_event->Signal();
+}
+
} // namespace webdriver
diff --git a/chrome/test/webdriver/session.h b/chrome/test/webdriver/session.h
index 5fea84c..d93db24 100644
--- a/chrome/test/webdriver/session.h
+++ b/chrome/test/webdriver/session.h
@@ -8,12 +8,7 @@
#include <string>
#include "base/scoped_ptr.h"
-
-#include "chrome/test/automation/automation_proxy.h"
-#include "chrome/test/automation/browser_proxy.h"
-#include "chrome/test/automation/tab_proxy.h"
-#include "chrome/test/automation/window_proxy.h"
-#include "chrome/test/ui/ui_test.h"
+#include "chrome/test/webdriver/automation.h"
#include "chrome/test/webdriver/error_codes.h"
namespace webdriver {
@@ -23,29 +18,17 @@ namespace webdriver {
// state necessary to control the chrome browser created.
// TODO(phajdan.jr): Abstract UITestBase classes, see:
// http://code.google.com/p/chromium/issues/detail?id=56865
-class Session : private UITestBase {
+class Session {
public:
-#if defined(OS_POSIX)
- typedef char ProfileDir[L_tmpnam + 1]; // +1 for \0
-#elif defined(OS_WIN)
- typedef char ProfileDir[MAX_PATH + 1]; // +1 for \0
-#endif
-
explicit Session(const std::string& id);
+ // Creates a browser.
bool Init();
// Terminates this session and disconnects its automation proxy. After
// invoking this method, the Session can safely be deleted.
void Terminate();
- // Finds the active tab that webdriver commands should go to.
- scoped_refptr<TabProxy> ActiveTab();
-
- void SetBrowserAndTab(const int window_num,
- const scoped_refptr<BrowserProxy>& browser,
- const scoped_refptr<TabProxy>& tab);
-
// Executes the given |script| in the context of the frame that is currently
// the focus of this session. The |script| should be in the form of a
// function body (e.g. "return arguments[0]"), where \args| is the list of
@@ -55,6 +38,19 @@ class Session : private UITestBase {
const ListValue* const args,
Value** value);
+
+ bool NavigateToURL(const std::string& url);
+ bool GoForward();
+ bool GoBack();
+ bool Reload();
+ bool GetURL(std::string* url);
+ bool GetTabTitle(std::string* tab_title);
+ void RunSessionTask(Task* task);
+ void RunSessionTaskOnSessionThread(
+ Task* task,
+ base::WaitableEvent* done_event);
+
+
inline const std::string& id() const { return id_; }
inline int implicit_wait() { return implicit_wait_; }
@@ -68,10 +64,6 @@ class Session : private UITestBase {
speed_ = speed;
}
- inline const char* tmp_profile_dir() {
- return tmp_profile_dir_;
- };
-
inline const std::wstring& current_frame_xpath() const {
return current_frame_xpath_;
}
@@ -81,19 +73,15 @@ class Session : private UITestBase {
}
private:
- bool CreateTemporaryProfileDirectory();
- bool LoadProxies();
- void SetupCommandLine();
+ scoped_ptr<Automation> automation_;
+ base::Thread thread_;
const std::string id_;
int window_num_;
- scoped_refptr<BrowserProxy> browser_;
- scoped_refptr<TabProxy> tab_;
int implicit_wait_;
Speed speed_;
- ProfileDir tmp_profile_dir_;
// The XPath to the frame within this session's active tab which all
// commands should be directed to. XPath strings can represent a frame deep
@@ -108,5 +96,6 @@ class Session : private UITestBase {
} // namespace webdriver
-#endif // CHROME_TEST_WEBDRIVER_SESSION_H_
+DISABLE_RUNNABLE_METHOD_REFCOUNT(webdriver::Session);
+#endif // CHROME_TEST_WEBDRIVER_SESSION_H_
diff --git a/chrome/test/webdriver/session_manager.cc b/chrome/test/webdriver/session_manager.cc
index 7b1981b..2d3a8bc 100644
--- a/chrome/test/webdriver/session_manager.cc
+++ b/chrome/test/webdriver/session_manager.cc
@@ -10,7 +10,7 @@
#include "base/process_util.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
-#include "base/test/test_timeouts.h"
+#include "base/threading/thread.h"
#include "chrome/common/chrome_constants.h"
#if defined(OS_POSIX)
@@ -120,59 +120,55 @@ std::string SessionManager::GenerateSessionID() {
#else
id += text[rand() % (sizeof text - 1)];
#endif
- id += count_; // Append the global count to generate a unique id.
}
session_generation_.Release();
return id;
}
-bool SessionManager::Create(std::string* id) {
- MessageLoop loop;
- TestTimeouts::Initialize();
-
- *id = GenerateSessionID();
- if (map_.find(*id) != map_.end()) {
- LOG(ERROR) << "Failed to generate a unique session ID";
- return false;
- }
-
- // Start chrome, if it doesn't startup quit.
- const int ap_timeout = TestTimeouts::command_execution_timeout_ms();
- VLOG(1) << "Waiting for a max of " << ap_timeout << " ms to start the chrome "
- "browser";
-
- scoped_ptr<Session> session(new Session(*id));
-
- if (!session->Init()) {
- LOG(ERROR) << "Could not establish a valid connection to the browser";
- return false;
+Session* SessionManager::Create() {
+ std::string id = GenerateSessionID();
+ {
+ base::AutoLock lock(map_lock_);
+ if (map_.find(id) != map_.end()) {
+ LOG(ERROR) << "Failed to generate a unique session ID";
+ return false;
+ }
}
- map_[*id] = session.release();
- return true;
+ Session* session = new Session(id);
+ base::AutoLock lock(map_lock_);
+ map_[id] = session;
+ return session;
}
bool SessionManager::Has(const std::string& id) const {
+ base::AutoLock lock(map_lock_);
return map_.find(id) != map_.end();
}
bool SessionManager::Delete(const std::string& id) {
std::map<std::string, Session*>::iterator it;
- VLOG(1) << "Deleting session with ID " << id;
- it = map_.find(id);
- if (it == map_.end()) {
- VLOG(1) << "No such session with ID " << id;
- return false;
+ Session* session;
+ {
+ base::AutoLock lock(map_lock_);
+ it = map_.find(id);
+ if (it == map_.end()) {
+ VLOG(1) << "No such session with ID " << id;
+ return false;
+ }
+ session = it->second;
+ map_.erase(it);
}
- it->second->Terminate();
- map_.erase(it);
+ VLOG(1) << "Deleting session with ID " << id;
+ delete session;
return true;
}
Session* SessionManager::GetSession(const std::string& id) const {
std::map<std::string, Session*>::const_iterator it;
+ base::AutoLock lock(map_lock_);
it = map_.find(id);
if (it == map_.end()) {
VLOG(1) << "No such session with ID " << id;
diff --git a/chrome/test/webdriver/session_manager.h b/chrome/test/webdriver/session_manager.h
index 0c5c5bfd..c4f8729 100644
--- a/chrome/test/webdriver/session_manager.h
+++ b/chrome/test/webdriver/session_manager.h
@@ -9,7 +9,7 @@
#include <string>
#include "base/singleton.h"
-
+#include "base/synchronization/lock.h"
#include "chrome/test/webdriver/session.h"
namespace webdriver {
@@ -27,7 +27,7 @@ class SessionManager {
std::string GetIPAddress();
bool SetIPAddress(const std::string& port);
- bool Create(std::string* id);
+ Session* Create();
bool Delete(const std::string& id);
bool Has(const std::string& id) const;
@@ -40,6 +40,7 @@ class SessionManager {
std::string IPLookup(const std::string& nic);
std::map<std::string, Session*> map_;
+ mutable base::Lock map_lock_;
base::Lock session_generation_;
// Record the address and port for the HTTP 303 See other redirect.
// We save the IP and Port of the machine chromedriver is running on since
@@ -55,4 +56,3 @@ class SessionManager {
} // namespace webdriver
#endif // CHROME_TEST_WEBDRIVER_SESSION_MANAGER_H_
-