1 // Copyright (c) 2012 The Chromium 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.
5 #include "chrome/browser/captive_portal/captive_portal_service.h"
7 #include "base/basictypes.h"
9 #include "base/command_line.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/run_loop.h"
12 #include "base/test/test_timeouts.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "chrome/common/pref_names.h"
16 #include "chrome/test/base/testing_profile.h"
17 #include "chrome/test/base/ui_test_utils.h"
18 #include "components/captive_portal/captive_portal_testing_utils.h"
19 #include "content/public/browser/notification_details.h"
20 #include "content/public/browser/notification_observer.h"
21 #include "content/public/browser/notification_registrar.h"
22 #include "content/public/browser/notification_source.h"
23 #include "content/public/test/test_browser_thread_bundle.h"
24 #include "net/base/net_errors.h"
25 #include "testing/gtest/include/gtest/gtest.h"
27 using captive_portal::CaptivePortalDetectorTestBase
;
28 using captive_portal::CaptivePortalResult
;
32 // An observer watches the CaptivePortalDetector. It tracks the last
33 // received result and the total number of received results.
34 class CaptivePortalObserver
: public content::NotificationObserver
{
36 CaptivePortalObserver(Profile
* profile
,
37 CaptivePortalService
* captive_portal_service
)
38 : captive_portal_result_(
39 captive_portal_service
->last_detection_result()),
40 num_results_received_(0),
42 captive_portal_service_(captive_portal_service
) {
44 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT
,
45 content::Source
<Profile
>(profile_
));
48 CaptivePortalResult
captive_portal_result() const {
49 return captive_portal_result_
;
52 int num_results_received() const { return num_results_received_
; }
55 virtual void Observe(int type
,
56 const content::NotificationSource
& source
,
57 const content::NotificationDetails
& details
) override
{
58 ASSERT_EQ(type
, chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT
);
59 ASSERT_EQ(profile_
, content::Source
<Profile
>(source
).ptr());
61 CaptivePortalService::Results
*results
=
62 content::Details
<CaptivePortalService::Results
>(details
).ptr();
64 EXPECT_EQ(captive_portal_result_
, results
->previous_result
);
65 EXPECT_EQ(captive_portal_service_
->last_detection_result(),
68 captive_portal_result_
= results
->result
;
69 ++num_results_received_
;
72 CaptivePortalResult captive_portal_result_
;
73 int num_results_received_
;
76 CaptivePortalService
* captive_portal_service_
;
78 content::NotificationRegistrar registrar_
;
80 DISALLOW_COPY_AND_ASSIGN(CaptivePortalObserver
);
85 class CaptivePortalServiceTest
: public testing::Test
,
86 public CaptivePortalDetectorTestBase
{
88 CaptivePortalServiceTest()
89 : old_captive_portal_testing_state_(
90 CaptivePortalService::get_state_for_testing()) {
93 virtual ~CaptivePortalServiceTest() {
94 CaptivePortalService::set_state_for_testing(
95 old_captive_portal_testing_state_
);
98 // |enable_service| is whether or not the captive portal service itself
99 // should be disabled. This is different from enabling the captive portal
100 // detection preference.
101 void Initialize(CaptivePortalService::TestingState testing_state
) {
102 CaptivePortalService::set_state_for_testing(testing_state
);
104 profile_
.reset(new TestingProfile());
105 service_
.reset(new CaptivePortalService(profile_
.get()));
106 service_
->set_time_ticks_for_testing(base::TimeTicks::Now());
108 // Use no delays for most tests.
109 set_initial_backoff_no_portal(base::TimeDelta());
110 set_initial_backoff_portal(base::TimeDelta());
112 set_detector(&service_
->captive_portal_detector_
);
113 SetTime(base::Time::Now());
115 // Disable jitter, so can check exact values.
116 set_jitter_factor(0.0);
118 // These values make checking exponential backoff easier.
119 set_multiply_factor(2.0);
120 set_maximum_backoff(base::TimeDelta::FromSeconds(1600));
122 // This means backoff starts after the second "failure", which is the third
123 // captive portal test in a row that ends up with the same result. Since
124 // the first request uses no delay, this means the delays will be in
125 // the pattern 0, 0, 100, 200, 400, etc. There are two zeros because the
126 // first check never has a delay, and the first check to have a new result
127 // is followed by no delay.
128 set_num_errors_to_ignore(1);
130 EnableCaptivePortalDetectionPreference(true);
133 // Sets the captive portal checking preference.
134 void EnableCaptivePortalDetectionPreference(bool enabled
) {
135 profile()->GetPrefs()->SetBoolean(prefs::kAlternateErrorPagesEnabled
,
139 // Triggers a captive portal check, then simulates the URL request
140 // returning with the specified |net_error| and |status_code|. If |net_error|
141 // is not OK, |status_code| is ignored. Expects the CaptivePortalService to
142 // return |expected_result|.
144 // |expected_delay_secs| is the expected value of GetTimeUntilNextRequest().
145 // The function makes sure the value is as expected, and then simulates
146 // waiting for that period of time before running the test.
148 // If |response_headers| is non-NULL, the response will use it as headers
149 // for the simulate URL request. It must use single linefeeds as line breaks.
150 void RunTest(CaptivePortalResult expected_result
,
153 int expected_delay_secs
,
154 const char* response_headers
) {
155 base::TimeDelta expected_delay
=
156 base::TimeDelta::FromSeconds(expected_delay_secs
);
158 ASSERT_EQ(CaptivePortalService::STATE_IDLE
, service()->state());
159 ASSERT_EQ(expected_delay
, GetTimeUntilNextRequest());
161 AdvanceTime(expected_delay
);
162 ASSERT_EQ(base::TimeDelta(), GetTimeUntilNextRequest());
164 CaptivePortalObserver
observer(profile(), service());
165 service()->DetectCaptivePortal();
167 EXPECT_EQ(CaptivePortalService::STATE_TIMER_RUNNING
, service()->state());
168 EXPECT_FALSE(FetchingURL());
169 ASSERT_TRUE(TimerRunning());
171 base::RunLoop().RunUntilIdle();
172 EXPECT_EQ(CaptivePortalService::STATE_CHECKING_FOR_PORTAL
,
174 ASSERT_TRUE(FetchingURL());
175 EXPECT_FALSE(TimerRunning());
177 CompleteURLFetch(net_error
, status_code
, response_headers
);
179 EXPECT_FALSE(FetchingURL());
180 EXPECT_FALSE(TimerRunning());
181 EXPECT_EQ(1, observer
.num_results_received());
182 EXPECT_EQ(expected_result
, observer
.captive_portal_result());
185 // Runs a test when the captive portal service is disabled.
186 void RunDisabledTest(int expected_delay_secs
) {
187 base::TimeDelta expected_delay
=
188 base::TimeDelta::FromSeconds(expected_delay_secs
);
190 ASSERT_EQ(CaptivePortalService::STATE_IDLE
, service()->state());
191 ASSERT_EQ(expected_delay
, GetTimeUntilNextRequest());
193 AdvanceTime(expected_delay
);
194 ASSERT_EQ(base::TimeDelta(), GetTimeUntilNextRequest());
196 CaptivePortalObserver
observer(profile(), service());
197 service()->DetectCaptivePortal();
199 EXPECT_EQ(CaptivePortalService::STATE_TIMER_RUNNING
, service()->state());
200 EXPECT_FALSE(FetchingURL());
201 ASSERT_TRUE(TimerRunning());
203 base::RunLoop().RunUntilIdle();
204 EXPECT_FALSE(FetchingURL());
205 EXPECT_FALSE(TimerRunning());
206 EXPECT_EQ(1, observer
.num_results_received());
207 EXPECT_EQ(captive_portal::RESULT_INTERNET_CONNECTED
,
208 observer
.captive_portal_result());
211 // Tests exponential backoff. Prior to calling, the relevant recheck settings
212 // must be set to have a minimum time of 100 seconds, with 2 checks before
213 // starting exponential backoff.
214 void RunBackoffTest(CaptivePortalResult expected_result
,
217 RunTest(expected_result
, net_error
, status_code
, 0, NULL
);
218 RunTest(expected_result
, net_error
, status_code
, 0, NULL
);
219 RunTest(expected_result
, net_error
, status_code
, 100, NULL
);
220 RunTest(expected_result
, net_error
, status_code
, 200, NULL
);
221 RunTest(expected_result
, net_error
, status_code
, 400, NULL
);
222 RunTest(expected_result
, net_error
, status_code
, 800, NULL
);
223 RunTest(expected_result
, net_error
, status_code
, 1600, NULL
);
224 RunTest(expected_result
, net_error
, status_code
, 1600, NULL
);
227 // Changes test time for the service and service's captive portal
229 void AdvanceTime(const base::TimeDelta
& delta
) {
230 service()->advance_time_ticks_for_testing(delta
);
231 CaptivePortalDetectorTestBase::AdvanceTime(delta
);
234 bool TimerRunning() {
235 return service()->TimerRunning();
238 base::TimeDelta
GetTimeUntilNextRequest() {
239 return service()->backoff_entry_
->GetTimeUntilRelease();
242 void set_initial_backoff_no_portal(
243 base::TimeDelta initial_backoff_no_portal
) {
244 service()->recheck_policy().initial_backoff_no_portal_ms
=
245 initial_backoff_no_portal
.InMilliseconds();
248 void set_initial_backoff_portal(base::TimeDelta initial_backoff_portal
) {
249 service()->recheck_policy().initial_backoff_portal_ms
=
250 initial_backoff_portal
.InMilliseconds();
253 void set_maximum_backoff(base::TimeDelta maximum_backoff
) {
254 service()->recheck_policy().backoff_policy
.maximum_backoff_ms
=
255 maximum_backoff
.InMilliseconds();
258 void set_num_errors_to_ignore(int num_errors_to_ignore
) {
259 service()->recheck_policy().backoff_policy
.num_errors_to_ignore
=
260 num_errors_to_ignore
;
263 void set_multiply_factor(double multiply_factor
) {
264 service()->recheck_policy().backoff_policy
.multiply_factor
=
268 void set_jitter_factor(double jitter_factor
) {
269 service()->recheck_policy().backoff_policy
.jitter_factor
= jitter_factor
;
272 TestingProfile
* profile() { return profile_
.get(); }
274 CaptivePortalService
* service() { return service_
.get(); }
277 // Stores the initial CaptivePortalService::TestingState so it can be restored
279 const CaptivePortalService::TestingState old_captive_portal_testing_state_
;
281 content::TestBrowserThreadBundle thread_bundle_
;
283 // Note that the construction order of these matters.
284 scoped_ptr
<TestingProfile
> profile_
;
285 scoped_ptr
<CaptivePortalService
> service_
;
288 // Verify that an observer doesn't get messages from the wrong profile.
289 TEST_F(CaptivePortalServiceTest
, CaptivePortalTwoProfiles
) {
290 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
291 TestingProfile profile2
;
292 scoped_ptr
<CaptivePortalService
> service2(
293 new CaptivePortalService(&profile2
));
294 CaptivePortalObserver
observer2(&profile2
, service2
.get());
296 RunTest(captive_portal::RESULT_INTERNET_CONNECTED
, net::OK
, 204, 0, NULL
);
297 EXPECT_EQ(0, observer2
.num_results_received());
300 // Checks exponential backoff when the Internet is connected.
301 TEST_F(CaptivePortalServiceTest
, CaptivePortalRecheckInternetConnected
) {
302 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
304 // This value should have no effect on this test, until the end.
305 set_initial_backoff_portal(base::TimeDelta::FromSeconds(1));
307 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100));
308 RunBackoffTest(captive_portal::RESULT_INTERNET_CONNECTED
, net::OK
, 204);
310 // Make sure that getting a new result resets the timer.
312 captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL
, net::OK
, 200, 1600, NULL
);
313 RunTest(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL
, net::OK
, 200, 0, NULL
);
314 RunTest(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL
, net::OK
, 200, 1, NULL
);
315 RunTest(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL
, net::OK
, 200, 2, NULL
);
318 // Checks exponential backoff when there's an HTTP error.
319 TEST_F(CaptivePortalServiceTest
, CaptivePortalRecheckError
) {
320 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
322 // This value should have no effect on this test.
323 set_initial_backoff_portal(base::TimeDelta::FromDays(1));
325 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100));
326 RunBackoffTest(captive_portal::RESULT_NO_RESPONSE
, net::OK
, 500);
328 // Make sure that getting a new result resets the timer.
329 RunTest(captive_portal::RESULT_INTERNET_CONNECTED
, net::OK
, 204, 1600, NULL
);
330 RunTest(captive_portal::RESULT_INTERNET_CONNECTED
, net::OK
, 204, 0, NULL
);
331 RunTest(captive_portal::RESULT_INTERNET_CONNECTED
, net::OK
, 204, 100, NULL
);
334 // Checks exponential backoff when there's a captive portal.
335 TEST_F(CaptivePortalServiceTest
, CaptivePortalRecheckBehindPortal
) {
336 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
338 // This value should have no effect on this test, until the end.
339 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(250));
341 set_initial_backoff_portal(base::TimeDelta::FromSeconds(100));
342 RunBackoffTest(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL
, net::OK
, 200);
344 // Make sure that getting a new result resets the timer.
345 RunTest(captive_portal::RESULT_INTERNET_CONNECTED
, net::OK
, 204, 1600, NULL
);
346 RunTest(captive_portal::RESULT_INTERNET_CONNECTED
, net::OK
, 204, 0, NULL
);
347 RunTest(captive_portal::RESULT_INTERNET_CONNECTED
, net::OK
, 204, 250, NULL
);
350 // Check that everything works as expected when captive portal checking is
351 // disabled, including throttling. Then enables it again and runs another test.
352 TEST_F(CaptivePortalServiceTest
, CaptivePortalPrefDisabled
) {
353 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
355 // This value should have no effect on this test.
356 set_initial_backoff_no_portal(base::TimeDelta::FromDays(1));
358 set_initial_backoff_portal(base::TimeDelta::FromSeconds(100));
360 EnableCaptivePortalDetectionPreference(false);
363 for (int i
= 0; i
< 6; ++i
)
364 RunDisabledTest(100);
366 EnableCaptivePortalDetectionPreference(true);
368 RunTest(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL
, net::OK
, 200, 0, NULL
);
371 // Check that disabling the captive portal service while a check is running
373 TEST_F(CaptivePortalServiceTest
, CaptivePortalPrefDisabledWhileRunning
) {
374 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
375 CaptivePortalObserver
observer(profile(), service());
377 // Needed to create the URLFetcher, even if it never returns any results.
378 service()->DetectCaptivePortal();
380 base::RunLoop().RunUntilIdle();
381 EXPECT_TRUE(FetchingURL());
382 EXPECT_FALSE(TimerRunning());
384 EnableCaptivePortalDetectionPreference(false);
385 EXPECT_FALSE(FetchingURL());
386 EXPECT_TRUE(TimerRunning());
387 EXPECT_EQ(0, observer
.num_results_received());
389 base::RunLoop().RunUntilIdle();
391 EXPECT_FALSE(FetchingURL());
392 EXPECT_FALSE(TimerRunning());
393 EXPECT_EQ(1, observer
.num_results_received());
395 EXPECT_EQ(captive_portal::RESULT_INTERNET_CONNECTED
,
396 observer
.captive_portal_result());
399 // Check that disabling the captive portal service while a check is pending
401 TEST_F(CaptivePortalServiceTest
, CaptivePortalPrefDisabledWhilePending
) {
402 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
403 set_initial_backoff_no_portal(base::TimeDelta::FromDays(1));
405 CaptivePortalObserver
observer(profile(), service());
406 service()->DetectCaptivePortal();
407 EXPECT_FALSE(FetchingURL());
408 EXPECT_TRUE(TimerRunning());
410 EnableCaptivePortalDetectionPreference(false);
411 EXPECT_FALSE(FetchingURL());
412 EXPECT_TRUE(TimerRunning());
413 EXPECT_EQ(0, observer
.num_results_received());
415 base::RunLoop().RunUntilIdle();
417 EXPECT_FALSE(FetchingURL());
418 EXPECT_FALSE(TimerRunning());
419 EXPECT_EQ(1, observer
.num_results_received());
421 EXPECT_EQ(captive_portal::RESULT_INTERNET_CONNECTED
,
422 observer
.captive_portal_result());
425 // Check that disabling the captive portal service while a check is pending
427 TEST_F(CaptivePortalServiceTest
, CaptivePortalPrefEnabledWhilePending
) {
428 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
430 EnableCaptivePortalDetectionPreference(false);
433 CaptivePortalObserver
observer(profile(), service());
434 service()->DetectCaptivePortal();
435 EXPECT_FALSE(FetchingURL());
436 EXPECT_TRUE(TimerRunning());
438 EnableCaptivePortalDetectionPreference(true);
439 EXPECT_FALSE(FetchingURL());
440 EXPECT_TRUE(TimerRunning());
442 base::RunLoop().RunUntilIdle();
443 ASSERT_TRUE(FetchingURL());
444 EXPECT_FALSE(TimerRunning());
446 CompleteURLFetch(net::OK
, 200, NULL
);
447 EXPECT_FALSE(FetchingURL());
448 EXPECT_FALSE(TimerRunning());
450 EXPECT_EQ(1, observer
.num_results_received());
451 EXPECT_EQ(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL
,
452 observer
.captive_portal_result());
455 // Checks that disabling for browser tests works as expected.
456 TEST_F(CaptivePortalServiceTest
, CaptivePortalDisableForTests
) {
457 Initialize(CaptivePortalService::DISABLED_FOR_TESTING
);
461 // Checks that jitter gives us values in the correct range.
462 TEST_F(CaptivePortalServiceTest
, CaptivePortalJitter
) {
463 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
464 set_jitter_factor(0.3);
465 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100));
466 RunTest(captive_portal::RESULT_INTERNET_CONNECTED
, net::OK
, 204, 0, NULL
);
467 RunTest(captive_portal::RESULT_INTERNET_CONNECTED
, net::OK
, 204, 0, NULL
);
469 for (int i
= 0; i
< 50; ++i
) {
470 int interval_sec
= GetTimeUntilNextRequest().InSeconds();
471 // Allow for roundoff, though shouldn't be necessary.
472 EXPECT_LE(69, interval_sec
);
473 EXPECT_LE(interval_sec
, 101);
477 // Check a Retry-After header that contains a delay in seconds.
478 TEST_F(CaptivePortalServiceTest
, CaptivePortalRetryAfterSeconds
) {
479 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
480 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100));
481 const char* retry_after
= "HTTP/1.1 503 OK\nRetry-After: 101\n\n";
483 // Check that Retry-After headers work both on the first request to return a
484 // result and on subsequent requests.
485 RunTest(captive_portal::RESULT_NO_RESPONSE
, net::OK
, 503, 0, retry_after
);
486 RunTest(captive_portal::RESULT_NO_RESPONSE
, net::OK
, 503, 101, retry_after
);
487 RunTest(captive_portal::RESULT_INTERNET_CONNECTED
, net::OK
, 204, 101, NULL
);
489 // Make sure that there's no effect on the next captive portal check after
491 EXPECT_EQ(base::TimeDelta::FromSeconds(0), GetTimeUntilNextRequest());
494 // Check that the RecheckPolicy is still respected on 503 responses with
495 // Retry-After headers.
496 TEST_F(CaptivePortalServiceTest
, CaptivePortalRetryAfterSecondsTooShort
) {
497 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
498 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100));
499 const char* retry_after
= "HTTP/1.1 503 OK\nRetry-After: 99\n\n";
501 RunTest(captive_portal::RESULT_NO_RESPONSE
, net::OK
, 503, 0, retry_after
);
502 // Normally would be no delay on the first check with a new result.
503 RunTest(captive_portal::RESULT_NO_RESPONSE
, net::OK
, 503, 99, retry_after
);
504 EXPECT_EQ(base::TimeDelta::FromSeconds(100), GetTimeUntilNextRequest());
507 // Check a Retry-After header that contains a date.
508 TEST_F(CaptivePortalServiceTest
, CaptivePortalRetryAfterDate
) {
509 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING
);
510 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(50));
512 // base has a function to get a time in the right format from a string, but
513 // not the other way around.
514 base::Time start_time
;
516 base::Time::FromString("Tue, 17 Apr 2012 18:02:00 GMT", &start_time
));
519 RunTest(captive_portal::RESULT_NO_RESPONSE
,
523 "HTTP/1.1 503 OK\nRetry-After: Tue, 17 Apr 2012 18:02:51 GMT\n\n");
524 EXPECT_EQ(base::TimeDelta::FromSeconds(51), GetTimeUntilNextRequest());