75efe88fedb1766b91a9993e8b4f78dd363b0863
[platform/framework/web/crosswalk.git] / src / chrome / test / chromedriver / chrome / devtools_client_impl_unittest.cc
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
23 namespace {
24
25 Status CloserFunc() {
26   return Status(kOk);
27 }
28
29 class 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
90 template <typename T>
91 scoped_ptr<SyncWebSocket> CreateMockSyncWebSocket() {
92   return scoped_ptr<SyncWebSocket>(new T());
93 }
94
95 class 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
104 TEST_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
115 TEST_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
131 namespace {
132
133 class 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
165 TEST_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
173 namespace {
174
175 class 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
210 TEST_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
220 namespace {
221
222 class 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
256 TEST_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
266 namespace {
267
268 class 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
301 bool 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
313 bool 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
325 bool 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
337 bool 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
349 class 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
373 bool 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
396 bool 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
409 bool 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
443 bool 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
452 Status AlwaysTrue(bool* is_met) {
453   *is_met = true;
454   return Status(kOk);
455 }
456
457 Status AlwaysError(bool* is_met) {
458   return Status(kUnknownError);
459 }
460
461 }  // namespace
462
463 TEST_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
475 TEST_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
486 TEST_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
497 TEST_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
508 TEST_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
527 TEST(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
535 TEST(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
543 TEST(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
554 TEST(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
568 TEST(ParseInspectorMessage, CommandNoErrorOrResult) {
569   internal::InspectorMessageType type;
570   internal::InspectorEvent event;
571   internal::InspectorCommandResponse response;
572   // As per Chromium issue 392577, DevTools does not necessarily return a
573   // "result" dictionary for every valid response. If neither "error" nor
574   // "result" keys are present, a blank result dictionary should be inferred.
575   ASSERT_TRUE(internal::ParseInspectorMessage(
576       "{\"id\":1}", 0, &type, &event, &response));
577   ASSERT_TRUE(response.result->empty());
578 }
579
580 TEST(ParseInspectorMessage, CommandError) {
581   internal::InspectorMessageType type;
582   internal::InspectorEvent event;
583   internal::InspectorCommandResponse response;
584   ASSERT_TRUE(internal::ParseInspectorMessage(
585       "{\"id\":1,\"error\":{}}", 0, &type, &event, &response));
586   ASSERT_EQ(internal::kCommandResponseMessageType, type);
587   ASSERT_EQ(1, response.id);
588   ASSERT_TRUE(response.error.length());
589   ASSERT_FALSE(response.result);
590 }
591
592 TEST(ParseInspectorMessage, Command) {
593   internal::InspectorMessageType type;
594   internal::InspectorEvent event;
595   internal::InspectorCommandResponse response;
596   ASSERT_TRUE(internal::ParseInspectorMessage(
597       "{\"id\":1,\"result\":{\"key\":1}}", 0, &type, &event, &response));
598   ASSERT_EQ(internal::kCommandResponseMessageType, type);
599   ASSERT_EQ(1, response.id);
600   ASSERT_FALSE(response.error.length());
601   int key;
602   ASSERT_TRUE(response.result->GetInteger("key", &key));
603   ASSERT_EQ(1, key);
604 }
605
606 TEST_F(DevToolsClientImplTest, HandleEventsUntil) {
607   MockListener listener;
608   SyncWebSocketFactory factory =
609       base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
610   DevToolsClientImpl client(factory, "http://url", "id",
611                             base::Bind(&CloserFunc),
612                             base::Bind(&ReturnEvent));
613   client.AddListener(&listener);
614   ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
615   Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue),
616                                            long_timeout_);
617   ASSERT_EQ(kOk, status.code());
618 }
619
620 TEST_F(DevToolsClientImplTest, HandleEventsUntilTimeout) {
621   SyncWebSocketFactory factory =
622       base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
623   DevToolsClientImpl client(factory, "http://url", "id",
624                             base::Bind(&CloserFunc),
625                             base::Bind(&ReturnEvent));
626   ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
627   Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue),
628                                            base::TimeDelta());
629   ASSERT_EQ(kTimeout, status.code());
630 }
631
632 TEST_F(DevToolsClientImplTest, WaitForNextEventCommand) {
633   SyncWebSocketFactory factory =
634       base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
635   DevToolsClientImpl client(factory, "http://url", "id",
636                             base::Bind(&CloserFunc),
637                             base::Bind(&ReturnCommand));
638   ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
639   Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue),
640                                            long_timeout_);
641   ASSERT_EQ(kUnknownError, status.code());
642 }
643
644 TEST_F(DevToolsClientImplTest, WaitForNextEventError) {
645   SyncWebSocketFactory factory =
646       base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
647   DevToolsClientImpl client(factory, "http://url", "id",
648                             base::Bind(&CloserFunc),
649                             base::Bind(&ReturnError));
650   ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
651   Status status = client.HandleEventsUntil(base::Bind(&AlwaysTrue),
652                                            long_timeout_);
653   ASSERT_EQ(kUnknownError, status.code());
654 }
655
656 TEST_F(DevToolsClientImplTest, WaitForNextEventConditionalFuncReturnsError) {
657   SyncWebSocketFactory factory =
658       base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
659   DevToolsClientImpl client(factory, "http://url", "id",
660                             base::Bind(&CloserFunc),
661                             base::Bind(&ReturnEvent));
662   ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
663   Status status = client.HandleEventsUntil(base::Bind(&AlwaysError),
664                                            long_timeout_);
665   ASSERT_EQ(kUnknownError, status.code());
666 }
667
668 TEST_F(DevToolsClientImplTest, NestedCommandsWithOutOfOrderResults) {
669   SyncWebSocketFactory factory =
670       base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>);
671   int recurse_count = 0;
672   DevToolsClientImpl client(factory, "http://url", "id",
673                             base::Bind(&CloserFunc));
674   ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
675   client.SetParserFuncForTesting(
676       base::Bind(&ReturnOutOfOrderResponses, &recurse_count, &client));
677   base::DictionaryValue params;
678   params.SetInteger("param", 1);
679   scoped_ptr<base::DictionaryValue> result;
680   ASSERT_TRUE(client.SendCommandAndGetResult("method", params, &result).IsOk());
681   ASSERT_TRUE(result);
682   int key;
683   ASSERT_TRUE(result->GetInteger("key", &key));
684   ASSERT_EQ(2, key);
685 }
686
687 namespace {
688
689 class OnConnectedListener : public DevToolsEventListener {
690  public:
691   OnConnectedListener(const std::string& method, DevToolsClient* client)
692       : method_(method),
693         client_(client),
694         on_connected_called_(false),
695         on_event_called_(false) {
696     client_->AddListener(this);
697   }
698   virtual ~OnConnectedListener() {}
699
700   void VerifyCalled() {
701     EXPECT_TRUE(on_connected_called_);
702     EXPECT_TRUE(on_event_called_);
703   }
704
705   virtual Status OnConnected(DevToolsClient* client) OVERRIDE {
706     EXPECT_EQ(client_, client);
707     EXPECT_STREQ("onconnected-id", client->GetId().c_str());
708     EXPECT_FALSE(on_connected_called_);
709     EXPECT_FALSE(on_event_called_);
710     on_connected_called_ = true;
711     base::DictionaryValue params;
712     return client_->SendCommand(method_, params);
713   }
714
715   virtual Status OnEvent(DevToolsClient* client,
716                          const std::string& method,
717                          const base::DictionaryValue& params) OVERRIDE {
718     EXPECT_EQ(client_, client);
719     EXPECT_STREQ("onconnected-id", client->GetId().c_str());
720     EXPECT_TRUE(on_connected_called_);
721     on_event_called_ = true;
722     return Status(kOk);
723   }
724
725  private:
726   std::string method_;
727   DevToolsClient* client_;
728   bool on_connected_called_;
729   bool on_event_called_;
730 };
731
732 class OnConnectedSyncWebSocket : public SyncWebSocket {
733  public:
734   OnConnectedSyncWebSocket() : connected_(false) {}
735   virtual ~OnConnectedSyncWebSocket() {}
736
737   virtual bool IsConnected() OVERRIDE {
738     return connected_;
739   }
740
741   virtual bool Connect(const GURL& url) OVERRIDE {
742     connected_ = true;
743     return true;
744   }
745
746   virtual bool Send(const std::string& message) OVERRIDE {
747     EXPECT_TRUE(connected_);
748     scoped_ptr<base::Value> value(base::JSONReader::Read(message));
749     base::DictionaryValue* dict = NULL;
750     EXPECT_TRUE(value->GetAsDictionary(&dict));
751     if (!dict)
752       return false;
753     int id;
754     EXPECT_TRUE(dict->GetInteger("id", &id));
755     std::string method;
756     EXPECT_TRUE(dict->GetString("method", &method));
757
758     base::DictionaryValue response;
759     response.SetInteger("id", id);
760     response.Set("result", new base::DictionaryValue());
761     std::string json_response;
762     base::JSONWriter::Write(&response, &json_response);
763     queued_response_.push_back(json_response);
764
765     // Push one event.
766     base::DictionaryValue event;
767     event.SetString("method", "updateEvent");
768     event.Set("params", new base::DictionaryValue());
769     std::string json_event;
770     base::JSONWriter::Write(&event, &json_event);
771     queued_response_.push_back(json_event);
772
773     return true;
774   }
775
776   virtual SyncWebSocket::StatusCode ReceiveNextMessage(
777       std::string* message,
778       const base::TimeDelta& timeout) OVERRIDE {
779     if (queued_response_.empty())
780       return SyncWebSocket::kDisconnected;
781     *message = queued_response_.front();
782     queued_response_.pop_front();
783     return SyncWebSocket::kOk;
784   }
785
786   virtual bool HasNextMessage() OVERRIDE {
787     return !queued_response_.empty();
788   }
789
790  private:
791   bool connected_;
792   std::list<std::string> queued_response_;
793 };
794
795 }  // namespace
796
797 TEST_F(DevToolsClientImplTest, ProcessOnConnectedFirstOnCommand) {
798   SyncWebSocketFactory factory =
799       base::Bind(&CreateMockSyncWebSocket<OnConnectedSyncWebSocket>);
800   DevToolsClientImpl client(factory, "http://url", "onconnected-id",
801                             base::Bind(&CloserFunc));
802   OnConnectedListener listener1("DOM.getDocument", &client);
803   OnConnectedListener listener2("Runtime.enable", &client);
804   OnConnectedListener listener3("Page.enable", &client);
805   ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
806   base::DictionaryValue params;
807   EXPECT_EQ(kOk, client.SendCommand("Runtime.execute", params).code());
808   listener1.VerifyCalled();
809   listener2.VerifyCalled();
810   listener3.VerifyCalled();
811 }
812
813 TEST_F(DevToolsClientImplTest, ProcessOnConnectedFirstOnHandleEventsUntil) {
814   SyncWebSocketFactory factory =
815       base::Bind(&CreateMockSyncWebSocket<OnConnectedSyncWebSocket>);
816   DevToolsClientImpl client(factory, "http://url", "onconnected-id",
817                             base::Bind(&CloserFunc));
818   OnConnectedListener listener1("DOM.getDocument", &client);
819   OnConnectedListener listener2("Runtime.enable", &client);
820   OnConnectedListener listener3("Page.enable", &client);
821   ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
822   EXPECT_EQ(kOk, client.HandleReceivedEvents().code());
823   listener1.VerifyCalled();
824   listener2.VerifyCalled();
825   listener3.VerifyCalled();
826 }
827
828 namespace {
829
830 class MockSyncWebSocket5 : public SyncWebSocket {
831  public:
832   MockSyncWebSocket5() : request_no_(0) {}
833   virtual ~MockSyncWebSocket5() {}
834
835   virtual bool IsConnected() OVERRIDE {
836     return true;
837   }
838
839   virtual bool Connect(const GURL& url) OVERRIDE {
840     return true;
841   }
842
843   virtual bool Send(const std::string& message) OVERRIDE {
844     return true;
845   }
846
847   virtual SyncWebSocket::StatusCode ReceiveNextMessage(
848       std::string* message,
849       const base::TimeDelta& timeout) OVERRIDE {
850     if (request_no_ == 0) {
851       *message = "{\"method\": \"m\", \"params\": {}}";
852     } else {
853       *message = base::StringPrintf(
854           "{\"result\": {}, \"id\": %d}", request_no_);
855     }
856     request_no_++;
857     return SyncWebSocket::kOk;
858   }
859
860   virtual bool HasNextMessage() OVERRIDE {
861     return false;
862   }
863
864  private:
865   int request_no_;
866 };
867
868 class OtherEventListener : public DevToolsEventListener {
869  public:
870   OtherEventListener() : received_event_(false) {}
871   virtual ~OtherEventListener() {}
872
873   virtual Status OnConnected(DevToolsClient* client) OVERRIDE {
874     return Status(kOk);
875   }
876   virtual Status OnEvent(DevToolsClient* client,
877                          const std::string& method,
878                          const base::DictionaryValue& params) OVERRIDE {
879     received_event_ = true;
880     return Status(kOk);
881   }
882
883   bool received_event_;
884 };
885
886 class OnEventListener : public DevToolsEventListener {
887  public:
888   OnEventListener(DevToolsClient* client,
889                   OtherEventListener* other_listener)
890       : client_(client),
891         other_listener_(other_listener) {}
892   virtual ~OnEventListener() {}
893
894   virtual Status OnConnected(DevToolsClient* client) OVERRIDE {
895     EXPECT_EQ(client_, client);
896     return Status(kOk);
897   }
898
899   virtual Status OnEvent(DevToolsClient* client,
900                          const std::string& method,
901                          const base::DictionaryValue& params) OVERRIDE {
902     EXPECT_EQ(client_, client);
903     client_->SendCommand("method", params);
904     EXPECT_TRUE(other_listener_->received_event_);
905     return Status(kOk);
906   }
907
908  private:
909   DevToolsClient* client_;
910   OtherEventListener* other_listener_;
911 };
912
913 }  // namespace
914
915 TEST_F(DevToolsClientImplTest, ProcessOnEventFirst) {
916   SyncWebSocketFactory factory =
917       base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket5>);
918   DevToolsClientImpl client(factory, "http://url", "id",
919                             base::Bind(&CloserFunc));
920   OtherEventListener listener2;
921   OnEventListener listener1(&client, &listener2);
922   client.AddListener(&listener1);
923   client.AddListener(&listener2);
924   base::DictionaryValue params;
925   EXPECT_EQ(kOk, client.SendCommand("method", params).code());
926 }
927
928 namespace {
929
930 class DisconnectedSyncWebSocket : public MockSyncWebSocket {
931  public:
932   DisconnectedSyncWebSocket() : connection_count_(0), command_count_(0) {}
933   virtual ~DisconnectedSyncWebSocket() {}
934
935   virtual bool Connect(const GURL& url) OVERRIDE {
936     connection_count_++;
937     connected_ = connection_count_ != 2;
938     return connected_;
939   }
940
941   virtual bool Send(const std::string& message) OVERRIDE {
942     command_count_++;
943     if (command_count_ == 1) {
944       connected_ = false;
945       return false;
946     }
947     return MockSyncWebSocket::Send(message);
948   }
949
950  private:
951   int connection_count_;
952   int command_count_;
953 };
954
955 Status CheckCloserFuncCalled(bool* is_called) {
956   *is_called = true;
957   return Status(kOk);
958 }
959
960 }  // namespace
961
962 TEST_F(DevToolsClientImplTest, Reconnect) {
963   SyncWebSocketFactory factory =
964       base::Bind(&CreateMockSyncWebSocket<DisconnectedSyncWebSocket>);
965   bool is_called = false;
966   DevToolsClientImpl client(factory,
967                             "http://url",
968                             "id",
969                             base::Bind(&CheckCloserFuncCalled, &is_called));
970   ASSERT_FALSE(is_called);
971   ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
972   ASSERT_FALSE(is_called);
973   base::DictionaryValue params;
974   params.SetInteger("param", 1);
975   is_called = false;
976   ASSERT_EQ(kDisconnected, client.SendCommand("method", params).code());
977   ASSERT_FALSE(is_called);
978   ASSERT_EQ(kDisconnected, client.HandleReceivedEvents().code());
979   ASSERT_FALSE(is_called);
980   ASSERT_EQ(kOk, client.ConnectIfNecessary().code());
981   ASSERT_TRUE(is_called);
982   is_called = false;
983   ASSERT_EQ(kOk, client.SendCommand("method", params).code());
984   ASSERT_FALSE(is_called);
985 }
986
987 namespace {
988
989 class MockSyncWebSocket6 : public SyncWebSocket {
990  public:
991   explicit MockSyncWebSocket6(std::list<std::string>* messages)
992       : messages_(messages) {}
993   virtual ~MockSyncWebSocket6() {}
994
995   virtual bool IsConnected() OVERRIDE { return true; }
996
997   virtual bool Connect(const GURL& url) OVERRIDE { return true; }
998
999   virtual bool Send(const std::string& message) OVERRIDE { return true; }
1000
1001   virtual SyncWebSocket::StatusCode ReceiveNextMessage(
1002       std::string* message,
1003       const base::TimeDelta& timeout) OVERRIDE {
1004     if (messages_->empty())
1005       return SyncWebSocket::kDisconnected;
1006     *message = messages_->front();
1007     messages_->pop_front();
1008     return SyncWebSocket::kOk;
1009   }
1010
1011   virtual bool HasNextMessage() OVERRIDE { return messages_->size(); }
1012
1013  private:
1014   std::list<std::string>* messages_;
1015 };
1016
1017 class MockDevToolsEventListener : public DevToolsEventListener {
1018  public:
1019   MockDevToolsEventListener() : id_(1) {}
1020   virtual ~MockDevToolsEventListener() {}
1021
1022   virtual Status OnConnected(DevToolsClient* client) OVERRIDE {
1023     return Status(kOk);
1024   }
1025
1026   virtual Status OnEvent(DevToolsClient* client,
1027                          const std::string& method,
1028                          const base::DictionaryValue& params) OVERRIDE {
1029     id_++;
1030     Status status = client->SendCommand("hello", params);
1031     id_--;
1032     if (id_ == 3) {
1033       EXPECT_EQ(kUnexpectedAlertOpen, status.code());
1034     } else {
1035       EXPECT_EQ(kOk, status.code());
1036     }
1037     return Status(kOk);
1038   }
1039
1040  private:
1041   int id_;
1042 };
1043
1044 scoped_ptr<SyncWebSocket> CreateMockSyncWebSocket6(
1045     std::list<std::string>* messages) {
1046   return scoped_ptr<MockSyncWebSocket6>(new MockSyncWebSocket6(messages))
1047       .PassAs<SyncWebSocket>();
1048 }
1049
1050 }  // namespace
1051
1052 TEST_F(DevToolsClientImplTest, BlockedByAlert) {
1053   std::list<std::string> msgs;
1054   SyncWebSocketFactory factory = base::Bind(&CreateMockSyncWebSocket6, &msgs);
1055   DevToolsClientImpl client(
1056       factory, "http://url", "id", base::Bind(&CloserFunc));
1057   msgs.push_back(
1058       "{\"method\": \"Page.javascriptDialogOpening\", \"params\": {}}");
1059   msgs.push_back("{\"id\": 2, \"result\": {}}");
1060   base::DictionaryValue params;
1061   ASSERT_EQ(kUnexpectedAlertOpen,
1062             client.SendCommand("first", params).code());
1063 }
1064
1065 TEST_F(DevToolsClientImplTest, CorrectlyDeterminesWhichIsBlockedByAlert) {
1066   // OUT                 | IN
1067   //                       FirstEvent
1068   // hello (id=1)
1069   //                       SecondEvent
1070   // hello (id=2)
1071   //                       ThirdEvent
1072   // hello (id=3)
1073   //                       FourthEvent
1074   // hello (id=4)
1075   //                       response for 1
1076   //                       alert
1077   // hello (id=5)
1078   // round trip command (id=6)
1079   //                       response for 2
1080   //                       response for 4
1081   //                       response for 5
1082   //                       response for 6
1083   std::list<std::string> msgs;
1084   SyncWebSocketFactory factory = base::Bind(&CreateMockSyncWebSocket6, &msgs);
1085   DevToolsClientImpl client(
1086       factory, "http://url", "id", base::Bind(&CloserFunc));
1087   MockDevToolsEventListener listener;
1088   client.AddListener(&listener);
1089   msgs.push_back("{\"method\": \"FirstEvent\", \"params\": {}}");
1090   msgs.push_back("{\"method\": \"SecondEvent\", \"params\": {}}");
1091   msgs.push_back("{\"method\": \"ThirdEvent\", \"params\": {}}");
1092   msgs.push_back("{\"method\": \"FourthEvent\", \"params\": {}}");
1093   msgs.push_back("{\"id\": 1, \"result\": {}}");
1094   msgs.push_back(
1095       "{\"method\": \"Page.javascriptDialogOpening\", \"params\": {}}");
1096   msgs.push_back("{\"id\": 2, \"result\": {}}");
1097   msgs.push_back("{\"id\": 4, \"result\": {}}");
1098   msgs.push_back("{\"id\": 5, \"result\": {}}");
1099   msgs.push_back("{\"id\": 6, \"result\": {}}");
1100   ASSERT_EQ(kOk, client.HandleReceivedEvents().code());
1101 }
1102
1103 namespace {
1104
1105 class MockCommandListener : public DevToolsEventListener {
1106  public:
1107   MockCommandListener() {}
1108   virtual ~MockCommandListener() {}
1109
1110   virtual Status OnEvent(DevToolsClient* client,
1111                          const std::string& method,
1112                          const base::DictionaryValue& params) OVERRIDE {
1113     msgs_.push_back(method);
1114     return Status(kOk);
1115   }
1116
1117   virtual Status OnCommandSuccess(DevToolsClient* client,
1118                                   const std::string& method) OVERRIDE {
1119     msgs_.push_back(method);
1120     if (!callback_.is_null())
1121       callback_.Run(client);
1122     return Status(kOk);
1123   }
1124
1125   base::Callback<void(DevToolsClient*)> callback_;
1126   std::list<std::string> msgs_;
1127 };
1128
1129 void HandleReceivedEvents(DevToolsClient* client) {
1130   EXPECT_EQ(kOk, client->HandleReceivedEvents().code());
1131 }
1132
1133 }  // namespace
1134
1135 TEST_F(DevToolsClientImplTest, ReceivesCommandResponse) {
1136   std::list<std::string> msgs;
1137   SyncWebSocketFactory factory = base::Bind(&CreateMockSyncWebSocket6, &msgs);
1138   DevToolsClientImpl client(
1139       factory, "http://url", "id", base::Bind(&CloserFunc));
1140   MockCommandListener listener1;
1141   listener1.callback_ = base::Bind(&HandleReceivedEvents);
1142   MockCommandListener listener2;
1143   client.AddListener(&listener1);
1144   client.AddListener(&listener2);
1145   msgs.push_back("{\"id\": 1, \"result\": {}}");
1146   msgs.push_back("{\"method\": \"event\", \"params\": {}}");
1147   base::DictionaryValue params;
1148   ASSERT_EQ(kOk, client.SendCommand("cmd", params).code());
1149   ASSERT_EQ(2u, listener2.msgs_.size());
1150   ASSERT_EQ("cmd", listener2.msgs_.front());
1151   ASSERT_EQ("event", listener2.msgs_.back());
1152 }