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
34using base::Bind;
35using base::Callback;
36using base::Unretained;
37using std::string;
38using std::vector;
39using testing::_;
40using testing::AtLeast;
41using testing::DoAll;
42using testing::InSequence;
43using testing::Mock;
44using testing::NiceMock;
45using testing::Return;
46using testing::ReturnRef;
47using testing::SetArgumentPointee;
48using testing::StrictMock;
49using testing::Test;
50
51namespace shill {
52
53namespace {
54const char kBadURL[] = "badurl";
55const char kInterfaceName[] = "int0";
56const char kURL[] = "http://www.chromium.org";
57const char kDNSServer0[] = "8.8.8.8";
58const char kDNSServer1[] = "8.8.4.4";
59const char* kDNSServers[] = { kDNSServer0, kDNSServer1 };
60}  // namespace
61
62MATCHER_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
69class PortalDetectorTest : public Test {
70 public:
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
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
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:
114    CallbackTarget()
115        : result_callback_(Bind(&CallbackTarget::ResultCallback,
116                                Unretained(this))) {
117    }
118
119    MOCK_METHOD1(ResultCallback, void(const PortalDetector::Result& result));
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
128  bool StartPortalRequest(const string& url_string) {
129    bool ret = portal_detector_->Start(url_string);
130    return ret;
131  }
132
133  PortalDetector* portal_detector() { return portal_detector_.get(); }
134  MockConnectivityTrial* connectivity_trial() { return connectivity_trial_; }
135  MockEventDispatcher& dispatcher() { return dispatcher_; }
136  CallbackTarget& callback_target() { return callback_target_; }
137
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
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
152  void AdvanceTime(int milliseconds) {
153    struct timeval tv = { milliseconds / 1000, (milliseconds % 1000) * 1000 };
154    timeradd(&current_time_, &tv, &current_time_);
155  }
156
157  void StartAttempt() {
158    EXPECT_CALL(*connectivity_trial(), Start(_, _)).WillOnce(Return(true));
159
160    EXPECT_TRUE(StartPortalRequest(kURL));
161  }
162
163 private:
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
183const int PortalDetectorTest::kNumAttempts = 0;
184
185TEST_F(PortalDetectorTest, Constructor) {
186  ExpectReset();
187}
188
189TEST_F(PortalDetectorTest, InvalidURL) {
190  EXPECT_CALL(*connectivity_trial(), Start(_, _)).WillOnce(Return(false));
191  EXPECT_FALSE(portal_detector()->Start(kBadURL));
192  ExpectReset();
193}
194
195TEST_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
216TEST_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
244TEST_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
253TEST_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
268TEST_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.
329TEST_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
384TEST_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
401TEST_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
417TEST_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
434TEST_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
453TEST_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