#include "chrome/test/chromedriver/chrome/stub_chrome.h"
#include "chrome/test/chromedriver/chrome/stub_web_view.h"
#include "chrome/test/chromedriver/chrome/web_view.h"
+#include "chrome/test/chromedriver/command_listener_proxy.h"
#include "chrome/test/chromedriver/commands.h"
#include "chrome/test/chromedriver/element_commands.h"
#include "chrome/test/chromedriver/session.h"
ExecuteFindChildElements(
1, &session, &web_view, element_id, params, &result).code());
}
+
+namespace {
+
+class MockCommandListener : public CommandListener {
+ public:
+ MockCommandListener() : called_(false) {}
+ virtual ~MockCommandListener() {}
+
+ virtual Status BeforeCommand(const std::string& command_name) OVERRIDE {
+ called_ = true;
+ EXPECT_STREQ("cmd", command_name.c_str());
+ return Status(kOk);
+ }
+
+ void VerifyCalled() {
+ EXPECT_TRUE(called_);
+ }
+
+ void VerifyNotCalled() {
+ EXPECT_FALSE(called_);
+ }
+
+ private:
+ bool called_;
+};
+
+Status ExecuteAddListenerToSessionCommand(
+ CommandListener* listener,
+ Session* session,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* return_value) {
+ session->command_listeners.push_back(listener);
+ return Status(kOk);
+}
+
+Status ExecuteQuitSessionCommand(
+ Session* session,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* return_value) {
+ session->quit = true;
+ return Status(kOk);
+}
+
+void OnSessionCommand(
+ base::RunLoop* run_loop,
+ const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id) {
+ ASSERT_EQ(kOk, status.code());
+ run_loop->Quit();
+}
+
+} // namespace
+
+TEST(CommandsTest, SuccessNotifyingCommandListeners) {
+ 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<MockCommandListener> listener(new MockCommandListener());
+ CommandListenerProxy* proxy = new CommandListenerProxy(listener.get());
+ // We add |proxy| to the session instead of adding |listener| directly so that
+ // after the session is destroyed by ExecuteQuitSessionCommand, we can still
+ // verify the listener was called. The session owns and will destroy |proxy|.
+ SessionCommand cmd = base::Bind(&ExecuteAddListenerToSessionCommand, proxy);
+ base::MessageLoop loop;
+ base::RunLoop run_loop_addlistener;
+
+ // |CommandListener|s are notified immediately before commands are run.
+ // Here, the command adds |listener| to the session, so |listener|
+ // should not be notified since it will not have been added yet.
+ ExecuteSessionCommand(
+ &map,
+ "cmd",
+ cmd,
+ false,
+ params,
+ id,
+ base::Bind(&OnSessionCommand, &run_loop_addlistener));
+ run_loop_addlistener.Run();
+
+ listener->VerifyNotCalled();
+
+ base::RunLoop run_loop_testlistener;
+ cmd = base::Bind(&ExecuteQuitSessionCommand);
+
+ // |listener| was added to |session| by ExecuteAddListenerToSessionCommand
+ // and should be notified before the next command, ExecuteQuitSessionCommand.
+ ExecuteSessionCommand(
+ &map,
+ "cmd",
+ cmd,
+ false,
+ params,
+ id,
+ base::Bind(&OnSessionCommand, &run_loop_testlistener));
+ run_loop_testlistener.Run();
+
+ listener->VerifyCalled();
+}
+
+namespace {
+
+class FailingCommandListener : public CommandListener {
+ public:
+ FailingCommandListener() {}
+ virtual ~FailingCommandListener() {}
+
+ virtual Status BeforeCommand(const std::string& command_name) OVERRIDE {
+ return Status(kUnknownError);
+ }
+};
+
+void AddListenerToSessionIfSessionExists(CommandListener* listener) {
+ Session* session = GetThreadLocalSession();
+ if (session) {
+ session->command_listeners.push_back(listener);
+ }
+}
+
+void OnFailBecauseErrorNotifyingListeners(
+ base::RunLoop* run_loop,
+ const Status& status,
+ scoped_ptr<base::Value> value,
+ const std::string& session_id) {
+ EXPECT_EQ(kUnknownError, status.code());
+ EXPECT_FALSE(value.get());
+ run_loop->Quit();
+}
+
+void VerifySessionWasDeleted() {
+ ASSERT_FALSE(GetThreadLocalSession());
+}
+
+} // namespace
+
+TEST(CommandsTest, ErrorNotifyingCommandListeners) {
+ 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;
+
+ // In SuccessNotifyingCommandListenersBeforeCommand, we verified BeforeCommand
+ // was called before (as opposed to after) command execution. We don't need to
+ // verify this again, so we can just add |listener| with PostTask.
+ CommandListener* listener = new FailingCommandListener();
+ thread->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AddListenerToSessionIfSessionExists, listener));
+
+ base::DictionaryValue params;
+ // The command should never be executed if BeforeCommand fails for a listener.
+ SessionCommand cmd = base::Bind(&ShouldNotBeCalled);
+ base::MessageLoop loop;
+ base::RunLoop run_loop;
+
+ ExecuteSessionCommand(
+ &map,
+ "cmd",
+ cmd,
+ false,
+ params,
+ id,
+ base::Bind(&OnFailBecauseErrorNotifyingListeners, &run_loop));
+ run_loop.Run();
+
+ thread->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&VerifySessionWasDeleted));
+}