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", &params));
57    if (!params)
58      return false;
59    int param = -1;
60    EXPECT_TRUE(params->GetInteger("param", &param));
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