Merge html-office-public repo into src
[chromium-blink-merge.git] / chrome / test / chromedriver / commands_unittest.cc
blobf6584e47b01829c7e28e893d2ffa788079e50661
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <string>
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/compiler_specific.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h"
14 #include "base/synchronization/lock.h"
15 #include "base/threading/thread.h"
16 #include "base/values.h"
17 #include "chrome/test/chromedriver/chrome/status.h"
18 #include "chrome/test/chromedriver/chrome/stub_chrome.h"
19 #include "chrome/test/chromedriver/chrome/stub_web_view.h"
20 #include "chrome/test/chromedriver/chrome/web_view.h"
21 #include "chrome/test/chromedriver/command_listener_proxy.h"
22 #include "chrome/test/chromedriver/commands.h"
23 #include "chrome/test/chromedriver/element_commands.h"
24 #include "chrome/test/chromedriver/session.h"
25 #include "chrome/test/chromedriver/session_commands.h"
26 #include "chrome/test/chromedriver/window_commands.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "third_party/webdriver/atoms.h"
30 namespace {
32 void OnGetStatus(const Status& status,
33 scoped_ptr<base::Value> value,
34 const std::string& session_id) {
35 ASSERT_EQ(kOk, status.code());
36 base::DictionaryValue* dict;
37 ASSERT_TRUE(value->GetAsDictionary(&dict));
38 base::Value* unused;
39 ASSERT_TRUE(dict->Get("os.name", &unused));
40 ASSERT_TRUE(dict->Get("os.version", &unused));
41 ASSERT_TRUE(dict->Get("os.arch", &unused));
42 ASSERT_TRUE(dict->Get("build.version", &unused));
45 } // namespace
47 TEST(CommandsTest, GetStatus) {
48 base::DictionaryValue params;
49 ExecuteGetStatus(params, std::string(), base::Bind(&OnGetStatus));
52 namespace {
54 void ExecuteStubGetSession(int* count,
55 const base::DictionaryValue& params,
56 const std::string& session_id,
57 const CommandCallback& callback) {
58 if (*count == 0) {
59 EXPECT_STREQ("id", session_id.c_str());
60 } else {
61 EXPECT_STREQ("id2", session_id.c_str());
63 (*count)++;
65 scoped_ptr<base::DictionaryValue> capabilities(new base::DictionaryValue());
67 capabilities->Set("capability1", new base::StringValue("test1"));
68 capabilities->Set("capability2", new base::StringValue("test2"));
70 callback.Run(Status(kOk), capabilities.Pass(), session_id);
73 void OnGetSessions(const Status& status,
74 scoped_ptr<base::Value> value,
75 const std::string& session_id) {
76 ASSERT_EQ(kOk, status.code());
77 ASSERT_TRUE(value.get());
78 base::ListValue* sessions;
79 ASSERT_TRUE(value->GetAsList(&sessions));
80 ASSERT_EQ(static_cast<size_t>(2), sessions->GetSize());
82 base::DictionaryValue* session1;
83 base::DictionaryValue* session2;
84 ASSERT_TRUE(sessions->GetDictionary(0, &session1));
85 ASSERT_TRUE(sessions->GetDictionary(1, &session2));
87 ASSERT_EQ(static_cast<size_t>(2), session1->size());
88 ASSERT_EQ(static_cast<size_t>(2), session2->size());
90 std::string session1_id;
91 std::string session2_id;
92 base::DictionaryValue* session1_capabilities;
93 base::DictionaryValue* session2_capabilities;
95 ASSERT_TRUE(session1->GetString("sessionId", &session1_id));
96 ASSERT_TRUE(session2->GetString("sessionId", &session2_id));
97 ASSERT_TRUE(session1->GetDictionary("capabilities", &session1_capabilities));
98 ASSERT_TRUE(session2->GetDictionary("capabilities", &session2_capabilities));
100 ASSERT_EQ((size_t) 2, session1_capabilities->size());
101 ASSERT_EQ((size_t) 2, session2_capabilities->size());
102 ASSERT_EQ("id", session1_id);
103 ASSERT_EQ("id2", session2_id);
105 std::string session1_capability1;
106 std::string session1_capability2;
107 std::string session2_capability1;
108 std::string session2_capability2;
110 ASSERT_TRUE(session1_capabilities->GetString("capability1",
111 &session1_capability1));
112 ASSERT_TRUE(session1_capabilities->GetString("capability2",
113 &session1_capability2));
114 ASSERT_TRUE(session2_capabilities->GetString("capability1",
115 &session2_capability1));
116 ASSERT_TRUE(session2_capabilities->GetString("capability2",
117 &session2_capability2));
119 ASSERT_EQ("test1", session1_capability1);
120 ASSERT_EQ("test2", session1_capability2);
121 ASSERT_EQ("test1", session2_capability1);
122 ASSERT_EQ("test2", session2_capability2);
125 } // namespace
127 TEST(CommandsTest, GetSessions) {
128 SessionThreadMap map;
129 Session session("id");
130 Session session2("id2");
131 map[session.id] = make_linked_ptr(new base::Thread("1"));
132 map[session2.id] = make_linked_ptr(new base::Thread("2"));
134 int count = 0;
136 Command cmd = base::Bind(&ExecuteStubGetSession, &count);
138 base::DictionaryValue params;
139 base::MessageLoop loop;
141 ExecuteGetSessions(cmd, &map, params, std::string(),
142 base::Bind(&OnGetSessions));
143 ASSERT_EQ(2, count);
146 namespace {
148 void ExecuteStubQuit(
149 int* count,
150 const base::DictionaryValue& params,
151 const std::string& session_id,
152 const CommandCallback& callback) {
153 if (*count == 0) {
154 EXPECT_STREQ("id", session_id.c_str());
155 } else {
156 EXPECT_STREQ("id2", session_id.c_str());
158 (*count)++;
159 callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
162 void OnQuitAll(const Status& status,
163 scoped_ptr<base::Value> value,
164 const std::string& session_id) {
165 ASSERT_EQ(kOk, status.code());
166 ASSERT_FALSE(value.get());
169 } // namespace
171 TEST(CommandsTest, QuitAll) {
172 SessionThreadMap map;
173 Session session("id");
174 Session session2("id2");
175 map[session.id] = make_linked_ptr(new base::Thread("1"));
176 map[session2.id] = make_linked_ptr(new base::Thread("2"));
178 int count = 0;
179 Command cmd = base::Bind(&ExecuteStubQuit, &count);
180 base::DictionaryValue params;
181 base::MessageLoop loop;
182 ExecuteQuitAll(cmd, &map, params, std::string(), base::Bind(&OnQuitAll));
183 ASSERT_EQ(2, count);
186 namespace {
188 Status ExecuteSimpleCommand(
189 const std::string& expected_id,
190 base::DictionaryValue* expected_params,
191 base::Value* value,
192 Session* session,
193 const base::DictionaryValue& params,
194 scoped_ptr<base::Value>* return_value) {
195 EXPECT_EQ(expected_id, session->id);
196 EXPECT_TRUE(expected_params->Equals(&params));
197 return_value->reset(value->DeepCopy());
198 session->quit = true;
199 return Status(kOk);
202 void OnSimpleCommand(base::RunLoop* run_loop,
203 const std::string& expected_session_id,
204 base::Value* expected_value,
205 const Status& status,
206 scoped_ptr<base::Value> value,
207 const std::string& session_id) {
208 ASSERT_EQ(kOk, status.code());
209 ASSERT_TRUE(expected_value->Equals(value.get()));
210 ASSERT_EQ(expected_session_id, session_id);
211 run_loop->Quit();
214 } // namespace
216 TEST(CommandsTest, ExecuteSessionCommand) {
217 SessionThreadMap map;
218 linked_ptr<base::Thread> thread(new base::Thread("1"));
219 ASSERT_TRUE(thread->Start());
220 std::string id("id");
221 thread->message_loop()->PostTask(
222 FROM_HERE,
223 base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id));
224 map[id] = thread;
226 base::DictionaryValue params;
227 params.SetInteger("param", 5);
228 base::FundamentalValue expected_value(6);
229 SessionCommand cmd = base::Bind(
230 &ExecuteSimpleCommand, id, &params, &expected_value);
232 base::MessageLoop loop;
233 base::RunLoop run_loop;
234 ExecuteSessionCommand(
235 &map,
236 "cmd",
237 cmd,
238 false,
239 params,
241 base::Bind(&OnSimpleCommand, &run_loop, id, &expected_value));
242 run_loop.Run();
245 namespace {
247 Status ShouldNotBeCalled(
248 Session* session,
249 const base::DictionaryValue& params,
250 scoped_ptr<base::Value>* value) {
251 EXPECT_TRUE(false);
252 return Status(kOk);
255 void OnNoSuchSession(const Status& status,
256 scoped_ptr<base::Value> value,
257 const std::string& session_id) {
258 EXPECT_EQ(kNoSuchSession, status.code());
259 EXPECT_FALSE(value.get());
262 void OnNoSuchSessionIsOk(const Status& status,
263 scoped_ptr<base::Value> value,
264 const std::string& session_id) {
265 EXPECT_EQ(kOk, status.code());
266 EXPECT_FALSE(value.get());
269 } // namespace
271 TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSession) {
272 SessionThreadMap map;
273 base::DictionaryValue params;
274 ExecuteSessionCommand(&map,
275 "cmd",
276 base::Bind(&ShouldNotBeCalled),
277 false,
278 params,
279 "session",
280 base::Bind(&OnNoSuchSession));
283 TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSessionWhenItExpectsOk) {
284 SessionThreadMap map;
285 base::DictionaryValue params;
286 ExecuteSessionCommand(&map,
287 "cmd",
288 base::Bind(&ShouldNotBeCalled),
289 true,
290 params,
291 "session",
292 base::Bind(&OnNoSuchSessionIsOk));
295 namespace {
297 void OnNoSuchSessionAndQuit(base::RunLoop* run_loop,
298 const Status& status,
299 scoped_ptr<base::Value> value,
300 const std::string& session_id) {
301 run_loop->Quit();
302 EXPECT_EQ(kNoSuchSession, status.code());
303 EXPECT_FALSE(value.get());
306 } // namespace
308 TEST(CommandsTest, ExecuteSessionCommandOnJustDeletedSession) {
309 SessionThreadMap map;
310 linked_ptr<base::Thread> thread(new base::Thread("1"));
311 ASSERT_TRUE(thread->Start());
312 std::string id("id");
313 map[id] = thread;
315 base::MessageLoop loop;
316 base::RunLoop run_loop;
317 ExecuteSessionCommand(&map,
318 "cmd",
319 base::Bind(&ShouldNotBeCalled),
320 false,
321 base::DictionaryValue(),
322 "session",
323 base::Bind(&OnNoSuchSessionAndQuit, &run_loop));
324 run_loop.Run();
327 namespace {
329 enum TestScenario {
330 kElementExistsQueryOnce = 0,
331 kElementExistsQueryTwice,
332 kElementNotExistsQueryOnce,
333 kElementExistsTimeout
336 class FindElementWebView : public StubWebView {
337 public:
338 FindElementWebView(bool only_one, TestScenario scenario)
339 : StubWebView("1"), only_one_(only_one), scenario_(scenario),
340 current_count_(0) {
341 switch (scenario_) {
342 case kElementExistsQueryOnce:
343 case kElementExistsQueryTwice:
344 case kElementExistsTimeout: {
345 if (only_one_) {
346 base::DictionaryValue element;
347 element.SetString("ELEMENT", "1");
348 result_.reset(element.DeepCopy());
349 } else {
350 base::DictionaryValue element1;
351 element1.SetString("ELEMENT", "1");
352 base::DictionaryValue element2;
353 element2.SetString("ELEMENT", "2");
354 base::ListValue list;
355 list.Append(element1.DeepCopy());
356 list.Append(element2.DeepCopy());
357 result_.reset(list.DeepCopy());
359 break;
361 case kElementNotExistsQueryOnce: {
362 if (only_one_)
363 result_.reset(base::Value::CreateNullValue());
364 else
365 result_.reset(new base::ListValue());
366 break;
370 ~FindElementWebView() override {}
372 void Verify(const std::string& expected_frame,
373 const base::ListValue* expected_args,
374 const base::Value* actrual_result) {
375 EXPECT_EQ(expected_frame, frame_);
376 std::string function;
377 if (only_one_)
378 function = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENT);
379 else
380 function = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENTS);
381 EXPECT_EQ(function, function_);
382 ASSERT_TRUE(args_.get());
383 EXPECT_TRUE(expected_args->Equals(args_.get()));
384 ASSERT_TRUE(actrual_result);
385 EXPECT_TRUE(result_->Equals(actrual_result));
388 // Overridden from WebView:
389 Status CallFunction(const std::string& frame,
390 const std::string& function,
391 const base::ListValue& args,
392 scoped_ptr<base::Value>* result) override {
393 ++current_count_;
394 if (scenario_ == kElementExistsTimeout ||
395 (scenario_ == kElementExistsQueryTwice && current_count_ == 1)) {
396 // Always return empty result when testing timeout.
397 if (only_one_)
398 result->reset(base::Value::CreateNullValue());
399 else
400 result->reset(new base::ListValue());
401 } else {
402 switch (scenario_) {
403 case kElementExistsQueryOnce:
404 case kElementNotExistsQueryOnce: {
405 EXPECT_EQ(1, current_count_);
406 break;
408 case kElementExistsQueryTwice: {
409 EXPECT_EQ(2, current_count_);
410 break;
412 default: {
413 break;
417 result->reset(result_->DeepCopy());
418 frame_ = frame;
419 function_ = function;
420 args_.reset(args.DeepCopy());
422 return Status(kOk);
425 private:
426 bool only_one_;
427 TestScenario scenario_;
428 int current_count_;
429 std::string frame_;
430 std::string function_;
431 scoped_ptr<base::ListValue> args_;
432 scoped_ptr<base::Value> result_;
435 } // namespace
437 TEST(CommandsTest, SuccessfulFindElement) {
438 FindElementWebView web_view(true, kElementExistsQueryTwice);
439 Session session("id");
440 session.implicit_wait = base::TimeDelta::FromSeconds(1);
441 session.SwitchToSubFrame("frame_id1", std::string());
442 base::DictionaryValue params;
443 params.SetString("using", "id");
444 params.SetString("value", "a");
445 scoped_ptr<base::Value> result;
446 ASSERT_EQ(kOk,
447 ExecuteFindElement(1, &session, &web_view, params, &result).code());
448 base::DictionaryValue param;
449 param.SetString("id", "a");
450 base::ListValue expected_args;
451 expected_args.Append(param.DeepCopy());
452 web_view.Verify("frame_id1", &expected_args, result.get());
455 TEST(CommandsTest, FailedFindElement) {
456 FindElementWebView web_view(true, kElementNotExistsQueryOnce);
457 Session session("id");
458 base::DictionaryValue params;
459 params.SetString("using", "id");
460 params.SetString("value", "a");
461 scoped_ptr<base::Value> result;
462 ASSERT_EQ(kNoSuchElement,
463 ExecuteFindElement(1, &session, &web_view, params, &result).code());
466 TEST(CommandsTest, SuccessfulFindElements) {
467 FindElementWebView web_view(false, kElementExistsQueryTwice);
468 Session session("id");
469 session.implicit_wait = base::TimeDelta::FromSeconds(1);
470 session.SwitchToSubFrame("frame_id2", std::string());
471 base::DictionaryValue params;
472 params.SetString("using", "name");
473 params.SetString("value", "b");
474 scoped_ptr<base::Value> result;
475 ASSERT_EQ(
476 kOk,
477 ExecuteFindElements(1, &session, &web_view, params, &result).code());
478 base::DictionaryValue param;
479 param.SetString("name", "b");
480 base::ListValue expected_args;
481 expected_args.Append(param.DeepCopy());
482 web_view.Verify("frame_id2", &expected_args, result.get());
485 TEST(CommandsTest, FailedFindElements) {
486 Session session("id");
487 FindElementWebView web_view(false, kElementNotExistsQueryOnce);
488 base::DictionaryValue params;
489 params.SetString("using", "id");
490 params.SetString("value", "a");
491 scoped_ptr<base::Value> result;
492 ASSERT_EQ(
493 kOk,
494 ExecuteFindElements(1, &session, &web_view, params, &result).code());
495 base::ListValue* list;
496 ASSERT_TRUE(result->GetAsList(&list));
497 ASSERT_EQ(0U, list->GetSize());
500 TEST(CommandsTest, SuccessfulFindChildElement) {
501 FindElementWebView web_view(true, kElementExistsQueryTwice);
502 Session session("id");
503 session.implicit_wait = base::TimeDelta::FromSeconds(1);
504 session.SwitchToSubFrame("frame_id3", std::string());
505 base::DictionaryValue params;
506 params.SetString("using", "tag name");
507 params.SetString("value", "div");
508 std::string element_id = "1";
509 scoped_ptr<base::Value> result;
510 ASSERT_EQ(
511 kOk,
512 ExecuteFindChildElement(
513 1, &session, &web_view, element_id, params, &result).code());
514 base::DictionaryValue locator_param;
515 locator_param.SetString("tag name", "div");
516 base::DictionaryValue root_element_param;
517 root_element_param.SetString("ELEMENT", element_id);
518 base::ListValue expected_args;
519 expected_args.Append(locator_param.DeepCopy());
520 expected_args.Append(root_element_param.DeepCopy());
521 web_view.Verify("frame_id3", &expected_args, result.get());
524 TEST(CommandsTest, FailedFindChildElement) {
525 Session session("id");
526 FindElementWebView web_view(true, kElementNotExistsQueryOnce);
527 base::DictionaryValue params;
528 params.SetString("using", "id");
529 params.SetString("value", "a");
530 std::string element_id = "1";
531 scoped_ptr<base::Value> result;
532 ASSERT_EQ(
533 kNoSuchElement,
534 ExecuteFindChildElement(
535 1, &session, &web_view, element_id, params, &result).code());
538 TEST(CommandsTest, SuccessfulFindChildElements) {
539 FindElementWebView web_view(false, kElementExistsQueryTwice);
540 Session session("id");
541 session.implicit_wait = base::TimeDelta::FromSeconds(1);
542 session.SwitchToSubFrame("frame_id4", std::string());
543 base::DictionaryValue params;
544 params.SetString("using", "class name");
545 params.SetString("value", "c");
546 std::string element_id = "1";
547 scoped_ptr<base::Value> result;
548 ASSERT_EQ(
549 kOk,
550 ExecuteFindChildElements(
551 1, &session, &web_view, element_id, params, &result).code());
552 base::DictionaryValue locator_param;
553 locator_param.SetString("class name", "c");
554 base::DictionaryValue root_element_param;
555 root_element_param.SetString("ELEMENT", element_id);
556 base::ListValue expected_args;
557 expected_args.Append(locator_param.DeepCopy());
558 expected_args.Append(root_element_param.DeepCopy());
559 web_view.Verify("frame_id4", &expected_args, result.get());
562 TEST(CommandsTest, FailedFindChildElements) {
563 Session session("id");
564 FindElementWebView web_view(false, kElementNotExistsQueryOnce);
565 base::DictionaryValue params;
566 params.SetString("using", "id");
567 params.SetString("value", "a");
568 std::string element_id = "1";
569 scoped_ptr<base::Value> result;
570 ASSERT_EQ(
571 kOk,
572 ExecuteFindChildElements(
573 1, &session, &web_view, element_id, params, &result).code());
574 base::ListValue* list;
575 ASSERT_TRUE(result->GetAsList(&list));
576 ASSERT_EQ(0U, list->GetSize());
579 TEST(CommandsTest, TimeoutInFindElement) {
580 Session session("id");
581 FindElementWebView web_view(true, kElementExistsTimeout);
582 session.implicit_wait = base::TimeDelta::FromMilliseconds(2);
583 base::DictionaryValue params;
584 params.SetString("using", "id");
585 params.SetString("value", "a");
586 params.SetString("id", "1");
587 scoped_ptr<base::Value> result;
588 ASSERT_EQ(kNoSuchElement,
589 ExecuteFindElement(1, &session, &web_view, params, &result).code());
592 namespace {
594 class ErrorCallFunctionWebView : public StubWebView {
595 public:
596 explicit ErrorCallFunctionWebView(StatusCode code)
597 : StubWebView("1"), code_(code) {}
598 ~ErrorCallFunctionWebView() override {}
600 // Overridden from WebView:
601 Status CallFunction(const std::string& frame,
602 const std::string& function,
603 const base::ListValue& args,
604 scoped_ptr<base::Value>* result) override {
605 return Status(code_);
608 private:
609 StatusCode code_;
612 } // namespace
614 TEST(CommandsTest, ErrorFindElement) {
615 Session session("id");
616 ErrorCallFunctionWebView web_view(kUnknownError);
617 base::DictionaryValue params;
618 params.SetString("using", "id");
619 params.SetString("value", "a");
620 scoped_ptr<base::Value> value;
621 ASSERT_EQ(kUnknownError,
622 ExecuteFindElement(1, &session, &web_view, params, &value).code());
623 ASSERT_EQ(kUnknownError,
624 ExecuteFindElements(1, &session, &web_view, params, &value).code());
627 TEST(CommandsTest, ErrorFindChildElement) {
628 Session session("id");
629 ErrorCallFunctionWebView web_view(kStaleElementReference);
630 base::DictionaryValue params;
631 params.SetString("using", "id");
632 params.SetString("value", "a");
633 std::string element_id = "1";
634 scoped_ptr<base::Value> result;
635 ASSERT_EQ(
636 kStaleElementReference,
637 ExecuteFindChildElement(
638 1, &session, &web_view, element_id, params, &result).code());
639 ASSERT_EQ(
640 kStaleElementReference,
641 ExecuteFindChildElements(
642 1, &session, &web_view, element_id, params, &result).code());
645 namespace {
647 class MockCommandListener : public CommandListener {
648 public:
649 MockCommandListener() : called_(false) {}
650 ~MockCommandListener() override {}
652 Status BeforeCommand(const std::string& command_name) override {
653 called_ = true;
654 EXPECT_STREQ("cmd", command_name.c_str());
655 return Status(kOk);
658 void VerifyCalled() {
659 EXPECT_TRUE(called_);
662 void VerifyNotCalled() {
663 EXPECT_FALSE(called_);
666 private:
667 bool called_;
670 Status ExecuteAddListenerToSessionCommand(
671 CommandListener* listener,
672 Session* session,
673 const base::DictionaryValue& params,
674 scoped_ptr<base::Value>* return_value) {
675 session->command_listeners.push_back(listener);
676 return Status(kOk);
679 Status ExecuteQuitSessionCommand(
680 Session* session,
681 const base::DictionaryValue& params,
682 scoped_ptr<base::Value>* return_value) {
683 session->quit = true;
684 return Status(kOk);
687 void OnSessionCommand(
688 base::RunLoop* run_loop,
689 const Status& status,
690 scoped_ptr<base::Value> value,
691 const std::string& session_id) {
692 ASSERT_EQ(kOk, status.code());
693 run_loop->Quit();
696 } // namespace
698 TEST(CommandsTest, SuccessNotifyingCommandListeners) {
699 SessionThreadMap map;
700 linked_ptr<base::Thread> thread(new base::Thread("1"));
701 ASSERT_TRUE(thread->Start());
702 std::string id("id");
703 thread->message_loop()->PostTask(
704 FROM_HERE,
705 base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id));
707 map[id] = thread;
709 base::DictionaryValue params;
710 scoped_ptr<MockCommandListener> listener(new MockCommandListener());
711 CommandListenerProxy* proxy = new CommandListenerProxy(listener.get());
712 // We add |proxy| to the session instead of adding |listener| directly so that
713 // after the session is destroyed by ExecuteQuitSessionCommand, we can still
714 // verify the listener was called. The session owns and will destroy |proxy|.
715 SessionCommand cmd = base::Bind(&ExecuteAddListenerToSessionCommand, proxy);
716 base::MessageLoop loop;
717 base::RunLoop run_loop_addlistener;
719 // |CommandListener|s are notified immediately before commands are run.
720 // Here, the command adds |listener| to the session, so |listener|
721 // should not be notified since it will not have been added yet.
722 ExecuteSessionCommand(
723 &map,
724 "cmd",
725 cmd,
726 false,
727 params,
729 base::Bind(&OnSessionCommand, &run_loop_addlistener));
730 run_loop_addlistener.Run();
732 listener->VerifyNotCalled();
734 base::RunLoop run_loop_testlistener;
735 cmd = base::Bind(&ExecuteQuitSessionCommand);
737 // |listener| was added to |session| by ExecuteAddListenerToSessionCommand
738 // and should be notified before the next command, ExecuteQuitSessionCommand.
739 ExecuteSessionCommand(
740 &map,
741 "cmd",
742 cmd,
743 false,
744 params,
746 base::Bind(&OnSessionCommand, &run_loop_testlistener));
747 run_loop_testlistener.Run();
749 listener->VerifyCalled();
752 namespace {
754 class FailingCommandListener : public CommandListener {
755 public:
756 FailingCommandListener() {}
757 ~FailingCommandListener() override {}
759 Status BeforeCommand(const std::string& command_name) override {
760 return Status(kUnknownError);
764 void AddListenerToSessionIfSessionExists(CommandListener* listener) {
765 Session* session = GetThreadLocalSession();
766 if (session) {
767 session->command_listeners.push_back(listener);
771 void OnFailBecauseErrorNotifyingListeners(
772 base::RunLoop* run_loop,
773 const Status& status,
774 scoped_ptr<base::Value> value,
775 const std::string& session_id) {
776 EXPECT_EQ(kUnknownError, status.code());
777 EXPECT_FALSE(value.get());
778 run_loop->Quit();
781 void VerifySessionWasDeleted() {
782 ASSERT_FALSE(GetThreadLocalSession());
785 } // namespace
787 TEST(CommandsTest, ErrorNotifyingCommandListeners) {
788 SessionThreadMap map;
789 linked_ptr<base::Thread> thread(new base::Thread("1"));
790 ASSERT_TRUE(thread->Start());
791 std::string id("id");
792 thread->message_loop()->PostTask(
793 FROM_HERE,
794 base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id));
795 map[id] = thread;
797 // In SuccessNotifyingCommandListenersBeforeCommand, we verified BeforeCommand
798 // was called before (as opposed to after) command execution. We don't need to
799 // verify this again, so we can just add |listener| with PostTask.
800 CommandListener* listener = new FailingCommandListener();
801 thread->message_loop()->PostTask(
802 FROM_HERE,
803 base::Bind(&AddListenerToSessionIfSessionExists, listener));
805 base::DictionaryValue params;
806 // The command should never be executed if BeforeCommand fails for a listener.
807 SessionCommand cmd = base::Bind(&ShouldNotBeCalled);
808 base::MessageLoop loop;
809 base::RunLoop run_loop;
811 ExecuteSessionCommand(
812 &map,
813 "cmd",
814 cmd,
815 false,
816 params,
818 base::Bind(&OnFailBecauseErrorNotifyingListeners, &run_loop));
819 run_loop.Run();
821 thread->message_loop()->PostTask(
822 FROM_HERE,
823 base::Bind(&VerifySessionWasDeleted));