devtools_client_impl_unittest.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 <list> 6#include <string> 7 8#include "base/bind.h" 9#include "base/compiler_specific.h" 10#include "base/json/json_reader.h" 11#include "base/json/json_writer.h" 12#include "base/memory/scoped_ptr.h" 13#include "base/strings/stringprintf.h" 14#include "base/values.h" 15#include "chrome/test/chromedriver/chrome/devtools_client_impl.h" 16#include "chrome/test/chromedriver/chrome/devtools_event_listener.h" 17#include "chrome/test/chromedriver/chrome/status.h" 18#include "chrome/test/chromedriver/net/sync_websocket.h" 19#include "chrome/test/chromedriver/net/sync_websocket_factory.h" 20#include "testing/gtest/include/gtest/gtest.h" 21#include "url/gurl.h" 22 23namespace { 24 25Status CloserFunc() { 26 return Status(kOk); 27} 28 29class MockSyncWebSocket : public SyncWebSocket { 30 public: 31 MockSyncWebSocket() : connected_(false), id_(-1), queued_messages_(1) {} 32 virtual ~MockSyncWebSocket() {} 33 34 virtual bool IsConnected() OVERRIDE { 35 return connected_; 36 } 37 38 virtual bool Connect(const GURL& url) OVERRIDE { 39 EXPECT_STREQ("http://url/", url.possibly_invalid_spec().c_str()); 40 connected_ = true; 41 return true; 42 } 43 44 virtual bool Send(const std::string& message) OVERRIDE { 45 EXPECT_TRUE(connected_); 46 scoped_ptr<base::Value> value(base::JSONReader::Read(message)); 47 base::DictionaryValue* dict = NULL; 48 EXPECT_TRUE(value->GetAsDictionary(&dict)); 49 if (!dict) 50 return false; 51 EXPECT_TRUE(dict->GetInteger("id", &id_)); 52 std::string method; 53 EXPECT_TRUE(dict->GetString("method", &method)); 54 EXPECT_STREQ("method", method.c_str()); 55 base::DictionaryValue* params = NULL; 56 EXPECT_TRUE(dict->GetDictionary("params", ¶ms)); 57 if (!params) 58 return false; 59 int param = -1; 60 EXPECT_TRUE(params->GetInteger("param", ¶m)); 61 EXPECT_EQ(1, param); 62 return true; 63 } 64 65 virtual SyncWebSocket::StatusCode ReceiveNextMessage( 66 std::string* message, 67 const base::TimeDelta& timeout) OVERRIDE { 68 if (timeout <= base::TimeDelta()) 69 return SyncWebSocket::kTimeout; 70 base::DictionaryValue response; 71 response.SetInteger("id", id_); 72 base::DictionaryValue result; 73 result.SetInteger("param", 1); 74 response.Set("result", result.DeepCopy()); 75 base::JSONWriter::Write(&response, message); 76 --queued_messages_; 77 return SyncWebSocket::kOk; 78 } 79 80 virtual bool HasNextMessage() OVERRIDE { 81 return queued_messages_ > 0; 82 } 83 84 protected: 85 bool connected_; 86 int id_; 87 int queued_messages_; 88}; 89 90template <typename T> 91scoped_ptr<SyncWebSocket> CreateMockSyncWebSocket() { 92 return scoped_ptr<SyncWebSocket>(new T()); 93} 94 95class DevToolsClientImplTest : public testing::Test { 96 protected: 97 DevToolsClientImplTest() : long_timeout_(base::TimeDelta::FromMinutes(5)) {} 98 99 const base::TimeDelta long_timeout_; 100}; 101 102} // namespace 103 104TEST_F(DevToolsClientImplTest, SendCommand) { 105 SyncWebSocketFactory factory = 106 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>); 107 DevToolsClientImpl client(factory, "http://url", "id", 108 base::Bind(&CloserFunc)); 109 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 110 base::DictionaryValue params; 111 params.SetInteger("param", 1); 112 ASSERT_EQ(kOk, client.SendCommand("method", params).code()); 113} 114 115TEST_F(DevToolsClientImplTest, SendCommandAndGetResult) { 116 SyncWebSocketFactory factory = 117 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>); 118 DevToolsClientImpl client(factory, "http://url", "id", 119 base::Bind(&CloserFunc)); 120 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 121 base::DictionaryValue params; 122 params.SetInteger("param", 1); 123 scoped_ptr<base::DictionaryValue> result; 124 Status status = client.SendCommandAndGetResult("method", params, &result); 125 ASSERT_EQ(kOk, status.code()); 126 std::string json; 127 base::JSONWriter::Write(result.get(), &json); 128 ASSERT_STREQ("{\"param\":1}", json.c_str()); 129} 130 131namespace { 132 133class MockSyncWebSocket2 : public SyncWebSocket { 134 public: 135 MockSyncWebSocket2() {} 136 virtual ~MockSyncWebSocket2() {} 137 138 virtual bool IsConnected() OVERRIDE { 139 return false; 140 } 141 142 virtual bool Connect(const GURL& url) OVERRIDE { 143 return false; 144 } 145 146 virtual bool Send(const std::string& message) OVERRIDE { 147 EXPECT_TRUE(false); 148 return false; 149 } 150 151 virtual SyncWebSocket::StatusCode ReceiveNextMessage( 152 std::string* message, 153 const base::TimeDelta& timeout) OVERRIDE { 154 EXPECT_TRUE(false); 155 return SyncWebSocket::kDisconnected; 156 } 157 158 virtual bool HasNextMessage() OVERRIDE { 159 return true; 160 } 161}; 162 163} // namespace 164 165TEST_F(DevToolsClientImplTest, ConnectIfNecessaryConnectFails) { 166 SyncWebSocketFactory factory = 167 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket2>); 168 DevToolsClientImpl client(factory, "http://url", "id", 169 base::Bind(&CloserFunc)); 170 ASSERT_EQ(kDisconnected, client.ConnectIfNecessary().code()); 171} 172 173namespace { 174 175class MockSyncWebSocket3 : public SyncWebSocket { 176 public: 177 MockSyncWebSocket3() : connected_(false) {} 178 virtual ~MockSyncWebSocket3() {} 179 180 virtual bool IsConnected() OVERRIDE { 181 return connected_; 182 } 183 184 virtual bool Connect(const GURL& url) OVERRIDE { 185 connected_ = true; 186 return true; 187 } 188 189 virtual bool Send(const std::string& message) OVERRIDE { 190 return false; 191 } 192 193 virtual SyncWebSocket::StatusCode ReceiveNextMessage( 194 std::string* message, 195 const base::TimeDelta& timeout) OVERRIDE { 196 EXPECT_TRUE(false); 197 return SyncWebSocket::kDisconnected; 198 } 199 200 virtual bool HasNextMessage() OVERRIDE { 201 return true; 202 } 203 204 private: 205 bool connected_; 206}; 207 208} // namespace 209 210TEST_F(DevToolsClientImplTest, SendCommandSendFails) { 211 SyncWebSocketFactory factory = 212 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket3>); 213 DevToolsClientImpl client(factory, "http://url", "id", 214 base::Bind(&CloserFunc)); 215 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 216 base::DictionaryValue params; 217 ASSERT_TRUE(client.SendCommand("method", params).IsError()); 218} 219 220namespace { 221 222class MockSyncWebSocket4 : public SyncWebSocket { 223 public: 224 MockSyncWebSocket4() : connected_(false) {} 225 virtual ~MockSyncWebSocket4() {} 226 227 virtual bool IsConnected() OVERRIDE { 228 return connected_; 229 } 230 231 virtual bool Connect(const GURL& url) OVERRIDE { 232 connected_ = true; 233 return true; 234 } 235 236 virtual bool Send(const std::string& message) OVERRIDE { 237 return true; 238 } 239 240 virtual SyncWebSocket::StatusCode ReceiveNextMessage( 241 std::string* message, 242 const base::TimeDelta& timeout) OVERRIDE { 243 return SyncWebSocket::kDisconnected; 244 } 245 246 virtual bool HasNextMessage() OVERRIDE { 247 return true; 248 } 249 250 private: 251 bool connected_; 252}; 253 254} // namespace 255 256TEST_F(DevToolsClientImplTest, SendCommandReceiveNextMessageFails) { 257 SyncWebSocketFactory factory = 258 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket4>); 259 DevToolsClientImpl client(factory, "http://url", "id", 260 base::Bind(&CloserFunc)); 261 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 262 base::DictionaryValue params; 263 ASSERT_TRUE(client.SendCommand("method", params).IsError()); 264} 265 266namespace { 267 268class FakeSyncWebSocket : public SyncWebSocket { 269 public: 270 FakeSyncWebSocket() : connected_(false) {} 271 virtual ~FakeSyncWebSocket() {} 272 273 virtual bool IsConnected() OVERRIDE { 274 return connected_; 275 } 276 277 virtual bool Connect(const GURL& url) OVERRIDE { 278 EXPECT_FALSE(connected_); 279 connected_ = true; 280 return true; 281 } 282 283 virtual bool Send(const std::string& message) OVERRIDE { 284 return true; 285 } 286 287 virtual SyncWebSocket::StatusCode ReceiveNextMessage( 288 std::string* message, 289 const base::TimeDelta& timeout) OVERRIDE { 290 return SyncWebSocket::kOk; 291 } 292 293 virtual bool HasNextMessage() OVERRIDE { 294 return true; 295 } 296 297 private: 298 bool connected_; 299}; 300 301bool ReturnCommand( 302 const std::string& message, 303 int expected_id, 304 internal::InspectorMessageType* type, 305 internal::InspectorEvent* event, 306 internal::InspectorCommandResponse* command_response) { 307 *type = internal::kCommandResponseMessageType; 308 command_response->id = expected_id; 309 command_response->result.reset(new base::DictionaryValue()); 310 return true; 311} 312 313bool ReturnBadResponse( 314 const std::string& message, 315 int expected_id, 316 internal::InspectorMessageType* type, 317 internal::InspectorEvent* event, 318 internal::InspectorCommandResponse* command_response) { 319 *type = internal::kCommandResponseMessageType; 320 command_response->id = expected_id; 321 command_response->result.reset(new base::DictionaryValue()); 322 return false; 323} 324 325bool ReturnCommandBadId( 326 const std::string& message, 327 int expected_id, 328 internal::InspectorMessageType* type, 329 internal::InspectorEvent* event, 330 internal::InspectorCommandResponse* command_response) { 331 *type = internal::kCommandResponseMessageType; 332 command_response->id = expected_id + 100; 333 command_response->result.reset(new base::DictionaryValue()); 334 return true; 335} 336 337bool ReturnCommandError( 338 const std::string& message, 339 int expected_id, 340 internal::InspectorMessageType* type, 341 internal::InspectorEvent* event, 342 internal::InspectorCommandResponse* command_response) { 343 *type = internal::kCommandResponseMessageType; 344 command_response->id = expected_id; 345 command_response->error = "err"; 346 return true; 347} 348 349class MockListener : public DevToolsEventListener { 350 public: 351 MockListener() : called_(false) {} 352 virtual ~MockListener() { 353 EXPECT_TRUE(called_); 354 } 355 356 virtual Status OnConnected(DevToolsClient* client) OVERRIDE { 357 return Status(kOk); 358 } 359 360 virtual Status OnEvent(DevToolsClient* client, 361 const std::string& method, 362 const base::DictionaryValue& params) OVERRIDE { 363 called_ = true; 364 EXPECT_STREQ("method", method.c_str()); 365 EXPECT_TRUE(params.HasKey("key")); 366 return Status(kOk); 367 } 368 369 private: 370 bool called_; 371}; 372 373bool ReturnEventThenResponse( 374 bool* first, 375 const std::string& message, 376 int expected_id, 377 internal::InspectorMessageType* type, 378 internal::InspectorEvent* event, 379 internal::InspectorCommandResponse* command_response) { 380 if (*first) { 381 *type = internal::kEventMessageType; 382 event->method = "method"; 383 event->params.reset(new base::DictionaryValue()); 384 event->params->SetInteger("key", 1); 385 } else { 386 *type = internal::kCommandResponseMessageType; 387 command_response->id = expected_id; 388 base::DictionaryValue params; 389 command_response->result.reset(new base::DictionaryValue()); 390 command_response->result->SetInteger("key", 2); 391 } 392 *first = false; 393 return true; 394} 395 396bool ReturnEvent( 397 const std::string& message, 398 int expected_id, 399 internal::InspectorMessageType* type, 400 internal::InspectorEvent* event, 401 internal::InspectorCommandResponse* command_response) { 402 *type = internal::kEventMessageType; 403 event->method = "method"; 404 event->params.reset(new base::DictionaryValue()); 405 event->params->SetInteger("key", 1); 406 return true; 407} 408 409bool ReturnOutOfOrderResponses( 410 int* recurse_count, 411 DevToolsClient* client, 412 const std::string& message, 413 int expected_id, 414 internal::InspectorMessageType* type, 415 internal::InspectorEvent* event, 416 internal::InspectorCommandResponse* command_response) { 417 int key = 0; 418 base::DictionaryValue params; 419 params.SetInteger("param", 1); 420 switch ((*recurse_count)++) { 421 case 0: 422 client->SendCommand("method", params); 423 *type = internal::kEventMessageType; 424 event->method = "method"; 425 event->params.reset(new base::DictionaryValue()); 426 event->params->SetInteger("key", 1); 427 return true; 428 case 1: 429 command_response->id = expected_id - 1; 430 key = 2; 431 break; 432 case 2: 433 command_response->id = expected_id; 434 key = 3; 435 break; 436 } 437 *type = internal::kCommandResponseMessageType; 438 command_response->result.reset(new base::DictionaryValue()); 439 command_response->result->SetInteger("key", key); 440 return true; 441} 442 443bool ReturnError( 444 const std::string& message, 445 int expected_id, 446 internal::InspectorMessageType* type, 447 internal::InspectorEvent* event, 448 internal::InspectorCommandResponse* command_response) { 449 return false; 450} 451 452Status AlwaysTrue(bool* is_met) { 453 *is_met = true; 454 return Status(kOk); 455} 456 457Status AlwaysError(bool* is_met) { 458 return Status(kUnknownError); 459} 460 461} // namespace 462 463TEST_F(DevToolsClientImplTest, SendCommandOnlyConnectsOnce) { 464 SyncWebSocketFactory factory = 465 base::Bind(&CreateMockSyncWebSocket<FakeSyncWebSocket>); 466 DevToolsClientImpl client(factory, "http://url", "id", 467 base::Bind(&CloserFunc), 468 base::Bind(&ReturnCommand)); 469 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 470 base::DictionaryValue params; 471 ASSERT_TRUE(client.SendCommand("method", params).IsOk()); 472 ASSERT_TRUE(client.SendCommand("method", params).IsOk()); 473} 474 475TEST_F(DevToolsClientImplTest, SendCommandBadResponse) { 476 SyncWebSocketFactory factory = 477 base::Bind(&CreateMockSyncWebSocket<FakeSyncWebSocket>); 478 DevToolsClientImpl client(factory, "http://url", "id", 479 base::Bind(&CloserFunc), 480 base::Bind(&ReturnBadResponse)); 481 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 482 base::DictionaryValue params; 483 ASSERT_TRUE(client.SendCommand("method", params).IsError()); 484} 485 486TEST_F(DevToolsClientImplTest, SendCommandBadId) { 487 SyncWebSocketFactory factory = 488 base::Bind(&CreateMockSyncWebSocket<FakeSyncWebSocket>); 489 DevToolsClientImpl client(factory, "http://url", "id", 490 base::Bind(&CloserFunc), 491 base::Bind(&ReturnCommandBadId)); 492 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 493 base::DictionaryValue params; 494 ASSERT_TRUE(client.SendCommand("method", params).IsError()); 495} 496 497TEST_F(DevToolsClientImplTest, SendCommandResponseError) { 498 SyncWebSocketFactory factory = 499 base::Bind(&CreateMockSyncWebSocket<FakeSyncWebSocket>); 500 DevToolsClientImpl client(factory, "http://url", "id", 501 base::Bind(&CloserFunc), 502 base::Bind(&ReturnCommandError)); 503 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 504 base::DictionaryValue params; 505 ASSERT_TRUE(client.SendCommand("method", params).IsError()); 506} 507 508TEST_F(DevToolsClientImplTest, SendCommandEventBeforeResponse) { 509 SyncWebSocketFactory factory = 510 base::Bind(&CreateMockSyncWebSocket<FakeSyncWebSocket>); 511 MockListener listener; 512 bool first = true; 513 DevToolsClientImpl client(factory, "http://url", "id", 514 base::Bind(&CloserFunc), 515 base::Bind(&ReturnEventThenResponse, &first)); 516 client.AddListener(&listener); 517 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 518 base::DictionaryValue params; 519 scoped_ptr<base::DictionaryValue> result; 520 ASSERT_TRUE(client.SendCommandAndGetResult("method", params, &result).IsOk()); 521 ASSERT_TRUE(result); 522 int key; 523 ASSERT_TRUE(result->GetInteger("key", &key)); 524 ASSERT_EQ(2, key); 525} 526 527TEST(ParseInspectorMessage, NonJson) { 528 internal::InspectorMessageType type; 529 internal::InspectorEvent event; 530 internal::InspectorCommandResponse response; 531 ASSERT_FALSE(internal::ParseInspectorMessage( 532 "hi", 0, &type, &event, &response)); 533} 534 535TEST(ParseInspectorMessage, NeitherCommandNorEvent) { 536 internal::InspectorMessageType type; 537 internal::InspectorEvent event; 538 internal::InspectorCommandResponse response; 539 ASSERT_FALSE(internal::ParseInspectorMessage( 540 "{}", 0, &type, &event, &response)); 541} 542 543TEST(ParseInspectorMessage, EventNoParams) { 544 internal::InspectorMessageType type; 545 internal::InspectorEvent event; 546 internal::InspectorCommandResponse response; 547 ASSERT_TRUE(internal::ParseInspectorMessage( 548 "{\"method\":\"method\"}", 0, &type, &event, &response)); 549 ASSERT_EQ(internal::kEventMessageType, type); 550 ASSERT_STREQ("method", event.method.c_str()); 551 ASSERT_TRUE(event.params->IsType(base::Value::TYPE_DICTIONARY)); 552} 553 554TEST(ParseInspectorMessage, EventWithParams) { 555 internal::InspectorMessageType type; 556 internal::InspectorEvent event; 557 internal::InspectorCommandResponse response; 558 ASSERT_TRUE(internal::ParseInspectorMessage( 559 "{\"method\":\"method\",\"params\":{\"key\":100}}", 560 0, &type, &event, &response)); 561 ASSERT_EQ(internal::kEventMessageType, type); 562 ASSERT_STREQ("method", event.method.c_str()); 563 int key; 564 ASSERT_TRUE(event.params->GetInteger("key", &key)); 565 ASSERT_EQ(100, key); 566} 567 568TEST(ParseInspectorMessage, CommandNoErrorOrResult) { 569 internal::InspectorMessageType type; 570 internal::InspectorEvent event; 571 internal::InspectorCommandResponse response; 572 ASSERT_FALSE(internal::ParseInspectorMessage( 573 "{\"id\":1}", 0, &type, &event, &response)); 574} 575 576TEST(ParseInspectorMessage, CommandError) { 577 internal::InspectorMessageType type; 578 internal::InspectorEvent event; 579 internal::InspectorCommandResponse response; 580 ASSERT_TRUE(internal::ParseInspectorMessage( 581 "{\"id\":1,\"error\":{}}", 0, &type, &event, &response)); 582 ASSERT_EQ(internal::kCommandResponseMessageType, type); 583 ASSERT_EQ(1, response.id); 584 ASSERT_TRUE(response.error.length()); 585 ASSERT_FALSE(response.result); 586} 587 588TEST(ParseInspectorMessage, Command) { 589 internal::InspectorMessageType type; 590 internal::InspectorEvent event; 591 internal::InspectorCommandResponse response; 592 ASSERT_TRUE(internal::ParseInspectorMessage( 593 "{\"id\":1,\"result\":{\"key\":1}}", 0, &type, &event, &response)); 594 ASSERT_EQ(internal::kCommandResponseMessageType, type); 595 ASSERT_EQ(1, response.id); 596 ASSERT_FALSE(response.error.length()); 597 int key; 598 ASSERT_TRUE(response.result->GetInteger("key", &key)); 599 ASSERT_EQ(1, key); 600} 601 602TEST_F(DevToolsClientImplTest, HandleEventsUntil) { 603 MockListener listener; 604 SyncWebSocketFactory factory = 605 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>); 606 DevToolsClientImpl client(factory, "http://url", "id", 607 base::Bind(&CloserFunc), 608 base::Bind(&ReturnEvent)); 609 client.AddListener(&listener); 610 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 611 Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue), 612 long_timeout_); 613 ASSERT_EQ(kOk, status.code()); 614} 615 616TEST_F(DevToolsClientImplTest, HandleEventsUntilTimeout) { 617 SyncWebSocketFactory factory = 618 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>); 619 DevToolsClientImpl client(factory, "http://url", "id", 620 base::Bind(&CloserFunc), 621 base::Bind(&ReturnEvent)); 622 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 623 Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue), 624 base::TimeDelta()); 625 ASSERT_EQ(kTimeout, status.code()); 626} 627 628TEST_F(DevToolsClientImplTest, WaitForNextEventCommand) { 629 SyncWebSocketFactory factory = 630 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>); 631 DevToolsClientImpl client(factory, "http://url", "id", 632 base::Bind(&CloserFunc), 633 base::Bind(&ReturnCommand)); 634 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 635 Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue), 636 long_timeout_); 637 ASSERT_EQ(kUnknownError, status.code()); 638} 639 640TEST_F(DevToolsClientImplTest, WaitForNextEventError) { 641 SyncWebSocketFactory factory = 642 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>); 643 DevToolsClientImpl client(factory, "http://url", "id", 644 base::Bind(&CloserFunc), 645 base::Bind(&ReturnError)); 646 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 647 Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue), 648 long_timeout_); 649 ASSERT_EQ(kUnknownError, status.code()); 650} 651 652TEST_F(DevToolsClientImplTest, WaitForNextEventConditionalFuncReturnsError) { 653 SyncWebSocketFactory factory = 654 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>); 655 DevToolsClientImpl client(factory, "http://url", "id", 656 base::Bind(&CloserFunc), 657 base::Bind(&ReturnEvent)); 658 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 659 Status status = client.HandleEventsUntil(base::Bind(&AlwaysError), 660 long_timeout_); 661 ASSERT_EQ(kUnknownError, status.code()); 662} 663 664TEST_F(DevToolsClientImplTest, NestedCommandsWithOutOfOrderResults) { 665 SyncWebSocketFactory factory = 666 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>); 667 int recurse_count = 0; 668 DevToolsClientImpl client(factory, "http://url", "id", 669 base::Bind(&CloserFunc)); 670 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 671 client.SetParserFuncForTesting( 672 base::Bind(&ReturnOutOfOrderResponses, &recurse_count, &client)); 673 base::DictionaryValue params; 674 params.SetInteger("param", 1); 675 scoped_ptr<base::DictionaryValue> result; 676 ASSERT_TRUE(client.SendCommandAndGetResult("method", params, &result).IsOk()); 677 ASSERT_TRUE(result); 678 int key; 679 ASSERT_TRUE(result->GetInteger("key", &key)); 680 ASSERT_EQ(2, key); 681} 682 683namespace { 684 685class OnConnectedListener : public DevToolsEventListener { 686 public: 687 OnConnectedListener(const std::string& method, DevToolsClient* client) 688 : method_(method), 689 client_(client), 690 on_connected_called_(false), 691 on_event_called_(false) { 692 client_->AddListener(this); 693 } 694 virtual ~OnConnectedListener() {} 695 696 void VerifyCalled() { 697 EXPECT_TRUE(on_connected_called_); 698 EXPECT_TRUE(on_event_called_); 699 } 700 701 virtual Status OnConnected(DevToolsClient* client) OVERRIDE { 702 EXPECT_EQ(client_, client); 703 EXPECT_STREQ("onconnected-id", client->GetId().c_str()); 704 EXPECT_FALSE(on_connected_called_); 705 EXPECT_FALSE(on_event_called_); 706 on_connected_called_ = true; 707 base::DictionaryValue params; 708 return client_->SendCommand(method_, params); 709 } 710 711 virtual Status OnEvent(DevToolsClient* client, 712 const std::string& method, 713 const base::DictionaryValue& params) OVERRIDE { 714 EXPECT_EQ(client_, client); 715 EXPECT_STREQ("onconnected-id", client->GetId().c_str()); 716 EXPECT_TRUE(on_connected_called_); 717 on_event_called_ = true; 718 return Status(kOk); 719 } 720 721 private: 722 std::string method_; 723 DevToolsClient* client_; 724 bool on_connected_called_; 725 bool on_event_called_; 726}; 727 728class OnConnectedSyncWebSocket : public SyncWebSocket { 729 public: 730 OnConnectedSyncWebSocket() : connected_(false) {} 731 virtual ~OnConnectedSyncWebSocket() {} 732 733 virtual bool IsConnected() OVERRIDE { 734 return connected_; 735 } 736 737 virtual bool Connect(const GURL& url) OVERRIDE { 738 connected_ = true; 739 return true; 740 } 741 742 virtual bool Send(const std::string& message) OVERRIDE { 743 EXPECT_TRUE(connected_); 744 scoped_ptr<base::Value> value(base::JSONReader::Read(message)); 745 base::DictionaryValue* dict = NULL; 746 EXPECT_TRUE(value->GetAsDictionary(&dict)); 747 if (!dict) 748 return false; 749 int id; 750 EXPECT_TRUE(dict->GetInteger("id", &id)); 751 std::string method; 752 EXPECT_TRUE(dict->GetString("method", &method)); 753 754 base::DictionaryValue response; 755 response.SetInteger("id", id); 756 response.Set("result", new base::DictionaryValue()); 757 std::string json_response; 758 base::JSONWriter::Write(&response, &json_response); 759 queued_response_.push_back(json_response); 760 761 // Push one event. 762 base::DictionaryValue event; 763 event.SetString("method", "updateEvent"); 764 event.Set("params", new base::DictionaryValue()); 765 std::string json_event; 766 base::JSONWriter::Write(&event, &json_event); 767 queued_response_.push_back(json_event); 768 769 return true; 770 } 771 772 virtual SyncWebSocket::StatusCode ReceiveNextMessage( 773 std::string* message, 774 const base::TimeDelta& timeout) OVERRIDE { 775 if (queued_response_.empty()) 776 return SyncWebSocket::kDisconnected; 777 *message = queued_response_.front(); 778 queued_response_.pop_front(); 779 return SyncWebSocket::kOk; 780 } 781 782 virtual bool HasNextMessage() OVERRIDE { 783 return !queued_response_.empty(); 784 } 785 786 private: 787 bool connected_; 788 std::list<std::string> queued_response_; 789}; 790 791} // namespace 792 793TEST_F(DevToolsClientImplTest, ProcessOnConnectedFirstOnCommand) { 794 SyncWebSocketFactory factory = 795 base::Bind(&CreateMockSyncWebSocket<OnConnectedSyncWebSocket>); 796 DevToolsClientImpl client(factory, "http://url", "onconnected-id", 797 base::Bind(&CloserFunc)); 798 OnConnectedListener listener1("DOM.getDocument", &client); 799 OnConnectedListener listener2("Runtime.enable", &client); 800 OnConnectedListener listener3("Page.enable", &client); 801 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 802 base::DictionaryValue params; 803 EXPECT_EQ(kOk, client.SendCommand("Runtime.execute", params).code()); 804 listener1.VerifyCalled(); 805 listener2.VerifyCalled(); 806 listener3.VerifyCalled(); 807} 808 809TEST_F(DevToolsClientImplTest, ProcessOnConnectedFirstOnHandleEventsUntil) { 810 SyncWebSocketFactory factory = 811 base::Bind(&CreateMockSyncWebSocket<OnConnectedSyncWebSocket>); 812 DevToolsClientImpl client(factory, "http://url", "onconnected-id", 813 base::Bind(&CloserFunc)); 814 OnConnectedListener listener1("DOM.getDocument", &client); 815 OnConnectedListener listener2("Runtime.enable", &client); 816 OnConnectedListener listener3("Page.enable", &client); 817 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 818 EXPECT_EQ(kOk, client.HandleReceivedEvents().code()); 819 listener1.VerifyCalled(); 820 listener2.VerifyCalled(); 821 listener3.VerifyCalled(); 822} 823 824namespace { 825 826class MockSyncWebSocket5 : public SyncWebSocket { 827 public: 828 MockSyncWebSocket5() : request_no_(0) {} 829 virtual ~MockSyncWebSocket5() {} 830 831 virtual bool IsConnected() OVERRIDE { 832 return true; 833 } 834 835 virtual bool Connect(const GURL& url) OVERRIDE { 836 return true; 837 } 838 839 virtual bool Send(const std::string& message) OVERRIDE { 840 return true; 841 } 842 843 virtual SyncWebSocket::StatusCode ReceiveNextMessage( 844 std::string* message, 845 const base::TimeDelta& timeout) OVERRIDE { 846 if (request_no_ == 0) { 847 *message = "{\"method\": \"m\", \"params\": {}}"; 848 } else { 849 *message = base::StringPrintf( 850 "{\"result\": {}, \"id\": %d}", request_no_); 851 } 852 request_no_++; 853 return SyncWebSocket::kOk; 854 } 855 856 virtual bool HasNextMessage() OVERRIDE { 857 return false; 858 } 859 860 private: 861 int request_no_; 862}; 863 864class OtherEventListener : public DevToolsEventListener { 865 public: 866 OtherEventListener() : received_event_(false) {} 867 virtual ~OtherEventListener() {} 868 869 virtual Status OnConnected(DevToolsClient* client) OVERRIDE { 870 return Status(kOk); 871 } 872 virtual Status OnEvent(DevToolsClient* client, 873 const std::string& method, 874 const base::DictionaryValue& params) OVERRIDE { 875 received_event_ = true; 876 return Status(kOk); 877 } 878 879 bool received_event_; 880}; 881 882class OnEventListener : public DevToolsEventListener { 883 public: 884 OnEventListener(DevToolsClient* client, 885 OtherEventListener* other_listener) 886 : client_(client), 887 other_listener_(other_listener) {} 888 virtual ~OnEventListener() {} 889 890 virtual Status OnConnected(DevToolsClient* client) OVERRIDE { 891 EXPECT_EQ(client_, client); 892 return Status(kOk); 893 } 894 895 virtual Status OnEvent(DevToolsClient* client, 896 const std::string& method, 897 const base::DictionaryValue& params) OVERRIDE { 898 EXPECT_EQ(client_, client); 899 client_->SendCommand("method", params); 900 EXPECT_TRUE(other_listener_->received_event_); 901 return Status(kOk); 902 } 903 904 private: 905 DevToolsClient* client_; 906 OtherEventListener* other_listener_; 907}; 908 909} // namespace 910 911TEST_F(DevToolsClientImplTest, ProcessOnEventFirst) { 912 SyncWebSocketFactory factory = 913 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket5>); 914 DevToolsClientImpl client(factory, "http://url", "id", 915 base::Bind(&CloserFunc)); 916 OtherEventListener listener2; 917 OnEventListener listener1(&client, &listener2); 918 client.AddListener(&listener1); 919 client.AddListener(&listener2); 920 base::DictionaryValue params; 921 EXPECT_EQ(kOk, client.SendCommand("method", params).code()); 922} 923 924namespace { 925 926class DisconnectedSyncWebSocket : public MockSyncWebSocket { 927 public: 928 DisconnectedSyncWebSocket() : connection_count_(0), command_count_(0) {} 929 virtual ~DisconnectedSyncWebSocket() {} 930 931 virtual bool Connect(const GURL& url) OVERRIDE { 932 connection_count_++; 933 connected_ = connection_count_ != 2; 934 return connected_; 935 } 936 937 virtual bool Send(const std::string& message) OVERRIDE { 938 command_count_++; 939 if (command_count_ == 1) { 940 connected_ = false; 941 return false; 942 } 943 return MockSyncWebSocket::Send(message); 944 } 945 946 private: 947 int connection_count_; 948 int command_count_; 949}; 950 951Status CheckCloserFuncCalled(bool* is_called) { 952 *is_called = true; 953 return Status(kOk); 954} 955 956} // namespace 957 958TEST_F(DevToolsClientImplTest, Reconnect) { 959 SyncWebSocketFactory factory = 960 base::Bind(&CreateMockSyncWebSocket<DisconnectedSyncWebSocket>); 961 bool is_called = false; 962 DevToolsClientImpl client(factory, 963 "http://url", 964 "id", 965 base::Bind(&CheckCloserFuncCalled, &is_called)); 966 ASSERT_FALSE(is_called); 967 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 968 ASSERT_FALSE(is_called); 969 base::DictionaryValue params; 970 params.SetInteger("param", 1); 971 is_called = false; 972 ASSERT_EQ(kDisconnected, client.SendCommand("method", params).code()); 973 ASSERT_FALSE(is_called); 974 ASSERT_EQ(kDisconnected, client.HandleReceivedEvents().code()); 975 ASSERT_FALSE(is_called); 976 ASSERT_EQ(kOk, client.ConnectIfNecessary().code()); 977 ASSERT_TRUE(is_called); 978 is_called = false; 979 ASSERT_EQ(kOk, client.SendCommand("method", params).code()); 980 ASSERT_FALSE(is_called); 981} 982 983namespace { 984 985class MockSyncWebSocket6 : public SyncWebSocket { 986 public: 987 explicit MockSyncWebSocket6(std::list<std::string>* messages) 988 : messages_(messages) {} 989 virtual ~MockSyncWebSocket6() {} 990 991 virtual bool IsConnected() OVERRIDE { return true; } 992 993 virtual bool Connect(const GURL& url) OVERRIDE { return true; } 994 995 virtual bool Send(const std::string& message) OVERRIDE { return true; } 996 997 virtual SyncWebSocket::StatusCode ReceiveNextMessage( 998 std::string* message, 999 const base::TimeDelta& timeout) OVERRIDE { 1000 if (messages_->empty()) 1001 return SyncWebSocket::kDisconnected; 1002 *message = messages_->front(); 1003 messages_->pop_front(); 1004 return SyncWebSocket::kOk; 1005 } 1006 1007 virtual bool HasNextMessage() OVERRIDE { return messages_->size(); } 1008 1009 private: 1010 std::list<std::string>* messages_; 1011}; 1012 1013class MockDevToolsEventListener : public DevToolsEventListener { 1014 public: 1015 MockDevToolsEventListener() : id_(1) {} 1016 virtual ~MockDevToolsEventListener() {} 1017 1018 virtual Status OnConnected(DevToolsClient* client) OVERRIDE { 1019 return Status(kOk); 1020 } 1021 1022 virtual Status OnEvent(DevToolsClient* client, 1023 const std::string& method, 1024 const base::DictionaryValue& params) OVERRIDE { 1025 id_++; 1026 Status status = client->SendCommand("hello", params); 1027 id_--; 1028 if (id_ == 3) { 1029 EXPECT_EQ(kUnexpectedAlertOpen, status.code()); 1030 } else { 1031 EXPECT_EQ(kOk, status.code()); 1032 } 1033 return Status(kOk); 1034 } 1035 1036 private: 1037 int id_; 1038}; 1039 1040scoped_ptr<SyncWebSocket> CreateMockSyncWebSocket6( 1041 std::list<std::string>* messages) { 1042 return scoped_ptr<MockSyncWebSocket6>(new MockSyncWebSocket6(messages)) 1043 .PassAs<SyncWebSocket>(); 1044} 1045 1046} // namespace 1047 1048TEST_F(DevToolsClientImplTest, BlockedByAlert) { 1049 std::list<std::string> msgs; 1050 SyncWebSocketFactory factory = base::Bind(&CreateMockSyncWebSocket6, &msgs); 1051 DevToolsClientImpl client( 1052 factory, "http://url", "id", base::Bind(&CloserFunc)); 1053 msgs.push_back( 1054 "{\"method\": \"Page.javascriptDialogOpening\", \"params\": {}}"); 1055 msgs.push_back("{\"id\": 2, \"result\": {}}"); 1056 base::DictionaryValue params; 1057 ASSERT_EQ(kUnexpectedAlertOpen, 1058 client.SendCommand("first", params).code()); 1059} 1060 1061TEST_F(DevToolsClientImplTest, CorrectlyDeterminesWhichIsBlockedByAlert) { 1062 // OUT | IN 1063 // FirstEvent 1064 // hello (id=1) 1065 // SecondEvent 1066 // hello (id=2) 1067 // ThirdEvent 1068 // hello (id=3) 1069 // FourthEvent 1070 // hello (id=4) 1071 // response for 1 1072 // alert 1073 // hello (id=5) 1074 // round trip command (id=6) 1075 // response for 2 1076 // response for 4 1077 // response for 5 1078 // response for 6 1079 std::list<std::string> msgs; 1080 SyncWebSocketFactory factory = base::Bind(&CreateMockSyncWebSocket6, &msgs); 1081 DevToolsClientImpl client( 1082 factory, "http://url", "id", base::Bind(&CloserFunc)); 1083 MockDevToolsEventListener listener; 1084 client.AddListener(&listener); 1085 msgs.push_back("{\"method\": \"FirstEvent\", \"params\": {}}"); 1086 msgs.push_back("{\"method\": \"SecondEvent\", \"params\": {}}"); 1087 msgs.push_back("{\"method\": \"ThirdEvent\", \"params\": {}}"); 1088 msgs.push_back("{\"method\": \"FourthEvent\", \"params\": {}}"); 1089 msgs.push_back("{\"id\": 1, \"result\": {}}"); 1090 msgs.push_back( 1091 "{\"method\": \"Page.javascriptDialogOpening\", \"params\": {}}"); 1092 msgs.push_back("{\"id\": 2, \"result\": {}}"); 1093 msgs.push_back("{\"id\": 4, \"result\": {}}"); 1094 msgs.push_back("{\"id\": 5, \"result\": {}}"); 1095 msgs.push_back("{\"id\": 6, \"result\": {}}"); 1096 ASSERT_EQ(kOk, client.HandleReceivedEvents().code()); 1097} 1098 1099namespace { 1100 1101class MockCommandListener : public DevToolsEventListener { 1102 public: 1103 MockCommandListener() {} 1104 virtual ~MockCommandListener() {} 1105 1106 virtual Status OnEvent(DevToolsClient* client, 1107 const std::string& method, 1108 const base::DictionaryValue& params) OVERRIDE { 1109 msgs_.push_back(method); 1110 return Status(kOk); 1111 } 1112 1113 virtual Status OnCommandSuccess(DevToolsClient* client, 1114 const std::string& method) OVERRIDE { 1115 msgs_.push_back(method); 1116 if (!callback_.is_null()) 1117 callback_.Run(client); 1118 return Status(kOk); 1119 } 1120 1121 base::Callback<void(DevToolsClient*)> callback_; 1122 std::list<std::string> msgs_; 1123}; 1124 1125void HandleReceivedEvents(DevToolsClient* client) { 1126 EXPECT_EQ(kOk, client->HandleReceivedEvents().code()); 1127} 1128 1129} // namespace 1130 1131TEST_F(DevToolsClientImplTest, ReceivesCommandResponse) { 1132 std::list<std::string> msgs; 1133 SyncWebSocketFactory factory = base::Bind(&CreateMockSyncWebSocket6, &msgs); 1134 DevToolsClientImpl client( 1135 factory, "http://url", "id", base::Bind(&CloserFunc)); 1136 MockCommandListener listener1; 1137 listener1.callback_ = base::Bind(&HandleReceivedEvents); 1138 MockCommandListener listener2; 1139 client.AddListener(&listener1); 1140 client.AddListener(&listener2); 1141 msgs.push_back("{\"id\": 1, \"result\": {}}"); 1142 msgs.push_back("{\"method\": \"event\", \"params\": {}}"); 1143 base::DictionaryValue params; 1144 ASSERT_EQ(kOk, client.SendCommand("cmd", params).code()); 1145 ASSERT_EQ(2u, listener2.msgs_.size()); 1146 ASSERT_EQ("cmd", listener2.msgs_.front()); 1147 ASSERT_EQ("event", listener2.msgs_.back()); 1148} 1149