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(&current_time_, &tv, &current_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