Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chrome / test / chromedriver / commands_unittest.cc
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.
4
5 #include <string>
6
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"
29
30 namespace {
31
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));
43 }
44
45 }  // namespace
46
47 TEST(CommandsTest, GetStatus) {
48   base::DictionaryValue params;
49   ExecuteGetStatus(params, std::string(), base::Bind(&OnGetStatus));
50 }
51
52 namespace {
53
54 void ExecuteStubQuit(
55     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());
63   }
64   (*count)++;
65   callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
66 }
67
68 void OnQuitAll(const Status& status,
69                scoped_ptr<base::Value> value,
70                const std::string& session_id) {
71   ASSERT_EQ(kOk, status.code());
72   ASSERT_FALSE(value.get());
73 }
74
75 }  // namespace
76
77 TEST(CommandsTest, QuitAll) {
78   SessionThreadMap map;
79   Session session("id");
80   Session session2("id2");
81   map[session.id] = make_linked_ptr(new base::Thread("1"));
82   map[session2.id] = make_linked_ptr(new base::Thread("2"));
83
84   int count = 0;
85   Command cmd = base::Bind(&ExecuteStubQuit, &count);
86   base::DictionaryValue params;
87   base::MessageLoop loop;
88   ExecuteQuitAll(cmd, &map, params, std::string(), base::Bind(&OnQuitAll));
89   ASSERT_EQ(2, count);
90 }
91
92 namespace {
93
94 Status ExecuteSimpleCommand(
95     const std::string& expected_id,
96     base::DictionaryValue* expected_params,
97     base::Value* value,
98     Session* session,
99     const base::DictionaryValue& params,
100     scoped_ptr<base::Value>* return_value) {
101   EXPECT_EQ(expected_id, session->id);
102   EXPECT_TRUE(expected_params->Equals(&params));
103   return_value->reset(value->DeepCopy());
104   session->quit = true;
105   return Status(kOk);
106 }
107
108 void OnSimpleCommand(base::RunLoop* run_loop,
109                      const std::string& expected_session_id,
110                      base::Value* expected_value,
111                      const Status& status,
112                      scoped_ptr<base::Value> value,
113                      const std::string& session_id) {
114   ASSERT_EQ(kOk, status.code());
115   ASSERT_TRUE(expected_value->Equals(value.get()));
116   ASSERT_EQ(expected_session_id, session_id);
117   run_loop->Quit();
118 }
119
120 }  // namespace
121
122 TEST(CommandsTest, ExecuteSessionCommand) {
123   SessionThreadMap map;
124   linked_ptr<base::Thread> thread(new base::Thread("1"));
125   ASSERT_TRUE(thread->Start());
126   std::string id("id");
127   thread->message_loop()->PostTask(
128       FROM_HERE,
129       base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id));
130   map[id] = thread;
131
132   base::DictionaryValue params;
133   params.SetInteger("param", 5);
134   base::FundamentalValue expected_value(6);
135   SessionCommand cmd = base::Bind(
136       &ExecuteSimpleCommand, id, &params, &expected_value);
137
138   base::MessageLoop loop;
139   base::RunLoop run_loop;
140   ExecuteSessionCommand(
141       &map,
142       "cmd",
143       cmd,
144       false,
145       params,
146       id,
147       base::Bind(&OnSimpleCommand, &run_loop, id, &expected_value));
148   run_loop.Run();
149 }
150
151 namespace {
152
153 Status ShouldNotBeCalled(
154     Session* session,
155     const base::DictionaryValue& params,
156     scoped_ptr<base::Value>* value) {
157   EXPECT_TRUE(false);
158   return Status(kOk);
159 }
160
161 void OnNoSuchSession(const Status& status,
162                      scoped_ptr<base::Value> value,
163                      const std::string& session_id) {
164   EXPECT_EQ(kNoSuchSession, status.code());
165   EXPECT_FALSE(value.get());
166 }
167
168 void OnNoSuchSessionIsOk(const Status& status,
169                          scoped_ptr<base::Value> value,
170                          const std::string& session_id) {
171   EXPECT_EQ(kOk, status.code());
172   EXPECT_FALSE(value.get());
173 }
174
175 }  // namespace
176
177 TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSession) {
178   SessionThreadMap map;
179   base::DictionaryValue params;
180   ExecuteSessionCommand(&map,
181                         "cmd",
182                         base::Bind(&ShouldNotBeCalled),
183                         false,
184                         params,
185                         "session",
186                         base::Bind(&OnNoSuchSession));
187 }
188
189 TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSessionWhenItExpectsOk) {
190   SessionThreadMap map;
191   base::DictionaryValue params;
192   ExecuteSessionCommand(&map,
193                         "cmd",
194                         base::Bind(&ShouldNotBeCalled),
195                         true,
196                         params,
197                         "session",
198                         base::Bind(&OnNoSuchSessionIsOk));
199 }
200
201 namespace {
202
203 void OnNoSuchSessionAndQuit(base::RunLoop* run_loop,
204                             const Status& status,
205                             scoped_ptr<base::Value> value,
206                             const std::string& session_id) {
207   run_loop->Quit();
208   EXPECT_EQ(kNoSuchSession, status.code());
209   EXPECT_FALSE(value.get());
210 }
211
212 }  // namespace
213
214 TEST(CommandsTest, ExecuteSessionCommandOnJustDeletedSession) {
215   SessionThreadMap map;
216   linked_ptr<base::Thread> thread(new base::Thread("1"));
217   ASSERT_TRUE(thread->Start());
218   std::string id("id");
219   map[id] = thread;
220
221   base::MessageLoop loop;
222   base::RunLoop run_loop;
223   ExecuteSessionCommand(&map,
224                         "cmd",
225                         base::Bind(&ShouldNotBeCalled),
226                         false,
227                         base::DictionaryValue(),
228                         "session",
229                         base::Bind(&OnNoSuchSessionAndQuit, &run_loop));
230   run_loop.Run();
231 }
232
233 namespace {
234
235 enum TestScenario {
236   kElementExistsQueryOnce = 0,
237   kElementExistsQueryTwice,
238   kElementNotExistsQueryOnce,
239   kElementExistsTimeout
240 };
241
242 class FindElementWebView : public StubWebView {
243  public:
244   FindElementWebView(bool only_one, TestScenario scenario)
245       : StubWebView("1"), only_one_(only_one), scenario_(scenario),
246         current_count_(0) {
247     switch (scenario_) {
248       case kElementExistsQueryOnce:
249       case kElementExistsQueryTwice:
250       case kElementExistsTimeout: {
251         if (only_one_) {
252           base::DictionaryValue element;
253           element.SetString("ELEMENT", "1");
254           result_.reset(element.DeepCopy());
255         } else {
256           base::DictionaryValue element1;
257           element1.SetString("ELEMENT", "1");
258           base::DictionaryValue element2;
259           element2.SetString("ELEMENT", "2");
260           base::ListValue list;
261           list.Append(element1.DeepCopy());
262           list.Append(element2.DeepCopy());
263           result_.reset(list.DeepCopy());
264         }
265         break;
266       }
267       case kElementNotExistsQueryOnce: {
268         if (only_one_)
269           result_.reset(base::Value::CreateNullValue());
270         else
271           result_.reset(new base::ListValue());
272         break;
273       }
274     }
275   }
276   ~FindElementWebView() override {}
277
278   void Verify(const std::string& expected_frame,
279               const base::ListValue* expected_args,
280               const base::Value* actrual_result) {
281     EXPECT_EQ(expected_frame, frame_);
282     std::string function;
283     if (only_one_)
284       function = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENT);
285     else
286       function = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENTS);
287     EXPECT_EQ(function, function_);
288     ASSERT_TRUE(args_.get());
289     EXPECT_TRUE(expected_args->Equals(args_.get()));
290     ASSERT_TRUE(actrual_result);
291     EXPECT_TRUE(result_->Equals(actrual_result));
292   }
293
294   // Overridden from WebView:
295   Status CallFunction(const std::string& frame,
296                       const std::string& function,
297                       const base::ListValue& args,
298                       scoped_ptr<base::Value>* result) override {
299     ++current_count_;
300     if (scenario_ == kElementExistsTimeout ||
301         (scenario_ == kElementExistsQueryTwice && current_count_ == 1)) {
302         // Always return empty result when testing timeout.
303         if (only_one_)
304           result->reset(base::Value::CreateNullValue());
305         else
306           result->reset(new base::ListValue());
307     } else {
308       switch (scenario_) {
309         case kElementExistsQueryOnce:
310         case kElementNotExistsQueryOnce: {
311           EXPECT_EQ(1, current_count_);
312           break;
313         }
314         case kElementExistsQueryTwice: {
315           EXPECT_EQ(2, current_count_);
316           break;
317         }
318         default: {
319           break;
320         }
321       }
322
323       result->reset(result_->DeepCopy());
324       frame_ = frame;
325       function_ = function;
326       args_.reset(args.DeepCopy());
327     }
328     return Status(kOk);
329   }
330
331  private:
332   bool only_one_;
333   TestScenario scenario_;
334   int current_count_;
335   std::string frame_;
336   std::string function_;
337   scoped_ptr<base::ListValue> args_;
338   scoped_ptr<base::Value> result_;
339 };
340
341 }  // namespace
342
343 TEST(CommandsTest, SuccessfulFindElement) {
344   FindElementWebView web_view(true, kElementExistsQueryTwice);
345   Session session("id");
346   session.implicit_wait = base::TimeDelta::FromSeconds(1);
347   session.SwitchToSubFrame("frame_id1", std::string());
348   base::DictionaryValue params;
349   params.SetString("using", "id");
350   params.SetString("value", "a");
351   scoped_ptr<base::Value> result;
352   ASSERT_EQ(kOk,
353             ExecuteFindElement(1, &session, &web_view, params, &result).code());
354   base::DictionaryValue param;
355   param.SetString("id", "a");
356   base::ListValue expected_args;
357   expected_args.Append(param.DeepCopy());
358   web_view.Verify("frame_id1", &expected_args, result.get());
359 }
360
361 TEST(CommandsTest, FailedFindElement) {
362   FindElementWebView web_view(true, kElementNotExistsQueryOnce);
363   Session session("id");
364   base::DictionaryValue params;
365   params.SetString("using", "id");
366   params.SetString("value", "a");
367   scoped_ptr<base::Value> result;
368   ASSERT_EQ(kNoSuchElement,
369             ExecuteFindElement(1, &session, &web_view, params, &result).code());
370 }
371
372 TEST(CommandsTest, SuccessfulFindElements) {
373   FindElementWebView web_view(false, kElementExistsQueryTwice);
374   Session session("id");
375   session.implicit_wait = base::TimeDelta::FromSeconds(1);
376   session.SwitchToSubFrame("frame_id2", std::string());
377   base::DictionaryValue params;
378   params.SetString("using", "name");
379   params.SetString("value", "b");
380   scoped_ptr<base::Value> result;
381   ASSERT_EQ(
382       kOk,
383       ExecuteFindElements(1, &session, &web_view, params, &result).code());
384   base::DictionaryValue param;
385   param.SetString("name", "b");
386   base::ListValue expected_args;
387   expected_args.Append(param.DeepCopy());
388   web_view.Verify("frame_id2", &expected_args, result.get());
389 }
390
391 TEST(CommandsTest, FailedFindElements) {
392   Session session("id");
393   FindElementWebView web_view(false, kElementNotExistsQueryOnce);
394   base::DictionaryValue params;
395   params.SetString("using", "id");
396   params.SetString("value", "a");
397   scoped_ptr<base::Value> result;
398   ASSERT_EQ(
399       kOk,
400       ExecuteFindElements(1, &session, &web_view, params, &result).code());
401   base::ListValue* list;
402   ASSERT_TRUE(result->GetAsList(&list));
403   ASSERT_EQ(0U, list->GetSize());
404 }
405
406 TEST(CommandsTest, SuccessfulFindChildElement) {
407   FindElementWebView web_view(true, kElementExistsQueryTwice);
408   Session session("id");
409   session.implicit_wait = base::TimeDelta::FromSeconds(1);
410   session.SwitchToSubFrame("frame_id3", std::string());
411   base::DictionaryValue params;
412   params.SetString("using", "tag name");
413   params.SetString("value", "div");
414   std::string element_id = "1";
415   scoped_ptr<base::Value> result;
416   ASSERT_EQ(
417       kOk,
418       ExecuteFindChildElement(
419           1, &session, &web_view, element_id, params, &result).code());
420   base::DictionaryValue locator_param;
421   locator_param.SetString("tag name", "div");
422   base::DictionaryValue root_element_param;
423   root_element_param.SetString("ELEMENT", element_id);
424   base::ListValue expected_args;
425   expected_args.Append(locator_param.DeepCopy());
426   expected_args.Append(root_element_param.DeepCopy());
427   web_view.Verify("frame_id3", &expected_args, result.get());
428 }
429
430 TEST(CommandsTest, FailedFindChildElement) {
431   Session session("id");
432   FindElementWebView web_view(true, kElementNotExistsQueryOnce);
433   base::DictionaryValue params;
434   params.SetString("using", "id");
435   params.SetString("value", "a");
436   std::string element_id = "1";
437   scoped_ptr<base::Value> result;
438   ASSERT_EQ(
439       kNoSuchElement,
440       ExecuteFindChildElement(
441           1, &session, &web_view, element_id, params, &result).code());
442 }
443
444 TEST(CommandsTest, SuccessfulFindChildElements) {
445   FindElementWebView web_view(false, kElementExistsQueryTwice);
446   Session session("id");
447   session.implicit_wait = base::TimeDelta::FromSeconds(1);
448   session.SwitchToSubFrame("frame_id4", std::string());
449   base::DictionaryValue params;
450   params.SetString("using", "class name");
451   params.SetString("value", "c");
452   std::string element_id = "1";
453   scoped_ptr<base::Value> result;
454   ASSERT_EQ(
455       kOk,
456       ExecuteFindChildElements(
457           1, &session, &web_view, element_id, params, &result).code());
458   base::DictionaryValue locator_param;
459   locator_param.SetString("class name", "c");
460   base::DictionaryValue root_element_param;
461   root_element_param.SetString("ELEMENT", element_id);
462   base::ListValue expected_args;
463   expected_args.Append(locator_param.DeepCopy());
464   expected_args.Append(root_element_param.DeepCopy());
465   web_view.Verify("frame_id4", &expected_args, result.get());
466 }
467
468 TEST(CommandsTest, FailedFindChildElements) {
469   Session session("id");
470   FindElementWebView web_view(false, kElementNotExistsQueryOnce);
471   base::DictionaryValue params;
472   params.SetString("using", "id");
473   params.SetString("value", "a");
474   std::string element_id = "1";
475   scoped_ptr<base::Value> result;
476   ASSERT_EQ(
477       kOk,
478       ExecuteFindChildElements(
479           1, &session, &web_view, element_id, params, &result).code());
480   base::ListValue* list;
481   ASSERT_TRUE(result->GetAsList(&list));
482   ASSERT_EQ(0U, list->GetSize());
483 }
484
485 TEST(CommandsTest, TimeoutInFindElement) {
486   Session session("id");
487   FindElementWebView web_view(true, kElementExistsTimeout);
488   session.implicit_wait = base::TimeDelta::FromMilliseconds(2);
489   base::DictionaryValue params;
490   params.SetString("using", "id");
491   params.SetString("value", "a");
492   params.SetString("id", "1");
493   scoped_ptr<base::Value> result;
494   ASSERT_EQ(kNoSuchElement,
495             ExecuteFindElement(1, &session, &web_view, params, &result).code());
496 }
497
498 namespace {
499
500 class ErrorCallFunctionWebView : public StubWebView {
501  public:
502   explicit ErrorCallFunctionWebView(StatusCode code)
503       : StubWebView("1"), code_(code) {}
504   ~ErrorCallFunctionWebView() override {}
505
506   // Overridden from WebView:
507   Status CallFunction(const std::string& frame,
508                       const std::string& function,
509                       const base::ListValue& args,
510                       scoped_ptr<base::Value>* result) override {
511     return Status(code_);
512   }
513
514  private:
515   StatusCode code_;
516 };
517
518 }  // namespace
519
520 TEST(CommandsTest, ErrorFindElement) {
521   Session session("id");
522   ErrorCallFunctionWebView web_view(kUnknownError);
523   base::DictionaryValue params;
524   params.SetString("using", "id");
525   params.SetString("value", "a");
526   scoped_ptr<base::Value> value;
527   ASSERT_EQ(kUnknownError,
528             ExecuteFindElement(1, &session, &web_view, params, &value).code());
529   ASSERT_EQ(kUnknownError,
530             ExecuteFindElements(1, &session, &web_view, params, &value).code());
531 }
532
533 TEST(CommandsTest, ErrorFindChildElement) {
534   Session session("id");
535   ErrorCallFunctionWebView web_view(kStaleElementReference);
536   base::DictionaryValue params;
537   params.SetString("using", "id");
538   params.SetString("value", "a");
539   std::string element_id = "1";
540   scoped_ptr<base::Value> result;
541   ASSERT_EQ(
542       kStaleElementReference,
543       ExecuteFindChildElement(
544           1, &session, &web_view, element_id, params, &result).code());
545   ASSERT_EQ(
546       kStaleElementReference,
547       ExecuteFindChildElements(
548           1, &session, &web_view, element_id, params, &result).code());
549 }
550
551 namespace {
552
553 class MockCommandListener : public CommandListener {
554  public:
555   MockCommandListener() : called_(false) {}
556   ~MockCommandListener() override {}
557
558   Status BeforeCommand(const std::string& command_name) override {
559     called_ = true;
560     EXPECT_STREQ("cmd", command_name.c_str());
561     return Status(kOk);
562   }
563
564   void VerifyCalled() {
565     EXPECT_TRUE(called_);
566   }
567
568   void VerifyNotCalled() {
569     EXPECT_FALSE(called_);
570   }
571
572  private:
573   bool called_;
574 };
575
576 Status ExecuteAddListenerToSessionCommand(
577     CommandListener* listener,
578     Session* session,
579     const base::DictionaryValue& params,
580     scoped_ptr<base::Value>* return_value) {
581   session->command_listeners.push_back(listener);
582   return Status(kOk);
583 }
584
585 Status ExecuteQuitSessionCommand(
586     Session* session,
587     const base::DictionaryValue& params,
588     scoped_ptr<base::Value>* return_value) {
589   session->quit = true;
590   return Status(kOk);
591 }
592
593 void OnSessionCommand(
594     base::RunLoop* run_loop,
595     const Status& status,
596     scoped_ptr<base::Value> value,
597     const std::string& session_id) {
598   ASSERT_EQ(kOk, status.code());
599   run_loop->Quit();
600 }
601
602 }  // namespace
603
604 TEST(CommandsTest, SuccessNotifyingCommandListeners) {
605   SessionThreadMap map;
606   linked_ptr<base::Thread> thread(new base::Thread("1"));
607   ASSERT_TRUE(thread->Start());
608   std::string id("id");
609   thread->message_loop()->PostTask(
610       FROM_HERE,
611       base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id));
612
613   map[id] = thread;
614
615   base::DictionaryValue params;
616   scoped_ptr<MockCommandListener> listener(new MockCommandListener());
617   CommandListenerProxy* proxy = new CommandListenerProxy(listener.get());
618   // We add |proxy| to the session instead of adding |listener| directly so that
619   // after the session is destroyed by ExecuteQuitSessionCommand, we can still
620   // verify the listener was called. The session owns and will destroy |proxy|.
621   SessionCommand cmd = base::Bind(&ExecuteAddListenerToSessionCommand, proxy);
622   base::MessageLoop loop;
623   base::RunLoop run_loop_addlistener;
624
625   // |CommandListener|s are notified immediately before commands are run.
626   // Here, the command adds |listener| to the session, so |listener|
627   // should not be notified since it will not have been added yet.
628   ExecuteSessionCommand(
629       &map,
630       "cmd",
631       cmd,
632       false,
633       params,
634       id,
635       base::Bind(&OnSessionCommand, &run_loop_addlistener));
636   run_loop_addlistener.Run();
637
638   listener->VerifyNotCalled();
639
640   base::RunLoop run_loop_testlistener;
641   cmd = base::Bind(&ExecuteQuitSessionCommand);
642
643   // |listener| was added to |session| by ExecuteAddListenerToSessionCommand
644   // and should be notified before the next command, ExecuteQuitSessionCommand.
645   ExecuteSessionCommand(
646       &map,
647       "cmd",
648       cmd,
649       false,
650       params,
651       id,
652       base::Bind(&OnSessionCommand, &run_loop_testlistener));
653   run_loop_testlistener.Run();
654
655   listener->VerifyCalled();
656 }
657
658 namespace {
659
660 class FailingCommandListener : public CommandListener {
661  public:
662   FailingCommandListener() {}
663   ~FailingCommandListener() override {}
664
665   Status BeforeCommand(const std::string& command_name) override {
666     return Status(kUnknownError);
667   }
668 };
669
670 void AddListenerToSessionIfSessionExists(CommandListener* listener) {
671   Session* session = GetThreadLocalSession();
672   if (session) {
673     session->command_listeners.push_back(listener);
674   }
675 }
676
677 void OnFailBecauseErrorNotifyingListeners(
678     base::RunLoop* run_loop,
679     const Status& status,
680     scoped_ptr<base::Value> value,
681     const std::string& session_id) {
682   EXPECT_EQ(kUnknownError, status.code());
683   EXPECT_FALSE(value.get());
684   run_loop->Quit();
685 }
686
687 void VerifySessionWasDeleted() {
688   ASSERT_FALSE(GetThreadLocalSession());
689 }
690
691 }  // namespace
692
693 TEST(CommandsTest, ErrorNotifyingCommandListeners) {
694   SessionThreadMap map;
695   linked_ptr<base::Thread> thread(new base::Thread("1"));
696   ASSERT_TRUE(thread->Start());
697   std::string id("id");
698   thread->message_loop()->PostTask(
699       FROM_HERE,
700       base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id));
701   map[id] = thread;
702
703   // In SuccessNotifyingCommandListenersBeforeCommand, we verified BeforeCommand
704   // was called before (as opposed to after) command execution. We don't need to
705   // verify this again, so we can just add |listener| with PostTask.
706   CommandListener* listener = new FailingCommandListener();
707   thread->message_loop()->PostTask(
708       FROM_HERE,
709       base::Bind(&AddListenerToSessionIfSessionExists, listener));
710
711   base::DictionaryValue params;
712   // The command should never be executed if BeforeCommand fails for a listener.
713   SessionCommand cmd = base::Bind(&ShouldNotBeCalled);
714   base::MessageLoop loop;
715   base::RunLoop run_loop;
716
717   ExecuteSessionCommand(
718       &map,
719       "cmd",
720       cmd,
721       false,
722       params,
723       id,
724       base::Bind(&OnFailBecauseErrorNotifyingListeners, &run_loop));
725   run_loop.Run();
726
727   thread->message_loop()->PostTask(
728       FROM_HERE,
729       base::Bind(&VerifySessionWasDeleted));
730 }