1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "shill/portal_detector.h"
18
19 #include <memory>
20 #include <string>
21
22 #include <base/bind.h>
23 #include <gmock/gmock.h>
24 #include <gtest/gtest.h>
25
26 #include "shill/connectivity_trial.h"
27 #include "shill/mock_connection.h"
28 #include "shill/mock_connectivity_trial.h"
29 #include "shill/mock_control.h"
30 #include "shill/mock_device_info.h"
31 #include "shill/mock_event_dispatcher.h"
32 #include "shill/net/mock_time.h"
33
34 using base::Bind;
35 using base::Callback;
36 using base::Unretained;
37 using std::string;
38 using std::vector;
39 using testing::_;
40 using testing::AtLeast;
41 using testing::DoAll;
42 using testing::InSequence;
43 using testing::Mock;
44 using testing::NiceMock;
45 using testing::Return;
46 using testing::ReturnRef;
47 using testing::SetArgumentPointee;
48 using testing::StrictMock;
49 using testing::Test;
50
51 namespace shill {
52
53 namespace {
54 const char kBadURL[] = "badurl";
55 const char kInterfaceName[] = "int0";
56 const char kURL[] = "http://www.chromium.org";
57 const char kDNSServer0[] = "8.8.8.8";
58 const char kDNSServer1[] = "8.8.4.4";
59 const char* kDNSServers[] = { kDNSServer0, kDNSServer1 };
60 } // namespace
61
62 MATCHER_P(IsResult, result, "") {
63 return (result.trial_result.phase == arg.trial_result.phase &&
64 result.trial_result.status == arg.trial_result.status &&
65 result.final == arg.final);
66 }
67
68
69 class PortalDetectorTest : public Test {
70 public:
PortalDetectorTest()71 PortalDetectorTest()
72 : device_info_(
73 new NiceMock<MockDeviceInfo>(&control_, nullptr, nullptr, nullptr)),
74 connection_(new StrictMock<MockConnection>(device_info_.get())),
75 portal_detector_(
76 new PortalDetector(connection_.get(), &dispatcher_,
77 callback_target_.result_callback())),
78 connectivity_trial_(new StrictMock<MockConnectivityTrial>(
79 connection_, PortalDetector::kRequestTimeoutSeconds)),
80 interface_name_(kInterfaceName),
81 dns_servers_(kDNSServers, kDNSServers + 2) {
82 current_time_.tv_sec = current_time_.tv_usec = 0;
83 }
84
SetUp()85 virtual void SetUp() {
86 EXPECT_CALL(*connection_.get(), IsIPv6())
87 .WillRepeatedly(Return(false));
88 EXPECT_CALL(*connection_.get(), interface_name())
89 .WillRepeatedly(ReturnRef(interface_name_));
90 portal_detector_->time_ = &time_;
91 EXPECT_CALL(time_, GetTimeMonotonic(_))
92 .WillRepeatedly(Invoke(this, &PortalDetectorTest::GetTimeMonotonic));
93 EXPECT_CALL(*connection_.get(), dns_servers())
94 .WillRepeatedly(ReturnRef(dns_servers_));
95 portal_detector_->connectivity_trial_
96 .reset(connectivity_trial_); // Passes ownership
97 EXPECT_TRUE(portal_detector()->connectivity_trial_.get());
98 }
99
TearDown()100 virtual void TearDown() {
101 if (portal_detector()->connectivity_trial_.get()) {
102 EXPECT_CALL(*connectivity_trial(), Stop());
103
104 // Delete the portal detector while expectations still exist.
105 portal_detector_.reset();
106 }
107 }
108
109 protected:
110 static const int kNumAttempts;
111
112 class CallbackTarget {
113 public:
CallbackTarget()114 CallbackTarget()
115 : result_callback_(Bind(&CallbackTarget::ResultCallback,
116 Unretained(this))) {
117 }
118
119 MOCK_METHOD1(ResultCallback, void(const PortalDetector::Result& result));
result_callback()120 Callback<void(const PortalDetector::Result&)>& result_callback() {
121 return result_callback_;
122 }
123
124 private:
125 Callback<void(const PortalDetector::Result&)> result_callback_;
126 };
127
StartPortalRequest(const string & url_string)128 bool StartPortalRequest(const string& url_string) {
129 bool ret = portal_detector_->Start(url_string);
130 return ret;
131 }
132
portal_detector()133 PortalDetector* portal_detector() { return portal_detector_.get(); }
connectivity_trial()134 MockConnectivityTrial* connectivity_trial() { return connectivity_trial_; }
dispatcher()135 MockEventDispatcher& dispatcher() { return dispatcher_; }
callback_target()136 CallbackTarget& callback_target() { return callback_target_; }
137
ExpectReset()138 void ExpectReset() {
139 EXPECT_FALSE(portal_detector_->attempt_count_);
140 EXPECT_FALSE(portal_detector_->failures_in_content_phase_);
141 EXPECT_TRUE(callback_target_.result_callback().
142 Equals(portal_detector_->portal_result_callback_));
143 }
144
ExpectAttemptRetry(const PortalDetector::Result & result)145 void ExpectAttemptRetry(const PortalDetector::Result& result) {
146 EXPECT_CALL(callback_target(),
147 ResultCallback(IsResult(result)));
148 EXPECT_CALL(*connectivity_trial(),
149 Retry(PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000));
150 }
151
AdvanceTime(int milliseconds)152 void AdvanceTime(int milliseconds) {
153 struct timeval tv = { milliseconds / 1000, (milliseconds % 1000) * 1000 };
154 timeradd(¤t_time_, &tv, ¤t_time_);
155 }
156
StartAttempt()157 void StartAttempt() {
158 EXPECT_CALL(*connectivity_trial(), Start(_, _)).WillOnce(Return(true));
159
160 EXPECT_TRUE(StartPortalRequest(kURL));
161 }
162
163 private:
GetTimeMonotonic(struct timeval * tv)164 int GetTimeMonotonic(struct timeval* tv) {
165 *tv = current_time_;
166 return 0;
167 }
168
169 StrictMock<MockEventDispatcher> dispatcher_;
170 MockControl control_;
171 std::unique_ptr<MockDeviceInfo> device_info_;
172 scoped_refptr<MockConnection> connection_;
173 CallbackTarget callback_target_;
174 std::unique_ptr<PortalDetector> portal_detector_;
175 MockConnectivityTrial* connectivity_trial_;
176 StrictMock<MockTime> time_;
177 struct timeval current_time_;
178 const string interface_name_;
179 vector<string> dns_servers_;
180 };
181
182 // static
183 const int PortalDetectorTest::kNumAttempts = 0;
184
TEST_F(PortalDetectorTest,Constructor)185 TEST_F(PortalDetectorTest, Constructor) {
186 ExpectReset();
187 }
188
TEST_F(PortalDetectorTest,InvalidURL)189 TEST_F(PortalDetectorTest, InvalidURL) {
190 EXPECT_CALL(*connectivity_trial(), Start(_, _)).WillOnce(Return(false));
191 EXPECT_FALSE(portal_detector()->Start(kBadURL));
192 ExpectReset();
193 }
194
TEST_F(PortalDetectorTest,StartAttemptFailed)195 TEST_F(PortalDetectorTest, StartAttemptFailed) {
196 EXPECT_CALL(*connectivity_trial(), Start(kURL, 0)).WillOnce(Return(true));
197 EXPECT_TRUE(StartPortalRequest(kURL));
198
199 // Expect that the request will be started -- return failure.
200 ConnectivityTrial::Result errorResult =
201 ConnectivityTrial::GetPortalResultForRequestResult(
202 HTTPRequest::kResultConnectionFailure);
203
204 // Expect a non-final failure to be relayed to the caller.
205 ExpectAttemptRetry(
206 PortalDetector::Result(
207 ConnectivityTrial::Result(
208 ConnectivityTrial::kPhaseConnection,
209 ConnectivityTrial::kStatusFailure),
210 kNumAttempts,
211 false));
212
213 portal_detector()->CompleteAttempt(errorResult);
214 }
215
TEST_F(PortalDetectorTest,IsInProgress)216 TEST_F(PortalDetectorTest, IsInProgress) {
217 EXPECT_FALSE(portal_detector()->IsInProgress());
218 // Starting the attempt immediately should result with IsInProgress returning
219 // true
220 EXPECT_CALL(*connectivity_trial(), Start(_, _))
221 .Times(2).WillRepeatedly(Return(true));
222 EXPECT_TRUE(StartPortalRequest(kURL));
223 EXPECT_CALL(*connectivity_trial(), IsActive()).WillOnce(Return(true));
224 EXPECT_TRUE(portal_detector()->IsInProgress());
225
226 // Starting the attempt with a delay should result with IsInProgress returning
227 // false
228 EXPECT_CALL(*connectivity_trial(), IsActive()).WillOnce(Return(false));
229 portal_detector()->StartAfterDelay(kURL, 2);
230 EXPECT_FALSE(portal_detector()->IsInProgress());
231
232 // Advance time, IsInProgress should now be true
233 EXPECT_CALL(*connectivity_trial(), IsActive()).WillOnce(Return(true));
234 AdvanceTime(2000);
235 EXPECT_TRUE(portal_detector()->IsInProgress());
236
237 // Times beyond the start time before the attempt finishes should also return
238 // true
239 EXPECT_CALL(*connectivity_trial(), IsActive()).WillOnce(Return(true));
240 AdvanceTime(1000);
241 EXPECT_TRUE(portal_detector()->IsInProgress());
242 }
243
TEST_F(PortalDetectorTest,AdjustStartDelayImmediate)244 TEST_F(PortalDetectorTest, AdjustStartDelayImmediate) {
245 EXPECT_CALL(*connectivity_trial(), Start(kURL, 0)).WillOnce(Return(true));
246 EXPECT_TRUE(StartPortalRequest(kURL));
247
248 // A second attempt should be delayed by kMinTimeBetweenAttemptsSeconds.
249 EXPECT_TRUE(portal_detector()->AdjustStartDelay(0)
250 == PortalDetector::kMinTimeBetweenAttemptsSeconds);
251 }
252
TEST_F(PortalDetectorTest,AdjustStartDelayAfterDelay)253 TEST_F(PortalDetectorTest, AdjustStartDelayAfterDelay) {
254 const int kDelaySeconds = 123;
255 // The first attempt should be delayed by kDelaySeconds.
256 EXPECT_CALL(*connectivity_trial(), Start(kURL, kDelaySeconds * 1000))
257 .WillOnce(Return(true));
258
259 portal_detector()->StartAfterDelay(kURL, kDelaySeconds);
260
261 AdvanceTime(kDelaySeconds * 1000);
262
263 // A second attempt should be delayed by kMinTimeBetweenAttemptsSeconds.
264 EXPECT_TRUE(portal_detector()->AdjustStartDelay(0)
265 == PortalDetector::kMinTimeBetweenAttemptsSeconds);
266 }
267
TEST_F(PortalDetectorTest,AttemptCount)268 TEST_F(PortalDetectorTest, AttemptCount) {
269 EXPECT_FALSE(portal_detector()->IsInProgress());
270 // Expect the PortalDetector to immediately post a task for the each attempt.
271 EXPECT_CALL(*connectivity_trial(), Start(_, _))
272 .Times(2).WillRepeatedly(Return(true));
273 EXPECT_CALL(*connectivity_trial(), IsActive()).WillOnce(Return(false));
274 portal_detector()->StartAfterDelay(kURL, 2);
275
276 EXPECT_FALSE(portal_detector()->IsInProgress());
277
278 // Expect that the request will be started -- return failure.
279 EXPECT_CALL(*connectivity_trial(), Retry(0))
280 .Times(PortalDetector::kMaxRequestAttempts - 1);
281
282 {
283 InSequence s;
284
285 // Expect non-final failures for all attempts but the last.
286 EXPECT_CALL(callback_target(),
287 ResultCallback(IsResult(
288 PortalDetector::Result(
289 ConnectivityTrial::Result(
290 ConnectivityTrial::kPhaseDNS,
291 ConnectivityTrial::kStatusFailure),
292 kNumAttempts,
293 false))))
294 .Times(PortalDetector::kMaxRequestAttempts - 1);
295
296 // Expect a single final failure.
297 EXPECT_CALL(callback_target(),
298 ResultCallback(IsResult(
299 PortalDetector::Result(
300 ConnectivityTrial::Result(
301 ConnectivityTrial::kPhaseDNS,
302 ConnectivityTrial::kStatusFailure),
303 kNumAttempts,
304 true))))
305 .Times(1);
306 }
307
308 // Expect the PortalDetector to stop the ConnectivityTrial after
309 // the final attempt.
310 EXPECT_CALL(*connectivity_trial(), Stop()).Times(1);
311
312 EXPECT_CALL(*connectivity_trial(), IsActive()).WillOnce(Return(true));
313 portal_detector()->Start(kURL);
314 for (int i = 0; i < PortalDetector::kMaxRequestAttempts; i++) {
315 EXPECT_TRUE(portal_detector()->IsInProgress());
316 AdvanceTime(PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000);
317 ConnectivityTrial::Result r =
318 ConnectivityTrial::GetPortalResultForRequestResult(
319 HTTPRequest::kResultDNSFailure);
320 portal_detector()->CompleteAttempt(r);
321 }
322
323 EXPECT_FALSE(portal_detector()->IsInProgress());
324 ExpectReset();
325 }
326
327 // Exactly like AttemptCount, except that the termination conditions are
328 // different because we're triggering a different sort of error.
TEST_F(PortalDetectorTest,ReadBadHeadersRetry)329 TEST_F(PortalDetectorTest, ReadBadHeadersRetry) {
330 EXPECT_FALSE(portal_detector()->IsInProgress());
331 // Expect the PortalDetector to immediately post a task for the each attempt.
332 EXPECT_CALL(*connectivity_trial(), Start(_, 0))
333 .Times(2).WillRepeatedly(Return(true));
334
335 EXPECT_TRUE(StartPortalRequest(kURL));
336
337 // Expect that the request will be started -- return failure.
338 EXPECT_CALL(*connectivity_trial(), Retry(0))
339 .Times(PortalDetector::kMaxFailuresInContentPhase - 1);
340 {
341 InSequence s;
342
343 // Expect non-final failures for all attempts but the last.
344 EXPECT_CALL(callback_target(),
345 ResultCallback(IsResult(
346 PortalDetector::Result(
347 ConnectivityTrial::Result(
348 ConnectivityTrial::kPhaseContent,
349 ConnectivityTrial::kStatusFailure),
350 kNumAttempts,
351 false))))
352 .Times(PortalDetector::kMaxFailuresInContentPhase - 1);
353
354 // Expect a single final failure.
355 EXPECT_CALL(callback_target(),
356 ResultCallback(IsResult(
357 PortalDetector::Result(
358 ConnectivityTrial::Result(
359 ConnectivityTrial::kPhaseContent,
360 ConnectivityTrial::kStatusFailure),
361 kNumAttempts,
362 true))))
363 .Times(1);
364 }
365
366 // Expect the PortalDetector to stop the current request each time, plus
367 // an extra time in PortalDetector::Stop().
368 EXPECT_CALL(*connectivity_trial(), Stop()).Times(1);
369
370 EXPECT_CALL(*connectivity_trial(), IsActive()).WillOnce(Return(true));
371 portal_detector()->Start(kURL);
372 for (int i = 0; i < PortalDetector::kMaxFailuresInContentPhase; i++) {
373 EXPECT_TRUE(portal_detector()->IsInProgress());
374 AdvanceTime(PortalDetector::kMinTimeBetweenAttemptsSeconds * 1000);
375 ConnectivityTrial::Result r =
376 ConnectivityTrial::Result(ConnectivityTrial::kPhaseContent,
377 ConnectivityTrial::kStatusFailure);
378 portal_detector()->CompleteAttempt(r);
379 }
380
381 EXPECT_FALSE(portal_detector()->IsInProgress());
382 }
383
TEST_F(PortalDetectorTest,ReadBadHeader)384 TEST_F(PortalDetectorTest, ReadBadHeader) {
385 StartAttempt();
386
387 ExpectAttemptRetry(
388 PortalDetector::Result(
389 ConnectivityTrial::Result(
390 ConnectivityTrial::kPhaseContent,
391 ConnectivityTrial::kStatusFailure),
392 kNumAttempts,
393 false));
394
395 ConnectivityTrial::Result r =
396 ConnectivityTrial::Result(ConnectivityTrial::kPhaseContent,
397 ConnectivityTrial::kStatusFailure);
398 portal_detector()->CompleteAttempt(r);
399 }
400
TEST_F(PortalDetectorTest,RequestTimeout)401 TEST_F(PortalDetectorTest, RequestTimeout) {
402 StartAttempt();
403 ExpectAttemptRetry(
404 PortalDetector::Result(
405 ConnectivityTrial::Result(
406 ConnectivityTrial::kPhaseUnknown,
407 ConnectivityTrial::kStatusTimeout),
408 kNumAttempts,
409 false));
410
411 ConnectivityTrial::Result r =
412 ConnectivityTrial::Result(ConnectivityTrial::kPhaseUnknown,
413 ConnectivityTrial::kStatusTimeout);
414 portal_detector()->CompleteAttempt(r);
415 }
416
TEST_F(PortalDetectorTest,ReadPartialHeaderTimeout)417 TEST_F(PortalDetectorTest, ReadPartialHeaderTimeout) {
418 StartAttempt();
419
420 ExpectAttemptRetry(
421 PortalDetector::Result(
422 ConnectivityTrial::Result(
423 ConnectivityTrial::kPhaseContent,
424 ConnectivityTrial::kStatusTimeout),
425 kNumAttempts,
426 false));
427
428 ConnectivityTrial::Result r =
429 ConnectivityTrial::Result(ConnectivityTrial::kPhaseContent,
430 ConnectivityTrial::kStatusTimeout);
431 portal_detector()->CompleteAttempt(r);
432 }
433
TEST_F(PortalDetectorTest,ReadCompleteHeader)434 TEST_F(PortalDetectorTest, ReadCompleteHeader) {
435 StartAttempt();
436
437 EXPECT_CALL(callback_target(),
438 ResultCallback(IsResult(
439 PortalDetector::Result(
440 ConnectivityTrial::Result(
441 ConnectivityTrial::kPhaseContent,
442 ConnectivityTrial::kStatusSuccess),
443 kNumAttempts,
444 true))));
445
446 EXPECT_CALL(*connectivity_trial(), Stop()).Times(1);
447 ConnectivityTrial::Result r =
448 ConnectivityTrial::Result(ConnectivityTrial::kPhaseContent,
449 ConnectivityTrial::kStatusSuccess);
450 portal_detector()->CompleteAttempt(r);
451 }
452
TEST_F(PortalDetectorTest,ReadMatchingHeader)453 TEST_F(PortalDetectorTest, ReadMatchingHeader) {
454 StartAttempt();
455
456 EXPECT_CALL(callback_target(),
457 ResultCallback(IsResult(
458 PortalDetector::Result(
459 ConnectivityTrial::Result(
460 ConnectivityTrial::kPhaseContent,
461 ConnectivityTrial::kStatusSuccess),
462 kNumAttempts,
463 true))));
464 EXPECT_CALL(*connectivity_trial(), Stop()).Times(1);
465 ConnectivityTrial::Result r =
466 ConnectivityTrial::Result(ConnectivityTrial::kPhaseContent,
467 ConnectivityTrial::kStatusSuccess);
468 portal_detector()->CompleteAttempt(r);
469 }
470
471 } // namespace shill
472