blob: 9fc2d8d66c38a01ad98d959d2061716a54d3157c [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_);
Christopher Wiley6e1dc0f2012-10-17 15:38:56 -0700147 EXPECT_FALSE(portal_detector_->failures_in_content_phase_);
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500148 EXPECT_TRUE(callback_target_.result_callback().
149 Equals(portal_detector_->portal_result_callback_));
Paul Stewarte6927402012-01-23 16:11:30 -0800150 EXPECT_FALSE(portal_detector_->request_.get());
151 }
152
153 void ExpectAttemptRetry(const PortalDetector::Result &result) {
154 EXPECT_CALL(callback_target(),
155 ResultCallback(IsResult(result)));
156
157 // Expect the PortalDetector to stop the current request.
158 EXPECT_CALL(*http_request(), Stop());
159
160 // Expect the PortalDetector to schedule the next attempt.
161 EXPECT_CALL(
162 dispatcher(),
163 PostDelayedTask(
164 _, PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000));
165 }
166
167 void AdvanceTime(int milliseconds) {
168 struct timeval tv = { milliseconds / 1000, (milliseconds % 1000) * 1000 };
169 timeradd(&current_time_, &tv, &current_time_);
170 }
171
172 void StartAttempt() {
173 EXPECT_CALL(dispatcher(), PostDelayedTask(_, 0));
174 EXPECT_TRUE(StartPortalRequest(kURL));
175
176 // Expect that the request will be started -- return failure.
177 EXPECT_CALL(*http_request(), Start(_, _, _))
178 .WillOnce(Return(HTTPRequest::kResultInProgress));
179 EXPECT_CALL(dispatcher(), PostDelayedTask(
180 _, PortalDetector::kRequestTimeoutSeconds * 1000));
181
182 portal_detector()->StartAttemptTask();
183 }
184
185 void AppendReadData(const string &read_data) {
186 response_data_.Append(ByteString(read_data, false));
Paul Stewartbdb02e62012-02-22 16:24:33 -0800187 portal_detector_->RequestReadCallback(response_data_);
Paul Stewarte6927402012-01-23 16:11:30 -0800188 }
189
190 private:
191 int GetTimeMonotonic(struct timeval *tv) {
192 *tv = current_time_;
193 return 0;
194 }
195
196 StrictMock<MockEventDispatcher> dispatcher_;
197 MockControl control_;
198 scoped_ptr<MockDeviceInfo> device_info_;
199 scoped_refptr<MockConnection> connection_;
200 CallbackTarget callback_target_;
201 scoped_ptr<PortalDetector> portal_detector_;
202 StrictMock<MockTime> time_;
203 struct timeval current_time_;
204 const string interface_name_;
205 vector<string> dns_servers_;
206 ByteString response_data_;
207
208 // Owned by the PortalDetector, but tracked here for EXPECT_CALL()
209 MockHTTPRequest *http_request_;
210};
211
Thieu Le85e050b2012-03-13 15:04:38 -0700212// static
213const int PortalDetectorTest::kNumAttempts = 0;
214
Paul Stewarte6927402012-01-23 16:11:30 -0800215TEST_F(PortalDetectorTest, Constructor) {
216 ExpectReset();
217}
218
219TEST_F(PortalDetectorTest, InvalidURL) {
220 EXPECT_FALSE(StartPortalRequest(kBadURL));
221 ExpectReset();
222}
223
224TEST_F(PortalDetectorTest, StartAttemptFailed) {
225 EXPECT_CALL(dispatcher(), PostDelayedTask(_, 0));
226 EXPECT_TRUE(StartPortalRequest(kURL));
227
228 // Expect that the request will be started -- return failure.
229 EXPECT_CALL(*http_request(), Start(_, _, _))
230 .WillOnce(Return(HTTPRequest::kResultConnectionFailure));
231 // Expect a non-final failure to be relayed to the caller.
232 ExpectAttemptRetry(PortalDetector::Result(
233 PortalDetector::kPhaseConnection,
234 PortalDetector::kStatusFailure,
Thieu Le85e050b2012-03-13 15:04:38 -0700235 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800236 false));
237 portal_detector()->StartAttemptTask();
238}
239
240TEST_F(PortalDetectorTest, StartAttemptRepeated) {
241 EXPECT_CALL(dispatcher(), PostDelayedTask(_, 0));
Paul Stewartc681fa02012-03-02 19:40:04 -0800242 portal_detector()->StartAttempt(0);
Paul Stewarte6927402012-01-23 16:11:30 -0800243
Paul Stewartc681fa02012-03-02 19:40:04 -0800244 StartAttemptTask();
Paul Stewarte6927402012-01-23 16:11:30 -0800245
246 // A second attempt should be delayed by kMinTimeBetweenAttemptsSeconds.
247 EXPECT_CALL(
248 dispatcher(),
249 PostDelayedTask(
250 _, PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000));
Paul Stewartc681fa02012-03-02 19:40:04 -0800251 portal_detector()->StartAttempt(0);
252}
253
254TEST_F(PortalDetectorTest, StartAttemptAfterDelay) {
255 const int kDelaySeconds = 123;
256 // The first attempt should be delayed by kDelaySeconds.
257 EXPECT_CALL(dispatcher(), PostDelayedTask(_, kDelaySeconds * 1000));
258 EXPECT_TRUE(portal_detector()->StartAfterDelay(kURL, kDelaySeconds));
259
260 AdvanceTime(kDelaySeconds * 1000);
261 StartAttemptTask();
262
263 // A second attempt should be delayed by kMinTimeBetweenAttemptsSeconds.
264 EXPECT_CALL(
265 dispatcher(),
266 PostDelayedTask(
267 _, PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000));
268 portal_detector()->StartAttempt(0);
Paul Stewarte6927402012-01-23 16:11:30 -0800269}
270
271TEST_F(PortalDetectorTest, AttemptCount) {
Paul Stewartc681fa02012-03-02 19:40:04 -0800272 EXPECT_FALSE(portal_detector()->IsInProgress());
Paul Stewarte6927402012-01-23 16:11:30 -0800273 // Expect the PortalDetector to immediately post a task for the each attempt.
274 EXPECT_CALL(dispatcher(), PostDelayedTask(_, 0))
275 .Times(PortalDetector::kMaxRequestAttempts);
276 EXPECT_TRUE(StartPortalRequest(kURL));
277
Paul Stewartc681fa02012-03-02 19:40:04 -0800278 EXPECT_FALSE(portal_detector()->IsInProgress());
279
Paul Stewarte6927402012-01-23 16:11:30 -0800280 // Expect that the request will be started -- return failure.
281 EXPECT_CALL(*http_request(), Start(_, _, _))
282 .Times(PortalDetector::kMaxRequestAttempts)
283 .WillRepeatedly(Return(HTTPRequest::kResultInProgress));
284
285 // Each HTTP request that gets started will have a request timeout.
286 EXPECT_CALL(dispatcher(), PostDelayedTask(
287 _, PortalDetector::kRequestTimeoutSeconds * 1000))
288 .Times(PortalDetector::kMaxRequestAttempts);
289
290 {
291 InSequence s;
292
293 // Expect non-final failures for all attempts but the last.
294 EXPECT_CALL(callback_target(),
295 ResultCallback(IsResult(
296 PortalDetector::Result(
297 PortalDetector::kPhaseDNS,
298 PortalDetector::kStatusFailure,
Thieu Le85e050b2012-03-13 15:04:38 -0700299 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800300 false))))
301 .Times(PortalDetector::kMaxRequestAttempts - 1);
302
303 // Expect a single final failure.
304 EXPECT_CALL(callback_target(),
305 ResultCallback(IsResult(
306 PortalDetector::Result(
307 PortalDetector::kPhaseDNS,
308 PortalDetector::kStatusFailure,
Thieu Le85e050b2012-03-13 15:04:38 -0700309 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800310 true))))
311 .Times(1);
312 }
313
314 // Expect the PortalDetector to stop the current request each time, plus
315 // an extra time in PortalDetector::Stop().
316 EXPECT_CALL(*http_request(), Stop())
317 .Times(PortalDetector::kMaxRequestAttempts + 1);
318
319
320 for (int i = 0; i < PortalDetector::kMaxRequestAttempts; i++) {
321 portal_detector()->StartAttemptTask();
Paul Stewartc681fa02012-03-02 19:40:04 -0800322 EXPECT_TRUE(portal_detector()->IsInProgress());
Paul Stewarte6927402012-01-23 16:11:30 -0800323 AdvanceTime(PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800324 portal_detector()->RequestResultCallback(HTTPRequest::kResultDNSFailure,
325 response_data());
Paul Stewarte6927402012-01-23 16:11:30 -0800326 }
327
Paul Stewartc681fa02012-03-02 19:40:04 -0800328 EXPECT_FALSE(portal_detector()->IsInProgress());
Paul Stewarte6927402012-01-23 16:11:30 -0800329 ExpectReset();
330}
331
Christopher Wiley6e1dc0f2012-10-17 15:38:56 -0700332// Exactly like AttemptCount, except that the termination conditions are
333// different because we're triggering a different sort of error.
334TEST_F(PortalDetectorTest, ReadBadHeadersRetry) {
335 EXPECT_FALSE(portal_detector()->IsInProgress());
336 // Expect the PortalDetector to immediately post a task for the each attempt.
337 EXPECT_CALL(dispatcher(), PostDelayedTask(_, 0))
338 .Times(PortalDetector::kMaxFailuresInContentPhase);
339 EXPECT_TRUE(StartPortalRequest(kURL));
340
341 EXPECT_FALSE(portal_detector()->IsInProgress());
342
343 // Expect that the request will be started -- return failure.
344 EXPECT_CALL(*http_request(), Start(_, _, _))
345 .Times(PortalDetector::kMaxFailuresInContentPhase)
346 .WillRepeatedly(Return(HTTPRequest::kResultInProgress));
347
348 // Each HTTP request that gets started will have a request timeout.
349 EXPECT_CALL(dispatcher(), PostDelayedTask(
350 _, PortalDetector::kRequestTimeoutSeconds * 1000))
351 .Times(PortalDetector::kMaxFailuresInContentPhase);
352 {
353 InSequence s;
354
355 // Expect non-final failures for all attempts but the last.
356 EXPECT_CALL(callback_target(),
357 ResultCallback(IsResult(
358 PortalDetector::Result(
359 PortalDetector::kPhaseContent,
360 PortalDetector::kStatusFailure,
361 kNumAttempts,
362 false))))
363 .Times(PortalDetector::kMaxFailuresInContentPhase - 1);
364
365 // Expect a single final failure.
366 EXPECT_CALL(callback_target(),
367 ResultCallback(IsResult(
368 PortalDetector::Result(
369 PortalDetector::kPhaseContent,
370 PortalDetector::kStatusFailure,
371 kNumAttempts,
372 true))))
373 .Times(1);
374 }
375
376 // Expect the PortalDetector to stop the current request each time, plus
377 // an extra time in PortalDetector::Stop().
378 EXPECT_CALL(*http_request(), Stop())
379 .Times(PortalDetector::kMaxFailuresInContentPhase + 1);
380 ByteString response_data("X", 1);
381
382 for (int i = 0; i < PortalDetector::kMaxFailuresInContentPhase; i++) {
383 portal_detector()->StartAttemptTask();
384 EXPECT_TRUE(portal_detector()->IsInProgress());
385 AdvanceTime(PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000);
386 portal_detector()->RequestReadCallback(response_data);
387 }
388}
389
Paul Stewarte6927402012-01-23 16:11:30 -0800390TEST_F(PortalDetectorTest, ReadBadHeader) {
391 StartAttempt();
392
393 ExpectAttemptRetry(PortalDetector::Result(
394 PortalDetector::kPhaseContent,
395 PortalDetector::kStatusFailure,
Thieu Le85e050b2012-03-13 15:04:38 -0700396 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800397 false));
398 AppendReadData("X");
399}
400
401TEST_F(PortalDetectorTest, RequestTimeout) {
402 StartAttempt();
403 ExpectAttemptRetry(PortalDetector::Result(
404 PortalDetector::kPhaseUnknown,
405 PortalDetector::kStatusTimeout,
Thieu Le85e050b2012-03-13 15:04:38 -0700406 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800407 false));
408
409 EXPECT_CALL(*http_request(), response_data())
410 .WillOnce(ReturnRef(response_data()));
411
412 TimeoutAttempt();
413}
414
415TEST_F(PortalDetectorTest, ReadPartialHeaderTimeout) {
416 StartAttempt();
417
418 const string response_expected(PortalDetector::kResponseExpected);
419 const size_t partial_size = response_expected.length() / 2;
420 AppendReadData(response_expected.substr(0, partial_size));
421
422 ExpectAttemptRetry(PortalDetector::Result(
423 PortalDetector::kPhaseContent,
424 PortalDetector::kStatusTimeout,
Thieu Le85e050b2012-03-13 15:04:38 -0700425 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800426 false));
427
428 EXPECT_CALL(*http_request(), response_data())
429 .WillOnce(ReturnRef(response_data()));
430
431 TimeoutAttempt();
432}
433
434TEST_F(PortalDetectorTest, ReadCompleteHeader) {
435 const string response_expected(PortalDetector::kResponseExpected);
436 const size_t partial_size = response_expected.length() / 2;
437
438 StartAttempt();
439 AppendReadData(response_expected.substr(0, partial_size));
440
441 EXPECT_CALL(callback_target(),
442 ResultCallback(IsResult(
443 PortalDetector::Result(
444 PortalDetector::kPhaseContent,
445 PortalDetector::kStatusSuccess,
Thieu Le85e050b2012-03-13 15:04:38 -0700446 kNumAttempts,
Paul Stewarte6927402012-01-23 16:11:30 -0800447 true))));
448 EXPECT_CALL(*http_request(), Stop())
449 .Times(2);
450
451 AppendReadData(response_expected.substr(partial_size));
452}
Paul Stewartc681fa02012-03-02 19:40:04 -0800453
Paul Stewart60519992012-09-10 17:45:01 -0700454TEST_F(PortalDetectorTest, ReadMatchingHeader) {
455 const string kResponse("HTTP/9.8 204");
456
457 StartAttempt();
458
459 EXPECT_CALL(callback_target(),
460 ResultCallback(IsResult(
461 PortalDetector::Result(
462 PortalDetector::kPhaseContent,
463 PortalDetector::kStatusSuccess,
464 kNumAttempts,
465 true))));
466 EXPECT_CALL(*http_request(), Stop())
467 .Times(2);
468
469 AppendReadData(kResponse);
470}
471
Paul Stewarte6927402012-01-23 16:11:30 -0800472struct ResultMapping {
473 ResultMapping() : http_result(HTTPRequest::kResultUnknown), portal_result() {}
474 ResultMapping(HTTPRequest::Result in_http_result,
475 const PortalDetector::Result &in_portal_result)
476 : http_result(in_http_result),
477 portal_result(in_portal_result) {}
478 HTTPRequest::Result http_result;
479 PortalDetector::Result portal_result;
480};
481
482class PortalDetectorResultMappingTest
483 : public testing::TestWithParam<ResultMapping> {};
484
485TEST_P(PortalDetectorResultMappingTest, MapResult) {
486 PortalDetector::Result portal_result =
487 PortalDetector::GetPortalResultForRequestResult(GetParam().http_result);
488 EXPECT_EQ(portal_result.phase, GetParam().portal_result.phase);
489 EXPECT_EQ(portal_result.status, GetParam().portal_result.status);
490}
491
492INSTANTIATE_TEST_CASE_P(
493 PortalResultMappingTest,
494 PortalDetectorResultMappingTest,
495 ::testing::Values(
496 ResultMapping(HTTPRequest::kResultUnknown,
497 PortalDetector::Result(PortalDetector::kPhaseUnknown,
498 PortalDetector::kStatusFailure)),
499 ResultMapping(HTTPRequest::kResultInProgress,
500 PortalDetector::Result(PortalDetector::kPhaseUnknown,
501 PortalDetector::kStatusFailure)),
502 ResultMapping(HTTPRequest::kResultDNSFailure,
503 PortalDetector::Result(PortalDetector::kPhaseDNS,
504 PortalDetector::kStatusFailure)),
505 ResultMapping(HTTPRequest::kResultDNSTimeout,
506 PortalDetector::Result(PortalDetector::kPhaseDNS,
507 PortalDetector::kStatusTimeout)),
508 ResultMapping(HTTPRequest::kResultConnectionFailure,
509 PortalDetector::Result(PortalDetector::kPhaseConnection,
510 PortalDetector::kStatusFailure)),
511 ResultMapping(HTTPRequest::kResultConnectionTimeout,
512 PortalDetector::Result(PortalDetector::kPhaseConnection,
513 PortalDetector::kStatusTimeout)),
514 ResultMapping(HTTPRequest::kResultRequestFailure,
515 PortalDetector::Result(PortalDetector::kPhaseHTTP,
516 PortalDetector::kStatusFailure)),
517 ResultMapping(HTTPRequest::kResultRequestTimeout,
518 PortalDetector::Result(PortalDetector::kPhaseHTTP,
519 PortalDetector::kStatusTimeout)),
520 ResultMapping(HTTPRequest::kResultResponseFailure,
521 PortalDetector::Result(PortalDetector::kPhaseHTTP,
522 PortalDetector::kStatusFailure)),
523 ResultMapping(HTTPRequest::kResultResponseTimeout,
524 PortalDetector::Result(PortalDetector::kPhaseHTTP,
525 PortalDetector::kStatusTimeout)),
526 ResultMapping(HTTPRequest::kResultSuccess,
527 PortalDetector::Result(PortalDetector::kPhaseContent,
528 PortalDetector::kStatusFailure))));
529
530} // namespace shill