Update CrOS OOBE throbber to MD throbber; delete old asset
[chromium-blink-merge.git] / chrome / test / chromedriver / commands_unittest.cc
bloba0e0a5e4a0440cce43f30adb25e02dfeff9c75f0
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/location.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/run_loop.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/synchronization/lock.h"
16 #include "base/threading/thread.h"
17 #include "base/values.h"
18 #include "chrome/test/chromedriver/chrome/status.h"
19 #include "chrome/test/chromedriver/chrome/stub_chrome.h"
20 #include "chrome/test/chromedriver/chrome/stub_web_view.h"
21 #include "chrome/test/chromedriver/chrome/web_view.h"
22 #include "chrome/test/chromedriver/command_listener_proxy.h"
23 #include "chrome/test/chromedriver/commands.h"
24 #include "chrome/test/chromedriver/element_commands.h"
25 #include "chrome/test/chromedriver/session.h"
26 #include "chrome/test/chromedriver/session_commands.h"
27 #include "chrome/test/chromedriver/window_commands.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "third_party/webdriver/atoms.h"
31 namespace {
33 void OnGetStatus(const Status& status,
34 scoped_ptr<base::Value> value,
35 const std::string& session_id) {
36 ASSERT_EQ(kOk, status.code());
37 base::DictionaryValue* dict;
38 ASSERT_TRUE(value->GetAsDictionary(&dict));
39 base::Value* unused;
40 ASSERT_TRUE(dict->Get("os.name", &unused));
41 ASSERT_TRUE(dict->Get("os.version", &unused));
42 ASSERT_TRUE(dict->Get("os.arch", &unused));
43 ASSERT_TRUE(dict->Get("build.version", &unused));
46 } // namespace
48 TEST(CommandsTest, GetStatus) {
49 base::DictionaryValue params;
50 ExecuteGetStatus(params, std::string(), base::Bind(&OnGetStatus));
53 namespace {
55 void ExecuteStubGetSession(int* count,
56 const base::DictionaryValue& params,
57 const std::string& session_id,
58 const CommandCallback& callback) {
59 if (*count == 0) {
60 EXPECT_STREQ("id", session_id.c_str());
61 } else {
62 EXPECT_STREQ("id2", session_id.c_str());
64 (*count)++;
66 scoped_ptr<base::DictionaryValue> capabilities(new base::DictionaryValue());
68 capabilities->Set("capability1", new base::StringValue("test1"));
69 capabilities->Set("capability2", new base::StringValue("test2"));
71 callback.Run(Status(kOk), capabilities.Pass(), session_id);
74 void OnGetSessions(const Status& status,
75 scoped_ptr<base::Value> value,
76 const std::string& session_id) {
77 ASSERT_EQ(kOk, status.code());
78 ASSERT_TRUE(value.get());
79 base::ListValue* sessions;
80 ASSERT_TRUE(value->GetAsList(&sessions));
81 ASSERT_EQ(static_cast<size_t>(2), sessions->GetSize());
83 base::DictionaryValue* session1;
84 base::DictionaryValue* session2;
85 ASSERT_TRUE(sessions->GetDictionary(0, &session1));
86 ASSERT_TRUE(sessions->GetDictionary(1, &session2));
88 ASSERT_EQ(static_cast<size_t>(2), session1->size());
89 ASSERT_EQ(static_cast<size_t>(2), session2->size());
91 std::string session1_id;
92 std::string session2_id;
93 base::DictionaryValue* session1_capabilities;
94 base::DictionaryValue* session2_capabilities;
96 ASSERT_TRUE(session1->GetString("sessionId", &session1_id));
97 ASSERT_TRUE(session2->GetString("sessionId", &session2_id));
98 ASSERT_TRUE(session1->GetDictionary("capabilities", &session1_capabilities));
99 ASSERT_TRUE(session2->GetDictionary("capabilities", &session2_capabilities));
101 ASSERT_EQ((size_t) 2, session1_capabilities->size());
102 ASSERT_EQ((size_t) 2, session2_capabilities->size());
103 ASSERT_EQ("id", session1_id);
104 ASSERT_EQ("id2", session2_id);
106 std::string session1_capability1;
107 std::string session1_capability2;
108 std::string session2_capability1;
109 std::string session2_capability2;
111 ASSERT_TRUE(session1_capabilities->GetString("capability1",
112 &session1_capability1));
113 ASSERT_TRUE(session1_capabilities->GetString("capability2",
114 &session1_capability2));
115 ASSERT_TRUE(session2_capabilities->GetString("capability1",
116 &session2_capability1));
117 ASSERT_TRUE(session2_capabilities->GetString("capability2",
118 &session2_capability2));
120 ASSERT_EQ("test1", session1_capability1);
121 ASSERT_EQ("test2", session1_capability2);
122 ASSERT_EQ("test1", session2_capability1);
123 ASSERT_EQ("test2", session2_capability2);
126 } // namespace
128 TEST(CommandsTest, GetSessions) {
129 SessionThreadMap map;
130 Session session("id");
131 Session session2("id2");
132 map[session.id] = make_linked_ptr(new base::Thread("1"));
133 map[session2.id] = make_linked_ptr(new base::Thread("2"));
135 int count = 0;
137 Command cmd = base::Bind(&ExecuteStubGetSession, &count);
139 base::DictionaryValue params;
140 base::MessageLoop loop;
142 ExecuteGetSessions(cmd, &map, params, std::string(),
143 base::Bind(&OnGetSessions));
144 ASSERT_EQ(2, count);
147 namespace {
149 void ExecuteStubQuit(
150 int* count,
151 const base::DictionaryValue& params,
152 const std::string& session_id,
153 const CommandCallback& callback) {
154 if (*count == 0) {
155 EXPECT_STREQ("id", session_id.c_str());
156 } else {
157 EXPECT_STREQ("id2", session_id.c_str());
159 (*count)++;
160 callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
163 void OnQuitAll(const Status& status,
164 scoped_ptr<base::Value> value,
165 const std::string& session_id) {
166 ASSERT_EQ(kOk, status.code());
167 ASSERT_FALSE(value.get());
170 } // namespace
172 TEST(CommandsTest, QuitAll) {
173 SessionThreadMap map;
174 Session session("id");
175 Session session2("id2");
176 map[session.id] = make_linked_ptr(new base::Thread("1"));
177 map[session2.id] = make_linked_ptr(new base::Thread("2"));
179 int count = 0;
180 Command cmd = base::Bind(&ExecuteStubQuit, &count);
181 base::DictionaryValue params;
182 base::MessageLoop loop;
183 ExecuteQuitAll(cmd, &map, params, std::string(), base::Bind(&OnQuitAll));
184 ASSERT_EQ(2, count);
187 namespace {
189 Status ExecuteSimpleCommand(
190 const std::string& expected_id,
191 base::DictionaryValue* expected_params,
192 base::Value* value,
193 Session* session,
194 const base::DictionaryValue& params,
195 scoped_ptr<base::Value>* return_value) {
196 EXPECT_EQ(expected_id, session->id);
197 EXPECT_TRUE(expected_params->Equals(&params));
198 return_value->reset(value->DeepCopy());
199 session->quit = true;
200 return Status(kOk);
203 void OnSimpleCommand(base::RunLoop* run_loop,
204 const std::string& expected_session_id,
205 base::Value* expected_value,
206 const Status& status,
207 scoped_ptr<base::Value> value,
208 const std::string& session_id) {
209 ASSERT_EQ(kOk, status.code());
210 ASSERT_TRUE(expected_value->Equals(value.get()));
211 ASSERT_EQ(expected_session_id, session_id);
212 run_loop->Quit();
215 } // namespace
217 TEST(CommandsTest, ExecuteSessionCommand) {
218 SessionThreadMap map;
219 linked_ptr<base::Thread> thread(new base::Thread("1"));
220 ASSERT_TRUE(thread->Start());
221 std::string id("id");
222 thread->task_runner()->PostTask(
223 FROM_HERE,
224 base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id));
225 map[id] = thread;
227 base::DictionaryValue params;
228 params.SetInteger("param", 5);
229 base::FundamentalValue expected_value(6);
230 SessionCommand cmd = base::Bind(
231 &ExecuteSimpleCommand, id, &params, &expected_value);
233 base::MessageLoop loop;
234 base::RunLoop run_loop;
235 ExecuteSessionCommand(
236 &map,
237 "cmd",
238 cmd,
239 false,
240 params,
242 base::Bind(&OnSimpleCommand, &run_loop, id, &expected_value));
243 run_loop.Run();
246 namespace {
248 Status ShouldNotBeCalled(
249 Session* session,
250 const base::DictionaryValue& params,
251 scoped_ptr<base::Value>* value) {
252 EXPECT_TRUE(false);
253 return Status(kOk);
256 void OnNoSuchSession(const Status& status,
257 scoped_ptr<base::Value> value,
258 const std::string& session_id) {
259 EXPECT_EQ(kNoSuchSession, status.code());
260 EXPECT_FALSE(value.get());
263 void OnNoSuchSessionIsOk(const Status& status,
264 scoped_ptr<base::Value> value,
265 const std::string& session_id) {
266 EXPECT_EQ(kOk, status.code());
267 EXPECT_FALSE(value.get());
270 } // namespace
272 TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSession) {
273 SessionThreadMap map;
274 base::DictionaryValue params;
275 ExecuteSessionCommand(&map,
276 "cmd",
277 base::Bind(&ShouldNotBeCalled),
278 false,
279 params,
280 "session",
281 base::Bind(&OnNoSuchSession));
284 TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSessionWhenItExpectsOk) {
285 SessionThreadMap map;
286 base::DictionaryValue params;
287 ExecuteSessionCommand(&map,
288 "cmd",
289 base::Bind(&ShouldNotBeCalled),
290 true,
291 params,
292 "session",
293 base::Bind(&OnNoSuchSessionIsOk));
296 namespace {
298 void OnNoSuchSessionAndQuit(base::RunLoop* run_loop,
299 const Status& status,
300 scoped_ptr<base::Value> value,
301 const std::string& session_id) {
302 run_loop->Quit();
303 EXPECT_EQ(kNoSuchSession, status.code());
304 EXPECT_FALSE(value.get());
307 } // namespace
309 TEST(CommandsTest, ExecuteSessionCommandOnJustDeletedSession) {
310 SessionThreadMap map;
311 linked_ptr<base::Thread> thread(new base::Thread("1"));
312 ASSERT_TRUE(thread->Start());
313 std::string id("id");
314 map[id] = thread;
316 base::MessageLoop loop;
317 base::RunLoop run_loop;
318 ExecuteSessionCommand(&map,
319 "cmd",
320 base::Bind(&ShouldNotBeCalled),
321 false,
322 base::DictionaryValue(),
323 "session",
324 base::Bind(&OnNoSuchSessionAndQuit, &run_loop));
325 run_loop.Run();
328 namespace {
330 enum TestScenario {
331 kElementExistsQueryOnce = 0,
332 kElementExistsQueryTwice,
333 kElementNotExistsQueryOnce,
334 kElementExistsTimeout
337 class FindElementWebView : public StubWebView {
338 public:
339 FindElementWebView(bool only_one, TestScenario scenario)
340 : StubWebView("1"), only_one_(only_one), scenario_(scenario),
341 current_count_(0) {
342 switch (scenario_) {
343 case kElementExistsQueryOnce:
344 case kElementExistsQueryTwice:
345 case kElementExistsTimeout: {
346 if (only_one_) {
347 base::DictionaryValue element;
348 element.SetString("ELEMENT", "1");
349 result_.reset(element.DeepCopy());
350 } else {
351 base::DictionaryValue element1;
352 element1.SetString("ELEMENT", "1");
353 base::DictionaryValue element2;
354 element2.SetString("ELEMENT", "2");
355 base::ListValue list;
356 list.Append(element1.DeepCopy());
357 list.Append(element2.DeepCopy());
358 result_.reset(list.DeepCopy());
360 break;
362 case kElementNotExistsQueryOnce: {
363 if (only_one_)
364 result_ = base::Value::CreateNullValue();
365 else
366 result_.reset(new base::ListValue());
367 break;
371 ~FindElementWebView() override {}
373 void Verify(const std::string& expected_frame,
374 const base::ListValue* expected_args,
375 const base::Value* actrual_result) {
376 EXPECT_EQ(expected_frame, frame_);
377 std::string function;
378 if (only_one_)
379 function = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENT);
380 else
381 function = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENTS);
382 EXPECT_EQ(function, function_);
383 ASSERT_TRUE(args_.get());
384 EXPECT_TRUE(expected_args->Equals(args_.get()));
385 ASSERT_TRUE(actrual_result);
386 EXPECT_TRUE(result_->Equals(actrual_result));
389 // Overridden from WebView:
390 Status CallFunction(const std::string& frame,
391 const std::string& function,
392 const base::ListValue& args,
393 scoped_ptr<base::Value>* result) override {
394 ++current_count_;
395 if (scenario_ == kElementExistsTimeout ||
396 (scenario_ == kElementExistsQueryTwice && current_count_ == 1)) {
397 // Always return empty result when testing timeout.
398 if (only_one_)
399 *result = base::Value::CreateNullValue();
400 else
401 result->reset(new base::ListValue());
402 } else {
403 switch (scenario_) {
404 case kElementExistsQueryOnce:
405 case kElementNotExistsQueryOnce: {
406 EXPECT_EQ(1, current_count_);
407 break;
409 case kElementExistsQueryTwice: {
410 EXPECT_EQ(2, current_count_);
411 break;
413 default: {
414 break;
418 *result = result_->CreateDeepCopy();
419 frame_ = frame;
420 function_ = function;
421 args_ = args.CreateDeepCopy();
423 return Status(kOk);
426 private:
427 bool only_one_;
428 TestScenario scenario_;
429 int current_count_;
430 std::string frame_;
431 std::string function_;
432 scoped_ptr<base::ListValue> args_;
433 scoped_ptr<base::Value> result_;
436 } // namespace
438 TEST(CommandsTest, SuccessfulFindElement) {
439 FindElementWebView web_view(true, kElementExistsQueryTwice);
440 Session session("id");
441 session.implicit_wait = base::TimeDelta::FromSeconds(1);
442 session.SwitchToSubFrame("frame_id1", std::string());
443 base::DictionaryValue params;
444 params.SetString("using", "id");
445 params.SetString("value", "a");
446 scoped_ptr<base::Value> result;
447 ASSERT_EQ(kOk,
448 ExecuteFindElement(1, &session, &web_view, params, &result).code());
449 base::DictionaryValue param;
450 param.SetString("id", "a");
451 base::ListValue expected_args;
452 expected_args.Append(param.DeepCopy());
453 web_view.Verify("frame_id1", &expected_args, result.get());
456 TEST(CommandsTest, FailedFindElement) {
457 FindElementWebView web_view(true, kElementNotExistsQueryOnce);
458 Session session("id");
459 base::DictionaryValue params;
460 params.SetString("using", "id");
461 params.SetString("value", "a");
462 scoped_ptr<base::Value> result;
463 ASSERT_EQ(kNoSuchElement,
464 ExecuteFindElement(1, &session, &web_view, params, &result).code());
467 TEST(CommandsTest, SuccessfulFindElements) {
468 FindElementWebView web_view(false, kElementExistsQueryTwice);
469 Session session("id");
470 session.implicit_wait = base::TimeDelta::FromSeconds(1);
471 session.SwitchToSubFrame("frame_id2", std::string());
472 base::DictionaryValue params;
473 params.SetString("using", "name");
474 params.SetString("value", "b");
475 scoped_ptr<base::Value> result;
476 ASSERT_EQ(
477 kOk,
478 ExecuteFindElements(1, &session, &web_view, params, &result).code());
479 base::DictionaryValue param;
480 param.SetString("name", "b");
481 base::ListValue expected_args;
482 expected_args.Append(param.DeepCopy());
483 web_view.Verify("frame_id2", &expected_args, result.get());
486 TEST(CommandsTest, FailedFindElements) {
487 Session session("id");
488 FindElementWebView web_view(false, kElementNotExistsQueryOnce);
489 base::DictionaryValue params;
490 params.SetString("using", "id");
491 params.SetString("value", "a");
492 scoped_ptr<base::Value> result;
493 ASSERT_EQ(
494 kOk,
495 ExecuteFindElements(1, &session, &web_view, params, &result).code());
496 base::ListValue* list;
497 ASSERT_TRUE(result->GetAsList(&list));
498 ASSERT_EQ(0U, list->GetSize());
501 TEST(CommandsTest, SuccessfulFindChildElement) {
502 FindElementWebView web_view(true, kElementExistsQueryTwice);
503 Session session("id");
504 session.implicit_wait = base::TimeDelta::FromSeconds(1);
505 session.SwitchToSubFrame("frame_id3", std::string());
506 base::DictionaryValue params;
507 params.SetString("using", "tag name");
508 params.SetString("value", "div");
509 std::string element_id = "1";
510 scoped_ptr<base::Value> result;
511 ASSERT_EQ(
512 kOk,
513 ExecuteFindChildElement(
514 1, &session, &web_view, element_id, params, &result).code());
515 base::DictionaryValue locator_param;
516 locator_param.SetString("tag name", "div");
517 base::DictionaryValue root_element_param;
518 root_element_param.SetString("ELEMENT", element_id);
519 base::ListValue expected_args;
520 expected_args.Append(locator_param.DeepCopy());
521 expected_args.Append(root_element_param.DeepCopy());
522 web_view.Verify("frame_id3", &expected_args, result.get());
525 TEST(CommandsTest, FailedFindChildElement) {
526 Session session("id");
527 FindElementWebView web_view(true, kElementNotExistsQueryOnce);
528 base::DictionaryValue params;
529 params.SetString("using", "id");
530 params.SetString("value", "a");
531 std::string element_id = "1";
532 scoped_ptr<base::Value> result;
533 ASSERT_EQ(
534 kNoSuchElement,
535 ExecuteFindChildElement(
536 1, &session, &web_view, element_id, params, &result).code());
539 TEST(CommandsTest, SuccessfulFindChildElements) {
540 FindElementWebView web_view(false, kElementExistsQueryTwice);
541 Session session("id");
542 session.implicit_wait = base::TimeDelta::FromSeconds(1);
543 session.SwitchToSubFrame("frame_id4", std::string());
544 base::DictionaryValue params;
545 params.SetString("using", "class name");
546 params.SetString("value", "c");
547 std::string element_id = "1";
548 scoped_ptr<base::Value> result;
549 ASSERT_EQ(
550 kOk,
551 ExecuteFindChildElements(
552 1, &session, &web_view, element_id, params, &result).code());
553 base::DictionaryValue locator_param;
554 locator_param.SetString("class name", "c");
555 base::DictionaryValue root_element_param;
556 root_element_param.SetString("ELEMENT", element_id);
557 base::ListValue expected_args;
558 expected_args.Append(locator_param.DeepCopy());
559 expected_args.Append(root_element_param.DeepCopy());
560 web_view.Verify("frame_id4", &expected_args, result.get());
563 TEST(CommandsTest, FailedFindChildElements) {
564 Session session("id");
565 FindElementWebView web_view(false, kElementNotExistsQueryOnce);
566 base::DictionaryValue params;
567 params.SetString("using", "id");
568 params.SetString("value", "a");
569 std::string element_id = "1";
570 scoped_ptr<base::Value> result;
571 ASSERT_EQ(
572 kOk,
573 ExecuteFindChildElements(
574 1, &session, &web_view, element_id, params, &result).code());
575 base::ListValue* list;
576 ASSERT_TRUE(result->GetAsList(&list));
577 ASSERT_EQ(0U, list->GetSize());
580 TEST(CommandsTest, TimeoutInFindElement) {
581 Session session("id");
582 FindElementWebView web_view(true, kElementExistsTimeout);
583 session.implicit_wait = base::TimeDelta::FromMilliseconds(2);
584 base::DictionaryValue params;
585 params.SetString("using", "id");
586 params.SetString("value", "a");
587 params.SetString("id", "1");
588 scoped_ptr<base::Value> result;
589 ASSERT_EQ(kNoSuchElement,
590 ExecuteFindElement(1, &session, &web_view, params, &result).code());
593 namespace {
595 class ErrorCallFunctionWebView : public StubWebView {
596 public:
597 explicit ErrorCallFunctionWebView(StatusCode code)
598 : StubWebView("1"), code_(code) {}
599 ~ErrorCallFunctionWebView() override {}
601 // Overridden from WebView:
602 Status CallFunction(const std::string& frame,
603 const std::string& function,
604 const base::ListValue& args,
605 scoped_ptr<base::Value>* result) override {
606 return Status(code_);
609 private:
610 StatusCode code_;
613 } // namespace
615 TEST(CommandsTest, ErrorFindElement) {
616 Session session("id");
617 ErrorCallFunctionWebView web_view(kUnknownError);
618 base::DictionaryValue params;
619 params.SetString("using", "id");
620 params.SetString("value", "a");
621 scoped_ptr<base::Value> value;
622 ASSERT_EQ(kUnknownError,
623 ExecuteFindElement(1, &session, &web_view, params, &value).code());
624 ASSERT_EQ(kUnknownError,
625 ExecuteFindElements(1, &session, &web_view, params, &value).code());
628 TEST(CommandsTest, ErrorFindChildElement) {
629 Session session("id");
630 ErrorCallFunctionWebView web_view(kStaleElementReference);
631 base::DictionaryValue params;
632 params.SetString("using", "id");
633 params.SetString("value", "a");
634 std::string element_id = "1";
635 scoped_ptr<base::Value> result;
636 ASSERT_EQ(
637 kStaleElementReference,
638 ExecuteFindChildElement(
639 1, &session, &web_view, element_id, params, &result).code());
640 ASSERT_EQ(
641 kStaleElementReference,
642 ExecuteFindChildElements(
643 1, &session, &web_view, element_id, params, &result).code());
646 namespace {
648 class MockCommandListener : public CommandListener {
649 public:
650 MockCommandListener() : called_(false) {}
651 ~MockCommandListener() override {}
653 Status BeforeCommand(const std::string& command_name) override {
654 called_ = true;
655 EXPECT_STREQ("cmd", command_name.c_str());
656 return Status(kOk);
659 void VerifyCalled() {
660 EXPECT_TRUE(called_);
663 void VerifyNotCalled() {
664 EXPECT_FALSE(called_);
667 private:
668 bool called_;
671 Status ExecuteAddListenerToSessionCommand(
672 CommandListener* listener,
673 Session* session,
674 const base::DictionaryValue& params,
675 scoped_ptr<base::Value>* return_value) {
676 session->command_listeners.push_back(listener);
677 return Status(kOk);
680 Status ExecuteQuitSessionCommand(
681 Session* session,
682 const base::DictionaryValue& params,
683 scoped_ptr<base::Value>* return_value) {
684 session->quit = true;
685 return Status(kOk);
688 void OnSessionCommand(
689 base::RunLoop* run_loop,
690 const Status& status,
691 scoped_ptr<base::Value> value,
692 const std::string& session_id) {
693 ASSERT_EQ(kOk, status.code());
694 run_loop->Quit();
697 } // namespace
699 TEST(CommandsTest, SuccessNotifyingCommandListeners) {
700 SessionThreadMap map;
701 linked_ptr<base::Thread> thread(new base::Thread("1"));
702 ASSERT_TRUE(thread->Start());
703 std::string id("id");
704 thread->task_runner()->PostTask(
705 FROM_HERE,
706 base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id));
708 map[id] = thread;
710 base::DictionaryValue params;
711 scoped_ptr<MockCommandListener> listener(new MockCommandListener());
712 CommandListenerProxy* proxy = new CommandListenerProxy(listener.get());
713 // We add |proxy| to the session instead of adding |listener| directly so that
714 // after the session is destroyed by ExecuteQuitSessionCommand, we can still
715 // verify the listener was called. The session owns and will destroy |proxy|.
716 SessionCommand cmd = base::Bind(&ExecuteAddListenerToSessionCommand, proxy);
717 base::MessageLoop loop;
718 base::RunLoop run_loop_addlistener;
720 // |CommandListener|s are notified immediately before commands are run.
721 // Here, the command adds |listener| to the session, so |listener|
722 // should not be notified since it will not have been added yet.
723 ExecuteSessionCommand(
724 &map,
725 "cmd",
726 cmd,
727 false,
728 params,
730 base::Bind(&OnSessionCommand, &run_loop_addlistener));
731 run_loop_addlistener.Run();
733 listener->VerifyNotCalled();
735 base::RunLoop run_loop_testlistener;
736 cmd = base::Bind(&ExecuteQuitSessionCommand);
738 // |listener| was added to |session| by ExecuteAddListenerToSessionCommand
739 // and should be notified before the next command, ExecuteQuitSessionCommand.
740 ExecuteSessionCommand(
741 &map,
742 "cmd",
743 cmd,
744 false,
745 params,
747 base::Bind(&OnSessionCommand, &run_loop_testlistener));
748 run_loop_testlistener.Run();
750 listener->VerifyCalled();
753 namespace {
755 class FailingCommandListener : public CommandListener {
756 public:
757 FailingCommandListener() {}
758 ~FailingCommandListener() override {}
760 Status BeforeCommand(const std::string& command_name) override {
761 return Status(kUnknownError);
765 void AddListenerToSessionIfSessionExists(CommandListener* listener) {
766 Session* session = GetThreadLocalSession();
767 if (session) {
768 session->command_listeners.push_back(listener);
772 void OnFailBecauseErrorNotifyingListeners(
773 base::RunLoop* run_loop,
774 const Status& status,
775 scoped_ptr<base::Value> value,
776 const std::string& session_id) {
777 EXPECT_EQ(kUnknownError, status.code());
778 EXPECT_FALSE(value.get());
779 run_loop->Quit();
782 void VerifySessionWasDeleted() {
783 ASSERT_FALSE(GetThreadLocalSession());
786 } // namespace
788 TEST(CommandsTest, ErrorNotifyingCommandListeners) {
789 SessionThreadMap map;
790 linked_ptr<base::Thread> thread(new base::Thread("1"));
791 ASSERT_TRUE(thread->Start());
792 std::string id("id");
793 thread->task_runner()->PostTask(
794 FROM_HERE,
795 base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id));
796 map[id] = thread;
798 // In SuccessNotifyingCommandListenersBeforeCommand, we verified BeforeCommand
799 // was called before (as opposed to after) command execution. We don't need to
800 // verify this again, so we can just add |listener| with PostTask.
801 CommandListener* listener = new FailingCommandListener();
802 thread->task_runner()->PostTask(
803 FROM_HERE, 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->task_runner()->PostTask(FROM_HERE,
822 base::Bind(&VerifySessionWasDeleted));