blob: 3544c8b14008903c13fb09cf01935ebeef07d6bf [file] [log] [blame]
Paul Stewarte6927402012-01-23 16:11:30 -08001// Copyright (c) 2012 The Chromium OS 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 "shill/portal_detector.h"
6
7#include <string>
8
Eric Shienbrood3e20a232012-02-16 11:35:56 -05009#include <base/bind.h>
Paul Stewarte6927402012-01-23 16:11:30 -080010#include <base/memory/scoped_ptr.h>
11#include <gmock/gmock.h>
12#include <gtest/gtest.h>
13
14#include "shill/mock_connection.h"
15#include "shill/mock_control.h"
16#include "shill/mock_device_info.h"
17#include "shill/mock_event_dispatcher.h"
18#include "shill/mock_http_request.h"
19#include "shill/mock_time.h"
20
Eric Shienbrood3e20a232012-02-16 11:35:56 -050021using base::Bind;
22using base::Callback;
23using base::Unretained;
Paul Stewarte6927402012-01-23 16:11:30 -080024using std::string;
25using std::vector;
26using testing::_;
27using testing::AtLeast;
28using testing::DoAll;
29using testing::InSequence;
30using testing::NiceMock;
31using testing::Return;
32using testing::ReturnRef;
33using testing::SetArgumentPointee;
34using testing::StrictMock;
35using testing::Test;
36
37namespace shill {
38
39namespace {
40const char kBadURL[] = "badurl";
41const char kInterfaceName[] = "int0";
42const char kURL[] = "http://www.chromium.org";
43const char kDNSServer0[] = "8.8.8.8";
44const char kDNSServer1[] = "8.8.4.4";
45const char *kDNSServers[] = { kDNSServer0, kDNSServer1 };
46} // namespace {}
47
48MATCHER_P(IsResult, result, "") {
49 return (result.phase == arg.phase &&
50 result.status == arg.status &&
51 result.final == arg.final);
52}
53
54class PortalDetectorTest : public Test {
55 public:
56 PortalDetectorTest()
57 : device_info_(new NiceMock<MockDeviceInfo>(
58 &control_,
59 reinterpret_cast<EventDispatcher *>(NULL),
60 reinterpret_cast<Metrics *>(NULL),
61 reinterpret_cast<Manager *>(NULL))),
62 connection_(new StrictMock<MockConnection>(device_info_.get())),
63 portal_detector_(new PortalDetector(
64 connection_.get(),
65 &dispatcher_,
66 callback_target_.result_callback())),
67 interface_name_(kInterfaceName),
68 dns_servers_(kDNSServers, kDNSServers + 2),
69 http_request_(NULL) {
70 current_time_.tv_sec = current_time_.tv_usec = 0;
71 }
72
73 virtual void SetUp() {
74 EXPECT_CALL(*connection_.get(), interface_name())
75 .WillRepeatedly(ReturnRef(interface_name_));
76 portal_detector_->time_ = &time_;
77 EXPECT_CALL(time_, GetTimeMonotonic(_))
78 .WillRepeatedly(Invoke(this, &PortalDetectorTest::GetTimeMonotonic));
79 EXPECT_CALL(*connection_.get(), dns_servers())
80 .WillRepeatedly(ReturnRef(dns_servers_));
81 }
82
83 virtual void TearDown() {
84 if (portal_detector_->request_.get()) {
85 EXPECT_CALL(*http_request(), Stop());
86
87 // Delete the portal detector while expectations still exist.
88 portal_detector_.reset();
89 }
90 }
91
92 protected:
Thieu Le85e050b2012-03-13 15:04:38 -070093 static const int kNumAttempts;
94
Paul Stewarte6927402012-01-23 16:11:30 -080095 class CallbackTarget {
96 public:
97 CallbackTarget()
Eric Shienbrood3e20a232012-02-16 11:35:56 -050098 : result_callback_(Bind(&CallbackTarget::ResultCallback,
99 Unretained(this))) {
Paul Stewarte6927402012-01-23 16:11:30 -0800100 }
101
102 MOCK_METHOD1(ResultCallback, void(const PortalDetector::Result &result));
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500103 Callback<void(const PortalDetector::Result &)> &result_callback() {
104 return result_callback_;
Paul Stewarte6927402012-01-23 16:11:30 -0800105 }
106
107 private:
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500108 Callback<void(const PortalDetector::Result &)> result_callback_;
Paul Stewarte6927402012-01-23 16:11:30 -0800109 };
110
111 void AssignHTTPRequest() {
112 http_request_ = new StrictMock<MockHTTPRequest>(connection_);
113 portal_detector_->request_.reset(http_request_); // Passes ownership.
114 }
115
116 bool StartPortalRequest(const string &url_string) {
117 bool ret = portal_detector_->Start(url_string);
118 if (ret) {
119 AssignHTTPRequest();
120 }
121 return ret;
122 }
123
Paul Stewartc681fa02012-03-02 19:40:04 -0800124 void StartAttemptTask() {
125 AssignHTTPRequest();
126 EXPECT_CALL(*http_request(), Start(_, _, _))
127 .WillOnce(Return(HTTPRequest::kResultInProgress));
128 EXPECT_CALL(
129 dispatcher(),
130 PostDelayedTask(
131 _, PortalDetector::kRequestTimeoutSeconds * 1000));
132 portal_detector()->StartAttemptTask();
133 }
134
Paul Stewarte6927402012-01-23 16:11:30 -0800135 void TimeoutAttempt() {
136 portal_detector_->TimeoutAttemptTask();
137 }
138
139 MockHTTPRequest *http_request() { return http_request_; }
140 PortalDetector *portal_detector() { return portal_detector_.get(); }
141 MockEventDispatcher &dispatcher() { return dispatcher_; }
142 CallbackTarget &callback_target() { return callback_target_; }
143 ByteString &response_data() { return response_data_; }
144
145 void ExpectReset() {
146 EXPECT_FALSE(portal_detector_->attempt_count_);
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500147 EXPECT_TRUE(callback_target_.result_callback().
148 Equals(portal_detector_->portal_result_callback_));
Paul Stewarte6927402012-01-23 16:11:30 -0800149 EXPECT_FALSE(portal_detector_->request_.get());
150 }
151
152 void ExpectAttemptRetry(const PortalDetector::Result &result) {
153 EXPECT_CALL(callback_target(),
154 ResultCallback(IsResult(result)));
155
156 // Expect the PortalDetector to stop the current request.
157 EXPECT_CALL(*http_request(), Stop());
158
159 // Expect the PortalDetector to schedule the next attempt.
160 EXPECT_CALL(
161 dispatcher(),
162 PostDelayedTask(
163 _, PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000));
164 }
165
166 void AdvanceTime(int milliseconds) {
167 struct timeval tv = { milliseconds / 1000, (milliseconds % 1000) * 1000 };
168 timeradd(&current_time_, &tv, &current_time_);
169 }
170
171 void StartAttempt() {
172 EXPECT_CALL(dispatcher(), PostDelayedTask(_, 0));
173 EXPECT_TRUE(StartPortalRequest(kURL));
174
175 // Expect that the request will be started -- return failure.
176 EXPECT_CALL(*http_request(), Start(_, _, _))
177 .WillOnce(Return(HTTPRequest::kResultInProgress));
178 EXPECT_CALL(dispatcher(), PostDelayedTask(
179 _, PortalDetector::kRequestTimeoutSeconds * 1000));
180
181 portal_detector()->StartAttemptTask();
182 }
183
184 void AppendReadData(const string &read_data) {
185 response_data_.Append(ByteString(read_data, false));
Paul Stewartbdb02e62012-02-22 16:24:33 -0800186 portal_detector_->RequestReadCallback(response_data_);
Paul Stewarte6927402012-01-23 16:11:30 -0800187 }
188
189 private:
190 int GetTimeMonotonic(struct timeval *tv) {
191 *tv = current_time_;
192 return 0;
193 }
194
195 StrictMock<MockEventDispatcher> dispatcher_;
196 MockControl control_;
197 scoped_ptr<MockDeviceInfo> device_info_;
198 scoped_refptr<MockConnection> connection_;
199 CallbackTarget callback_target_;
200 scoped_ptr<PortalDetector> portal_detector_;
201 StrictMock<MockTime> time_;
202 struct timeval current_time_;
203 const string interface_name_;
204 vector<string> dns_servers_;
205 ByteString response_data_;
206
207 // Owned by the PortalDetector, but tracked here for EXPECT_CALL()
208 MockHTTPRequest *http_request_;
209};
210
Thieu Le85e050b2012-03-13 15:04:38 -0700211// static
212const int PortalDetectorTest::kNumAttempts = 0;
213
Paul Stewarte6927402012-01-23 16:11:30 -0800214TEST_F(PortalDetectorTest, Constructor) {
215 ExpectReset();
216}
217
218TEST_F(PortalDetectorTest, InvalidURL) {
219 EXPECT_FALSE(StartPortalRequest(kBadURL));
220 ExpectReset();
221}
222
223TEST_F(PortalDetectorTest, StartAttemptFailed) {
224 EXPECT_CALL(dispatcher(), PostDelayedTask(_, 0));
225 EXPECT_TRUE(StartPortalRequest(kURL));
226
227 // Expect that the request will be started -- return failure.
228 EXPECT_CALL(*http_request(), Start(_, _, _))
229 .WillOnce(Return(HTTPRequest::kResultConnectionFailure));
230 // Expect a non-final failure to be relayed to the caller.
231 ExpectAttemptRetry(PortalDetector::Result(
232 PortalDetector::kPhaseConnection,
233 PortalDetector::kStatusFailure,
Thieu Le85e050b2012-03-13 15:04:38 -0700234 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800235 false));
236 portal_detector()->StartAttemptTask();
237}
238
239TEST_F(PortalDetectorTest, StartAttemptRepeated) {
240 EXPECT_CALL(dispatcher(), PostDelayedTask(_, 0));
Paul Stewartc681fa02012-03-02 19:40:04 -0800241 portal_detector()->StartAttempt(0);
Paul Stewarte6927402012-01-23 16:11:30 -0800242
Paul Stewartc681fa02012-03-02 19:40:04 -0800243 StartAttemptTask();
Paul Stewarte6927402012-01-23 16:11:30 -0800244
245 // A second attempt should be delayed by kMinTimeBetweenAttemptsSeconds.
246 EXPECT_CALL(
247 dispatcher(),
248 PostDelayedTask(
249 _, PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000));
Paul Stewartc681fa02012-03-02 19:40:04 -0800250 portal_detector()->StartAttempt(0);
251}
252
253TEST_F(PortalDetectorTest, StartAttemptAfterDelay) {
254 const int kDelaySeconds = 123;
255 // The first attempt should be delayed by kDelaySeconds.
256 EXPECT_CALL(dispatcher(), PostDelayedTask(_, kDelaySeconds * 1000));
257 EXPECT_TRUE(portal_detector()->StartAfterDelay(kURL, kDelaySeconds));
258
259 AdvanceTime(kDelaySeconds * 1000);
260 StartAttemptTask();
261
262 // A second attempt should be delayed by kMinTimeBetweenAttemptsSeconds.
263 EXPECT_CALL(
264 dispatcher(),
265 PostDelayedTask(
266 _, PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000));
267 portal_detector()->StartAttempt(0);
Paul Stewarte6927402012-01-23 16:11:30 -0800268}
269
270TEST_F(PortalDetectorTest, AttemptCount) {
Paul Stewartc681fa02012-03-02 19:40:04 -0800271 EXPECT_FALSE(portal_detector()->IsInProgress());
Paul Stewarte6927402012-01-23 16:11:30 -0800272 // Expect the PortalDetector to immediately post a task for the each attempt.
273 EXPECT_CALL(dispatcher(), PostDelayedTask(_, 0))
274 .Times(PortalDetector::kMaxRequestAttempts);
275 EXPECT_TRUE(StartPortalRequest(kURL));
276
Paul Stewartc681fa02012-03-02 19:40:04 -0800277 EXPECT_FALSE(portal_detector()->IsInProgress());
278
Paul Stewarte6927402012-01-23 16:11:30 -0800279 // Expect that the request will be started -- return failure.
280 EXPECT_CALL(*http_request(), Start(_, _, _))
281 .Times(PortalDetector::kMaxRequestAttempts)
282 .WillRepeatedly(Return(HTTPRequest::kResultInProgress));
283
284 // Each HTTP request that gets started will have a request timeout.
285 EXPECT_CALL(dispatcher(), PostDelayedTask(
286 _, PortalDetector::kRequestTimeoutSeconds * 1000))
287 .Times(PortalDetector::kMaxRequestAttempts);
288
289 {
290 InSequence s;
291
292 // Expect non-final failures for all attempts but the last.
293 EXPECT_CALL(callback_target(),
294 ResultCallback(IsResult(
295 PortalDetector::Result(
296 PortalDetector::kPhaseDNS,
297 PortalDetector::kStatusFailure,
Thieu Le85e050b2012-03-13 15:04:38 -0700298 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800299 false))))
300 .Times(PortalDetector::kMaxRequestAttempts - 1);
301
302 // Expect a single final failure.
303 EXPECT_CALL(callback_target(),
304 ResultCallback(IsResult(
305 PortalDetector::Result(
306 PortalDetector::kPhaseDNS,
307 PortalDetector::kStatusFailure,
Thieu Le85e050b2012-03-13 15:04:38 -0700308 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800309 true))))
310 .Times(1);
311 }
312
313 // Expect the PortalDetector to stop the current request each time, plus
314 // an extra time in PortalDetector::Stop().
315 EXPECT_CALL(*http_request(), Stop())
316 .Times(PortalDetector::kMaxRequestAttempts + 1);
317
318
319 for (int i = 0; i < PortalDetector::kMaxRequestAttempts; i++) {
320 portal_detector()->StartAttemptTask();
Paul Stewartc681fa02012-03-02 19:40:04 -0800321 EXPECT_TRUE(portal_detector()->IsInProgress());
Paul Stewarte6927402012-01-23 16:11:30 -0800322 AdvanceTime(PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800323 portal_detector()->RequestResultCallback(HTTPRequest::kResultDNSFailure,
324 response_data());
Paul Stewarte6927402012-01-23 16:11:30 -0800325 }
326
Paul Stewartc681fa02012-03-02 19:40:04 -0800327 EXPECT_FALSE(portal_detector()->IsInProgress());
Paul Stewarte6927402012-01-23 16:11:30 -0800328 ExpectReset();
329}
330
331TEST_F(PortalDetectorTest, ReadBadHeader) {
332 StartAttempt();
333
334 ExpectAttemptRetry(PortalDetector::Result(
335 PortalDetector::kPhaseContent,
336 PortalDetector::kStatusFailure,
Thieu Le85e050b2012-03-13 15:04:38 -0700337 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800338 false));
339 AppendReadData("X");
340}
341
342TEST_F(PortalDetectorTest, RequestTimeout) {
343 StartAttempt();
344 ExpectAttemptRetry(PortalDetector::Result(
345 PortalDetector::kPhaseUnknown,
346 PortalDetector::kStatusTimeout,
Thieu Le85e050b2012-03-13 15:04:38 -0700347 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800348 false));
349
350 EXPECT_CALL(*http_request(), response_data())
351 .WillOnce(ReturnRef(response_data()));
352
353 TimeoutAttempt();
354}
355
356TEST_F(PortalDetectorTest, ReadPartialHeaderTimeout) {
357 StartAttempt();
358
359 const string response_expected(PortalDetector::kResponseExpected);
360 const size_t partial_size = response_expected.length() / 2;
361 AppendReadData(response_expected.substr(0, partial_size));
362
363 ExpectAttemptRetry(PortalDetector::Result(
364 PortalDetector::kPhaseContent,
365 PortalDetector::kStatusTimeout,
Thieu Le85e050b2012-03-13 15:04:38 -0700366 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800367 false));
368
369 EXPECT_CALL(*http_request(), response_data())
370 .WillOnce(ReturnRef(response_data()));
371
372 TimeoutAttempt();
373}
374
375TEST_F(PortalDetectorTest, ReadCompleteHeader) {
376 const string response_expected(PortalDetector::kResponseExpected);
377 const size_t partial_size = response_expected.length() / 2;
378
379 StartAttempt();
380 AppendReadData(response_expected.substr(0, partial_size));
381
382 EXPECT_CALL(callback_target(),
383 ResultCallback(IsResult(
384 PortalDetector::Result(
385 PortalDetector::kPhaseContent,
386 PortalDetector::kStatusSuccess,
Thieu Le85e050b2012-03-13 15:04:38 -0700387 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800388 true))));
389 EXPECT_CALL(*http_request(), Stop())
390 .Times(2);
391
392 AppendReadData(response_expected.substr(partial_size));
393}
Paul Stewartc681fa02012-03-02 19:40:04 -0800394
Paul Stewarte6927402012-01-23 16:11:30 -0800395struct ResultMapping {
396 ResultMapping() : http_result(HTTPRequest::kResultUnknown), portal_result() {}
397 ResultMapping(HTTPRequest::Result in_http_result,
398 const PortalDetector::Result &in_portal_result)
399 : http_result(in_http_result),
400 portal_result(in_portal_result) {}
401 HTTPRequest::Result http_result;
402 PortalDetector::Result portal_result;
403};
404
405class PortalDetectorResultMappingTest
406 : public testing::TestWithParam<ResultMapping> {};
407
408TEST_P(PortalDetectorResultMappingTest, MapResult) {
409 PortalDetector::Result portal_result =
410 PortalDetector::GetPortalResultForRequestResult(GetParam().http_result);
411 EXPECT_EQ(portal_result.phase, GetParam().portal_result.phase);
412 EXPECT_EQ(portal_result.status, GetParam().portal_result.status);
413}
414
415INSTANTIATE_TEST_CASE_P(
416 PortalResultMappingTest,
417 PortalDetectorResultMappingTest,
418 ::testing::Values(
419 ResultMapping(HTTPRequest::kResultUnknown,
420 PortalDetector::Result(PortalDetector::kPhaseUnknown,
421 PortalDetector::kStatusFailure)),
422 ResultMapping(HTTPRequest::kResultInProgress,
423 PortalDetector::Result(PortalDetector::kPhaseUnknown,
424 PortalDetector::kStatusFailure)),
425 ResultMapping(HTTPRequest::kResultDNSFailure,
426 PortalDetector::Result(PortalDetector::kPhaseDNS,
427 PortalDetector::kStatusFailure)),
428 ResultMapping(HTTPRequest::kResultDNSTimeout,
429 PortalDetector::Result(PortalDetector::kPhaseDNS,
430 PortalDetector::kStatusTimeout)),
431 ResultMapping(HTTPRequest::kResultConnectionFailure,
432 PortalDetector::Result(PortalDetector::kPhaseConnection,
433 PortalDetector::kStatusFailure)),
434 ResultMapping(HTTPRequest::kResultConnectionTimeout,
435 PortalDetector::Result(PortalDetector::kPhaseConnection,
436 PortalDetector::kStatusTimeout)),
437 ResultMapping(HTTPRequest::kResultRequestFailure,
438 PortalDetector::Result(PortalDetector::kPhaseHTTP,
439 PortalDetector::kStatusFailure)),
440 ResultMapping(HTTPRequest::kResultRequestTimeout,
441 PortalDetector::Result(PortalDetector::kPhaseHTTP,
442 PortalDetector::kStatusTimeout)),
443 ResultMapping(HTTPRequest::kResultResponseFailure,
444 PortalDetector::Result(PortalDetector::kPhaseHTTP,
445 PortalDetector::kStatusFailure)),
446 ResultMapping(HTTPRequest::kResultResponseTimeout,
447 PortalDetector::Result(PortalDetector::kPhaseHTTP,
448 PortalDetector::kStatusTimeout)),
449 ResultMapping(HTTPRequest::kResultSuccess,
450 PortalDetector::Result(PortalDetector::kPhaseContent,
451 PortalDetector::kStatusFailure))));
452
453} // namespace shill