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
30namespace {
31
32void 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
47TEST(CommandsTest, GetStatus) {
48  base::DictionaryValue params;
49  ExecuteGetStatus(params, std::string(), base::Bind(&OnGetStatus));
50}
51
52namespace {
53
54void 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
68void 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
77TEST(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
92namespace {
93
94Status 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
108void 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
122TEST(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
151namespace {
152
153Status 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
161void 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
168void 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
177TEST(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
189TEST(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
201namespace {
202
203void 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
214TEST(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
233namespace {
234
235enum TestScenario {
236  kElementExistsQueryOnce = 0,
237  kElementExistsQueryTwice,
238  kElementNotExistsQueryOnce,
239  kElementExistsTimeout
240};
241
242class 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  virtual ~FindElementWebView() {}
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  virtual 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
343TEST(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
361TEST(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
372TEST(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
391TEST(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
406TEST(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
430TEST(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
444TEST(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
468TEST(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
485TEST(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
498namespace {
499
500class ErrorCallFunctionWebView : public StubWebView {
501 public:
502  explicit ErrorCallFunctionWebView(StatusCode code)
503      : StubWebView("1"), code_(code) {}
504  virtual ~ErrorCallFunctionWebView() {}
505
506  // Overridden from WebView:
507  virtual 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
520TEST(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
533TEST(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
551namespace {
552
553class MockCommandListener : public CommandListener {
554 public:
555  MockCommandListener() : called_(false) {}
556  virtual ~MockCommandListener() {}
557
558  virtual 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
576Status 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
585Status 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
593void 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
604TEST(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
658namespace {
659
660class FailingCommandListener : public CommandListener {
661 public:
662  FailingCommandListener() {}
663  virtual ~FailingCommandListener() {}
664
665  virtual Status BeforeCommand(const std::string& command_name) OVERRIDE {
666    return Status(kUnknownError);
667  }
668};
669
670void AddListenerToSessionIfSessionExists(CommandListener* listener) {
671  Session* session = GetThreadLocalSession();
672  if (session) {
673    session->command_listeners.push_back(listener);
674  }
675}
676
677void 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
687void VerifySessionWasDeleted() {
688  ASSERT_FALSE(GetThreadLocalSession());
689}
690
691}  // namespace
692
693TEST(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}
731