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/commands.h" 22#include "chrome/test/chromedriver/element_commands.h" 23#include "chrome/test/chromedriver/session.h" 24#include "chrome/test/chromedriver/session_commands.h" 25#include "chrome/test/chromedriver/window_commands.h" 26#include "testing/gtest/include/gtest/gtest.h" 27#include "third_party/webdriver/atoms.h" 28 29namespace { 30 31void OnGetStatus(const Status& status, 32 scoped_ptr<base::Value> value, 33 const std::string& session_id) { 34 ASSERT_EQ(kOk, status.code()); 35 base::DictionaryValue* dict; 36 ASSERT_TRUE(value->GetAsDictionary(&dict)); 37 base::Value* unused; 38 ASSERT_TRUE(dict->Get("os.name", &unused)); 39 ASSERT_TRUE(dict->Get("os.version", &unused)); 40 ASSERT_TRUE(dict->Get("os.arch", &unused)); 41 ASSERT_TRUE(dict->Get("build.version", &unused)); 42} 43 44} // namespace 45 46TEST(CommandsTest, GetStatus) { 47 base::DictionaryValue params; 48 ExecuteGetStatus(params, std::string(), base::Bind(&OnGetStatus)); 49} 50 51namespace { 52 53void ExecuteStubQuit( 54 int* count, 55 const base::DictionaryValue& params, 56 const std::string& session_id, 57 const CommandCallback& callback) { 58 if (*count == 0) { 59 EXPECT_STREQ("id", session_id.c_str()); 60 } else { 61 EXPECT_STREQ("id2", session_id.c_str()); 62 } 63 (*count)++; 64 callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id); 65} 66 67void OnQuitAll(const Status& status, 68 scoped_ptr<base::Value> value, 69 const std::string& session_id) { 70 ASSERT_EQ(kOk, status.code()); 71 ASSERT_FALSE(value.get()); 72} 73 74} // namespace 75 76TEST(CommandsTest, QuitAll) { 77 SessionThreadMap map; 78 Session session("id"); 79 Session session2("id2"); 80 map[session.id] = make_linked_ptr(new base::Thread("1")); 81 map[session2.id] = make_linked_ptr(new base::Thread("2")); 82 83 int count = 0; 84 Command cmd = base::Bind(&ExecuteStubQuit, &count); 85 base::DictionaryValue params; 86 base::MessageLoop loop; 87 ExecuteQuitAll(cmd, &map, params, std::string(), base::Bind(&OnQuitAll)); 88 ASSERT_EQ(2, count); 89} 90 91namespace { 92 93Status ExecuteSimpleCommand( 94 const std::string& expected_id, 95 base::DictionaryValue* expected_params, 96 base::Value* value, 97 Session* session, 98 const base::DictionaryValue& params, 99 scoped_ptr<base::Value>* return_value) { 100 EXPECT_EQ(expected_id, session->id); 101 EXPECT_TRUE(expected_params->Equals(¶ms)); 102 return_value->reset(value->DeepCopy()); 103 session->quit = true; 104 return Status(kOk); 105} 106 107void OnSimpleCommand(base::RunLoop* run_loop, 108 const std::string& expected_session_id, 109 base::Value* expected_value, 110 const Status& status, 111 scoped_ptr<base::Value> value, 112 const std::string& session_id) { 113 ASSERT_EQ(kOk, status.code()); 114 ASSERT_TRUE(expected_value->Equals(value.get())); 115 ASSERT_EQ(expected_session_id, session_id); 116 run_loop->Quit(); 117} 118 119} // namespace 120 121TEST(CommandsTest, ExecuteSessionCommand) { 122 SessionThreadMap map; 123 linked_ptr<base::Thread> thread(new base::Thread("1")); 124 ASSERT_TRUE(thread->Start()); 125 std::string id("id"); 126 thread->message_loop()->PostTask( 127 FROM_HERE, 128 base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id)); 129 map[id] = thread; 130 131 base::DictionaryValue params; 132 params.SetInteger("param", 5); 133 base::FundamentalValue expected_value(6); 134 SessionCommand cmd = base::Bind( 135 &ExecuteSimpleCommand, id, ¶ms, &expected_value); 136 137 base::MessageLoop loop; 138 base::RunLoop run_loop; 139 ExecuteSessionCommand( 140 &map, 141 cmd, 142 false, 143 params, 144 id, 145 base::Bind(&OnSimpleCommand, &run_loop, id, &expected_value)); 146 run_loop.Run(); 147} 148 149namespace { 150 151Status ShouldNotBeCalled( 152 Session* session, 153 const base::DictionaryValue& params, 154 scoped_ptr<base::Value>* value) { 155 EXPECT_TRUE(false); 156 return Status(kOk); 157} 158 159void OnNoSuchSession(const Status& status, 160 scoped_ptr<base::Value> value, 161 const std::string& session_id) { 162 EXPECT_EQ(kNoSuchSession, status.code()); 163 EXPECT_FALSE(value.get()); 164} 165 166void OnNoSuchSessionIsOk(const Status& status, 167 scoped_ptr<base::Value> value, 168 const std::string& session_id) { 169 EXPECT_EQ(kOk, status.code()); 170 EXPECT_FALSE(value.get()); 171} 172 173} // namespace 174 175TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSession) { 176 SessionThreadMap map; 177 base::DictionaryValue params; 178 ExecuteSessionCommand(&map, 179 base::Bind(&ShouldNotBeCalled), 180 false, 181 params, 182 "session", 183 base::Bind(&OnNoSuchSession)); 184} 185 186TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSessionWhenItExpectsOk) { 187 SessionThreadMap map; 188 base::DictionaryValue params; 189 ExecuteSessionCommand(&map, 190 base::Bind(&ShouldNotBeCalled), 191 true, 192 params, 193 "session", 194 base::Bind(&OnNoSuchSessionIsOk)); 195} 196 197namespace { 198 199void OnNoSuchSessionAndQuit(base::RunLoop* run_loop, 200 const Status& status, 201 scoped_ptr<base::Value> value, 202 const std::string& session_id) { 203 run_loop->Quit(); 204 EXPECT_EQ(kNoSuchSession, status.code()); 205 EXPECT_FALSE(value.get()); 206} 207 208} // namespace 209 210TEST(CommandsTest, ExecuteSessionCommandOnJustDeletedSession) { 211 SessionThreadMap map; 212 linked_ptr<base::Thread> thread(new base::Thread("1")); 213 ASSERT_TRUE(thread->Start()); 214 std::string id("id"); 215 map[id] = thread; 216 217 base::MessageLoop loop; 218 base::RunLoop run_loop; 219 ExecuteSessionCommand(&map, 220 base::Bind(&ShouldNotBeCalled), 221 false, 222 base::DictionaryValue(), 223 "session", 224 base::Bind(&OnNoSuchSessionAndQuit, &run_loop)); 225 run_loop.Run(); 226} 227 228namespace { 229 230enum TestScenario { 231 kElementExistsQueryOnce = 0, 232 kElementExistsQueryTwice, 233 kElementNotExistsQueryOnce, 234 kElementExistsTimeout 235}; 236 237class FindElementWebView : public StubWebView { 238 public: 239 FindElementWebView(bool only_one, TestScenario scenario) 240 : StubWebView("1"), only_one_(only_one), scenario_(scenario), 241 current_count_(0) { 242 switch (scenario_) { 243 case kElementExistsQueryOnce: 244 case kElementExistsQueryTwice: 245 case kElementExistsTimeout: { 246 if (only_one_) { 247 base::DictionaryValue element; 248 element.SetString("ELEMENT", "1"); 249 result_.reset(element.DeepCopy()); 250 } else { 251 base::DictionaryValue element1; 252 element1.SetString("ELEMENT", "1"); 253 base::DictionaryValue element2; 254 element2.SetString("ELEMENT", "2"); 255 base::ListValue list; 256 list.Append(element1.DeepCopy()); 257 list.Append(element2.DeepCopy()); 258 result_.reset(list.DeepCopy()); 259 } 260 break; 261 } 262 case kElementNotExistsQueryOnce: { 263 if (only_one_) 264 result_.reset(base::Value::CreateNullValue()); 265 else 266 result_.reset(new base::ListValue()); 267 break; 268 } 269 } 270 } 271 virtual ~FindElementWebView() {} 272 273 void Verify(const std::string& expected_frame, 274 const base::ListValue* expected_args, 275 const base::Value* actrual_result) { 276 EXPECT_EQ(expected_frame, frame_); 277 std::string function; 278 if (only_one_) 279 function = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENT); 280 else 281 function = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENTS); 282 EXPECT_EQ(function, function_); 283 ASSERT_TRUE(args_.get()); 284 EXPECT_TRUE(expected_args->Equals(args_.get())); 285 ASSERT_TRUE(actrual_result); 286 EXPECT_TRUE(result_->Equals(actrual_result)); 287 } 288 289 // Overridden from WebView: 290 virtual Status CallFunction(const std::string& frame, 291 const std::string& function, 292 const base::ListValue& args, 293 scoped_ptr<base::Value>* result) OVERRIDE { 294 ++current_count_; 295 if (scenario_ == kElementExistsTimeout || 296 (scenario_ == kElementExistsQueryTwice && current_count_ == 1)) { 297 // Always return empty result when testing timeout. 298 if (only_one_) 299 result->reset(base::Value::CreateNullValue()); 300 else 301 result->reset(new base::ListValue()); 302 } else { 303 switch (scenario_) { 304 case kElementExistsQueryOnce: 305 case kElementNotExistsQueryOnce: { 306 EXPECT_EQ(1, current_count_); 307 break; 308 } 309 case kElementExistsQueryTwice: { 310 EXPECT_EQ(2, current_count_); 311 break; 312 } 313 default: { 314 break; 315 } 316 } 317 318 result->reset(result_->DeepCopy()); 319 frame_ = frame; 320 function_ = function; 321 args_.reset(args.DeepCopy()); 322 } 323 return Status(kOk); 324 } 325 326 private: 327 bool only_one_; 328 TestScenario scenario_; 329 int current_count_; 330 std::string frame_; 331 std::string function_; 332 scoped_ptr<base::ListValue> args_; 333 scoped_ptr<base::Value> result_; 334}; 335 336} // namespace 337 338TEST(CommandsTest, SuccessfulFindElement) { 339 FindElementWebView web_view(true, kElementExistsQueryTwice); 340 Session session("id"); 341 session.implicit_wait = 1000; 342 session.SwitchToSubFrame("frame_id1", std::string()); 343 base::DictionaryValue params; 344 params.SetString("using", "id"); 345 params.SetString("value", "a"); 346 scoped_ptr<base::Value> result; 347 ASSERT_EQ(kOk, 348 ExecuteFindElement(1, &session, &web_view, params, &result).code()); 349 base::DictionaryValue param; 350 param.SetString("id", "a"); 351 base::ListValue expected_args; 352 expected_args.Append(param.DeepCopy()); 353 web_view.Verify("frame_id1", &expected_args, result.get()); 354} 355 356TEST(CommandsTest, FailedFindElement) { 357 FindElementWebView web_view(true, kElementNotExistsQueryOnce); 358 Session session("id"); 359 base::DictionaryValue params; 360 params.SetString("using", "id"); 361 params.SetString("value", "a"); 362 scoped_ptr<base::Value> result; 363 ASSERT_EQ(kNoSuchElement, 364 ExecuteFindElement(1, &session, &web_view, params, &result).code()); 365} 366 367TEST(CommandsTest, SuccessfulFindElements) { 368 FindElementWebView web_view(false, kElementExistsQueryTwice); 369 Session session("id"); 370 session.implicit_wait = 1000; 371 session.SwitchToSubFrame("frame_id2", std::string()); 372 base::DictionaryValue params; 373 params.SetString("using", "name"); 374 params.SetString("value", "b"); 375 scoped_ptr<base::Value> result; 376 ASSERT_EQ( 377 kOk, 378 ExecuteFindElements(1, &session, &web_view, params, &result).code()); 379 base::DictionaryValue param; 380 param.SetString("name", "b"); 381 base::ListValue expected_args; 382 expected_args.Append(param.DeepCopy()); 383 web_view.Verify("frame_id2", &expected_args, result.get()); 384} 385 386TEST(CommandsTest, FailedFindElements) { 387 Session session("id"); 388 FindElementWebView web_view(false, kElementNotExistsQueryOnce); 389 base::DictionaryValue params; 390 params.SetString("using", "id"); 391 params.SetString("value", "a"); 392 scoped_ptr<base::Value> result; 393 ASSERT_EQ( 394 kOk, 395 ExecuteFindElements(1, &session, &web_view, params, &result).code()); 396 base::ListValue* list; 397 ASSERT_TRUE(result->GetAsList(&list)); 398 ASSERT_EQ(0U, list->GetSize()); 399} 400 401TEST(CommandsTest, SuccessfulFindChildElement) { 402 FindElementWebView web_view(true, kElementExistsQueryTwice); 403 Session session("id"); 404 session.implicit_wait = 1000; 405 session.SwitchToSubFrame("frame_id3", std::string()); 406 base::DictionaryValue params; 407 params.SetString("using", "tag name"); 408 params.SetString("value", "div"); 409 std::string element_id = "1"; 410 scoped_ptr<base::Value> result; 411 ASSERT_EQ( 412 kOk, 413 ExecuteFindChildElement( 414 1, &session, &web_view, element_id, params, &result).code()); 415 base::DictionaryValue locator_param; 416 locator_param.SetString("tag name", "div"); 417 base::DictionaryValue root_element_param; 418 root_element_param.SetString("ELEMENT", element_id); 419 base::ListValue expected_args; 420 expected_args.Append(locator_param.DeepCopy()); 421 expected_args.Append(root_element_param.DeepCopy()); 422 web_view.Verify("frame_id3", &expected_args, result.get()); 423} 424 425TEST(CommandsTest, FailedFindChildElement) { 426 Session session("id"); 427 FindElementWebView web_view(true, kElementNotExistsQueryOnce); 428 base::DictionaryValue params; 429 params.SetString("using", "id"); 430 params.SetString("value", "a"); 431 std::string element_id = "1"; 432 scoped_ptr<base::Value> result; 433 ASSERT_EQ( 434 kNoSuchElement, 435 ExecuteFindChildElement( 436 1, &session, &web_view, element_id, params, &result).code()); 437} 438 439TEST(CommandsTest, SuccessfulFindChildElements) { 440 FindElementWebView web_view(false, kElementExistsQueryTwice); 441 Session session("id"); 442 session.implicit_wait = 1000; 443 session.SwitchToSubFrame("frame_id4", std::string()); 444 base::DictionaryValue params; 445 params.SetString("using", "class name"); 446 params.SetString("value", "c"); 447 std::string element_id = "1"; 448 scoped_ptr<base::Value> result; 449 ASSERT_EQ( 450 kOk, 451 ExecuteFindChildElements( 452 1, &session, &web_view, element_id, params, &result).code()); 453 base::DictionaryValue locator_param; 454 locator_param.SetString("class name", "c"); 455 base::DictionaryValue root_element_param; 456 root_element_param.SetString("ELEMENT", element_id); 457 base::ListValue expected_args; 458 expected_args.Append(locator_param.DeepCopy()); 459 expected_args.Append(root_element_param.DeepCopy()); 460 web_view.Verify("frame_id4", &expected_args, result.get()); 461} 462 463TEST(CommandsTest, FailedFindChildElements) { 464 Session session("id"); 465 FindElementWebView web_view(false, kElementNotExistsQueryOnce); 466 base::DictionaryValue params; 467 params.SetString("using", "id"); 468 params.SetString("value", "a"); 469 std::string element_id = "1"; 470 scoped_ptr<base::Value> result; 471 ASSERT_EQ( 472 kOk, 473 ExecuteFindChildElements( 474 1, &session, &web_view, element_id, params, &result).code()); 475 base::ListValue* list; 476 ASSERT_TRUE(result->GetAsList(&list)); 477 ASSERT_EQ(0U, list->GetSize()); 478} 479 480TEST(CommandsTest, TimeoutInFindElement) { 481 Session session("id"); 482 FindElementWebView web_view(true, kElementExistsTimeout); 483 session.implicit_wait = 2; 484 base::DictionaryValue params; 485 params.SetString("using", "id"); 486 params.SetString("value", "a"); 487 params.SetString("id", "1"); 488 scoped_ptr<base::Value> result; 489 ASSERT_EQ(kNoSuchElement, 490 ExecuteFindElement(1, &session, &web_view, params, &result).code()); 491} 492 493namespace { 494 495class ErrorCallFunctionWebView : public StubWebView { 496 public: 497 explicit ErrorCallFunctionWebView(StatusCode code) 498 : StubWebView("1"), code_(code) {} 499 virtual ~ErrorCallFunctionWebView() {} 500 501 // Overridden from WebView: 502 virtual Status CallFunction(const std::string& frame, 503 const std::string& function, 504 const base::ListValue& args, 505 scoped_ptr<base::Value>* result) OVERRIDE { 506 return Status(code_); 507 } 508 509 private: 510 StatusCode code_; 511}; 512 513} // namespace 514 515TEST(CommandsTest, ErrorFindElement) { 516 Session session("id"); 517 ErrorCallFunctionWebView web_view(kUnknownError); 518 base::DictionaryValue params; 519 params.SetString("using", "id"); 520 params.SetString("value", "a"); 521 scoped_ptr<base::Value> value; 522 ASSERT_EQ(kUnknownError, 523 ExecuteFindElement(1, &session, &web_view, params, &value).code()); 524 ASSERT_EQ(kUnknownError, 525 ExecuteFindElements(1, &session, &web_view, params, &value).code()); 526} 527 528TEST(CommandsTest, ErrorFindChildElement) { 529 Session session("id"); 530 ErrorCallFunctionWebView web_view(kStaleElementReference); 531 base::DictionaryValue params; 532 params.SetString("using", "id"); 533 params.SetString("value", "a"); 534 std::string element_id = "1"; 535 scoped_ptr<base::Value> result; 536 ASSERT_EQ( 537 kStaleElementReference, 538 ExecuteFindChildElement( 539 1, &session, &web_view, element_id, params, &result).code()); 540 ASSERT_EQ( 541 kStaleElementReference, 542 ExecuteFindChildElements( 543 1, &session, &web_view, element_id, params, &result).code()); 544} 545