Roll src/third_party/WebKit bf18a82:a9cee16 (svn 185297:185304)
[chromium-blink-merge.git] / chrome / test / chromedriver / commands_unittest.cc
blobfa6bd9cb2b54f3bfab39d3fcb8e9e0344d454c66
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 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());
64 (*count)++;
65 callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
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());
75 } // namespace
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"));
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);
92 namespace {
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);
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();
120 } // namespace
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;
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);
138 base::MessageLoop loop;
139 base::RunLoop run_loop;
140 ExecuteSessionCommand(
141 &map,
142 "cmd",
143 cmd,
144 false,
145 params,
147 base::Bind(&OnSimpleCommand, &run_loop, id, &expected_value));
148 run_loop.Run();
151 namespace {
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);
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());
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());
175 } // namespace
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));
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));
201 namespace {
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());
212 } // namespace
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;
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();
233 namespace {
235 enum TestScenario {
236 kElementExistsQueryOnce = 0,
237 kElementExistsQueryTwice,
238 kElementNotExistsQueryOnce,
239 kElementExistsTimeout
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());
265 break;
267 case kElementNotExistsQueryOnce: {
268 if (only_one_)
269 result_.reset(base::Value::CreateNullValue());
270 else
271 result_.reset(new base::ListValue());
272 break;
276 ~FindElementWebView() override {}
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));
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;
314 case kElementExistsQueryTwice: {
315 EXPECT_EQ(2, current_count_);
316 break;
318 default: {
319 break;
323 result->reset(result_->DeepCopy());
324 frame_ = frame;
325 function_ = function;
326 args_.reset(args.DeepCopy());
328 return Status(kOk);
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_;
341 } // namespace
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());
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());
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());
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());
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());
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());
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());
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());
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());
498 namespace {
500 class ErrorCallFunctionWebView : public StubWebView {
501 public:
502 explicit ErrorCallFunctionWebView(StatusCode code)
503 : StubWebView("1"), code_(code) {}
504 ~ErrorCallFunctionWebView() override {}
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_);
514 private:
515 StatusCode code_;
518 } // namespace
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());
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());
551 namespace {
553 class MockCommandListener : public CommandListener {
554 public:
555 MockCommandListener() : called_(false) {}
556 ~MockCommandListener() override {}
558 Status BeforeCommand(const std::string& command_name) override {
559 called_ = true;
560 EXPECT_STREQ("cmd", command_name.c_str());
561 return Status(kOk);
564 void VerifyCalled() {
565 EXPECT_TRUE(called_);
568 void VerifyNotCalled() {
569 EXPECT_FALSE(called_);
572 private:
573 bool called_;
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);
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);
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();
602 } // namespace
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));
613 map[id] = thread;
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;
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,
635 base::Bind(&OnSessionCommand, &run_loop_addlistener));
636 run_loop_addlistener.Run();
638 listener->VerifyNotCalled();
640 base::RunLoop run_loop_testlistener;
641 cmd = base::Bind(&ExecuteQuitSessionCommand);
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,
652 base::Bind(&OnSessionCommand, &run_loop_testlistener));
653 run_loop_testlistener.Run();
655 listener->VerifyCalled();
658 namespace {
660 class FailingCommandListener : public CommandListener {
661 public:
662 FailingCommandListener() {}
663 ~FailingCommandListener() override {}
665 Status BeforeCommand(const std::string& command_name) override {
666 return Status(kUnknownError);
670 void AddListenerToSessionIfSessionExists(CommandListener* listener) {
671 Session* session = GetThreadLocalSession();
672 if (session) {
673 session->command_listeners.push_back(listener);
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();
687 void VerifySessionWasDeleted() {
688 ASSERT_FALSE(GetThreadLocalSession());
691 } // namespace
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;
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));
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;
717 ExecuteSessionCommand(
718 &map,
719 "cmd",
720 cmd,
721 false,
722 params,
724 base::Bind(&OnFailBecauseErrorNotifyingListeners, &run_loop));
725 run_loop.Run();
727 thread->message_loop()->PostTask(
728 FROM_HERE,
729 base::Bind(&VerifySessionWasDeleted));