1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <string>
6
7#include "base/memory/ref_counted.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/message_loop/message_loop.h"
10#include "base/run_loop.h"
11#include "chrome/browser/devtools/devtools_network_conditions.h"
12#include "chrome/browser/devtools/devtools_network_controller.h"
13#include "chrome/browser/devtools/devtools_network_interceptor.h"
14#include "chrome/browser/devtools/devtools_network_transaction.h"
15#include "net/http/http_transaction_test_util.h"
16#include "testing/gtest/include/gtest/gtest.h"
17#include "url/gurl.h"
18
19namespace test {
20
21const char kClientId[] = "42";
22const char kAnotherClientId[] = "24";
23
24class TestCallback {
25 public:
26  TestCallback() : run_count_(0), value_(0) {}
27  void Run(int value) {
28    run_count_++;
29    value_ = value;
30  }
31  int run_count() { return run_count_; }
32  int value() { return value_; }
33
34 private:
35  int run_count_;
36  int value_;
37};
38
39class DevToolsNetworkControllerHelper {
40 public:
41  DevToolsNetworkControllerHelper() :
42      completion_callback_(
43          base::Bind(&TestCallback::Run, base::Unretained(&callback_))),
44      mock_transaction_(kSimpleGET_Transaction),
45      buffer_(new net::IOBuffer(64)) {
46    mock_transaction_.test_mode = TEST_MODE_SYNC_NET_START;
47    mock_transaction_.url = "http://dot.com";
48    mock_transaction_.request_headers =
49        "X-DevTools-Emulate-Network-Conditions-Client-Id: 42\r\n";
50    AddMockTransaction(&mock_transaction_);
51
52    scoped_ptr<net::HttpTransaction> network_transaction;
53    network_layer_.CreateTransaction(
54        net::DEFAULT_PRIORITY, &network_transaction);
55    transaction_.reset(new DevToolsNetworkTransaction(
56        &controller_, network_transaction.Pass()));
57  }
58
59  net::HttpRequestInfo* GetRequest() {
60    if (!request_)
61      request_.reset(new MockHttpRequest(mock_transaction_));
62    return request_.get();
63  }
64
65  void SetNetworkState(const std::string id, bool offline) {
66    scoped_ptr<DevToolsNetworkConditions> conditions(
67        new DevToolsNetworkConditions(offline));
68    controller_.SetNetworkStateOnIO(id, conditions.Pass());
69  }
70
71  int Start() {
72    return transaction_->Start(
73        GetRequest(), completion_callback_, net::BoundNetLog());
74  }
75
76  int Read() {
77    return transaction_->Read(buffer_.get(), 64, completion_callback_);
78  }
79
80  bool ShouldFail() {
81    return transaction_->interceptor_->ShouldFail(transaction_.get());
82  }
83
84  ~DevToolsNetworkControllerHelper() {
85    RemoveMockTransaction(&mock_transaction_);
86  }
87
88  TestCallback* callback() { return &callback_; }
89  MockTransaction* mock_transaction() { return &mock_transaction_; }
90  DevToolsNetworkController* controller() { return &controller_; }
91  DevToolsNetworkTransaction* transaction() { return transaction_.get(); }
92
93 private:
94  base::MessageLoop message_loop_;
95  MockNetworkLayer network_layer_;
96  TestCallback callback_;
97  net::CompletionCallback completion_callback_;
98  MockTransaction mock_transaction_;
99  DevToolsNetworkController controller_;
100  scoped_ptr<DevToolsNetworkTransaction> transaction_;
101  scoped_refptr<net::IOBuffer> buffer_;
102  scoped_ptr<MockHttpRequest> request_;
103};
104
105TEST(DevToolsNetworkControllerTest, SingleDisableEnable) {
106  DevToolsNetworkControllerHelper helper;
107  helper.SetNetworkState(kClientId, false);
108  helper.Start();
109
110  EXPECT_FALSE(helper.ShouldFail());
111  helper.SetNetworkState(kClientId, true);
112  EXPECT_TRUE(helper.ShouldFail());
113  helper.SetNetworkState(kClientId, false);
114  EXPECT_FALSE(helper.ShouldFail());
115
116  base::RunLoop().RunUntilIdle();
117}
118
119TEST(DevToolsNetworkControllerTest, InterceptorIsolation) {
120  DevToolsNetworkControllerHelper helper;
121  helper.SetNetworkState(kClientId, false);
122  helper.Start();
123
124  EXPECT_FALSE(helper.ShouldFail());
125  helper.SetNetworkState(kAnotherClientId, true);
126  EXPECT_FALSE(helper.ShouldFail());
127  helper.SetNetworkState(kClientId, true);
128  EXPECT_TRUE(helper.ShouldFail());
129
130  helper.SetNetworkState(kAnotherClientId, false);
131  helper.SetNetworkState(kClientId, false);
132  base::RunLoop().RunUntilIdle();
133}
134
135TEST(DevToolsNetworkControllerTest, FailOnStart) {
136  DevToolsNetworkControllerHelper helper;
137  helper.SetNetworkState(kClientId, true);
138
139  int rv = helper.Start();
140  EXPECT_EQ(rv, net::ERR_INTERNET_DISCONNECTED);
141
142  base::RunLoop().RunUntilIdle();
143  EXPECT_EQ(helper.callback()->run_count(), 0);
144}
145
146TEST(DevToolsNetworkControllerTest, FailRunningTransaction) {
147  DevToolsNetworkControllerHelper helper;
148  helper.SetNetworkState(kClientId, false);
149  TestCallback* callback = helper.callback();
150
151  int rv = helper.Start();
152  EXPECT_EQ(rv, net::OK);
153
154  scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(64));
155  rv = helper.Read();
156  EXPECT_EQ(rv, net::ERR_IO_PENDING);
157  EXPECT_EQ(callback->run_count(), 0);
158
159  helper.SetNetworkState(kClientId, true);
160  EXPECT_EQ(callback->run_count(), 1);
161  EXPECT_EQ(callback->value(), net::ERR_INTERNET_DISCONNECTED);
162
163  // Wait until HttpTrancation completes reading and invokes callback.
164  // DevToolsNetworkTransaction should ignore callback, because it has
165  // reported network error already.
166  base::RunLoop().RunUntilIdle();
167  EXPECT_EQ(callback->run_count(), 1);
168
169  // Check that transaction in not failed second time.
170  helper.SetNetworkState(kClientId, false);
171  helper.SetNetworkState(kClientId, true);
172  EXPECT_EQ(callback->run_count(), 1);
173}
174
175TEST(DevToolsNetworkControllerTest, ReadAfterFail) {
176  DevToolsNetworkControllerHelper helper;
177  helper.SetNetworkState(kClientId, false);
178
179  int rv = helper.Start();
180  EXPECT_EQ(rv, net::OK);
181  EXPECT_TRUE(helper.transaction()->request());
182
183  helper.SetNetworkState(kClientId, true);
184  EXPECT_TRUE(helper.transaction()->failed());
185
186  scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(64));
187  rv = helper.Read();
188  EXPECT_EQ(rv, net::ERR_INTERNET_DISCONNECTED);
189
190  // Check that callback is never invoked.
191  base::RunLoop().RunUntilIdle();
192  EXPECT_EQ(helper.callback()->run_count(), 0);
193}
194
195TEST(DevToolsNetworkControllerTest, AllowsDevToolsRequests) {
196  DevToolsNetworkControllerHelper helper;
197  helper.SetNetworkState(kClientId, false);
198  helper.mock_transaction()->request_headers =
199      "X-DevTools-Emulate-Network-Conditions-Client-Id: 42\r\n"
200      "X-DevTools-Request-Initiator: frontend\r\n";
201  helper.Start();
202
203  EXPECT_FALSE(helper.ShouldFail());
204  helper.SetNetworkState(kClientId, true);
205  EXPECT_FALSE(helper.ShouldFail());
206}
207
208}  // namespace test
209