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.
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"
29 class MockSyncWebSocket : public SyncWebSocket {
31 MockSyncWebSocket() : connected_(false), id_(-1), queued_messages_(1) {}
32 ~MockSyncWebSocket() override {}
34 bool IsConnected() override { return connected_; }
36 bool Connect(const GURL& url) override {
37 EXPECT_STREQ("http://url/", url.possibly_invalid_spec().c_str());
42 bool Send(const std::string& message) override {
43 EXPECT_TRUE(connected_);
44 scoped_ptr<base::Value> value(base::JSONReader::Read(message));
45 base::DictionaryValue* dict = NULL;
46 EXPECT_TRUE(value->GetAsDictionary(&dict));
49 EXPECT_TRUE(dict->GetInteger("id", &id_));
51 EXPECT_TRUE(dict->GetString("method", &method));
52 EXPECT_STREQ("method", method.c_str());
53 base::DictionaryValue* params = NULL;
54 EXPECT_TRUE(dict->GetDictionary("params", ¶ms));
58 EXPECT_TRUE(params->GetInteger("param", ¶m));
63 SyncWebSocket::StatusCode ReceiveNextMessage(
65 const base::TimeDelta& timeout) override {
66 if (timeout <= base::TimeDelta())
67 return SyncWebSocket::kTimeout;
68 base::DictionaryValue response;
69 response.SetInteger("id", id_);
70 base::DictionaryValue result;
71 result.SetInteger("param", 1);
72 response.Set("result", result.DeepCopy());
73 base::JSONWriter::Write(&response, message);
75 return SyncWebSocket::kOk;
78 bool HasNextMessage() override { return queued_messages_ > 0; }
87 scoped_ptr<SyncWebSocket> CreateMockSyncWebSocket() {
88 return scoped_ptr<SyncWebSocket>(new T());
91 class DevToolsClientImplTest : public testing::Test {
93 DevToolsClientImplTest() : long_timeout_(base::TimeDelta::FromMinutes(5)) {}
95 const base::TimeDelta long_timeout_;
100 TEST_F(DevToolsClientImplTest, SendCommand) {
101 SyncWebSocketFactory factory =
102 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
103 DevToolsClientImpl client(factory, "http://url", "id",
104 base::Bind(&CloserFunc));
105 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
106 base::DictionaryValue params;
107 params.SetInteger("param", 1);
108 ASSERT_EQ(kOk, client.SendCommand("method", params).code());
111 TEST_F(DevToolsClientImplTest, SendCommandAndGetResult) {
112 SyncWebSocketFactory factory =
113 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
114 DevToolsClientImpl client(factory, "http://url", "id",
115 base::Bind(&CloserFunc));
116 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
117 base::DictionaryValue params;
118 params.SetInteger("param", 1);
119 scoped_ptr<base::DictionaryValue> result;
120 Status status = client.SendCommandAndGetResult("method", params, &result);
121 ASSERT_EQ(kOk, status.code());
123 base::JSONWriter::Write(result.get(), &json);
124 ASSERT_STREQ("{\"param\":1}", json.c_str());
129 class MockSyncWebSocket2 : public SyncWebSocket {
131 MockSyncWebSocket2() {}
132 ~MockSyncWebSocket2() override {}
134 bool IsConnected() override { return false; }
136 bool Connect(const GURL& url) override { return false; }
138 bool Send(const std::string& message) override {
143 SyncWebSocket::StatusCode ReceiveNextMessage(
144 std::string* message,
145 const base::TimeDelta& timeout) override {
147 return SyncWebSocket::kDisconnected;
150 bool HasNextMessage() override { return true; }
155 TEST_F(DevToolsClientImplTest, ConnectIfNecessaryConnectFails) {
156 SyncWebSocketFactory factory =
157 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket2>);
158 DevToolsClientImpl client(factory, "http://url", "id",
159 base::Bind(&CloserFunc));
160 ASSERT_EQ(kDisconnected, client.ConnectIfNecessary().code());
165 class MockSyncWebSocket3 : public SyncWebSocket {
167 MockSyncWebSocket3() : connected_(false) {}
168 ~MockSyncWebSocket3() override {}
170 bool IsConnected() override { return connected_; }
172 bool Connect(const GURL& url) override {
177 bool Send(const std::string& message) override { return false; }
179 SyncWebSocket::StatusCode ReceiveNextMessage(
180 std::string* message,
181 const base::TimeDelta& timeout) override {
183 return SyncWebSocket::kDisconnected;
186 bool HasNextMessage() override { return true; }
194 TEST_F(DevToolsClientImplTest, SendCommandSendFails) {
195 SyncWebSocketFactory factory =
196 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket3>);
197 DevToolsClientImpl client(factory, "http://url", "id",
198 base::Bind(&CloserFunc));
199 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
200 base::DictionaryValue params;
201 ASSERT_TRUE(client.SendCommand("method", params).IsError());
206 class MockSyncWebSocket4 : public SyncWebSocket {
208 MockSyncWebSocket4() : connected_(false) {}
209 ~MockSyncWebSocket4() override {}
211 bool IsConnected() override { return connected_; }
213 bool Connect(const GURL& url) override {
218 bool Send(const std::string& message) override { return true; }
220 SyncWebSocket::StatusCode ReceiveNextMessage(
221 std::string* message,
222 const base::TimeDelta& timeout) override {
223 return SyncWebSocket::kDisconnected;
226 bool HasNextMessage() override { return true; }
234 TEST_F(DevToolsClientImplTest, SendCommandReceiveNextMessageFails) {
235 SyncWebSocketFactory factory =
236 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket4>);
237 DevToolsClientImpl client(factory, "http://url", "id",
238 base::Bind(&CloserFunc));
239 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
240 base::DictionaryValue params;
241 ASSERT_TRUE(client.SendCommand("method", params).IsError());
246 class FakeSyncWebSocket : public SyncWebSocket {
248 FakeSyncWebSocket() : connected_(false) {}
249 ~FakeSyncWebSocket() override {}
251 bool IsConnected() override { return connected_; }
253 bool Connect(const GURL& url) override {
254 EXPECT_FALSE(connected_);
259 bool Send(const std::string& message) override { return true; }
261 SyncWebSocket::StatusCode ReceiveNextMessage(
262 std::string* message,
263 const base::TimeDelta& timeout) override {
264 return SyncWebSocket::kOk;
267 bool HasNextMessage() override { return true; }
274 const std::string& message,
276 internal::InspectorMessageType* type,
277 internal::InspectorEvent* event,
278 internal::InspectorCommandResponse* command_response) {
279 *type = internal::kCommandResponseMessageType;
280 command_response->id = expected_id;
281 command_response->result.reset(new base::DictionaryValue());
285 bool ReturnBadResponse(
286 const std::string& message,
288 internal::InspectorMessageType* type,
289 internal::InspectorEvent* event,
290 internal::InspectorCommandResponse* command_response) {
291 *type = internal::kCommandResponseMessageType;
292 command_response->id = expected_id;
293 command_response->result.reset(new base::DictionaryValue());
297 bool ReturnCommandBadId(
298 const std::string& message,
300 internal::InspectorMessageType* type,
301 internal::InspectorEvent* event,
302 internal::InspectorCommandResponse* command_response) {
303 *type = internal::kCommandResponseMessageType;
304 command_response->id = expected_id + 100;
305 command_response->result.reset(new base::DictionaryValue());
309 bool ReturnCommandError(
310 const std::string& message,
312 internal::InspectorMessageType* type,
313 internal::InspectorEvent* event,
314 internal::InspectorCommandResponse* command_response) {
315 *type = internal::kCommandResponseMessageType;
316 command_response->id = expected_id;
317 command_response->error = "err";
321 class MockListener : public DevToolsEventListener {
323 MockListener() : called_(false) {}
324 ~MockListener() override { EXPECT_TRUE(called_); }
326 Status OnConnected(DevToolsClient* client) override { return Status(kOk); }
328 Status OnEvent(DevToolsClient* client,
329 const std::string& method,
330 const base::DictionaryValue& params) override {
332 EXPECT_STREQ("method", method.c_str());
333 EXPECT_TRUE(params.HasKey("key"));
341 bool ReturnEventThenResponse(
343 const std::string& message,
345 internal::InspectorMessageType* type,
346 internal::InspectorEvent* event,
347 internal::InspectorCommandResponse* command_response) {
349 *type = internal::kEventMessageType;
350 event->method = "method";
351 event->params.reset(new base::DictionaryValue());
352 event->params->SetInteger("key", 1);
354 *type = internal::kCommandResponseMessageType;
355 command_response->id = expected_id;
356 base::DictionaryValue params;
357 command_response->result.reset(new base::DictionaryValue());
358 command_response->result->SetInteger("key", 2);
365 const std::string& message,
367 internal::InspectorMessageType* type,
368 internal::InspectorEvent* event,
369 internal::InspectorCommandResponse* command_response) {
370 *type = internal::kEventMessageType;
371 event->method = "method";
372 event->params.reset(new base::DictionaryValue());
373 event->params->SetInteger("key", 1);
377 bool ReturnOutOfOrderResponses(
379 DevToolsClient* client,
380 const std::string& message,
382 internal::InspectorMessageType* type,
383 internal::InspectorEvent* event,
384 internal::InspectorCommandResponse* command_response) {
386 base::DictionaryValue params;
387 params.SetInteger("param", 1);
388 switch ((*recurse_count)++) {
390 client->SendCommand("method", params);
391 *type = internal::kEventMessageType;
392 event->method = "method";
393 event->params.reset(new base::DictionaryValue());
394 event->params->SetInteger("key", 1);
397 command_response->id = expected_id - 1;
401 command_response->id = expected_id;
405 *type = internal::kCommandResponseMessageType;
406 command_response->result.reset(new base::DictionaryValue());
407 command_response->result->SetInteger("key", key);
412 const std::string& message,
414 internal::InspectorMessageType* type,
415 internal::InspectorEvent* event,
416 internal::InspectorCommandResponse* command_response) {
420 Status AlwaysTrue(bool* is_met) {
425 Status AlwaysError(bool* is_met) {
426 return Status(kUnknownError);
431 TEST_F(DevToolsClientImplTest, SendCommandOnlyConnectsOnce) {
432 SyncWebSocketFactory factory =
433 base::Bind(&CreateMockSyncWebSocket<FakeSyncWebSocket>);
434 DevToolsClientImpl client(factory, "http://url", "id",
435 base::Bind(&CloserFunc),
436 base::Bind(&ReturnCommand));
437 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
438 base::DictionaryValue params;
439 ASSERT_TRUE(client.SendCommand("method", params).IsOk());
440 ASSERT_TRUE(client.SendCommand("method", params).IsOk());
443 TEST_F(DevToolsClientImplTest, SendCommandBadResponse) {
444 SyncWebSocketFactory factory =
445 base::Bind(&CreateMockSyncWebSocket<FakeSyncWebSocket>);
446 DevToolsClientImpl client(factory, "http://url", "id",
447 base::Bind(&CloserFunc),
448 base::Bind(&ReturnBadResponse));
449 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
450 base::DictionaryValue params;
451 ASSERT_TRUE(client.SendCommand("method", params).IsError());
454 TEST_F(DevToolsClientImplTest, SendCommandBadId) {
455 SyncWebSocketFactory factory =
456 base::Bind(&CreateMockSyncWebSocket<FakeSyncWebSocket>);
457 DevToolsClientImpl client(factory, "http://url", "id",
458 base::Bind(&CloserFunc),
459 base::Bind(&ReturnCommandBadId));
460 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
461 base::DictionaryValue params;
462 ASSERT_TRUE(client.SendCommand("method", params).IsError());
465 TEST_F(DevToolsClientImplTest, SendCommandResponseError) {
466 SyncWebSocketFactory factory =
467 base::Bind(&CreateMockSyncWebSocket<FakeSyncWebSocket>);
468 DevToolsClientImpl client(factory, "http://url", "id",
469 base::Bind(&CloserFunc),
470 base::Bind(&ReturnCommandError));
471 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
472 base::DictionaryValue params;
473 ASSERT_TRUE(client.SendCommand("method", params).IsError());
476 TEST_F(DevToolsClientImplTest, SendCommandEventBeforeResponse) {
477 SyncWebSocketFactory factory =
478 base::Bind(&CreateMockSyncWebSocket<FakeSyncWebSocket>);
479 MockListener listener;
481 DevToolsClientImpl client(factory, "http://url", "id",
482 base::Bind(&CloserFunc),
483 base::Bind(&ReturnEventThenResponse, &first));
484 client.AddListener(&listener);
485 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
486 base::DictionaryValue params;
487 scoped_ptr<base::DictionaryValue> result;
488 ASSERT_TRUE(client.SendCommandAndGetResult("method", params, &result).IsOk());
491 ASSERT_TRUE(result->GetInteger("key", &key));
495 TEST(ParseInspectorMessage, NonJson) {
496 internal::InspectorMessageType type;
497 internal::InspectorEvent event;
498 internal::InspectorCommandResponse response;
499 ASSERT_FALSE(internal::ParseInspectorMessage(
500 "hi", 0, &type, &event, &response));
503 TEST(ParseInspectorMessage, NeitherCommandNorEvent) {
504 internal::InspectorMessageType type;
505 internal::InspectorEvent event;
506 internal::InspectorCommandResponse response;
507 ASSERT_FALSE(internal::ParseInspectorMessage(
508 "{}", 0, &type, &event, &response));
511 TEST(ParseInspectorMessage, EventNoParams) {
512 internal::InspectorMessageType type;
513 internal::InspectorEvent event;
514 internal::InspectorCommandResponse response;
515 ASSERT_TRUE(internal::ParseInspectorMessage(
516 "{\"method\":\"method\"}", 0, &type, &event, &response));
517 ASSERT_EQ(internal::kEventMessageType, type);
518 ASSERT_STREQ("method", event.method.c_str());
519 ASSERT_TRUE(event.params->IsType(base::Value::TYPE_DICTIONARY));
522 TEST(ParseInspectorMessage, EventWithParams) {
523 internal::InspectorMessageType type;
524 internal::InspectorEvent event;
525 internal::InspectorCommandResponse response;
526 ASSERT_TRUE(internal::ParseInspectorMessage(
527 "{\"method\":\"method\",\"params\":{\"key\":100}}",
528 0, &type, &event, &response));
529 ASSERT_EQ(internal::kEventMessageType, type);
530 ASSERT_STREQ("method", event.method.c_str());
532 ASSERT_TRUE(event.params->GetInteger("key", &key));
536 TEST(ParseInspectorMessage, CommandNoErrorOrResult) {
537 internal::InspectorMessageType type;
538 internal::InspectorEvent event;
539 internal::InspectorCommandResponse response;
540 // As per Chromium issue 392577, DevTools does not necessarily return a
541 // "result" dictionary for every valid response. If neither "error" nor
542 // "result" keys are present, a blank result dictionary should be inferred.
543 ASSERT_TRUE(internal::ParseInspectorMessage(
544 "{\"id\":1}", 0, &type, &event, &response));
545 ASSERT_TRUE(response.result->empty());
548 TEST(ParseInspectorMessage, CommandError) {
549 internal::InspectorMessageType type;
550 internal::InspectorEvent event;
551 internal::InspectorCommandResponse response;
552 ASSERT_TRUE(internal::ParseInspectorMessage(
553 "{\"id\":1,\"error\":{}}", 0, &type, &event, &response));
554 ASSERT_EQ(internal::kCommandResponseMessageType, type);
555 ASSERT_EQ(1, response.id);
556 ASSERT_TRUE(response.error.length());
557 ASSERT_FALSE(response.result);
560 TEST(ParseInspectorMessage, Command) {
561 internal::InspectorMessageType type;
562 internal::InspectorEvent event;
563 internal::InspectorCommandResponse response;
564 ASSERT_TRUE(internal::ParseInspectorMessage(
565 "{\"id\":1,\"result\":{\"key\":1}}", 0, &type, &event, &response));
566 ASSERT_EQ(internal::kCommandResponseMessageType, type);
567 ASSERT_EQ(1, response.id);
568 ASSERT_FALSE(response.error.length());
570 ASSERT_TRUE(response.result->GetInteger("key", &key));
574 TEST_F(DevToolsClientImplTest, HandleEventsUntil) {
575 MockListener listener;
576 SyncWebSocketFactory factory =
577 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
578 DevToolsClientImpl client(factory, "http://url", "id",
579 base::Bind(&CloserFunc),
580 base::Bind(&ReturnEvent));
581 client.AddListener(&listener);
582 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
583 Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue),
585 ASSERT_EQ(kOk, status.code());
588 TEST_F(DevToolsClientImplTest, HandleEventsUntilTimeout) {
589 SyncWebSocketFactory factory =
590 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
591 DevToolsClientImpl client(factory, "http://url", "id",
592 base::Bind(&CloserFunc),
593 base::Bind(&ReturnEvent));
594 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
595 Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue),
597 ASSERT_EQ(kTimeout, status.code());
600 TEST_F(DevToolsClientImplTest, WaitForNextEventCommand) {
601 SyncWebSocketFactory factory =
602 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
603 DevToolsClientImpl client(factory, "http://url", "id",
604 base::Bind(&CloserFunc),
605 base::Bind(&ReturnCommand));
606 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
607 Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue),
609 ASSERT_EQ(kUnknownError, status.code());
612 TEST_F(DevToolsClientImplTest, WaitForNextEventError) {
613 SyncWebSocketFactory factory =
614 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
615 DevToolsClientImpl client(factory, "http://url", "id",
616 base::Bind(&CloserFunc),
617 base::Bind(&ReturnError));
618 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
619 Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue),
621 ASSERT_EQ(kUnknownError, status.code());
624 TEST_F(DevToolsClientImplTest, WaitForNextEventConditionalFuncReturnsError) {
625 SyncWebSocketFactory factory =
626 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
627 DevToolsClientImpl client(factory, "http://url", "id",
628 base::Bind(&CloserFunc),
629 base::Bind(&ReturnEvent));
630 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
631 Status status = client.HandleEventsUntil(base::Bind(&AlwaysError),
633 ASSERT_EQ(kUnknownError, status.code());
636 TEST_F(DevToolsClientImplTest, NestedCommandsWithOutOfOrderResults) {
637 SyncWebSocketFactory factory =
638 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
639 int recurse_count = 0;
640 DevToolsClientImpl client(factory, "http://url", "id",
641 base::Bind(&CloserFunc));
642 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
643 client.SetParserFuncForTesting(
644 base::Bind(&ReturnOutOfOrderResponses, &recurse_count, &client));
645 base::DictionaryValue params;
646 params.SetInteger("param", 1);
647 scoped_ptr<base::DictionaryValue> result;
648 ASSERT_TRUE(client.SendCommandAndGetResult("method", params, &result).IsOk());
651 ASSERT_TRUE(result->GetInteger("key", &key));
657 class OnConnectedListener : public DevToolsEventListener {
659 OnConnectedListener(const std::string& method, DevToolsClient* client)
662 on_connected_called_(false),
663 on_event_called_(false) {
664 client_->AddListener(this);
666 ~OnConnectedListener() override {}
668 void VerifyCalled() {
669 EXPECT_TRUE(on_connected_called_);
670 EXPECT_TRUE(on_event_called_);
673 Status OnConnected(DevToolsClient* client) override {
674 EXPECT_EQ(client_, client);
675 EXPECT_STREQ("onconnected-id", client->GetId().c_str());
676 EXPECT_FALSE(on_connected_called_);
677 EXPECT_FALSE(on_event_called_);
678 on_connected_called_ = true;
679 base::DictionaryValue params;
680 return client_->SendCommand(method_, params);
683 Status OnEvent(DevToolsClient* client,
684 const std::string& method,
685 const base::DictionaryValue& params) override {
686 EXPECT_EQ(client_, client);
687 EXPECT_STREQ("onconnected-id", client->GetId().c_str());
688 EXPECT_TRUE(on_connected_called_);
689 on_event_called_ = true;
695 DevToolsClient* client_;
696 bool on_connected_called_;
697 bool on_event_called_;
700 class OnConnectedSyncWebSocket : public SyncWebSocket {
702 OnConnectedSyncWebSocket() : connected_(false) {}
703 ~OnConnectedSyncWebSocket() override {}
705 bool IsConnected() override { return connected_; }
707 bool Connect(const GURL& url) override {
712 bool Send(const std::string& message) override {
713 EXPECT_TRUE(connected_);
714 scoped_ptr<base::Value> value(base::JSONReader::Read(message));
715 base::DictionaryValue* dict = NULL;
716 EXPECT_TRUE(value->GetAsDictionary(&dict));
720 EXPECT_TRUE(dict->GetInteger("id", &id));
722 EXPECT_TRUE(dict->GetString("method", &method));
724 base::DictionaryValue response;
725 response.SetInteger("id", id);
726 response.Set("result", new base::DictionaryValue());
727 std::string json_response;
728 base::JSONWriter::Write(&response, &json_response);
729 queued_response_.push_back(json_response);
732 base::DictionaryValue event;
733 event.SetString("method", "updateEvent");
734 event.Set("params", new base::DictionaryValue());
735 std::string json_event;
736 base::JSONWriter::Write(&event, &json_event);
737 queued_response_.push_back(json_event);
742 SyncWebSocket::StatusCode ReceiveNextMessage(
743 std::string* message,
744 const base::TimeDelta& timeout) override {
745 if (queued_response_.empty())
746 return SyncWebSocket::kDisconnected;
747 *message = queued_response_.front();
748 queued_response_.pop_front();
749 return SyncWebSocket::kOk;
752 bool HasNextMessage() override { return !queued_response_.empty(); }
756 std::list<std::string> queued_response_;
761 TEST_F(DevToolsClientImplTest, ProcessOnConnectedFirstOnCommand) {
762 SyncWebSocketFactory factory =
763 base::Bind(&CreateMockSyncWebSocket<OnConnectedSyncWebSocket>);
764 DevToolsClientImpl client(factory, "http://url", "onconnected-id",
765 base::Bind(&CloserFunc));
766 OnConnectedListener listener1("DOM.getDocument", &client);
767 OnConnectedListener listener2("Runtime.enable", &client);
768 OnConnectedListener listener3("Page.enable", &client);
769 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
770 base::DictionaryValue params;
771 EXPECT_EQ(kOk, client.SendCommand("Runtime.execute", params).code());
772 listener1.VerifyCalled();
773 listener2.VerifyCalled();
774 listener3.VerifyCalled();
777 TEST_F(DevToolsClientImplTest, ProcessOnConnectedFirstOnHandleEventsUntil) {
778 SyncWebSocketFactory factory =
779 base::Bind(&CreateMockSyncWebSocket<OnConnectedSyncWebSocket>);
780 DevToolsClientImpl client(factory, "http://url", "onconnected-id",
781 base::Bind(&CloserFunc));
782 OnConnectedListener listener1("DOM.getDocument", &client);
783 OnConnectedListener listener2("Runtime.enable", &client);
784 OnConnectedListener listener3("Page.enable", &client);
785 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
786 EXPECT_EQ(kOk, client.HandleReceivedEvents().code());
787 listener1.VerifyCalled();
788 listener2.VerifyCalled();
789 listener3.VerifyCalled();
794 class MockSyncWebSocket5 : public SyncWebSocket {
796 MockSyncWebSocket5() : request_no_(0) {}
797 ~MockSyncWebSocket5() override {}
799 bool IsConnected() override { return true; }
801 bool Connect(const GURL& url) override { return true; }
803 bool Send(const std::string& message) override { return true; }
805 SyncWebSocket::StatusCode ReceiveNextMessage(
806 std::string* message,
807 const base::TimeDelta& timeout) override {
808 if (request_no_ == 0) {
809 *message = "{\"method\": \"m\", \"params\": {}}";
811 *message = base::StringPrintf(
812 "{\"result\": {}, \"id\": %d}", request_no_);
815 return SyncWebSocket::kOk;
818 bool HasNextMessage() override { return false; }
824 class OtherEventListener : public DevToolsEventListener {
826 OtherEventListener() : received_event_(false) {}
827 ~OtherEventListener() override {}
829 Status OnConnected(DevToolsClient* client) override { return Status(kOk); }
830 Status OnEvent(DevToolsClient* client,
831 const std::string& method,
832 const base::DictionaryValue& params) override {
833 received_event_ = true;
837 bool received_event_;
840 class OnEventListener : public DevToolsEventListener {
842 OnEventListener(DevToolsClient* client,
843 OtherEventListener* other_listener)
845 other_listener_(other_listener) {}
846 ~OnEventListener() override {}
848 Status OnConnected(DevToolsClient* client) override {
849 EXPECT_EQ(client_, client);
853 Status OnEvent(DevToolsClient* client,
854 const std::string& method,
855 const base::DictionaryValue& params) override {
856 EXPECT_EQ(client_, client);
857 client_->SendCommand("method", params);
858 EXPECT_TRUE(other_listener_->received_event_);
863 DevToolsClient* client_;
864 OtherEventListener* other_listener_;
869 TEST_F(DevToolsClientImplTest, ProcessOnEventFirst) {
870 SyncWebSocketFactory factory =
871 base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket5>);
872 DevToolsClientImpl client(factory, "http://url", "id",
873 base::Bind(&CloserFunc));
874 OtherEventListener listener2;
875 OnEventListener listener1(&client, &listener2);
876 client.AddListener(&listener1);
877 client.AddListener(&listener2);
878 base::DictionaryValue params;
879 EXPECT_EQ(kOk, client.SendCommand("method", params).code());
884 class DisconnectedSyncWebSocket : public MockSyncWebSocket {
886 DisconnectedSyncWebSocket() : connection_count_(0), command_count_(0) {}
887 ~DisconnectedSyncWebSocket() override {}
889 bool Connect(const GURL& url) override {
891 connected_ = connection_count_ != 2;
895 bool Send(const std::string& message) override {
897 if (command_count_ == 1) {
901 return MockSyncWebSocket::Send(message);
905 int connection_count_;
909 Status CheckCloserFuncCalled(bool* is_called) {
916 TEST_F(DevToolsClientImplTest, Reconnect) {
917 SyncWebSocketFactory factory =
918 base::Bind(&CreateMockSyncWebSocket<DisconnectedSyncWebSocket>);
919 bool is_called = false;
920 DevToolsClientImpl client(factory,
923 base::Bind(&CheckCloserFuncCalled, &is_called));
924 ASSERT_FALSE(is_called);
925 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
926 ASSERT_FALSE(is_called);
927 base::DictionaryValue params;
928 params.SetInteger("param", 1);
930 ASSERT_EQ(kDisconnected, client.SendCommand("method", params).code());
931 ASSERT_FALSE(is_called);
932 ASSERT_EQ(kDisconnected, client.HandleReceivedEvents().code());
933 ASSERT_FALSE(is_called);
934 ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
935 ASSERT_TRUE(is_called);
937 ASSERT_EQ(kOk, client.SendCommand("method", params).code());
938 ASSERT_FALSE(is_called);
943 class MockSyncWebSocket6 : public SyncWebSocket {
945 explicit MockSyncWebSocket6(std::list<std::string>* messages)
946 : messages_(messages) {}
947 ~MockSyncWebSocket6() override {}
949 bool IsConnected() override { return true; }
951 bool Connect(const GURL& url) override { return true; }
953 bool Send(const std::string& message) override { return true; }
955 SyncWebSocket::StatusCode ReceiveNextMessage(
956 std::string* message,
957 const base::TimeDelta& timeout) override {
958 if (messages_->empty())
959 return SyncWebSocket::kDisconnected;
960 *message = messages_->front();
961 messages_->pop_front();
962 return SyncWebSocket::kOk;
965 bool HasNextMessage() override { return messages_->size(); }
968 std::list<std::string>* messages_;
971 class MockDevToolsEventListener : public DevToolsEventListener {
973 MockDevToolsEventListener() : id_(1) {}
974 ~MockDevToolsEventListener() override {}
976 Status OnConnected(DevToolsClient* client) override { return Status(kOk); }
978 Status OnEvent(DevToolsClient* client,
979 const std::string& method,
980 const base::DictionaryValue& params) override {
982 Status status = client->SendCommand("hello", params);
985 EXPECT_EQ(kUnexpectedAlertOpen, status.code());
987 EXPECT_EQ(kOk, status.code());
996 scoped_ptr<SyncWebSocket> CreateMockSyncWebSocket6(
997 std::list<std::string>* messages) {
998 return make_scoped_ptr(new MockSyncWebSocket6(messages));
1003 TEST_F(DevToolsClientImplTest, BlockedByAlert) {
1004 std::list<std::string> msgs;
1005 SyncWebSocketFactory factory = base::Bind(&CreateMockSyncWebSocket6, &msgs);
1006 DevToolsClientImpl client(
1007 factory, "http://url", "id", base::Bind(&CloserFunc));
1009 "{\"method\": \"Page.javascriptDialogOpening\", \"params\": {}}");
1010 msgs.push_back("{\"id\": 2, \"result\": {}}");
1011 base::DictionaryValue params;
1012 ASSERT_EQ(kUnexpectedAlertOpen,
1013 client.SendCommand("first", params).code());
1016 TEST_F(DevToolsClientImplTest, CorrectlyDeterminesWhichIsBlockedByAlert) {
1029 // round trip command (id=6)
1034 std::list<std::string> msgs;
1035 SyncWebSocketFactory factory = base::Bind(&CreateMockSyncWebSocket6, &msgs);
1036 DevToolsClientImpl client(
1037 factory, "http://url", "id", base::Bind(&CloserFunc));
1038 MockDevToolsEventListener listener;
1039 client.AddListener(&listener);
1040 msgs.push_back("{\"method\": \"FirstEvent\", \"params\": {}}");
1041 msgs.push_back("{\"method\": \"SecondEvent\", \"params\": {}}");
1042 msgs.push_back("{\"method\": \"ThirdEvent\", \"params\": {}}");
1043 msgs.push_back("{\"method\": \"FourthEvent\", \"params\": {}}");
1044 msgs.push_back("{\"id\": 1, \"result\": {}}");
1046 "{\"method\": \"Page.javascriptDialogOpening\", \"params\": {}}");
1047 msgs.push_back("{\"id\": 2, \"result\": {}}");
1048 msgs.push_back("{\"id\": 4, \"result\": {}}");
1049 msgs.push_back("{\"id\": 5, \"result\": {}}");
1050 msgs.push_back("{\"id\": 6, \"result\": {}}");
1051 ASSERT_EQ(kOk, client.HandleReceivedEvents().code());
1056 class MockCommandListener : public DevToolsEventListener {
1058 MockCommandListener() {}
1059 ~MockCommandListener() override {}
1061 Status OnEvent(DevToolsClient* client,
1062 const std::string& method,
1063 const base::DictionaryValue& params) override {
1064 msgs_.push_back(method);
1068 Status OnCommandSuccess(DevToolsClient* client,
1069 const std::string& method) override {
1070 msgs_.push_back(method);
1071 if (!callback_.is_null())
1072 callback_.Run(client);
1076 base::Callback<void(DevToolsClient*)> callback_;
1077 std::list<std::string> msgs_;
1080 void HandleReceivedEvents(DevToolsClient* client) {
1081 EXPECT_EQ(kOk, client->HandleReceivedEvents().code());
1086 TEST_F(DevToolsClientImplTest, ReceivesCommandResponse) {
1087 std::list<std::string> msgs;
1088 SyncWebSocketFactory factory = base::Bind(&CreateMockSyncWebSocket6, &msgs);
1089 DevToolsClientImpl client(
1090 factory, "http://url", "id", base::Bind(&CloserFunc));
1091 MockCommandListener listener1;
1092 listener1.callback_ = base::Bind(&HandleReceivedEvents);
1093 MockCommandListener listener2;
1094 client.AddListener(&listener1);
1095 client.AddListener(&listener2);
1096 msgs.push_back("{\"id\": 1, \"result\": {}}");
1097 msgs.push_back("{\"method\": \"event\", \"params\": {}}");
1098 base::DictionaryValue params;
1099 ASSERT_EQ(kOk, client.SendCommand("cmd", params).code());
1100 ASSERT_EQ(2u, listener2.msgs_.size());
1101 ASSERT_EQ("cmd", listener2.msgs_.front());
1102 ASSERT_EQ("event", listener2.msgs_.back());