summaryrefslogtreecommitdiff
path: root/chrome/test
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2013-07-24 10:36:34 +0100
committerBen Murdoch <benm@google.com>2013-07-24 10:36:34 +0100
commita3f7b4e666c476898878fa745f637129375cd889 (patch)
tree1d78b48780e4c8603c226fd88d8f4b786f00bb81 /chrome/test
parentd4336a7d5c149891bede0c3201c8e831520067af (diff)
downloadchromium_org-a3f7b4e666c476898878fa745f637129375cd889.tar.gz
Merge from Chromium at DEPS revision r213371
This commit was generated by merge_to_master.py. Change-Id: I35a74205de4bff52a2dd6b15330f8a002a39efaf
Diffstat (limited to 'chrome/test')
-rw-r--r--chrome/test/base/browser_with_test_window_test.cc23
-rw-r--r--chrome/test/base/browser_with_test_window_test.h10
-rw-r--r--chrome/test/base/testing_io_thread_state.cc102
-rw-r--r--chrome/test/base/testing_io_thread_state.h38
-rw-r--r--chrome/test/base/testing_profile.cc40
-rw-r--r--chrome/test/base/testing_profile.h24
-rw-r--r--chrome/test/base/ui_test_utils.cc7
-rw-r--r--chrome/test/base/view_event_test_base.cc6
-rw-r--r--chrome/test/base/view_event_test_base.h6
-rw-r--r--chrome/test/chromedriver/README.txt41
-rw-r--r--chrome/test/chromedriver/alert_commands.cc1
-rw-r--r--chrome/test/chromedriver/chrome/adb_impl.cc10
-rw-r--r--chrome/test/chromedriver/chrome/adb_impl.h6
-rw-r--r--chrome/test/chromedriver/command.h10
-rw-r--r--chrome/test/chromedriver/commands.cc243
-rw-r--r--chrome/test/chromedriver/commands.h50
-rw-r--r--chrome/test/chromedriver/commands_unittest.cc250
-rw-r--r--chrome/test/chromedriver/fake_session_accessor.cc28
-rw-r--r--chrome/test/chromedriver/fake_session_accessor.h36
-rw-r--r--chrome/test/chromedriver/server/chromedriver_server.cc76
-rw-r--r--chrome/test/chromedriver/server/http_handler.cc608
-rw-r--r--chrome/test/chromedriver/server/http_handler.h39
-rw-r--r--chrome/test/chromedriver/server/http_handler_unittest.cc31
-rw-r--r--chrome/test/chromedriver/session.cc18
-rw-r--r--chrome/test/chromedriver/session.h33
-rw-r--r--chrome/test/chromedriver/session_commands.cc55
-rw-r--r--chrome/test/chromedriver/session_commands.h21
-rw-r--r--chrome/test/chromedriver/session_commands_unittest.cc154
-rw-r--r--chrome/test/chromedriver/session_map.h18
-rw-r--r--chrome/test/chromedriver/session_thread_map.h16
-rw-r--r--chrome/test/chromedriver/session_unittest.cc12
-rw-r--r--chrome/test/chromedriver/synchronized_map.h86
-rw-r--r--chrome/test/chromedriver/synchronized_map_unittest.cc78
-rw-r--r--chrome/test/ppapi/ppapi_test.cc12
34 files changed, 1189 insertions, 999 deletions
diff --git a/chrome/test/base/browser_with_test_window_test.cc b/chrome/test/base/browser_with_test_window_test.cc
index 28f5787cd7..d9b8c06952 100644
--- a/chrome/test/base/browser_with_test_window_test.cc
+++ b/chrome/test/base/browser_with_test_window_test.cc
@@ -4,7 +4,7 @@
#include "chrome/test/base/browser_with_test_window_test.h"
-#include "base/synchronization/waitable_event.h"
+#include "base/run_loop.h"
#include "chrome/browser/profiles/profile_destroyer.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_navigator.h"
@@ -25,24 +25,16 @@
#include "ash/test/ash_test_helper.h"
#endif
-using content::BrowserThread;
using content::NavigationController;
using content::RenderViewHost;
using content::RenderViewHostTester;
using content::WebContents;
BrowserWithTestWindowTest::BrowserWithTestWindowTest()
- : ui_thread_(BrowserThread::UI, message_loop()),
- db_thread_(BrowserThread::DB),
- file_thread_(BrowserThread::FILE, message_loop()),
- file_user_blocking_thread_(
- BrowserThread::FILE_USER_BLOCKING, message_loop()),
- host_desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE) {
- db_thread_.Start();
+ : host_desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE) {
}
BrowserWithTestWindowTest::~BrowserWithTestWindowTest() {
- db_thread_.Stop();
}
void BrowserWithTestWindowTest::SetHostDesktopType(
@@ -57,10 +49,12 @@ void BrowserWithTestWindowTest::SetUp() {
// TODO(jamescook): Windows Ash support. This will require refactoring
// AshTestHelper and AuraTestHelper so they can be used at the same time,
// perhaps by AshTestHelper owning an AuraTestHelper.
- ash_test_helper_.reset(new ash::test::AshTestHelper(&ui_loop_));
+ ash_test_helper_.reset(new ash::test::AshTestHelper(
+ base::MessageLoopForUI::current()));
ash_test_helper_->SetUp();
#elif defined(USE_AURA)
- aura_test_helper_.reset(new aura::test::AuraTestHelper(&ui_loop_));
+ aura_test_helper_.reset(new aura::test::AuraTestHelper(
+ base::MessageLoopForUI::current()));
aura_test_helper_->SetUp();
#endif // USE_AURA
@@ -79,10 +73,7 @@ void BrowserWithTestWindowTest::SetUp() {
void BrowserWithTestWindowTest::TearDown() {
// Some tests end up posting tasks to the DB thread that must be completed
// before the profile can be destroyed and the test safely shut down.
- base::WaitableEvent done(false, false);
- BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
- base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
- done.Wait();
+ base::RunLoop().RunUntilIdle();
// Reset the profile here because some profile keyed services (like the
// audio service) depend on test stubs that the helpers below will remove.
diff --git a/chrome/test/base/browser_with_test_window_test.h b/chrome/test/base/browser_with_test_window_test.h
index 4faaa4743b..f4e6e51107 100644
--- a/chrome/test/base/browser_with_test_window_test.h
+++ b/chrome/test/base/browser_with_test_window_test.h
@@ -11,7 +11,7 @@
#include "chrome/browser/ui/host_desktop.h"
#include "chrome/test/base/test_browser_window.h"
#include "chrome/test/base/testing_profile.h"
-#include "content/public/test/test_browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -99,8 +99,6 @@ class BrowserWithTestWindowTest : public testing::Test {
TestingProfile* GetProfile() { return profile_.get(); }
- base::MessageLoop* message_loop() { return &ui_loop_; }
-
BrowserWindow* release_browser_window() WARN_UNUSED_RESULT {
return window_.release();
}
@@ -142,12 +140,8 @@ class BrowserWithTestWindowTest : public testing::Test {
private:
// We need to create a MessageLoop, otherwise a bunch of things fails.
- base::MessageLoopForUI ui_loop_;
+ content::TestBrowserThreadBundle thread_bundle_;
base::ShadowingAtExitManager at_exit_manager_;
- content::TestBrowserThread ui_thread_;
- content::TestBrowserThread db_thread_;
- content::TestBrowserThread file_thread_;
- content::TestBrowserThread file_user_blocking_thread_;
#if defined(OS_CHROMEOS)
chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
diff --git a/chrome/test/base/testing_io_thread_state.cc b/chrome/test/base/testing_io_thread_state.cc
new file mode 100644
index 0000000000..46f396afd3
--- /dev/null
+++ b/chrome/test/base/testing_io_thread_state.cc
@@ -0,0 +1,102 @@
+// Copyright 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/base/testing_io_thread_state.h"
+
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "chrome/browser/io_thread.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/network_time_notifier.h"
+
+#if defined(OS_CHROMEOS)
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/network/network_handler.h"
+#endif
+
+using content::BrowserThread;
+
+namespace {
+
+base::Closure ThreadSafeQuit(base::RunLoop* run_loop) {
+ if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ return run_loop->QuitClosure();
+ } else {
+ using base::Bind;
+ using base::IgnoreResult;
+ return Bind(IgnoreResult(&base::MessageLoopProxy::PostTask),
+ base::MessageLoopProxy::current(),
+ FROM_HERE,
+ run_loop->QuitClosure());
+ }
+}
+
+} // namespace
+
+namespace chrome {
+
+TestingIOThreadState::TestingIOThreadState() {
+#if defined(OS_CHROMEOS)
+ // Needed by IOThread constructor.
+ chromeos::DBusThreadManager::InitializeWithStub();
+ chromeos::NetworkHandler::Initialize();
+#endif
+
+ io_thread_state_.reset(
+ new IOThread(TestingBrowserProcess::GetGlobal()->local_state(),
+ TestingBrowserProcess::GetGlobal()->policy_service(),
+ NULL, NULL));
+
+ // Safe because there are no virtuals.
+ base::RunLoop run_loop;
+ CHECK(BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&TestingIOThreadState::Initialize,
+ base::Unretained(this),
+ ThreadSafeQuit(&run_loop))));
+ run_loop.Run();
+
+ TestingBrowserProcess::GetGlobal()->SetIOThread(io_thread_state_.get());
+}
+
+TestingIOThreadState::~TestingIOThreadState() {
+ // Remove all the local IOThread state.
+ base::RunLoop run_loop;
+ CHECK(BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&TestingIOThreadState::Shutdown,
+ base::Unretained(this),
+ ThreadSafeQuit(&run_loop))));
+ run_loop.Run();
+ TestingBrowserProcess::GetGlobal()->SetIOThread(NULL);
+
+ io_thread_state_.reset();
+
+#if defined(OS_CHROMEOS)
+ chromeos::NetworkHandler::Shutdown();
+ chromeos::DBusThreadManager::Shutdown();
+#endif
+}
+
+void TestingIOThreadState::Initialize(const base::Closure& done) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ io_thread_state_->SetGlobalsForTesting(new IOThread::Globals());
+ io_thread_state_->globals()->network_time_notifier.reset(
+ new net::NetworkTimeNotifier(
+ scoped_ptr<base::TickClock>(new base::DefaultTickClock())));
+
+ done.Run();
+}
+
+void TestingIOThreadState::Shutdown(const base::Closure& done) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ delete io_thread_state_->globals();
+ io_thread_state_->SetGlobalsForTesting(NULL);
+ done.Run();
+}
+
+} // namespace chrome
diff --git a/chrome/test/base/testing_io_thread_state.h b/chrome/test/base/testing_io_thread_state.h
new file mode 100644
index 0000000000..5028103d0a
--- /dev/null
+++ b/chrome/test/base/testing_io_thread_state.h
@@ -0,0 +1,38 @@
+// Copyright 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_BASE_TESTING_IO_THREAD_STATE_H_
+#define CHROME_TEST_BASE_TESTING_IO_THREAD_STATE_H_
+
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+
+class IOThread;
+
+namespace chrome {
+
+// Convenience class for creating an IOThread object in unittests.
+// Usual usage is to create one of these in the test fixture, after the
+// BrowserThreadBundle and TestingBrowserProcess have been initialized.
+//
+// If code requires the use of io thread globals, those can be set by
+// accessing io_thread_state()->globals() on the IO thread during test setup.
+class TestingIOThreadState {
+ public:
+ TestingIOThreadState();
+ ~TestingIOThreadState();
+ IOThread* io_thread_state() { return io_thread_state_.get(); }
+
+ private:
+ void Initialize(const base::Closure& done);
+ void Shutdown(const base::Closure& done);
+
+ scoped_ptr<IOThread> io_thread_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestingIOThreadState);
+};
+
+} // namespace chrome
+
+#endif // CHROME_TEST_BASE_TESTING_IO_THREAD_STATE_H_
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 2704bc7c6f..3a3c0190f1 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -170,6 +170,7 @@ TestingProfile::TestingProfile()
last_session_exited_cleanly_(true),
browser_context_dependency_manager_(
BrowserContextDependencyManager::GetInstance()),
+ resource_context_(NULL),
delegate_(NULL) {
CreateTempProfileDir();
profile_path_ = temp_dir_.path();
@@ -187,6 +188,7 @@ TestingProfile::TestingProfile(const base::FilePath& path)
profile_path_(path),
browser_context_dependency_manager_(
BrowserContextDependencyManager::GetInstance()),
+ resource_context_(NULL),
delegate_(NULL) {
Init();
FinishInit();
@@ -202,6 +204,7 @@ TestingProfile::TestingProfile(const base::FilePath& path,
profile_path_(path),
browser_context_dependency_manager_(
BrowserContextDependencyManager::GetInstance()),
+ resource_context_(NULL),
delegate_(delegate) {
Init();
if (delegate_) {
@@ -228,6 +231,7 @@ TestingProfile::TestingProfile(
profile_path_(path),
browser_context_dependency_manager_(
BrowserContextDependencyManager::GetInstance()),
+ resource_context_(NULL),
delegate_(delegate) {
// If no profile path was supplied, create one.
@@ -320,6 +324,9 @@ void TestingProfile::FinishInit() {
}
TestingProfile::~TestingProfile() {
+ // Any objects holding live URLFetchers should be deleted before teardown.
+ TemplateURLFetcherFactory::ShutdownForProfile(this);
+
MaybeSendDestroyedNotification();
browser_context_dependency_manager_->DestroyBrowserContextServices(this);
@@ -331,6 +338,14 @@ TestingProfile::~TestingProfile() {
if (pref_proxy_config_tracker_.get())
pref_proxy_config_tracker_->DetachFromPrefService();
+ // Failing a post == leaks == heapcheck failure. Make that an immediate test
+ // failure.
+ if (resource_context_) {
+ CHECK(BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE,
+ resource_context_));
+ resource_context_ = NULL;
+ content::RunAllPendingInMessageLoop(BrowserThread::IO);
+ }
}
static BrowserContextKeyedService* BuildFaviconService(
@@ -596,12 +611,13 @@ DownloadManagerDelegate* TestingProfile::GetDownloadManagerDelegate() {
}
net::URLRequestContextGetter* TestingProfile::GetRequestContext() {
- return request_context_.get();
+ return GetDefaultStoragePartition(this)->GetURLRequestContext();
}
net::URLRequestContextGetter* TestingProfile::CreateRequestContext(
content::ProtocolHandlerMap* protocol_handlers) {
- return request_context_.get();
+ return new net::TestURLRequestContextGetter(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO));
}
net::URLRequestContextGetter* TestingProfile::GetRequestContextForRenderProcess(
@@ -611,20 +627,6 @@ net::URLRequestContextGetter* TestingProfile::GetRequestContextForRenderProcess(
return rph->GetStoragePartition()->GetURLRequestContext();
}
-void TestingProfile::CreateRequestContext() {
- if (!request_context_.get())
- request_context_ = new net::TestURLRequestContextGetter(
- BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO));
-}
-
-void TestingProfile::ResetRequestContext() {
- // Any objects holding live URLFetchers should be deleted before the request
- // context is shut down.
- TemplateURLFetcherFactory::ShutdownForProfile(this);
-
- request_context_ = NULL;
-}
-
net::URLRequestContextGetter* TestingProfile::GetMediaRequestContext() {
return NULL;
}
@@ -674,9 +676,9 @@ TestingProfile::CreateRequestContextForStoragePartition(
}
content::ResourceContext* TestingProfile::GetResourceContext() {
- if (!resource_context_.get())
- resource_context_.reset(new content::MockResourceContext());
- return resource_context_.get();
+ if (!resource_context_)
+ resource_context_ = new content::MockResourceContext();
+ return resource_context_;
}
HostContentSettingsMap* TestingProfile::GetHostContentSettingsMap() {
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 916bc33dc3..939e772312 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -174,14 +174,6 @@ class TestingProfile : public Profile {
virtual bool IsOffTheRecord() const OVERRIDE;
virtual content::DownloadManagerDelegate*
GetDownloadManagerDelegate() OVERRIDE;
- // Returns a testing ContextGetter (if one has been created via
- // CreateRequestContext) or NULL. This is not done on-demand for two reasons:
- // (1) Some tests depend on GetRequestContext() returning NULL. (2) Because
- // of the special memory management considerations for the
- // TestURLRequestContextGetter class, many tests would find themseleves
- // leaking if they called this method without the necessary IO thread. This
- // getter is currently only capable of returning a Context that helps test
- // the CookieMonster. See implementation comments for more details.
virtual net::URLRequestContextGetter* GetRequestContext() OVERRIDE;
virtual net::URLRequestContextGetter* CreateRequestContext(
content::ProtocolHandlerMap* protocol_handlers) OVERRIDE;
@@ -207,9 +199,9 @@ class TestingProfile : public Profile {
ExtensionSpecialStoragePolicy* extension_special_storage_policy);
virtual ExtensionSpecialStoragePolicy*
GetExtensionSpecialStoragePolicy() OVERRIDE;
- // The CookieMonster will only be returned if a Context has been created. Do
- // this by calling CreateRequestContext(). See the note at GetRequestContext
- // for more information.
+ // TODO(ajwong): Remove this API in favor of directly retrieving the
+ // CookieStore from the StoragePartition after ExtensionURLRequestContext
+ // has been removed.
net::CookieMonster* GetCookieMonster();
virtual PrefService* GetPrefs() OVERRIDE;
@@ -217,11 +209,6 @@ class TestingProfile : public Profile {
virtual history::TopSites* GetTopSites() OVERRIDE;
virtual history::TopSites* GetTopSitesWithoutCreating() OVERRIDE;
- void CreateRequestContext();
- // Clears out the created request context (which must be done before shutting
- // down the IO thread to avoid leaks).
- void ResetRequestContext();
-
virtual net::URLRequestContextGetter* GetMediaRequestContext() OVERRIDE;
virtual net::URLRequestContextGetter* GetMediaRequestContextForRenderProcess(
int renderer_child_id) OVERRIDE;
@@ -313,7 +300,6 @@ class TestingProfile : public Profile {
// Internally, this is a TestURLRequestContextGetter that creates a dummy
// request context. Currently, only the CookieMonster is hooked up.
- scoped_refptr<net::URLRequestContextGetter> request_context_;
scoped_refptr<net::URLRequestContextGetter> extensions_request_context_;
std::wstring id_;
@@ -349,7 +335,9 @@ class TestingProfile : public Profile {
// testing.
BrowserContextDependencyManager* browser_context_dependency_manager_;
- scoped_ptr<content::MockResourceContext> resource_context_;
+ // Owned, but must be deleted on the IO thread so not placing in a
+ // scoped_ptr<>.
+ content::MockResourceContext* resource_context_;
scoped_ptr<policy::ProfilePolicyConnector> profile_policy_connector_;
diff --git a/chrome/test/base/ui_test_utils.cc b/chrome/test/base/ui_test_utils.cc
index a5fcc48cde..302b6d8b0d 100644
--- a/chrome/test/base/ui_test_utils.cc
+++ b/chrome/test/base/ui_test_utils.cc
@@ -371,8 +371,13 @@ void WaitForBookmarkModelToLoad(Profile* profile) {
void WaitForTemplateURLServiceToLoad(TemplateURLService* service) {
if (service->loaded())
return;
+
+ content::WindowedNotificationObserver observer(
+ chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED,
+ content::Source<TemplateURLService>(service));
service->Load();
- TemplateURLServiceTestUtil::BlockTillServiceProcessesRequests();
+ observer.Wait();
+
ASSERT_TRUE(service->loaded());
}
diff --git a/chrome/test/base/view_event_test_base.cc b/chrome/test/base/view_event_test_base.cc
index 0c84147243..aabfb25f61 100644
--- a/chrome/test/base/view_event_test_base.cc
+++ b/chrome/test/base/view_event_test_base.cc
@@ -71,8 +71,7 @@ const int kMouseMoveDelayMS = 200;
ViewEventTestBase::ViewEventTestBase()
: window_(NULL),
- content_view_(NULL),
- ui_thread_(content::BrowserThread::UI, &message_loop_) {
+ content_view_(NULL) {
}
void ViewEventTestBase::Done() {
@@ -119,7 +118,8 @@ void ViewEventTestBase::SetUp() {
#elif defined(USE_AURA)
// Instead of using the ash shell, use an AuraTestHelper to create and manage
// the test screen.
- aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_));
+ aura_test_helper_.reset(
+ new aura::test::AuraTestHelper(base::MessageLoopForUI::current()));
aura_test_helper_->SetUp();
context = aura_test_helper_->root_window();
#endif
diff --git a/chrome/test/base/view_event_test_base.h b/chrome/test/base/view_event_test_base.h
index a139f264e7..c6c7be7650 100644
--- a/chrome/test/base/view_event_test_base.h
+++ b/chrome/test/base/view_event_test_base.h
@@ -15,7 +15,7 @@
#include "base/compiler_specific.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/thread.h"
-#include "content/public/test/test_browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/widget/widget_delegate.h"
@@ -141,9 +141,7 @@ class ViewEventTestBase : public views::WidgetDelegate,
// Thread for posting background MouseMoves.
scoped_ptr<base::Thread> dnd_thread_;
- base::MessageLoopForUI message_loop_;
-
- content::TestBrowserThread ui_thread_;
+ content::TestBrowserThreadBundle thread_bundle_;
#if defined(OS_WIN)
ui::ScopedOleInitializer ole_initializer_;
diff --git a/chrome/test/chromedriver/README.txt b/chrome/test/chromedriver/README.txt
index 35f0d5466f..03154fecae 100644
--- a/chrome/test/chromedriver/README.txt
+++ b/chrome/test/chromedriver/README.txt
@@ -4,10 +4,12 @@ contribute.
ChromeDriver is an implementation of the WebDriver standard,
which allows users to automate testing of their website across browsers.
+See the user site at http://code.google.com/p/chromedriver.
+
=====Getting started=====
Build ChromeDriver by building the 'chromedriver2_server' target. This will
-create an executable binary in the build folder named 'chromedriver2_server.exe'
-on Windows or 'chromedriver2_server' on Mac and Linux.
+create an executable binary in the build folder named
+'chromedriver2_server[.exe]'.
Once built, ChromeDriver can be used interactively with python.
@@ -31,26 +33,30 @@ more detailed instructions see the wiki:
=====Architecture=====
ChromeDriver is shipped separately from Chrome. It controls Chrome out of
-process through DevTools (WebKit Inspector). ChromeDriver is a standalone server
-executable which communicates via the WebDriver JSON wire protocol. This can be
-used with the open source WebDriver client libraries.
-
-When a new session is created, a new thread is started that is dedicated to the
-session. All commands for the session runs on this thread. This thread is
-stopped when the session is deleted. Besides, there is an IO thread and it is
-used to keep reading incoming data from Chrome in the background.
+process through DevTools. ChromeDriver is a standalone server which
+communicates with the WebDriver client via the WebDriver wire protocol, which
+is essentially synchronous JSON commands over HTTP. WebDriver clients are
+available in many languages, and many are available from the open source
+selenium/webdriver project: http://code.google.com/p/selenium.
+
+ChromeDriver has several threads. The webserver code, third_party/mongoose,
+spawns a thread for the server socket and a certain amount of request handling
+threads. When a request is received, the command is processed on the message
+loop of the main thread, also called the command thread. Commands may be handled
+asynchronously on the command thread, but the request handler threads
+will block waiting for the response. One of the commands allows the user to
+create a session, which includes spawning a dedicated session thread. Session
+commands will be dispatched to the session thread and handled synchronously
+there. Lastly, there is an IO/net thread on which the net/ code operates.
+This is used to keep reading incoming data from Chrome in the background.
=====Code structure=====
-Code under the 'chrome' subdirectory is intended to be unaware of WebDriver and
-serve as a basic C++ interface for controlling Chrome remotely via DevTools.
-As such, it should not have any WebDriver-related dependencies.
-
1) chrome/test/chromedriver
Implements chromedriver commands.
2) chrome/test/chromedriver/chrome
-Code to deal with chrome specific stuff, like starting Chrome on different OS
-platforms, controlling Chrome via DevTools, handling events from DevTools, etc.
+A basic interface for controlling Chrome via DevTools. Should not have
+knowledge about WebDriver, and thus not depend on chrome/test/chromedriver.
3) chrome/test/chromedriver/js
Javascript helper scripts.
@@ -71,6 +77,9 @@ An extension used for automating the desktop browser.
8) chrome/test/chromedriver/third_party
Third party libraries used by chromedriver.
+9) third_party/mongoose
+The webserver for chromedriver.
+
=====Testing=====
There are 4 test suites for verifying ChromeDriver's correctness:
diff --git a/chrome/test/chromedriver/alert_commands.cc b/chrome/test/chromedriver/alert_commands.cc
index 2eef25705a..85836b9a3f 100644
--- a/chrome/test/chromedriver/alert_commands.cc
+++ b/chrome/test/chromedriver/alert_commands.cc
@@ -4,6 +4,7 @@
#include "chrome/test/chromedriver/alert_commands.h"
+#include "base/callback.h"
#include "base/values.h"
#include "chrome/test/chromedriver/chrome/chrome.h"
#include "chrome/test/chromedriver/chrome/devtools_client.h"
diff --git a/chrome/test/chromedriver/chrome/adb_impl.cc b/chrome/test/chromedriver/chrome/adb_impl.cc
index c830608ac4..cd4919f85f 100644
--- a/chrome/test/chromedriver/chrome/adb_impl.cc
+++ b/chrome/test/chromedriver/chrome/adb_impl.cc
@@ -9,7 +9,7 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
@@ -71,10 +71,10 @@ void ExecuteCommandOnIOThread(
} // namespace
AdbImpl::AdbImpl(
- const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy,
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
Log* log)
- : io_message_loop_proxy_(io_message_loop_proxy), log_(log) {
- CHECK(io_message_loop_proxy_.get());
+ : io_task_runner_(io_task_runner), log_(log) {
+ CHECK(io_task_runner_.get());
}
AdbImpl::~AdbImpl() {}
@@ -185,7 +185,7 @@ Status AdbImpl::ExecuteCommand(
const std::string& command, std::string* response) {
scoped_refptr<ResponseBuffer> response_buffer = new ResponseBuffer;
log_->AddEntry(Log::kDebug, "Sending adb command: " + command);
- io_message_loop_proxy_->PostTask(
+ io_task_runner_->PostTask(
FROM_HERE,
base::Bind(&ExecuteCommandOnIOThread, command, response_buffer));
Status status = response_buffer->GetResponse(
diff --git a/chrome/test/chromedriver/chrome/adb_impl.h b/chrome/test/chromedriver/chrome/adb_impl.h
index 47f9853fe9..b8bb411bbd 100644
--- a/chrome/test/chromedriver/chrome/adb_impl.h
+++ b/chrome/test/chromedriver/chrome/adb_impl.h
@@ -13,7 +13,7 @@
#include "chrome/test/chromedriver/chrome/adb.h"
namespace base {
-class MessageLoopProxy;
+class SingleThreadTaskRunner;
}
class Log;
@@ -22,7 +22,7 @@ class Status;
class AdbImpl : public Adb {
public:
explicit AdbImpl(
- const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy,
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_message_loop_proxy,
Log* log);
virtual ~AdbImpl();
@@ -53,7 +53,7 @@ class AdbImpl : public Adb {
const std::string& shell_command,
std::string* response);
- scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
Log* log_;
};
diff --git a/chrome/test/chromedriver/command.h b/chrome/test/chromedriver/command.h
index 6654694af5..e57a5f2ff0 100644
--- a/chrome/test/chromedriver/command.h
+++ b/chrome/test/chromedriver/command.h
@@ -17,10 +17,14 @@ class Value;
class Status;
-typedef base::Callback<Status(
+typedef base::Callback<void(
+ const Status&,
+ scoped_ptr<base::Value>,
+ const std::string&)> CommandCallback;
+
+typedef base::Callback<void(
const base::DictionaryValue&,
const std::string&,
- scoped_ptr<base::Value>*,
- std::string*)> Command;
+ const CommandCallback&)> Command;
#endif // CHROME_TEST_CHROMEDRIVER_COMMAND_H_
diff --git a/chrome/test/chromedriver/commands.cc b/chrome/test/chromedriver/commands.cc
index 36f403f45d..6ab6135198 100644
--- a/chrome/test/chromedriver/commands.cc
+++ b/chrome/test/chromedriver/commands.cc
@@ -5,10 +5,19 @@
#include "chrome/test/chromedriver/commands.h"
#include <list>
+#include <utility>
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/lazy_instance.h"
#include "base/logging.h" // For CHECK macros.
+#include "base/memory/linked_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
+#include "base/threading/thread_local.h"
#include "base/values.h"
#include "chrome/test/chromedriver/capabilities.h"
#include "chrome/test/chromedriver/chrome/chrome.h"
@@ -24,14 +33,13 @@
#include "chrome/test/chromedriver/net/net_util.h"
#include "chrome/test/chromedriver/net/url_request_context_getter.h"
#include "chrome/test/chromedriver/session.h"
-#include "chrome/test/chromedriver/session_map.h"
+#include "chrome/test/chromedriver/session_thread_map.h"
#include "chrome/test/chromedriver/util.h"
-Status ExecuteGetStatus(
+void ExecuteGetStatus(
const base::DictionaryValue& params,
const std::string& session_id,
- scoped_ptr<base::Value>* out_value,
- std::string* out_session_id) {
+ const CommandCallback& callback) {
base::DictionaryValue build;
build.SetString("version", "alpha");
@@ -43,30 +51,34 @@ Status ExecuteGetStatus(
base::DictionaryValue info;
info.Set("build", build.DeepCopy());
info.Set("os", os.DeepCopy());
- out_value->reset(info.DeepCopy());
- return Status(kOk);
+ callback.Run(
+ Status(kOk), scoped_ptr<base::Value>(info.DeepCopy()), std::string());
}
NewSessionParams::NewSessionParams(
Log* log,
- SessionMap* session_map,
+ SessionThreadMap* session_thread_map,
scoped_refptr<URLRequestContextGetter> context_getter,
const SyncWebSocketFactory& socket_factory,
DeviceManager* device_manager)
: log(log),
- session_map(session_map),
+ session_thread_map(session_thread_map),
context_getter(context_getter),
socket_factory(socket_factory),
device_manager(device_manager) {}
NewSessionParams::~NewSessionParams() {}
-Status ExecuteNewSession(
+namespace {
+
+base::LazyInstance<base::ThreadLocalPointer<Session> >
+ lazy_tls_session = LAZY_INSTANCE_INITIALIZER;
+
+Status CreateSessionOnSessionThreadHelper(
const NewSessionParams& bound_params,
const base::DictionaryValue& params,
const std::string& session_id,
- scoped_ptr<base::Value>* out_value,
- std::string* out_session_id) {
+ scoped_ptr<base::Value>* out_value) {
int port;
if (!FindOpenPort(&port))
return Status(kUnknownError, "failed to find an open port for Chrome");
@@ -108,67 +120,182 @@ Status ExecuteNewSession(
Status(kUnknownError, "unable to discover open window in chrome");
}
- std::string new_id = session_id;
- if (new_id.empty())
- new_id = GenerateId();
- scoped_ptr<Session> session(new Session(new_id, chrome.Pass()));
+ scoped_ptr<Session> session(new Session(session_id, chrome.Pass()));
session->devtools_logs.swap(devtools_logs);
- if (!session->thread.Start()) {
- chrome->Quit();
- return Status(kUnknownError,
- "failed to start a thread for the new session");
- }
session->window = web_view_ids.front();
session->detach = capabilities.detach;
out_value->reset(session->capabilities->DeepCopy());
- *out_session_id = new_id;
+ lazy_tls_session.Pointer()->Set(session.release());
+ return Status(kOk);
+}
+
+void CreateSessionOnSessionThread(
+ const scoped_refptr<base::SingleThreadTaskRunner>& cmd_task_runner,
+ const NewSessionParams& bound_params,
+ scoped_ptr<base::DictionaryValue> params,
+ const std::string& session_id,
+ const CommandCallback& callback_on_cmd) {
+ scoped_ptr<base::Value> value;
+ Status status = CreateSessionOnSessionThreadHelper(
+ bound_params, *params, session_id, &value);
+ cmd_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(callback_on_cmd, status, base::Passed(&value), session_id));
+}
- scoped_refptr<SessionAccessor> accessor(
- new SessionAccessorImpl(session.Pass()));
- bound_params.session_map->Set(new_id, accessor);
+} // namespace
- return Status(kOk);
+void ExecuteNewSession(
+ const NewSessionParams& bound_params,
+ const base::DictionaryValue& params,
+ const std::string& session_id,
+ const CommandCallback& callback) {
+ std::string new_id = session_id;
+ if (new_id.empty())
+ new_id = GenerateId();
+ scoped_ptr<base::Thread> thread(new base::Thread(new_id.c_str()));
+ if (!thread->Start()) {
+ callback.Run(
+ Status(kUnknownError, "failed to start a thread for the new session"),
+ scoped_ptr<base::Value>(),
+ std::string());
+ return;
+ }
+
+ thread->message_loop()
+ ->PostTask(FROM_HERE,
+ base::Bind(&CreateSessionOnSessionThread,
+ base::MessageLoopProxy::current(),
+ bound_params,
+ base::Passed(make_scoped_ptr(params.DeepCopy())),
+ new_id,
+ callback));
+ bound_params.session_thread_map
+ ->insert(std::make_pair(new_id, make_linked_ptr(thread.release())));
+}
+
+namespace {
+
+void OnSessionQuit(const base::WeakPtr<size_t>& quit_remaining_count,
+ const base::Closure& all_quit_func,
+ const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id) {
+ // |quit_remaining_count| may no longer be valid if a timeout occurred.
+ if (!quit_remaining_count)
+ return;
+
+ (*quit_remaining_count)--;
+ if (!*quit_remaining_count)
+ all_quit_func.Run();
}
-Status ExecuteQuit(
- bool allow_detach,
- SessionMap* session_map,
+} // namespace
+
+void ExecuteQuitAll(
+ const Command& quit_command,
+ SessionThreadMap* session_thread_map,
const base::DictionaryValue& params,
const std::string& session_id,
- scoped_ptr<base::Value>* out_value,
- std::string* out_session_id) {
- *out_session_id = session_id;
- scoped_refptr<SessionAccessor> session_accessor;
- if (!session_map->Get(session_id, &session_accessor))
- return Status(kOk);
- scoped_ptr<base::AutoLock> session_lock;
- Session* session = session_accessor->Access(&session_lock);
- if (!session)
- return Status(kOk);
- CHECK(session_map->Remove(session->id));
- if (allow_detach && session->detach) {
- session_accessor->DeleteSession();
- return Status(kOk);
- } else {
- Status status = session->chrome->Quit();
- session_accessor->DeleteSession();
- return status;
+ const CommandCallback& callback) {
+ size_t quit_remaining_count = session_thread_map->size();
+ base::WeakPtrFactory<size_t> weak_ptr_factory(&quit_remaining_count);
+ if (!quit_remaining_count) {
+ callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
+ return;
+ }
+ base::RunLoop run_loop;
+ for (SessionThreadMap::const_iterator iter = session_thread_map->begin();
+ iter != session_thread_map->end();
+ ++iter) {
+ quit_command.Run(params,
+ iter->first,
+ base::Bind(&OnSessionQuit,
+ weak_ptr_factory.GetWeakPtr(),
+ run_loop.QuitClosure()));
+ }
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(10));
+ // Uses a nested run loop to block this thread until all the quit
+ // commands have executed, or the timeout expires.
+ base::MessageLoop::current()->SetNestableTasksAllowed(true);
+ run_loop.Run();
+ callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
+}
+
+namespace {
+
+void TerminateSessionThreadOnCommandThread(SessionThreadMap* session_thread_map,
+ const std::string& session_id) {
+ session_thread_map->erase(session_id);
+}
+
+void ExecuteSessionCommandOnSessionThread(
+ const SessionCommand& command,
+ bool return_ok_without_session,
+ scoped_ptr<base::DictionaryValue> params,
+ scoped_refptr<base::SingleThreadTaskRunner> cmd_task_runner,
+ const CommandCallback& callback_on_cmd,
+ const base::Closure& terminate_on_cmd) {
+ Session* session = lazy_tls_session.Pointer()->Get();
+ if (!session) {
+ cmd_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(callback_on_cmd,
+ Status(return_ok_without_session ? kOk : kNoSuchSession),
+ base::Passed(scoped_ptr<base::Value>()),
+ std::string()));
+ return;
+ }
+
+ scoped_ptr<base::Value> value;
+ Status status = command.Run(session, *params, &value);
+ if (status.IsError() && session->chrome)
+ status.AddDetails("Session info: chrome=" + session->chrome->GetVersion());
+
+ cmd_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(callback_on_cmd, status, base::Passed(&value), session->id));
+
+ if (session->quit) {
+ lazy_tls_session.Pointer()->Set(NULL);
+ delete session;
+ cmd_task_runner->PostTask(FROM_HERE, terminate_on_cmd);
}
}
-Status ExecuteQuitAll(
- Command quit_command,
- SessionMap* session_map,
+} // namespace
+
+void ExecuteSessionCommand(
+ SessionThreadMap* session_thread_map,
+ const SessionCommand& command,
+ bool return_ok_without_session,
const base::DictionaryValue& params,
const std::string& session_id,
- scoped_ptr<base::Value>* out_value,
- std::string* out_session_id) {
- std::vector<std::string> session_ids;
- session_map->GetKeys(&session_ids);
- for (size_t i = 0; i < session_ids.size(); ++i) {
- scoped_ptr<base::Value> unused_value;
- std::string unused_session_id;
- quit_command.Run(params, session_ids[i], &unused_value, &unused_session_id);
+ const CommandCallback& callback) {
+ SessionThreadMap::iterator iter = session_thread_map->find(session_id);
+ if (iter == session_thread_map->end()) {
+ Status status(return_ok_without_session ? kOk : kNoSuchSession);
+ callback.Run(status, scoped_ptr<base::Value>(), session_id);
+ } else {
+ iter->second->message_loop()
+ ->PostTask(FROM_HERE,
+ base::Bind(&ExecuteSessionCommandOnSessionThread,
+ command,
+ return_ok_without_session,
+ base::Passed(make_scoped_ptr(params.DeepCopy())),
+ base::MessageLoopProxy::current(),
+ callback,
+ base::Bind(&TerminateSessionThreadOnCommandThread,
+ session_thread_map,
+ session_id)));
}
- return Status(kOk);
}
+
+namespace internal {
+
+void CreateSessionOnSessionThreadForTesting(const std::string& id) {
+ lazy_tls_session.Pointer()->Set(new Session(id));
+}
+
+} // namespace internal
diff --git a/chrome/test/chromedriver/commands.h b/chrome/test/chromedriver/commands.h
index 2e624e4259..e69764cfa0 100644
--- a/chrome/test/chromedriver/commands.h
+++ b/chrome/test/chromedriver/commands.h
@@ -12,7 +12,7 @@
#include "base/memory/scoped_ptr.h"
#include "chrome/test/chromedriver/command.h"
#include "chrome/test/chromedriver/net/sync_websocket_factory.h"
-#include "chrome/test/chromedriver/session_map.h"
+#include "chrome/test/chromedriver/session_thread_map.h"
namespace base {
class DictionaryValue;
@@ -21,55 +21,63 @@ class Value;
class DeviceManager;
class Log;
+struct Session;
class Status;
class URLRequestContextGetter;
// Gets status/info about ChromeDriver.
-Status ExecuteGetStatus(
+void ExecuteGetStatus(
const base::DictionaryValue& params,
const std::string& session_id,
- scoped_ptr<base::Value>* out_value,
- std::string* out_session_id);
+ const CommandCallback& callback);
struct NewSessionParams {
NewSessionParams(Log* log,
- SessionMap* session_map,
+ SessionThreadMap* session_thread_map,
scoped_refptr<URLRequestContextGetter> context_getter,
const SyncWebSocketFactory& socket_factory,
DeviceManager* device_manager);
~NewSessionParams();
Log* log;
- SessionMap* session_map;
+ SessionThreadMap* session_thread_map;
scoped_refptr<URLRequestContextGetter> context_getter;
SyncWebSocketFactory socket_factory;
DeviceManager* device_manager;
};
// Creates a new session.
-Status ExecuteNewSession(
+void ExecuteNewSession(
const NewSessionParams& bound_params,
const base::DictionaryValue& params,
const std::string& session_id,
- scoped_ptr<base::Value>* out_value,
- std::string* out_session_id);
+ const CommandCallback& callback);
-// Quits a particular session.
-Status ExecuteQuit(
- bool allow_detach,
- SessionMap* session_map,
+// Quits all sessions.
+void ExecuteQuitAll(
+ const Command& quit_command,
+ SessionThreadMap* session_thread_map,
const base::DictionaryValue& params,
const std::string& session_id,
- scoped_ptr<base::Value>* out_value,
- std::string* out_session_id);
+ const CommandCallback& callback);
-// Quits all sessions.
-Status ExecuteQuitAll(
- Command quit_command,
- SessionMap* session_map,
+typedef base::Callback<Status(
+ Session* session,
+ const base::DictionaryValue&,
+ scoped_ptr<base::Value>*)> SessionCommand;
+
+// Executes a given session command, after acquiring access to the appropriate
+// session.
+void ExecuteSessionCommand(
+ SessionThreadMap* session_thread_map,
+ const SessionCommand& command,
+ bool return_ok_without_session,
const base::DictionaryValue& params,
const std::string& session_id,
- scoped_ptr<base::Value>* out_value,
- std::string* out_session_id);
+ const CommandCallback& callback);
+
+namespace internal {
+void CreateSessionOnSessionThreadForTesting(const std::string& id);
+} // namespace internal
#endif // CHROME_TEST_CHROMEDRIVER_COMMANDS_H_
diff --git a/chrome/test/chromedriver/commands_unittest.cc b/chrome/test/chromedriver/commands_unittest.cc
index 5e7ea33b1f..064fb95664 100644
--- a/chrome/test/chromedriver/commands_unittest.cc
+++ b/chrome/test/chromedriver/commands_unittest.cc
@@ -9,7 +9,10 @@
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
#include "base/values.h"
#include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/chrome/stub_chrome.h"
@@ -17,18 +20,18 @@
#include "chrome/test/chromedriver/chrome/web_view.h"
#include "chrome/test/chromedriver/commands.h"
#include "chrome/test/chromedriver/element_commands.h"
-#include "chrome/test/chromedriver/fake_session_accessor.h"
+#include "chrome/test/chromedriver/session.h"
#include "chrome/test/chromedriver/session_commands.h"
#include "chrome/test/chromedriver/window_commands.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/webdriver/atoms.h"
-TEST(CommandsTest, GetStatus) {
- base::DictionaryValue params;
- scoped_ptr<base::Value> value;
- std::string session_id;
- ASSERT_EQ(
- kOk, ExecuteGetStatus(params, std::string(), &value, &session_id).code());
+namespace {
+
+void OnGetStatus(const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id) {
+ ASSERT_EQ(kOk, status.code());
base::DictionaryValue* dict;
ASSERT_TRUE(value->GetAsDictionary(&dict));
base::Value* unused;
@@ -38,145 +41,188 @@ TEST(CommandsTest, GetStatus) {
ASSERT_TRUE(dict->Get("build.version", &unused));
}
+} // namespace
+
+TEST(CommandsTest, GetStatus) {
+ base::DictionaryValue params;
+ ExecuteGetStatus(params, std::string(), base::Bind(&OnGetStatus));
+}
+
namespace {
-Status ExecuteStubQuit(
+void ExecuteStubQuit(
int* count,
const base::DictionaryValue& params,
const std::string& session_id,
- scoped_ptr<base::Value>* value,
- std::string* out_session_id) {
+ const CommandCallback& callback) {
if (*count == 0) {
EXPECT_STREQ("id", session_id.c_str());
} else {
EXPECT_STREQ("id2", session_id.c_str());
}
(*count)++;
- return Status(kOk);
+ callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
+}
+
+void OnQuitAll(const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id) {
+ ASSERT_EQ(kOk, status.code());
+ ASSERT_FALSE(value.get());
}
} // namespace
TEST(CommandsTest, QuitAll) {
- SessionMap map;
+ SessionThreadMap map;
Session session("id");
Session session2("id2");
- map.Set(session.id,
- scoped_refptr<SessionAccessor>(new FakeSessionAccessor(&session)));
- map.Set(session2.id,
- scoped_refptr<SessionAccessor>(new FakeSessionAccessor(&session2)));
+ map[session.id] = make_linked_ptr(new base::Thread("1"));
+ map[session2.id] = make_linked_ptr(new base::Thread("2"));
int count = 0;
Command cmd = base::Bind(&ExecuteStubQuit, &count);
base::DictionaryValue params;
- scoped_ptr<base::Value> value;
- std::string session_id;
- Status status =
- ExecuteQuitAll(cmd, &map, params, std::string(), &value, &session_id);
- ASSERT_EQ(kOk, status.code());
- ASSERT_FALSE(value.get());
+ base::MessageLoop loop;
+ ExecuteQuitAll(cmd, &map, params, std::string(), base::Bind(&OnQuitAll));
ASSERT_EQ(2, count);
}
-TEST(CommandsTest, Quit) {
- SessionMap map;
- Session session("id", scoped_ptr<Chrome>(new StubChrome()));
- scoped_refptr<FakeSessionAccessor> session_accessor(
- new FakeSessionAccessor(&session));
- map.Set(session.id, session_accessor);
+namespace {
+
+Status ExecuteSimpleCommand(
+ const std::string& expected_id,
+ base::DictionaryValue* expected_params,
+ base::Value* value,
+ Session* session,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* return_value) {
+ EXPECT_EQ(expected_id, session->id);
+ EXPECT_TRUE(expected_params->Equals(&params));
+ return_value->reset(value->DeepCopy());
+ session->quit = true;
+ return Status(kOk);
+}
+
+void OnSimpleCommand(base::RunLoop* run_loop,
+ const std::string& expected_session_id,
+ base::Value* expected_value,
+ const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id) {
+ ASSERT_EQ(kOk, status.code());
+ ASSERT_TRUE(expected_value->Equals(value.get()));
+ ASSERT_EQ(expected_session_id, session_id);
+ run_loop->Quit();
+}
+
+} // namespace
+
+TEST(CommandsTest, ExecuteSessionCommand) {
+ SessionThreadMap map;
+ linked_ptr<base::Thread> thread(new base::Thread("1"));
+ ASSERT_TRUE(thread->Start());
+ std::string id("id");
+ thread->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id));
+ map[id] = thread;
+
base::DictionaryValue params;
- scoped_ptr<base::Value> value;
- std::string out_session_id;
- ASSERT_EQ(kOk,
- ExecuteQuit(false, &map, params, session.id, &value,
- &out_session_id).code());
- ASSERT_FALSE(map.Has(session.id));
- ASSERT_TRUE(session_accessor->IsSessionDeleted());
- ASSERT_FALSE(value.get());
+ params.SetInteger("param", 5);
+ base::FundamentalValue expected_value(6);
+ SessionCommand cmd = base::Bind(
+ &ExecuteSimpleCommand, id, &params, &expected_value);
+
+ base::MessageLoop loop;
+ base::RunLoop run_loop;
+ ExecuteSessionCommand(
+ &map,
+ cmd,
+ false,
+ params,
+ id,
+ base::Bind(&OnSimpleCommand, &run_loop, id, &expected_value));
+ run_loop.Run();
}
namespace {
-class DetachChrome : public StubChrome {
- public:
- DetachChrome() : quit_called(false) {}
- virtual ~DetachChrome() {}
-
- bool IsQuitCalled() {
- return quit_called;
- }
+Status ShouldNotBeCalled(
+ Session* session,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value) {
+ EXPECT_TRUE(false);
+ return Status(kOk);
+}
- // Overridden from Chrome:
- virtual Status Quit() OVERRIDE {
- quit_called = true;
- return Status(kOk);
- }
+void OnNoSuchSession(const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id) {
+ EXPECT_EQ(kNoSuchSession, status.code());
+ EXPECT_FALSE(value.get());
+}
- private:
- bool quit_called;
-};
+void OnNoSuchSessionIsOk(const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id) {
+ EXPECT_EQ(kOk, status.code());
+ EXPECT_FALSE(value.get());
+}
} // namespace
-TEST(CommandsTest, QuitWhenDetach) {
- SessionMap map;
- DetachChrome* chrome = new DetachChrome();
- Session session("id", scoped_ptr<Chrome>(chrome));
- session.detach = true;
-
- scoped_refptr<FakeSessionAccessor> session_accessor(
- new FakeSessionAccessor(&session));
+TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSession) {
+ SessionThreadMap map;
base::DictionaryValue params;
- scoped_ptr<base::Value> value;
- std::string out_session_id;
-
- map.Set(session.id, session_accessor);
- ASSERT_EQ(kOk,
- ExecuteQuit(true, &map, params, session.id, &value,
- &out_session_id).code());
- ASSERT_FALSE(map.Has(session.id));
- ASSERT_FALSE(value.get());
- ASSERT_FALSE(chrome->IsQuitCalled());
+ ExecuteSessionCommand(&map,
+ base::Bind(&ShouldNotBeCalled),
+ false,
+ params,
+ "session",
+ base::Bind(&OnNoSuchSession));
+}
- map.Set(session.id, session_accessor);
- ASSERT_EQ(kOk,
- ExecuteQuit(false, &map, params, session.id, &value,
- &out_session_id).code());
- ASSERT_FALSE(map.Has(session.id));
- ASSERT_FALSE(value.get());
- ASSERT_TRUE(chrome->IsQuitCalled());
+TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSessionWhenItExpectsOk) {
+ SessionThreadMap map;
+ base::DictionaryValue params;
+ ExecuteSessionCommand(&map,
+ base::Bind(&ShouldNotBeCalled),
+ true,
+ params,
+ "session",
+ base::Bind(&OnNoSuchSessionIsOk));
}
namespace {
-class FailsToQuitChrome : public StubChrome {
- public:
- FailsToQuitChrome() {}
- virtual ~FailsToQuitChrome() {}
-
- // Overridden from Chrome:
- virtual Status Quit() OVERRIDE {
- return Status(kUnknownError);
- }
-};
+void OnNoSuchSessionAndQuit(base::RunLoop* run_loop,
+ const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id) {
+ run_loop->Quit();
+ EXPECT_EQ(kNoSuchSession, status.code());
+ EXPECT_FALSE(value.get());
+}
} // namespace
-TEST(CommandsTest, QuitFails) {
- SessionMap map;
- Session session("id", scoped_ptr<Chrome>(new FailsToQuitChrome()));
- scoped_refptr<FakeSessionAccessor> session_accessor(
- new FakeSessionAccessor(&session));
- map.Set(session.id, session_accessor);
- base::DictionaryValue params;
- scoped_ptr<base::Value> value;
- std::string out_session_id;
- ASSERT_EQ(kUnknownError,
- ExecuteQuit(false, &map, params, session.id, &value,
- &out_session_id).code());
- ASSERT_FALSE(map.Has(session.id));
- ASSERT_TRUE(session_accessor->IsSessionDeleted());
- ASSERT_FALSE(value.get());
+TEST(CommandsTest, ExecuteSessionCommandOnJustDeletedSession) {
+ SessionThreadMap map;
+ linked_ptr<base::Thread> thread(new base::Thread("1"));
+ ASSERT_TRUE(thread->Start());
+ std::string id("id");
+ map[id] = thread;
+
+ base::MessageLoop loop;
+ base::RunLoop run_loop;
+ ExecuteSessionCommand(&map,
+ base::Bind(&ShouldNotBeCalled),
+ false,
+ base::DictionaryValue(),
+ "session",
+ base::Bind(&OnNoSuchSessionAndQuit, &run_loop));
+ run_loop.Run();
}
namespace {
diff --git a/chrome/test/chromedriver/fake_session_accessor.cc b/chrome/test/chromedriver/fake_session_accessor.cc
deleted file mode 100644
index 2114e224f9..0000000000
--- a/chrome/test/chromedriver/fake_session_accessor.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2012 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/fake_session_accessor.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-FakeSessionAccessor::FakeSessionAccessor(Session* session)
- : session_(session),
- is_accessed_(false),
- is_session_deleted_(false) {}
-
-Session* FakeSessionAccessor::Access(
- scoped_ptr<base::AutoLock>* lock) {
- is_accessed_ = true;
- return session_;
-}
-
-bool FakeSessionAccessor::IsSessionDeleted() const {
- return is_session_deleted_;
-}
-
-void FakeSessionAccessor::DeleteSession() {
- ASSERT_TRUE(is_accessed_);
- is_session_deleted_ = true;
-}
-
-FakeSessionAccessor::~FakeSessionAccessor() {}
diff --git a/chrome/test/chromedriver/fake_session_accessor.h b/chrome/test/chromedriver/fake_session_accessor.h
deleted file mode 100644
index b9b52753ee..0000000000
--- a/chrome/test/chromedriver/fake_session_accessor.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2012 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_FAKE_SESSION_ACCESSOR_H_
-#define CHROME_TEST_CHROMEDRIVER_FAKE_SESSION_ACCESSOR_H_
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "chrome/test/chromedriver/session.h"
-
-namespace base {
-class AutoLock;
-}
-
-// Fake session accessor that doesn't actually lock.
-class FakeSessionAccessor : public SessionAccessor {
- public:
- explicit FakeSessionAccessor(Session* session);
-
- bool IsSessionDeleted() const;
-
- // Overridden from SessionAccessor:
- virtual Session* Access(scoped_ptr<base::AutoLock>* lock) OVERRIDE;
- virtual void DeleteSession() OVERRIDE;
-
- private:
- virtual ~FakeSessionAccessor();
-
- Session* session_;
- bool is_accessed_;
- bool is_session_deleted_;
-};
-
-#endif // CHROME_TEST_CHROMEDRIVER_FAKE_SESSION_ACCESSOR_H_
diff --git a/chrome/test/chromedriver/server/chromedriver_server.cc b/chrome/test/chromedriver/server/chromedriver_server.cc
index 75cc9c2cab..1da71e8213 100644
--- a/chrome/test/chromedriver/server/chromedriver_server.cc
+++ b/chrome/test/chromedriver/server/chromedriver_server.cc
@@ -7,14 +7,18 @@
#include <vector>
#include "base/at_exit.h"
+#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
#include "chrome/test/chromedriver/chrome/log.h"
#include "chrome/test/chromedriver/chrome/version.h"
#include "chrome/test/chromedriver/server/http_handler.h"
@@ -29,6 +33,23 @@
namespace {
+void SendHttpResponse(bool shutdown,
+ const HttpResponseSenderFunc& send_response_func,
+ scoped_ptr<HttpResponse> response) {
+ send_response_func.Run(response.Pass());
+ if (shutdown)
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+void HandleHttpRequest(HttpHandler* handler,
+ const net::HttpServerRequestInfo& request,
+ const HttpResponseSenderFunc& send_response_func) {
+ handler->Handle(request,
+ base::Bind(&SendHttpResponse,
+ handler->ShouldShutdown(request),
+ send_response_func));
+}
+
void ReadRequestBody(const struct mg_request_info* const request_info,
struct mg_connection* const connection,
std::string* request_body) {
@@ -56,11 +77,22 @@ void ReadRequestBody(const struct mg_request_info* const request_info,
}
}
+typedef base::Callback<
+ void(const net::HttpServerRequestInfo&, const HttpResponseSenderFunc&)>
+ HttpRequestHandlerFunc;
+
struct MongooseUserData {
- HttpHandler* handler;
- base::WaitableEvent* shutdown_event;
+ base::SingleThreadTaskRunner* cmd_task_runner;
+ HttpRequestHandlerFunc* handler_func;
};
+void DoneProcessing(base::WaitableEvent* event,
+ scoped_ptr<HttpResponse>* response_to_set,
+ scoped_ptr<HttpResponse> response) {
+ *response_to_set = response.Pass();
+ event->Signal();
+}
+
void* ProcessHttpRequest(mg_event event_raised,
struct mg_connection* connection,
const struct mg_request_info* request_info) {
@@ -74,16 +106,20 @@ void* ProcessHttpRequest(mg_event event_raised,
request.path = request_info->uri;
ReadRequestBody(request_info, connection, &request.data);
- HttpResponse response;
- user_data->handler->Handle(request, &response);
+ base::WaitableEvent event(false, false);
+ scoped_ptr<HttpResponse> response;
+ user_data->cmd_task_runner
+ ->PostTask(FROM_HERE,
+ base::Bind(*user_data->handler_func,
+ request,
+ base::Bind(&DoneProcessing, &event, &response)));
+ event.Wait();
// Don't allow HTTP keep alive.
- response.AddHeader("connection", "close");
+ response->AddHeader("connection", "close");
std::string data;
- response.GetData(&data);
+ response->GetData(&data);
mg_write(connection, data.data(), data.length());
- if (user_data->handler->ShouldShutdown(request))
- user_data->shutdown_event->Signal();
return reinterpret_cast<void*>(true);
}
@@ -103,7 +139,7 @@ void MakeMongooseOptions(const std::string& port,
int main(int argc, char *argv[]) {
CommandLine::Init(argc, argv);
- base::AtExitManager exit;
+ base::AtExitManager at_exit;
CommandLine* cmd_line = CommandLine::ForCurrentProcess();
// Parse command line flags.
@@ -175,10 +211,16 @@ int main(int argc, char *argv[]) {
if (!cmd_line->HasSwitch("verbose"))
logging::SetMinLogLevel(logging::LOG_FATAL);
+ base::Thread io_thread("ChromeDriver IO");
+ CHECK(io_thread.StartWithOptions(
+ base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
+
scoped_ptr<Log> log(new Logger(log_level));
- HttpHandler handler(log.get(), url_base);
- base::WaitableEvent shutdown_event(false, false);
- MongooseUserData user_data = { &handler, &shutdown_event };
+ HttpHandler handler(io_thread.message_loop_proxy(), log.get(), url_base);
+ base::MessageLoop cmd_loop;
+ HttpRequestHandlerFunc handler_func =
+ base::Bind(&HandleHttpRequest, &handler);
+ MongooseUserData user_data = { cmd_loop.message_loop_proxy(), &handler_func };
std::vector<std::string> args;
MakeMongooseOptions(port, http_threads, &args);
@@ -210,8 +252,10 @@ int main(int argc, char *argv[]) {
}
#endif
- // Run until we receive command to shutdown.
- shutdown_event.Wait();
-
- return 0;
+ base::RunLoop cmd_run_loop;
+ cmd_run_loop.Run();
+ // Don't run destructors for objects passed via MongooseUserData,
+ // because ProcessHttpRequest may be accessing them.
+ // TODO(kkania): Fix when switching to net::HttpServer.
+ exit(0);
}
diff --git a/chrome/test/chromedriver/server/http_handler.cc b/chrome/test/chromedriver/server/http_handler.cc
index fb408b6c4f..4699b93b56 100644
--- a/chrome/test/chromedriver/server/http_handler.cc
+++ b/chrome/test/chromedriver/server/http_handler.cc
@@ -23,15 +23,11 @@
#include "chrome/test/chromedriver/chrome/log.h"
#include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/chrome/version.h"
-#include "chrome/test/chromedriver/commands.h"
-#include "chrome/test/chromedriver/element_commands.h"
#include "chrome/test/chromedriver/net/url_request_context_getter.h"
#include "chrome/test/chromedriver/server/http_response.h"
#include "chrome/test/chromedriver/session.h"
-#include "chrome/test/chromedriver/session_commands.h"
-#include "chrome/test/chromedriver/session_map.h"
+#include "chrome/test/chromedriver/session_thread_map.h"
#include "chrome/test/chromedriver/util.h"
-#include "chrome/test/chromedriver/window_commands.h"
#include "net/server/http_server_request_info.h"
#if defined(OS_MACOSX)
@@ -44,12 +40,11 @@ const char kLocalStorage[] = "localStorage";
const char kSessionStorage[] = "sessionStorage";
const char kShutdownPath[] = "shutdown";
-Status UnimplementedCommand(
+void UnimplementedCommand(
const base::DictionaryValue& params,
const std::string& session_id,
- scoped_ptr<base::Value>* value,
- std::string* out_session_id) {
- return Status(kUnknownCommand);
+ const CommandCallback& callback) {
+ callback.Run(Status(kUnknownCommand), scoped_ptr<base::Value>(), session_id);
}
} // namespace
@@ -63,294 +58,411 @@ CommandMapping::~CommandMapping() {}
HttpHandler::HttpHandler(Log* log, const std::string& url_base)
: log_(log),
- io_thread_("ChromeDriver IO"),
- url_base_(url_base) {
+ url_base_(url_base),
+ received_shutdown_(false),
+ command_map_(new CommandMap()),
+ weak_ptr_factory_(this) {}
+
+HttpHandler::HttpHandler(
+ const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ Log* log,
+ const std::string& url_base)
+ : log_(log),
+ url_base_(url_base),
+ received_shutdown_(false),
+ weak_ptr_factory_(this) {
#if defined(OS_MACOSX)
base::mac::ScopedNSAutoreleasePool autorelease_pool;
#endif
- base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
- CHECK(io_thread_.StartWithOptions(options));
- context_getter_ = new URLRequestContextGetter(
- io_thread_.message_loop_proxy());
+ context_getter_ = new URLRequestContextGetter(io_task_runner);
socket_factory_ = CreateSyncWebSocketFactory(context_getter_.get());
- adb_.reset(new AdbImpl(io_thread_.message_loop_proxy(), log_));
+ adb_.reset(new AdbImpl(io_task_runner, log_));
device_manager_.reset(new DeviceManager(adb_.get()));
CommandMapping commands[] = {
- CommandMapping(kPost, internal::kNewSessionPathPattern,
+ CommandMapping(kPost,
+ internal::kNewSessionPathPattern,
base::Bind(&ExecuteNewSession,
- NewSessionParams(log_, &session_map_,
- context_getter_, socket_factory_,
- device_manager_.get()))),
- CommandMapping(kGet, "session/:sessionId",
- WrapToCommand(
- base::Bind(&ExecuteGetSessionCapabilities,
- &session_map_))),
- CommandMapping(kDelete, "session/:sessionId",
- base::Bind(&ExecuteQuit, false, &session_map_)),
- CommandMapping(kGet, "session/:sessionId/window_handle",
+ NewSessionParams(log_,
+ &session_thread_map_,
+ context_getter_,
+ socket_factory_,
+ device_manager_.get()))),
+ CommandMapping(kGet,
+ "session/:sessionId",
+ WrapToCommand(base::Bind(&ExecuteGetSessionCapabilities))),
+ CommandMapping(kDelete,
+ "session/:sessionId",
+ base::Bind(&ExecuteSessionCommand,
+ &session_thread_map_,
+ base::Bind(&ExecuteQuit, false),
+ true)),
+ CommandMapping(kGet,
+ "session/:sessionId/window_handle",
WrapToCommand(base::Bind(&ExecuteGetCurrentWindowHandle))),
- CommandMapping(kGet, "session/:sessionId/window_handles",
+ CommandMapping(kGet,
+ "session/:sessionId/window_handles",
WrapToCommand(base::Bind(&ExecuteGetWindowHandles))),
- CommandMapping(kPost, "session/:sessionId/url",
+ CommandMapping(kPost,
+ "session/:sessionId/url",
WrapToCommand(base::Bind(&ExecuteGet))),
- CommandMapping(kGet, "session/:sessionId/alert",
- WrapToCommand(
- base::Bind(&ExecuteAlertCommand,
- base::Bind(&ExecuteGetAlert)))),
- CommandMapping(kPost, "session/:sessionId/dismiss_alert",
- WrapToCommand(
- base::Bind(&ExecuteAlertCommand,
- base::Bind(&ExecuteDismissAlert)))),
- CommandMapping(kPost, "session/:sessionId/accept_alert",
- WrapToCommand(
- base::Bind(&ExecuteAlertCommand,
- base::Bind(&ExecuteAcceptAlert)))),
- CommandMapping(kGet, "session/:sessionId/alert_text",
- WrapToCommand(
- base::Bind(&ExecuteAlertCommand,
- base::Bind(&ExecuteGetAlertText)))),
- CommandMapping(kPost, "session/:sessionId/alert_text",
- WrapToCommand(
- base::Bind(&ExecuteAlertCommand,
- base::Bind(&ExecuteSetAlertValue)))),
- CommandMapping(kPost, "session/:sessionId/forward",
+ CommandMapping(kGet,
+ "session/:sessionId/alert",
+ WrapToCommand(base::Bind(&ExecuteAlertCommand,
+ base::Bind(&ExecuteGetAlert)))),
+ CommandMapping(
+ kPost,
+ "session/:sessionId/dismiss_alert",
+ WrapToCommand(base::Bind(&ExecuteAlertCommand,
+ base::Bind(&ExecuteDismissAlert)))),
+ CommandMapping(
+ kPost,
+ "session/:sessionId/accept_alert",
+ WrapToCommand(base::Bind(&ExecuteAlertCommand,
+ base::Bind(&ExecuteAcceptAlert)))),
+ CommandMapping(
+ kGet,
+ "session/:sessionId/alert_text",
+ WrapToCommand(base::Bind(&ExecuteAlertCommand,
+ base::Bind(&ExecuteGetAlertText)))),
+ CommandMapping(
+ kPost,
+ "session/:sessionId/alert_text",
+ WrapToCommand(base::Bind(&ExecuteAlertCommand,
+ base::Bind(&ExecuteSetAlertValue)))),
+ CommandMapping(kPost,
+ "session/:sessionId/forward",
WrapToCommand(base::Bind(&ExecuteGoForward))),
- CommandMapping(kPost, "session/:sessionId/back",
+ CommandMapping(kPost,
+ "session/:sessionId/back",
WrapToCommand(base::Bind(&ExecuteGoBack))),
- CommandMapping(kPost, "session/:sessionId/refresh",
+ CommandMapping(kPost,
+ "session/:sessionId/refresh",
WrapToCommand(base::Bind(&ExecuteRefresh))),
- CommandMapping(kPost, "session/:sessionId/execute",
+ CommandMapping(kPost,
+ "session/:sessionId/execute",
WrapToCommand(base::Bind(&ExecuteExecuteScript))),
- CommandMapping(kPost, "session/:sessionId/execute_async",
+ CommandMapping(kPost,
+ "session/:sessionId/execute_async",
WrapToCommand(base::Bind(&ExecuteExecuteAsyncScript))),
- CommandMapping(kGet, "session/:sessionId/url",
+ CommandMapping(kGet,
+ "session/:sessionId/url",
WrapToCommand(base::Bind(&ExecuteGetCurrentUrl))),
- CommandMapping(kGet, "session/:sessionId/title",
+ CommandMapping(kGet,
+ "session/:sessionId/title",
WrapToCommand(base::Bind(&ExecuteGetTitle))),
- CommandMapping(kGet, "session/:sessionId/source",
+ CommandMapping(kGet,
+ "session/:sessionId/source",
WrapToCommand(base::Bind(&ExecuteGetPageSource))),
- CommandMapping(kGet, "session/:sessionId/screenshot",
+ CommandMapping(kGet,
+ "session/:sessionId/screenshot",
WrapToCommand(base::Bind(&ExecuteScreenshot))),
- CommandMapping(kPost, "session/:sessionId/visible",
+ CommandMapping(kPost,
+ "session/:sessionId/visible",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kGet, "session/:sessionId/visible",
+ CommandMapping(kGet,
+ "session/:sessionId/visible",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/element",
+ CommandMapping(kPost,
+ "session/:sessionId/element",
WrapToCommand(base::Bind(&ExecuteFindElement, 50))),
- CommandMapping(kPost, "session/:sessionId/elements",
+ CommandMapping(kPost,
+ "session/:sessionId/elements",
WrapToCommand(base::Bind(&ExecuteFindElements, 50))),
- CommandMapping(kPost, "session/:sessionId/element/active",
+ CommandMapping(kPost,
+ "session/:sessionId/element/active",
WrapToCommand(base::Bind(&ExecuteGetActiveElement))),
- CommandMapping(kPost, "session/:sessionId/element/:id/element",
+ CommandMapping(kPost,
+ "session/:sessionId/element/:id/element",
WrapToCommand(base::Bind(&ExecuteFindChildElement, 50))),
- CommandMapping(kPost, "session/:sessionId/element/:id/elements",
+ CommandMapping(kPost,
+ "session/:sessionId/element/:id/elements",
WrapToCommand(base::Bind(&ExecuteFindChildElements, 50))),
- CommandMapping(kPost, "session/:sessionId/element/:id/click",
+ CommandMapping(kPost,
+ "session/:sessionId/element/:id/click",
WrapToCommand(base::Bind(&ExecuteClickElement))),
- CommandMapping(kPost, "session/:sessionId/element/:id/clear",
+ CommandMapping(kPost,
+ "session/:sessionId/element/:id/clear",
WrapToCommand(base::Bind(&ExecuteClearElement))),
- CommandMapping(kPost, "session/:sessionId/element/:id/submit",
+ CommandMapping(kPost,
+ "session/:sessionId/element/:id/submit",
WrapToCommand(base::Bind(&ExecuteSubmitElement))),
- CommandMapping(kGet, "session/:sessionId/element/:id/text",
+ CommandMapping(kGet,
+ "session/:sessionId/element/:id/text",
WrapToCommand(base::Bind(&ExecuteGetElementText))),
- CommandMapping(kPost, "session/:sessionId/element/:id/value",
+ CommandMapping(kPost,
+ "session/:sessionId/element/:id/value",
WrapToCommand(base::Bind(&ExecuteSendKeysToElement))),
- CommandMapping(kPost, "session/:sessionId/file",
+ CommandMapping(kPost,
+ "session/:sessionId/file",
WrapToCommand(base::Bind(&ExecuteUploadFile))),
- CommandMapping(kGet, "session/:sessionId/element/:id/value",
+ CommandMapping(kGet,
+ "session/:sessionId/element/:id/value",
WrapToCommand(base::Bind(&ExecuteGetElementValue))),
- CommandMapping(kGet, "session/:sessionId/element/:id/name",
+ CommandMapping(kGet,
+ "session/:sessionId/element/:id/name",
WrapToCommand(base::Bind(&ExecuteGetElementTagName))),
- CommandMapping(kGet, "session/:sessionId/element/:id/selected",
+ CommandMapping(kGet,
+ "session/:sessionId/element/:id/selected",
WrapToCommand(base::Bind(&ExecuteIsElementSelected))),
- CommandMapping(kGet, "session/:sessionId/element/:id/enabled",
+ CommandMapping(kGet,
+ "session/:sessionId/element/:id/enabled",
WrapToCommand(base::Bind(&ExecuteIsElementEnabled))),
- CommandMapping(kGet, "session/:sessionId/element/:id/displayed",
+ CommandMapping(kGet,
+ "session/:sessionId/element/:id/displayed",
WrapToCommand(base::Bind(&ExecuteIsElementDisplayed))),
- CommandMapping(kPost, "session/:sessionId/element/:id/hover",
+ CommandMapping(kPost,
+ "session/:sessionId/element/:id/hover",
WrapToCommand(base::Bind(&ExecuteHoverOverElement))),
- CommandMapping(kGet, "session/:sessionId/element/:id/location",
+ CommandMapping(kGet,
+ "session/:sessionId/element/:id/location",
WrapToCommand(base::Bind(&ExecuteGetElementLocation))),
- CommandMapping(kGet, "session/:sessionId/element/:id/location_in_view",
- WrapToCommand(
- base::Bind(
- &ExecuteGetElementLocationOnceScrolledIntoView))),
- CommandMapping(kGet, "session/:sessionId/element/:id/size",
+ CommandMapping(kGet,
+ "session/:sessionId/element/:id/location_in_view",
+ WrapToCommand(base::Bind(
+ &ExecuteGetElementLocationOnceScrolledIntoView))),
+ CommandMapping(kGet,
+ "session/:sessionId/element/:id/size",
WrapToCommand(base::Bind(&ExecuteGetElementSize))),
- CommandMapping(kGet, "session/:sessionId/element/:id/attribute/:name",
+ CommandMapping(kGet,
+ "session/:sessionId/element/:id/attribute/:name",
WrapToCommand(base::Bind(&ExecuteGetElementAttribute))),
- CommandMapping(kGet, "session/:sessionId/element/:id/equals/:other",
+ CommandMapping(kGet,
+ "session/:sessionId/element/:id/equals/:other",
WrapToCommand(base::Bind(&ExecuteElementEquals))),
- CommandMapping(kGet, "session/:sessionId/cookie",
+ CommandMapping(kGet,
+ "session/:sessionId/cookie",
WrapToCommand(base::Bind(&ExecuteGetCookies))),
- CommandMapping(kPost, "session/:sessionId/cookie",
+ CommandMapping(kPost,
+ "session/:sessionId/cookie",
WrapToCommand(base::Bind(&ExecuteAddCookie))),
- CommandMapping(kDelete, "session/:sessionId/cookie",
+ CommandMapping(kDelete,
+ "session/:sessionId/cookie",
WrapToCommand(base::Bind(&ExecuteDeleteAllCookies))),
- CommandMapping(kDelete, "session/:sessionId/cookie/:name",
+ CommandMapping(kDelete,
+ "session/:sessionId/cookie/:name",
WrapToCommand(base::Bind(&ExecuteDeleteCookie))),
- CommandMapping(kPost, "session/:sessionId/frame",
+ CommandMapping(kPost,
+ "session/:sessionId/frame",
WrapToCommand(base::Bind(&ExecuteSwitchToFrame))),
- CommandMapping(kPost, "session/:sessionId/window",
+ CommandMapping(kPost,
+ "session/:sessionId/window",
WrapToCommand(base::Bind(&ExecuteSwitchToWindow))),
- CommandMapping(kGet, "session/:sessionId/window/:windowHandle/size",
+ CommandMapping(kGet,
+ "session/:sessionId/window/:windowHandle/size",
WrapToCommand(base::Bind(&ExecuteGetWindowSize))),
- CommandMapping(kGet, "session/:sessionId/window/:windowHandle/position",
+ CommandMapping(kGet,
+ "session/:sessionId/window/:windowHandle/position",
WrapToCommand(base::Bind(&ExecuteGetWindowPosition))),
- CommandMapping(kPost, "session/:sessionId/window/:windowHandle/size",
+ CommandMapping(kPost,
+ "session/:sessionId/window/:windowHandle/size",
WrapToCommand(base::Bind(&ExecuteSetWindowSize))),
- CommandMapping(kPost, "session/:sessionId/window/:windowHandle/position",
+ CommandMapping(kPost,
+ "session/:sessionId/window/:windowHandle/position",
WrapToCommand(base::Bind(&ExecuteSetWindowPosition))),
- CommandMapping(kPost, "session/:sessionId/window/:windowHandle/maximize",
+ CommandMapping(kPost,
+ "session/:sessionId/window/:windowHandle/maximize",
WrapToCommand(base::Bind(&ExecuteMaximizeWindow))),
- CommandMapping(kDelete, "session/:sessionId/window",
- WrapToCommand(base::Bind(&ExecuteClose, &session_map_))),
- CommandMapping(kPost, "session/:sessionId/element/:id/drag",
+ CommandMapping(kDelete,
+ "session/:sessionId/window",
+ WrapToCommand(base::Bind(&ExecuteClose))),
+ CommandMapping(kPost,
+ "session/:sessionId/element/:id/drag",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kGet, "session/:sessionId/element/:id/css/:propertyName",
- WrapToCommand(
- base::Bind(&ExecuteGetElementValueOfCSSProperty))),
- CommandMapping(kPost, "session/:sessionId/timeouts/implicit_wait",
+ CommandMapping(
+ kGet,
+ "session/:sessionId/element/:id/css/:propertyName",
+ WrapToCommand(base::Bind(&ExecuteGetElementValueOfCSSProperty))),
+ CommandMapping(kPost,
+ "session/:sessionId/timeouts/implicit_wait",
WrapToCommand(base::Bind(&ExecuteImplicitlyWait))),
- CommandMapping(kPost, "session/:sessionId/timeouts/async_script",
+ CommandMapping(kPost,
+ "session/:sessionId/timeouts/async_script",
WrapToCommand(base::Bind(&ExecuteSetScriptTimeout))),
- CommandMapping(kPost, "session/:sessionId/timeouts",
+ CommandMapping(kPost,
+ "session/:sessionId/timeouts",
WrapToCommand(base::Bind(&ExecuteSetTimeout))),
- CommandMapping(kPost, "session/:sessionId/execute_sql",
+ CommandMapping(kPost,
+ "session/:sessionId/execute_sql",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kGet, "session/:sessionId/location",
+ CommandMapping(kGet,
+ "session/:sessionId/location",
WrapToCommand(base::Bind(&ExecuteGetLocation))),
- CommandMapping(kPost, "session/:sessionId/location",
+ CommandMapping(kPost,
+ "session/:sessionId/location",
WrapToCommand(base::Bind(&ExecuteSetLocation))),
- CommandMapping(kGet, "session/:sessionId/application_cache/status",
+ CommandMapping(kGet,
+ "session/:sessionId/application_cache/status",
base::Bind(&ExecuteGetStatus)),
- CommandMapping(kGet, "session/:sessionId/browser_connection",
+ CommandMapping(kGet,
+ "session/:sessionId/browser_connection",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/browser_connection",
+ CommandMapping(kPost,
+ "session/:sessionId/browser_connection",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kGet, "session/:sessionId/local_storage/key/:key",
- WrapToCommand(
- base::Bind(&ExecuteGetStorageItem, kLocalStorage))),
- CommandMapping(kDelete, "session/:sessionId/local_storage/key/:key",
- WrapToCommand(
- base::Bind(&ExecuteRemoveStorageItem, kLocalStorage))),
- CommandMapping(kGet, "session/:sessionId/local_storage",
- WrapToCommand(
- base::Bind(&ExecuteGetStorageKeys, kLocalStorage))),
- CommandMapping(kPost, "session/:sessionId/local_storage",
- WrapToCommand(
- base::Bind(&ExecuteSetStorageItem, kLocalStorage))),
- CommandMapping(kDelete, "session/:sessionId/local_storage",
- WrapToCommand(
- base::Bind(&ExecuteClearStorage, kLocalStorage))),
- CommandMapping(kGet, "session/:sessionId/local_storage/size",
- WrapToCommand(
- base::Bind(&ExecuteGetStorageSize, kLocalStorage))),
- CommandMapping(kGet, "session/:sessionId/session_storage/key/:key",
- WrapToCommand(
- base::Bind(&ExecuteGetStorageItem, kSessionStorage))),
- CommandMapping(kDelete, "session/:sessionId/session_storage/key/:key",
- WrapToCommand(
- base::Bind(
- &ExecuteRemoveStorageItem, kSessionStorage))),
- CommandMapping(kGet, "session/:sessionId/session_storage",
- WrapToCommand(
- base::Bind(&ExecuteGetStorageKeys, kSessionStorage))),
- CommandMapping(kPost, "session/:sessionId/session_storage",
- WrapToCommand(
- base::Bind(&ExecuteSetStorageItem, kSessionStorage))),
- CommandMapping(kDelete, "session/:sessionId/session_storage",
- WrapToCommand(
- base::Bind(&ExecuteClearStorage, kSessionStorage))),
- CommandMapping(kGet, "session/:sessionId/session_storage/size",
- WrapToCommand(
- base::Bind(&ExecuteGetStorageSize, kSessionStorage))),
- CommandMapping(kGet, "session/:sessionId/orientation",
+ CommandMapping(
+ kGet,
+ "session/:sessionId/local_storage/key/:key",
+ WrapToCommand(base::Bind(&ExecuteGetStorageItem, kLocalStorage))),
+ CommandMapping(
+ kDelete,
+ "session/:sessionId/local_storage/key/:key",
+ WrapToCommand(base::Bind(&ExecuteRemoveStorageItem, kLocalStorage))),
+ CommandMapping(
+ kGet,
+ "session/:sessionId/local_storage",
+ WrapToCommand(base::Bind(&ExecuteGetStorageKeys, kLocalStorage))),
+ CommandMapping(
+ kPost,
+ "session/:sessionId/local_storage",
+ WrapToCommand(base::Bind(&ExecuteSetStorageItem, kLocalStorage))),
+ CommandMapping(
+ kDelete,
+ "session/:sessionId/local_storage",
+ WrapToCommand(base::Bind(&ExecuteClearStorage, kLocalStorage))),
+ CommandMapping(
+ kGet,
+ "session/:sessionId/local_storage/size",
+ WrapToCommand(base::Bind(&ExecuteGetStorageSize, kLocalStorage))),
+ CommandMapping(
+ kGet,
+ "session/:sessionId/session_storage/key/:key",
+ WrapToCommand(base::Bind(&ExecuteGetStorageItem, kSessionStorage))),
+ CommandMapping(kDelete,
+ "session/:sessionId/session_storage/key/:key",
+ WrapToCommand(base::Bind(&ExecuteRemoveStorageItem,
+ kSessionStorage))),
+ CommandMapping(
+ kGet,
+ "session/:sessionId/session_storage",
+ WrapToCommand(base::Bind(&ExecuteGetStorageKeys, kSessionStorage))),
+ CommandMapping(
+ kPost,
+ "session/:sessionId/session_storage",
+ WrapToCommand(base::Bind(&ExecuteSetStorageItem, kSessionStorage))),
+ CommandMapping(
+ kDelete,
+ "session/:sessionId/session_storage",
+ WrapToCommand(base::Bind(&ExecuteClearStorage, kSessionStorage))),
+ CommandMapping(
+ kGet,
+ "session/:sessionId/session_storage/size",
+ WrapToCommand(base::Bind(&ExecuteGetStorageSize, kSessionStorage))),
+ CommandMapping(kGet,
+ "session/:sessionId/orientation",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/orientation",
+ CommandMapping(kPost,
+ "session/:sessionId/orientation",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/click",
+ CommandMapping(kPost,
+ "session/:sessionId/click",
WrapToCommand(base::Bind(&ExecuteMouseClick))),
- CommandMapping(kPost, "session/:sessionId/doubleclick",
+ CommandMapping(kPost,
+ "session/:sessionId/doubleclick",
WrapToCommand(base::Bind(&ExecuteMouseDoubleClick))),
- CommandMapping(kPost, "session/:sessionId/buttondown",
+ CommandMapping(kPost,
+ "session/:sessionId/buttondown",
WrapToCommand(base::Bind(&ExecuteMouseButtonDown))),
- CommandMapping(kPost, "session/:sessionId/buttonup",
+ CommandMapping(kPost,
+ "session/:sessionId/buttonup",
WrapToCommand(base::Bind(&ExecuteMouseButtonUp))),
- CommandMapping(kPost, "session/:sessionId/moveto",
+ CommandMapping(kPost,
+ "session/:sessionId/moveto",
WrapToCommand(base::Bind(&ExecuteMouseMoveTo))),
- CommandMapping(kPost, "session/:sessionId/keys",
- WrapToCommand(
- base::Bind(&ExecuteSendKeysToActiveElement))),
- CommandMapping(kGet, "session/:sessionId/ime/available_engines",
+ CommandMapping(
+ kPost,
+ "session/:sessionId/keys",
+ WrapToCommand(base::Bind(&ExecuteSendKeysToActiveElement))),
+ CommandMapping(kGet,
+ "session/:sessionId/ime/available_engines",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kGet, "session/:sessionId/ime/active_engine",
+ CommandMapping(kGet,
+ "session/:sessionId/ime/active_engine",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kGet, "session/:sessionId/ime/activated",
+ CommandMapping(kGet,
+ "session/:sessionId/ime/activated",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/ime/deactivate",
+ CommandMapping(kPost,
+ "session/:sessionId/ime/deactivate",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/ime/activate",
+ CommandMapping(kPost,
+ "session/:sessionId/ime/activate",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/touch/click",
+ CommandMapping(kPost,
+ "session/:sessionId/touch/click",
WrapToCommand(base::Bind(&ExecuteTouchSingleTap))),
- CommandMapping(kPost, "session/:sessionId/touch/down",
+ CommandMapping(kPost,
+ "session/:sessionId/touch/down",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/touch/up",
+ CommandMapping(kPost,
+ "session/:sessionId/touch/up",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/touch/move",
+ CommandMapping(kPost,
+ "session/:sessionId/touch/move",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/touch/scroll",
+ CommandMapping(kPost,
+ "session/:sessionId/touch/scroll",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/touch/doubleclick",
+ CommandMapping(kPost,
+ "session/:sessionId/touch/doubleclick",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/touch/longclick",
+ CommandMapping(kPost,
+ "session/:sessionId/touch/longclick",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/touch/flick",
+ CommandMapping(kPost,
+ "session/:sessionId/touch/flick",
base::Bind(&UnimplementedCommand)),
- CommandMapping(kPost, "session/:sessionId/log",
+ CommandMapping(kPost,
+ "session/:sessionId/log",
WrapToCommand(base::Bind(&ExecuteGetLog))),
- CommandMapping(kGet, "session/:sessionId/log/types",
+ CommandMapping(kGet,
+ "session/:sessionId/log/types",
WrapToCommand(base::Bind(&ExecuteGetAvailableLogTypes))),
CommandMapping(kPost, "logs", base::Bind(&UnimplementedCommand)),
CommandMapping(kGet, "status", base::Bind(&ExecuteGetStatus)),
// Custom Chrome commands:
// Allow quit all to be called with GET or POST.
- CommandMapping(kGet, kShutdownPath,
+ CommandMapping(kGet,
+ kShutdownPath,
base::Bind(&ExecuteQuitAll,
- base::Bind(&ExecuteQuit, true, &session_map_),
- &session_map_)),
- CommandMapping(kPost, kShutdownPath,
+ WrapToCommand(base::Bind(&ExecuteQuit, true)),
+ &session_thread_map_)),
+ CommandMapping(kPost,
+ kShutdownPath,
base::Bind(&ExecuteQuitAll,
- base::Bind(&ExecuteQuit, true, &session_map_),
- &session_map_)),
- CommandMapping(kGet, "session/:sessionId/is_loading",
- WrapToCommand(base::Bind(&ExecuteIsLoading))),
- };
- this->command_map_.reset(
+ WrapToCommand(base::Bind(&ExecuteQuit, true)),
+ &session_thread_map_)),
+ CommandMapping(kGet,
+ "session/:sessionId/is_loading",
+ WrapToCommand(base::Bind(&ExecuteIsLoading))), };
+ command_map_.reset(
new CommandMap(commands, commands + arraysize(commands)));
}
HttpHandler::~HttpHandler() {}
void HttpHandler::Handle(const net::HttpServerRequestInfo& request,
- HttpResponse* response) {
- log_->AddEntry(
- Log::kLog,
- base::StringPrintf("received WebDriver request: %s %s %s",
- request.method.c_str(),
- request.path.c_str(),
- request.data.c_str()));
-
- HandleInternal(request, response);
-
- log_->AddEntry(
- Log::kLog,
- base::StringPrintf("sending WebDriver response: %d %s",
- response->status(),
- response->body().c_str()));
+ const HttpResponseSenderFunc& send_response_func) {
+ CHECK(thread_checker_.CalledOnValidThread());
+
+ if (received_shutdown_)
+ return;
+ if (ShouldShutdown(request))
+ received_shutdown_ = true;
+
+ std::string path = request.path;
+ if (!StartsWithASCII(path, url_base_, true)) {
+ scoped_ptr<HttpResponse> response(
+ new HttpResponse(HttpResponse::kBadRequest));
+ response->set_body("unhandled request");
+ send_response_func.Run(response.Pass());
+ return;
+ }
+
+ path.erase(0, url_base_.length());
+
+ HandleCommand(request, path, send_response_func);
}
bool HttpHandler::ShouldShutdown(const net::HttpServerRequestInfo& request) {
@@ -359,7 +471,8 @@ bool HttpHandler::ShouldShutdown(const net::HttpServerRequestInfo& request) {
Command HttpHandler::WrapToCommand(
const SessionCommand& session_command) {
- return base::Bind(&ExecuteSessionCommand, &session_map_, session_command);
+ return base::Bind(
+ &ExecuteSessionCommand, &session_thread_map_, session_command, false);
}
Command HttpHandler::WrapToCommand(
@@ -374,34 +487,26 @@ Command HttpHandler::WrapToCommand(
base::Bind(&ExecuteElementCommand, element_command));
}
-void HttpHandler::HandleInternal(const net::HttpServerRequestInfo& request,
- HttpResponse* response) {
- std::string path = request.path;
- if (!StartsWithASCII(path, url_base_, true)) {
- *response = HttpResponse(HttpResponse::kBadRequest);
- response->set_body("unhandled request");
- return;
- }
-
- path.erase(0, url_base_.length());
-
- if (!HandleWebDriverCommand(request, path, response)) {
- *response = HttpResponse(HttpResponse::kNotFound);
- response->set_body("unknown command: " + path);
- return;
- }
-}
-
-bool HttpHandler::HandleWebDriverCommand(
+void HttpHandler::HandleCommand(
const net::HttpServerRequestInfo& request,
const std::string& trimmed_path,
- HttpResponse* response) {
+ const HttpResponseSenderFunc& send_response_func) {
+ log_->AddEntry(Log::kLog,
+ base::StringPrintf("handling command: %s %s %s",
+ request.method.c_str(),
+ trimmed_path.c_str(),
+ request.data.c_str()));
+
base::DictionaryValue params;
std::string session_id;
CommandMap::const_iterator iter = command_map_->begin();
while (true) {
if (iter == command_map_->end()) {
- return false;
+ scoped_ptr<HttpResponse> response(
+ new HttpResponse(HttpResponse::kNotFound));
+ response->set_body("unknown command: " + trimmed_path);
+ send_response_func.Run(response.Pass());
+ return;
}
if (internal::MatchesCommand(
request.method, trimmed_path, *iter, &session_id, &params)) {
@@ -414,40 +519,69 @@ bool HttpHandler::HandleWebDriverCommand(
base::DictionaryValue* body_params;
scoped_ptr<base::Value> parsed_body(base::JSONReader::Read(request.data));
if (!parsed_body || !parsed_body->GetAsDictionary(&body_params)) {
- *response = HttpResponse(HttpResponse::kBadRequest);
+ scoped_ptr<HttpResponse> response(
+ new HttpResponse(HttpResponse::kBadRequest));
response->set_body("missing command parameters");
- return true;
+ send_response_func.Run(response.Pass());
+ return;
}
params.MergeDictionary(body_params);
}
- scoped_ptr<base::Value> value;
- std::string out_session_id;
- Status status = iter->command.Run(
- params, session_id, &value, &out_session_id);
+ iter->command.Run(params,
+ session_id,
+ base::Bind(&HttpHandler::PrepareResponse,
+ weak_ptr_factory_.GetWeakPtr(),
+ trimmed_path,
+ send_response_func));
+}
+
+void HttpHandler::PrepareResponse(
+ const std::string& trimmed_path,
+ const HttpResponseSenderFunc& send_response_func,
+ const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id) {
+ CHECK(thread_checker_.CalledOnValidThread());
+ scoped_ptr<HttpResponse> response =
+ PrepareResponseHelper(trimmed_path, status, value.Pass(), session_id);
+ log_->AddEntry(Log::kLog,
+ base::StringPrintf("sending response: %d %s",
+ response->status(),
+ response->body().c_str()));
+ send_response_func.Run(response.Pass());
+}
+scoped_ptr<HttpResponse> HttpHandler::PrepareResponseHelper(
+ const std::string& trimmed_path,
+ const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id) {
if (status.code() == kUnknownCommand) {
- *response = HttpResponse(HttpResponse::kNotImplemented);
+ scoped_ptr<HttpResponse> response(
+ new HttpResponse(HttpResponse::kNotImplemented));
response->set_body("unimplemented command: " + trimmed_path);
- return true;
+ return response.Pass();
}
- if (iter->path_pattern == internal::kNewSessionPathPattern && status.IsOk()) {
+ if (trimmed_path == internal::kNewSessionPathPattern && status.IsOk()) {
// Creating a session involves a HTTP request to /session, which is
// supposed to redirect to /session/:sessionId, which returns the
// session info.
- *response = HttpResponse(HttpResponse::kSeeOther);
- response->AddHeader("Location", url_base_ + "session/" + out_session_id);
- return true;
+ scoped_ptr<HttpResponse> response(
+ new HttpResponse(HttpResponse::kSeeOther));
+ response->AddHeader("Location", url_base_ + "session/" + session_id);
+ return response.Pass();
} else if (status.IsError()) {
- status.AddDetails(base::StringPrintf(
+ Status full_status(status);
+ full_status.AddDetails(base::StringPrintf(
"Driver info: chromedriver=%s,platform=%s %s %s",
kChromeDriverVersion,
base::SysInfo::OperatingSystemName().c_str(),
base::SysInfo::OperatingSystemVersion().c_str(),
base::SysInfo::OperatingSystemArchitecture().c_str()));
scoped_ptr<base::DictionaryValue> error(new base::DictionaryValue());
- error->SetString("message", status.message());
+ error->SetString("message", full_status.message());
value.reset(error.release());
}
if (!value)
@@ -456,15 +590,15 @@ bool HttpHandler::HandleWebDriverCommand(
base::DictionaryValue body_params;
body_params.SetInteger("status", status.code());
body_params.Set("value", value.release());
- body_params.SetString("sessionId", out_session_id);
+ body_params.SetString("sessionId", session_id);
std::string body;
base::JSONWriter::WriteWithOptions(
&body_params, base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION,
&body);
- *response = HttpResponse(HttpResponse::kOk);
+ scoped_ptr<HttpResponse> response(new HttpResponse(HttpResponse::kOk));
response->SetMimeType("application/json; charset=utf-8");
response->set_body(body);
- return true;
+ return response.Pass();
}
namespace internal {
diff --git a/chrome/test/chromedriver/server/http_handler.h b/chrome/test/chromedriver/server/http_handler.h
index d40fab8dc5..3ad42a12b5 100644
--- a/chrome/test/chromedriver/server/http_handler.h
+++ b/chrome/test/chromedriver/server/http_handler.h
@@ -10,17 +10,21 @@
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "base/threading/thread.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
#include "chrome/test/chromedriver/command.h"
+#include "chrome/test/chromedriver/commands.h"
#include "chrome/test/chromedriver/element_commands.h"
#include "chrome/test/chromedriver/net/sync_websocket_factory.h"
#include "chrome/test/chromedriver/session_commands.h"
-#include "chrome/test/chromedriver/session_map.h"
+#include "chrome/test/chromedriver/session_thread_map.h"
#include "chrome/test/chromedriver/window_commands.h"
namespace base {
class DictionaryValue;
+class SingleThreadTaskRunner;
}
namespace net {
@@ -50,13 +54,18 @@ struct CommandMapping {
Command command;
};
+typedef base::Callback<void(scoped_ptr<HttpResponse>)> HttpResponseSenderFunc;
+
class HttpHandler {
public:
HttpHandler(Log* log, const std::string& url_base);
+ HttpHandler(const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ Log* log,
+ const std::string& url_base);
~HttpHandler();
void Handle(const net::HttpServerRequestInfo& request,
- HttpResponse* response);
+ const HttpResponseSenderFunc& send_response_func);
bool ShouldShutdown(const net::HttpServerRequestInfo& request);
private:
@@ -70,23 +79,33 @@ class HttpHandler {
Command WrapToCommand(const SessionCommand& session_command);
Command WrapToCommand(const WindowCommand& window_command);
Command WrapToCommand(const ElementCommand& element_command);
- void HandleInternal(const net::HttpServerRequestInfo& request,
- HttpResponse* response);
- bool HandleWebDriverCommand(
- const net::HttpServerRequestInfo& request,
+ void HandleCommand(const net::HttpServerRequestInfo& request,
+ const std::string& trimmed_path,
+ const HttpResponseSenderFunc& send_response_func);
+ void PrepareResponse(const std::string& trimmed_path,
+ const HttpResponseSenderFunc& send_response_func,
+ const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id);
+ scoped_ptr<HttpResponse> PrepareResponseHelper(
const std::string& trimmed_path,
- HttpResponse* response);
+ const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id);
+ base::ThreadChecker thread_checker_;
Log* log_;
- base::Thread io_thread_;
std::string url_base_;
+ bool received_shutdown_;
scoped_refptr<URLRequestContextGetter> context_getter_;
SyncWebSocketFactory socket_factory_;
- SessionMap session_map_;
+ SessionThreadMap session_thread_map_;
scoped_ptr<CommandMap> command_map_;
scoped_ptr<Adb> adb_;
scoped_ptr<DeviceManager> device_manager_;
+ base::WeakPtrFactory<HttpHandler> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(HttpHandler);
};
diff --git a/chrome/test/chromedriver/server/http_handler_unittest.cc b/chrome/test/chromedriver/server/http_handler_unittest.cc
index 76c4f6e5f7..3fc6cd8900 100644
--- a/chrome/test/chromedriver/server/http_handler_unittest.cc
+++ b/chrome/test/chromedriver/server/http_handler_unittest.cc
@@ -10,6 +10,7 @@
#include "base/values.h"
#include "chrome/test/chromedriver/chrome/log.h"
#include "chrome/test/chromedriver/chrome/status.h"
+#include "chrome/test/chromedriver/command.h"
#include "chrome/test/chromedriver/server/http_handler.h"
#include "chrome/test/chromedriver/server/http_response.h"
#include "net/server/http_server_request_info.h"
@@ -17,15 +18,19 @@
namespace {
-Status DummyCommand(
- Status status,
+void DummyCommand(
+ const Status& status,
const base::DictionaryValue& params,
const std::string& session_id,
- scoped_ptr<base::Value>* value,
- std::string* out_session_id) {
- value->reset(new base::FundamentalValue(1));
- *out_session_id = "session_id";
- return status;
+ const CommandCallback& callback) {
+ callback.Run(status,
+ scoped_ptr<base::Value>(new base::FundamentalValue(1)),
+ "session_id");
+}
+
+void OnResponse(HttpResponse* response_to_set,
+ scoped_ptr<HttpResponse> response) {
+ *response_to_set = *response;
}
} // namespace
@@ -38,7 +43,7 @@ TEST(HttpHandlerTest, HandleOutsideOfBaseUrl) {
request.path = "base/path";
request.data = "body";
HttpResponse response;
- handler.Handle(request, &response);
+ handler.Handle(request, base::Bind(&OnResponse, &response));
ASSERT_EQ(HttpResponse::kBadRequest, response.status());
}
@@ -49,7 +54,7 @@ TEST(HttpHandlerTest, HandleUnknownCommand) {
request.method = "get";
request.path = "/path";
HttpResponse response;
- handler.Handle(request, &response);
+ handler.Handle(request, base::Bind(&OnResponse, &response));
ASSERT_EQ(HttpResponse::kNotFound, response.status());
}
@@ -64,7 +69,7 @@ TEST(HttpHandlerTest, HandleNewSession) {
request.method = "post";
request.path = "/base/session";
HttpResponse response;
- handler.Handle(request, &response);
+ handler.Handle(request, base::Bind(&OnResponse, &response));
ASSERT_EQ(HttpResponse::kSeeOther, response.status());
std::string location;
ASSERT_TRUE(response.GetHeader("Location", &location));
@@ -82,7 +87,7 @@ TEST(HttpHandlerTest, HandleInvalidPost) {
request.path = "/path";
request.data = "should be a dictionary";
HttpResponse response;
- handler.Handle(request, &response);
+ handler.Handle(request, base::Bind(&OnResponse, &response));
ASSERT_EQ(HttpResponse::kBadRequest, response.status());
}
@@ -96,7 +101,7 @@ TEST(HttpHandlerTest, HandleUnimplementedCommand) {
request.method = "post";
request.path = "/path";
HttpResponse response;
- handler.Handle(request, &response);
+ handler.Handle(request, base::Bind(&OnResponse, &response));
ASSERT_EQ(HttpResponse::kNotImplemented, response.status());
}
@@ -109,7 +114,7 @@ TEST(HttpHandlerTest, HandleCommand) {
request.method = "post";
request.path = "/path";
HttpResponse response;
- handler.Handle(request, &response);
+ handler.Handle(request, base::Bind(&OnResponse, &response));
ASSERT_EQ(HttpResponse::kOk, response.status());
std::string mime;
ASSERT_TRUE(response.GetHeader("Content-Type", &mime));
diff --git a/chrome/test/chromedriver/session.cc b/chrome/test/chromedriver/session.cc
index 51a409db1e..ea6da2cac1 100644
--- a/chrome/test/chromedriver/session.cc
+++ b/chrome/test/chromedriver/session.cc
@@ -24,7 +24,7 @@ const int Session::kDefaultPageLoadTimeoutMs = 5 * 60 * 1000;
Session::Session(const std::string& id)
: id(id),
- thread(("SessionThread_" + id).c_str()),
+ quit(false),
detach(false),
sticky_modifiers(0),
mouse_position(0, 0),
@@ -35,7 +35,7 @@ Session::Session(const std::string& id)
Session::Session(const std::string& id, scoped_ptr<Chrome> chrome)
: id(id),
- thread(("SessionThread_" + id).c_str()),
+ quit(false),
detach(false),
chrome(chrome.Pass()),
sticky_modifiers(0),
@@ -96,17 +96,3 @@ scoped_ptr<base::DictionaryValue> Session::CreateCapabilities() {
caps->SetBoolean("nativeEvents", true);
return caps.Pass();
}
-
-SessionAccessorImpl::SessionAccessorImpl(scoped_ptr<Session> session)
- : session_(session.Pass()) {}
-
-Session* SessionAccessorImpl::Access(scoped_ptr<base::AutoLock>* lock) {
- lock->reset(new base::AutoLock(session_lock_));
- return session_.get();
-}
-
-void SessionAccessorImpl::DeleteSession() {
- session_.reset();
-}
-
-SessionAccessorImpl::~SessionAccessorImpl() {}
diff --git a/chrome/test/chromedriver/session.h b/chrome/test/chromedriver/session.h
index 8b2ac53e0c..40ec9e9438 100644
--- a/chrome/test/chromedriver/session.h
+++ b/chrome/test/chromedriver/session.h
@@ -10,11 +10,8 @@
#include "base/basictypes.h"
#include "base/files/scoped_temp_dir.h"
-#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/thread.h"
#include "chrome/test/chromedriver/basic_types.h"
#include "chrome/test/chromedriver/chrome/geoposition.h"
@@ -52,7 +49,7 @@ struct Session {
std::string GetCurrentFrameId() const;
const std::string id;
- base::Thread thread;
+ bool quit;
bool detach;
scoped_ptr<Chrome> chrome;
std::string window;
@@ -76,32 +73,4 @@ struct Session {
scoped_ptr<base::DictionaryValue> CreateCapabilities();
};
-class SessionAccessor : public base::RefCountedThreadSafe<SessionAccessor> {
- public:
- virtual Session* Access(scoped_ptr<base::AutoLock>* lock) = 0;
-
- // The session should be accessed before its deletion.
- virtual void DeleteSession() = 0;
-
- protected:
- friend class base::RefCountedThreadSafe<SessionAccessor>;
- virtual ~SessionAccessor() {}
-};
-
-class SessionAccessorImpl : public SessionAccessor {
- public:
- explicit SessionAccessorImpl(scoped_ptr<Session> session);
-
- virtual Session* Access(scoped_ptr<base::AutoLock>* lock) OVERRIDE;
- virtual void DeleteSession() OVERRIDE;
-
- private:
- virtual ~SessionAccessorImpl();
-
- base::Lock session_lock_;
- scoped_ptr<Session> session_;
-
- DISALLOW_COPY_AND_ASSIGN(SessionAccessorImpl);
-};
-
#endif // CHROME_TEST_CHROMEDRIVER_SESSION_H_
diff --git a/chrome/test/chromedriver/session_commands.cc b/chrome/test/chromedriver/session_commands.cc
index 45c6f54b78..23f362baf4 100644
--- a/chrome/test/chromedriver/session_commands.cc
+++ b/chrome/test/chromedriver/session_commands.cc
@@ -23,7 +23,6 @@
#include "chrome/test/chromedriver/chrome/web_view.h"
#include "chrome/test/chromedriver/logging.h"
#include "chrome/test/chromedriver/session.h"
-#include "chrome/test/chromedriver/session_map.h"
#include "chrome/test/chromedriver/util.h"
namespace {
@@ -43,53 +42,22 @@ bool WindowHandleToWebViewId(const std::string& window_handle,
return true;
}
-void ExecuteOnSessionThread(
- const SessionCommand& command,
- Session* session,
- const base::DictionaryValue* params,
- scoped_ptr<base::Value>* value,
- Status* status,
- base::WaitableEvent* event) {
- *status = command.Run(session, *params, value);
- event->Signal();
-}
-
} // namespace
-Status ExecuteSessionCommand(
- SessionMap* session_map,
- const SessionCommand& command,
+Status ExecuteQuit(
+ bool allow_detach,
+ Session* session,
const base::DictionaryValue& params,
- const std::string& session_id,
- scoped_ptr<base::Value>* out_value,
- std::string* out_session_id) {
- *out_session_id = session_id;
- scoped_refptr<SessionAccessor> session_accessor;
- if (!session_map->Get(session_id, &session_accessor))
- return Status(kNoSuchSession, session_id);
- scoped_ptr<base::AutoLock> session_lock;
- Session* session = session_accessor->Access(&session_lock);
- if (!session)
- return Status(kNoSuchSession, session_id);
-
- Status status(kUnknownError);
- base::WaitableEvent event(false, false);
- session->thread.message_loop_proxy()->PostTask(
- FROM_HERE,
- base::Bind(&ExecuteOnSessionThread, command, session,
- &params, out_value, &status, &event));
- event.Wait();
- if (status.IsError() && session->chrome)
- status.AddDetails("Session info: chrome=" + session->chrome->GetVersion());
- // Delete the session, because concurrent requests might hold a reference to
- // the SessionAccessor already.
- if (!session_map->Has(session_id))
- session_accessor->DeleteSession();
- return status;
+ scoped_ptr<base::Value>* value) {
+ if (allow_detach && session->detach) {
+ return Status(kOk);
+ } else {
+ session->quit = true;
+ return session->chrome->Quit();
+ }
}
Status ExecuteGetSessionCapabilities(
- SessionMap* session_map,
Session* session,
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value) {
@@ -111,7 +79,6 @@ Status ExecuteGetCurrentWindowHandle(
}
Status ExecuteClose(
- SessionMap* session_map,
Session* session,
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value) {
@@ -135,7 +102,7 @@ Status ExecuteClose(
if ((status.code() == kChromeNotReachable && is_last_web_view) ||
(status.IsOk() && web_view_ids.empty())) {
// If no window is open, close is the equivalent of calling "quit".
- CHECK(session_map->Remove(session->id));
+ session->quit = true;
return session->chrome->Quit();
}
diff --git a/chrome/test/chromedriver/session_commands.h b/chrome/test/chromedriver/session_commands.h
index 23af156760..618f124a0c 100644
--- a/chrome/test/chromedriver/session_commands.h
+++ b/chrome/test/chromedriver/session_commands.h
@@ -9,7 +9,8 @@
#include "base/callback_forward.h"
#include "base/memory/scoped_ptr.h"
-#include "chrome/test/chromedriver/session_map.h"
+#include "chrome/test/chromedriver/command.h"
+#include "chrome/test/chromedriver/session_thread_map.h"
namespace base {
class DictionaryValue;
@@ -19,24 +20,15 @@ class Value;
struct Session;
class Status;
-typedef base::Callback<Status(
+// Quits a session.
+Status ExecuteQuit(
+ bool allow_detach,
Session* session,
- const base::DictionaryValue&,
- scoped_ptr<base::Value>*)> SessionCommand;
-
-// Executes a given session command, after acquiring access to the appropriate
-// session.
-Status ExecuteSessionCommand(
- SessionMap* session_map,
- const SessionCommand& command,
const base::DictionaryValue& params,
- const std::string& session_id,
- scoped_ptr<base::Value>* out_value,
- std::string* out_session_id);
+ scoped_ptr<base::Value>* value);
// Gets the capabilities of a particular session.
Status ExecuteGetSessionCapabilities(
- SessionMap* session_map,
Session* session,
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value);
@@ -49,7 +41,6 @@ Status ExecuteGetCurrentWindowHandle(
// Close the target window.
Status ExecuteClose(
- SessionMap* session_map,
Session* session,
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value);
diff --git a/chrome/test/chromedriver/session_commands_unittest.cc b/chrome/test/chromedriver/session_commands_unittest.cc
index 33a748b3fb..1b6ef7b42e 100644
--- a/chrome/test/chromedriver/session_commands_unittest.cc
+++ b/chrome/test/chromedriver/session_commands_unittest.cc
@@ -8,114 +8,106 @@
#include "base/callback.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
#include "base/values.h"
#include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/chrome/stub_chrome.h"
-#include "chrome/test/chromedriver/fake_session_accessor.h"
+#include "chrome/test/chromedriver/commands.h"
#include "chrome/test/chromedriver/session.h"
#include "chrome/test/chromedriver/session_commands.h"
-#include "chrome/test/chromedriver/session_map.h"
#include "testing/gtest/include/gtest/gtest.h"
+TEST(SessionCommandTest, FileUpload) {
+ Session session("id");
+ base::DictionaryValue params;
+ scoped_ptr<base::Value> value;
+ // Zip file entry that contains a single file with contents 'COW\n', base64
+ // encoded following RFC 1521.
+ const char* kBase64ZipEntry =
+ "UEsDBBQAAAAAAMROi0K/wAzGBAAAAAQAAAADAAAAbW9vQ09XClBLAQIUAxQAAAAAAMROi0K/"
+ "wAzG\nBAAAAAQAAAADAAAAAAAAAAAAAACggQAAAABtb29QSwUGAAAAAAEAAQAxAAAAJQAAAA"
+ "AA\n";
+ params.SetString("file", kBase64ZipEntry);
+ Status status = ExecuteUploadFile(&session, params, &value);
+ ASSERT_EQ(kOk, status.code()) << status.message();
+ base::FilePath::StringType path;
+ ASSERT_TRUE(value->GetAsString(&path));
+ ASSERT_TRUE(base::PathExists(base::FilePath(path)));
+ std::string data;
+ ASSERT_TRUE(file_util::ReadFileToString(base::FilePath(path), &data));
+ ASSERT_STREQ("COW\n", data.c_str());
+}
+
namespace {
-Status ExecuteSimpleCommand(
- Session* expected_session,
- base::DictionaryValue* expected_params,
- base::Value* value,
- Session* session,
- const base::DictionaryValue& params,
- scoped_ptr<base::Value>* return_value) {
- EXPECT_EQ(expected_session, session);
- EXPECT_TRUE(expected_params->Equals(&params));
- return_value->reset(value->DeepCopy());
- return Status(kOk);
-}
+class DetachChrome : public StubChrome {
+ public:
+ DetachChrome() : quit_called_(false) {}
+ virtual ~DetachChrome() {}
+
+ // Overridden from Chrome:
+ virtual Status Quit() OVERRIDE {
+ quit_called_ = true;
+ return Status(kOk);
+ }
+
+ bool quit_called_;
+};
} // namespace
-TEST(SessionCommandTest, SimpleCommand) {
- SessionMap map;
- Session session("session", scoped_ptr<Chrome>(new StubChrome()));
- ASSERT_TRUE(session.thread.Start());
- scoped_refptr<SessionAccessor> accessor(new FakeSessionAccessor(&session));
- map.Set(session.id, accessor);
+TEST(SessionCommandsTest, Quit) {
+ DetachChrome* chrome = new DetachChrome();
+ Session session("id", scoped_ptr<Chrome>(chrome));
base::DictionaryValue params;
- params.SetInteger("param", 5);
- base::FundamentalValue expected_value(6);
- SessionCommand cmd = base::Bind(
- &ExecuteSimpleCommand, &session, &params, &expected_value);
-
scoped_ptr<base::Value> value;
- std::string session_id;
- Status status = ExecuteSessionCommand(
- &map, cmd, params,
- session.id, &value, &session_id);
- ASSERT_EQ(kOk, status.code());
- ASSERT_TRUE(expected_value.Equals(value.get()));
- ASSERT_STREQ(session.id.c_str(), session_id.c_str());
-}
-namespace {
+ ASSERT_EQ(kOk, ExecuteQuit(false, &session, params, &value).code());
+ ASSERT_TRUE(chrome->quit_called_);
-Status ShouldNotBeCalled(
- Session* session,
- const base::DictionaryValue& params,
- scoped_ptr<base::Value>* value) {
- EXPECT_TRUE(false);
- return Status(kOk);
+ chrome->quit_called_ = false;
+ ASSERT_EQ(kOk, ExecuteQuit(true, &session, params, &value).code());
+ ASSERT_TRUE(chrome->quit_called_);
}
-} // namespace
+TEST(SessionCommandsTest, QuitWithDetach) {
+ DetachChrome* chrome = new DetachChrome();
+ Session session("id", scoped_ptr<Chrome>(chrome));
+ session.detach = true;
-TEST(SessionCommandTest, NoSuchSession) {
- SessionMap map;
base::DictionaryValue params;
scoped_ptr<base::Value> value;
- std::string session_id;
- Status status = ExecuteSessionCommand(
- &map, base::Bind(&ShouldNotBeCalled), params,
- "session", &value, &session_id);
- ASSERT_EQ(kNoSuchSession, status.code());
- ASSERT_FALSE(value.get());
- ASSERT_STREQ("session", session_id.c_str());
-}
-TEST(SessionCommandTest, SessionDeletedWhileWaiting) {
- SessionMap map;
- scoped_refptr<SessionAccessor> accessor(new FakeSessionAccessor(NULL));
- map.Set("session", accessor);
+ ASSERT_EQ(kOk, ExecuteQuit(true, &session, params, &value).code());
+ ASSERT_FALSE(chrome->quit_called_);
- base::DictionaryValue params;
- scoped_ptr<base::Value> value;
- std::string session_id;
- Status status = ExecuteSessionCommand(
- &map, base::Bind(&ShouldNotBeCalled), params,
- "session", &value, &session_id);
- ASSERT_EQ(kNoSuchSession, status.code());
- ASSERT_FALSE(value.get());
- ASSERT_STREQ("session", session_id.c_str());
+ ASSERT_EQ(kOk, ExecuteQuit(false, &session, params, &value).code());
+ ASSERT_TRUE(chrome->quit_called_);
}
-TEST(SessionCommandTest, FileUpload) {
- Session session("id");
+namespace {
+
+class FailsToQuitChrome : public StubChrome {
+ public:
+ FailsToQuitChrome() {}
+ virtual ~FailsToQuitChrome() {}
+
+ // Overridden from Chrome:
+ virtual Status Quit() OVERRIDE {
+ return Status(kUnknownError);
+ }
+};
+
+} // namespace
+
+TEST(SessionCommandsTest, QuitFails) {
+ Session session("id", scoped_ptr<Chrome>(new FailsToQuitChrome()));
base::DictionaryValue params;
scoped_ptr<base::Value> value;
- // Zip file entry that contains a single file with contents 'COW\n', base64
- // encoded following RFC 1521.
- const char* kBase64ZipEntry =
- "UEsDBBQAAAAAAMROi0K/wAzGBAAAAAQAAAADAAAAbW9vQ09XClBLAQIUAxQAAAAAAMROi0K/"
- "wAzG\nBAAAAAQAAAADAAAAAAAAAAAAAACggQAAAABtb29QSwUGAAAAAAEAAQAxAAAAJQAAAA"
- "AA\n";
- params.SetString("file", kBase64ZipEntry);
- Status status = ExecuteUploadFile(&session, params, &value);
- ASSERT_EQ(kOk, status.code()) << status.message();
- base::FilePath::StringType path;
- ASSERT_TRUE(value->GetAsString(&path));
- ASSERT_TRUE(base::PathExists(base::FilePath(path)));
- std::string data;
- ASSERT_TRUE(file_util::ReadFileToString(base::FilePath(path), &data));
- ASSERT_STREQ("COW\n", data.c_str());
+ ASSERT_EQ(kUnknownError, ExecuteQuit(false, &session, params, &value).code());
}
diff --git a/chrome/test/chromedriver/session_map.h b/chrome/test/chromedriver/session_map.h
deleted file mode 100644
index 1af8884f34..0000000000
--- a/chrome/test/chromedriver/session_map.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2012 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_SESSION_MAP_H_
-#define CHROME_TEST_CHROMEDRIVER_SESSION_MAP_H_
-
-#include <string>
-
-#include "base/memory/ref_counted.h"
-#include "chrome/test/chromedriver/synchronized_map.h"
-
-class SessionAccessor;
-
-typedef SynchronizedMap<std::string, scoped_refptr<SessionAccessor> >
- SessionMap;
-
-#endif // CHROME_TEST_CHROMEDRIVER_SESSION_MAP_H_
diff --git a/chrome/test/chromedriver/session_thread_map.h b/chrome/test/chromedriver/session_thread_map.h
new file mode 100644
index 0000000000..b0eaa57f9e
--- /dev/null
+++ b/chrome/test/chromedriver/session_thread_map.h
@@ -0,0 +1,16 @@
+// Copyright 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_SESSION_THREAD_MAP_H_
+#define CHROME_TEST_CHROMEDRIVER_SESSION_THREAD_MAP_H_
+
+#include <map>
+#include <string>
+
+#include "base/memory/linked_ptr.h"
+#include "base/threading/thread.h"
+
+typedef std::map<std::string, linked_ptr<base::Thread> > SessionThreadMap;
+
+#endif // CHROME_TEST_CHROMEDRIVER_SESSION_THREAD_MAP_H_
diff --git a/chrome/test/chromedriver/session_unittest.cc b/chrome/test/chromedriver/session_unittest.cc
index 5f6d0d02fc..8f0afb2649 100644
--- a/chrome/test/chromedriver/session_unittest.cc
+++ b/chrome/test/chromedriver/session_unittest.cc
@@ -5,25 +5,13 @@
#include <list>
#include <string>
-#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "base/synchronization/lock.h"
#include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/chrome/stub_chrome.h"
#include "chrome/test/chromedriver/chrome/stub_web_view.h"
#include "chrome/test/chromedriver/session.h"
#include "testing/gtest/include/gtest/gtest.h"
-TEST(SessionAccessorTest, LocksSession) {
- scoped_ptr<Session> scoped_session(new Session("id"));
- Session* session = scoped_session.get();
- scoped_refptr<SessionAccessor> accessor(
- new SessionAccessorImpl(scoped_session.Pass()));
- scoped_ptr<base::AutoLock> lock;
- ASSERT_EQ(session, accessor->Access(&lock));
- ASSERT_TRUE(lock.get());
-}
-
namespace {
class MockChrome : public StubChrome {
diff --git a/chrome/test/chromedriver/synchronized_map.h b/chrome/test/chromedriver/synchronized_map.h
deleted file mode 100644
index a7bbb782df..0000000000
--- a/chrome/test/chromedriver/synchronized_map.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) 2012 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_SYNCHRONIZED_MAP_H_
-#define CHROME_TEST_CHROMEDRIVER_SYNCHRONIZED_MAP_H_
-
-#include <map>
-#include <utility>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/synchronization/lock.h"
-
-template <typename K, typename V>
-class SynchronizedMap {
- public:
- SynchronizedMap();
- ~SynchronizedMap();
-
- void Set(const K& key, const V& value);
- bool Get(const K& key, V* value) const;
- bool Has(const K& key) const;
- bool Remove(const K& key);
-
- void GetKeys(std::vector<K>* keys) const;
-
- private:
- typedef std::map<K, V> Map;
- Map map_;
- mutable base::Lock map_lock_;
-
- DISALLOW_COPY_AND_ASSIGN(SynchronizedMap);
-};
-
-template <typename K, typename V>
-SynchronizedMap<K, V>::SynchronizedMap() {}
-
-template <typename K, typename V>
-SynchronizedMap<K, V>::~SynchronizedMap() {}
-
-template <typename K, typename V>
-void SynchronizedMap<K, V>::Set(const K& key, const V& value) {
- base::AutoLock lock(map_lock_);
- typename Map::iterator iter = map_.find(key);
- if (iter != map_.end())
- map_.erase(iter);
- map_.insert(std::make_pair(key, value));
-}
-
-template <typename K, typename V>
-bool SynchronizedMap<K, V>::Get(const K& key, V* value) const {
- base::AutoLock lock(map_lock_);
- typename Map::const_iterator iter = map_.find(key);
- if (iter == map_.end())
- return false;
- *value = iter->second;
- return true;
-}
-
-template <typename K, typename V>
-bool SynchronizedMap<K, V>::Has(const K& key) const {
- base::AutoLock lock(map_lock_);
- return map_.find(key) != map_.end();
-}
-
-template <typename K, typename V>
-bool SynchronizedMap<K, V>::Remove(const K& key) {
- base::AutoLock lock(map_lock_);
- typename Map::iterator iter = map_.find(key);
- if (iter == map_.end())
- return false;
- map_.erase(iter);
- return true;
-}
-
-template <typename K, typename V>
-void SynchronizedMap<K, V>::GetKeys(std::vector<K>* keys) const {
- keys->clear();
- base::AutoLock lock(map_lock_);
- typename Map::const_iterator iter;
- for (iter = map_.begin(); iter != map_.end(); iter++)
- keys->push_back(iter->first);
-}
-
-#endif // CHROME_TEST_CHROMEDRIVER_SYNCHRONIZED_MAP_H_
diff --git a/chrome/test/chromedriver/synchronized_map_unittest.cc b/chrome/test/chromedriver/synchronized_map_unittest.cc
deleted file mode 100644
index 4e523958bb..0000000000
--- a/chrome/test/chromedriver/synchronized_map_unittest.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2012 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 <vector>
-
-#include "chrome/test/chromedriver/synchronized_map.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-TEST(SynchronizedMapTest, Set) {
- SynchronizedMap<int, int> map;
- map.Set(1, 2);
- ASSERT_TRUE(map.Has(1));
- int val = 0;
- ASSERT_TRUE(map.Get(1, &val));
- ASSERT_EQ(2, val);
-
- map.Set(1, 3);
- ASSERT_TRUE(map.Has(1));
- ASSERT_TRUE(map.Get(1, &val));
- ASSERT_EQ(3, val);
-
- map.Set(3, 1);
- ASSERT_TRUE(map.Has(1));
- ASSERT_TRUE(map.Get(1, &val));
- ASSERT_EQ(3, val);
- ASSERT_TRUE(map.Has(3));
- ASSERT_TRUE(map.Get(3, &val));
- ASSERT_EQ(1, val);
-}
-
-TEST(SynchronizedMapTest, Get) {
- SynchronizedMap<int, int> map;
- int val = 0;
- ASSERT_FALSE(map.Get(1, &val));
- map.Set(1, 2);
- ASSERT_TRUE(map.Get(1, &val));
- ASSERT_EQ(2, val);
-
- ASSERT_TRUE(map.Remove(1));
- val = 100;
- ASSERT_FALSE(map.Get(1, &val));
- ASSERT_EQ(100, val);
-}
-
-TEST(SynchronizedMapTest, Has) {
- SynchronizedMap<int, int> map;
- ASSERT_FALSE(map.Has(1));
- map.Set(1, 2);
- ASSERT_TRUE(map.Has(1));
- ASSERT_FALSE(map.Has(2));
-
- ASSERT_TRUE(map.Remove(1));
- ASSERT_FALSE(map.Has(1));
-}
-
-TEST(SynchronizedMapTest, GetKeys) {
- SynchronizedMap<int, int> map;
-
- std::vector<int> keys;
- map.GetKeys(&keys);
- ASSERT_EQ(0u, keys.size());
-
- keys.push_back(100);
- map.GetKeys(&keys);
- ASSERT_EQ(0u, keys.size());
-
- map.Set(1, 2);
- map.GetKeys(&keys);
- ASSERT_EQ(1u, keys.size());
- ASSERT_EQ(1, keys[0]);
-
- map.Set(2, 4);
- map.GetKeys(&keys);
- ASSERT_EQ(2u, keys.size());
- ASSERT_EQ(1, keys[0]);
- ASSERT_EQ(2, keys[1]);
-}
diff --git a/chrome/test/ppapi/ppapi_test.cc b/chrome/test/ppapi/ppapi_test.cc
index 1953ffec92..df11ee762c 100644
--- a/chrome/test/ppapi/ppapi_test.cc
+++ b/chrome/test/ppapi/ppapi_test.cc
@@ -38,6 +38,10 @@
#include "ui/gl/gl_switches.h"
#include "webkit/plugins/plugin_switches.h"
+#if defined(OS_WIN) && defined(USE_ASH)
+#include "base/win/windows_version.h"
+#endif
+
using content::DomOperationNotificationDetails;
using content::RenderViewHost;
@@ -254,6 +258,14 @@ std::string PPAPITestBase::StripPrefixes(const std::string& test_name) {
}
void PPAPITestBase::RunTestURL(const GURL& test_url) {
+#if defined(OS_WIN) && defined(USE_ASH)
+ // PPAPITests are broken in Ash browser tests (http://crbug.com/263548).
+ if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
+ LOG(WARNING) << "PPAPITests are disabled for Ash browser tests.";
+ return;
+ }
+#endif
+
// See comment above TestingInstance in ppapi/test/testing_instance.h.
// Basically it sends messages using the DOM automation controller. The
// value of "..." means it's still working and we should continue to wait,