diff options
author | Ben Murdoch <benm@google.com> | 2013-07-24 10:36:34 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2013-07-24 10:36:34 +0100 |
commit | a3f7b4e666c476898878fa745f637129375cd889 (patch) | |
tree | 1d78b48780e4c8603c226fd88d8f4b786f00bb81 /chrome/test | |
parent | d4336a7d5c149891bede0c3201c8e831520067af (diff) | |
download | chromium_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')
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(¶ms)); + 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, ¶ms, &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, ¶ms)) { @@ -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, - ¶ms, 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(¶ms)); - 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, ¶ms, &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, |