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. 4 5 #include "chrome/browser/captive_portal/captive_portal_service.h" 6 7 #include "base/basictypes.h" 8 #include "base/bind.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/captive_portal/testing_utils.h" 14 #include "chrome/browser/chrome_notification_types.h" 15 #include "chrome/common/chrome_switches.h" 16 #include "chrome/common/pref_names.h" 17 #include "chrome/test/base/testing_profile.h" 18 #include "chrome/test/base/ui_test_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" 26 27 namespace captive_portal { 28 29 namespace { 30 31 // A short amount of time that some tests wait for. 32 const int kShortTimeMs = 10; 33 34 // An observer watches the CaptivePortalDetector. It tracks the last 35 // received result and the total number of received results. 36 class CaptivePortalObserver : public content::NotificationObserver { 37 public: 38 CaptivePortalObserver(Profile* profile, 39 CaptivePortalService* captive_portal_service) 40 : captive_portal_result_( 41 captive_portal_service->last_detection_result()), 42 num_results_received_(0), 43 profile_(profile), 44 captive_portal_service_(captive_portal_service) { 45 registrar_.Add(this, 46 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, 47 content::Source<Profile>(profile_)); 48 } 49 50 Result captive_portal_result() const { return captive_portal_result_; } 51 52 int num_results_received() const { return num_results_received_; } 53 54 private: 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()); 60 61 CaptivePortalService::Results *results = 62 content::Details<CaptivePortalService::Results>(details).ptr(); 63 64 EXPECT_EQ(captive_portal_result_, results->previous_result); 65 EXPECT_EQ(captive_portal_service_->last_detection_result(), 66 results->result); 67 68 captive_portal_result_ = results->result; 69 ++num_results_received_; 70 } 71 72 Result captive_portal_result_; 73 int num_results_received_; 74 75 Profile* profile_; 76 CaptivePortalService* captive_portal_service_; 77 78 content::NotificationRegistrar registrar_; 79 80 DISALLOW_COPY_AND_ASSIGN(CaptivePortalObserver); 81 }; 82 83 } // namespace 84 85 class CaptivePortalServiceTest : public testing::Test, 86 public CaptivePortalDetectorTestBase { 87 public: 88 CaptivePortalServiceTest() 89 : old_captive_portal_testing_state_( 90 CaptivePortalService::get_state_for_testing()) { 91 } 92 93 virtual ~CaptivePortalServiceTest() { 94 CaptivePortalService::set_state_for_testing( 95 old_captive_portal_testing_state_); 96 } 97 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); 103 104 profile_.reset(new TestingProfile()); 105 service_.reset(new CaptivePortalService(profile_.get())); 106 service_->set_time_ticks_for_testing(base::TimeTicks::Now()); 107 108 // Use no delays for most tests. 109 set_initial_backoff_no_portal(base::TimeDelta()); 110 set_initial_backoff_portal(base::TimeDelta()); 111 112 set_detector(&service_->captive_portal_detector_); 113 SetTime(base::Time::Now()); 114 115 // Disable jitter, so can check exact values. 116 set_jitter_factor(0.0); 117 118 // These values make checking exponential backoff easier. 119 set_multiply_factor(2.0); 120 set_maximum_backoff(base::TimeDelta::FromSeconds(1600)); 121 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); 129 130 EnableCaptivePortalDetectionPreference(true); 131 } 132 133 // Sets the captive portal checking preference. 134 void EnableCaptivePortalDetectionPreference(bool enabled) { 135 profile()->GetPrefs()->SetBoolean(prefs::kAlternateErrorPagesEnabled, 136 enabled); 137 } 138 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|. 143 // 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. 147 // 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(Result expected_result, 151 int net_error, 152 int status_code, 153 int expected_delay_secs, 154 const char* response_headers) { 155 base::TimeDelta expected_delay = 156 base::TimeDelta::FromSeconds(expected_delay_secs); 157 158 ASSERT_EQ(CaptivePortalService::STATE_IDLE, service()->state()); 159 ASSERT_EQ(expected_delay, GetTimeUntilNextRequest()); 160 161 AdvanceTime(expected_delay); 162 ASSERT_EQ(base::TimeDelta(), GetTimeUntilNextRequest()); 163 164 CaptivePortalObserver observer(profile(), service()); 165 service()->DetectCaptivePortal(); 166 167 EXPECT_EQ(CaptivePortalService::STATE_TIMER_RUNNING, service()->state()); 168 EXPECT_FALSE(FetchingURL()); 169 ASSERT_TRUE(TimerRunning()); 170 171 base::RunLoop().RunUntilIdle(); 172 EXPECT_EQ(CaptivePortalService::STATE_CHECKING_FOR_PORTAL, 173 service()->state()); 174 ASSERT_TRUE(FetchingURL()); 175 EXPECT_FALSE(TimerRunning()); 176 177 CompleteURLFetch(net_error, status_code, response_headers); 178 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()); 183 } 184 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); 189 190 ASSERT_EQ(CaptivePortalService::STATE_IDLE, service()->state()); 191 ASSERT_EQ(expected_delay, GetTimeUntilNextRequest()); 192 193 AdvanceTime(expected_delay); 194 ASSERT_EQ(base::TimeDelta(), GetTimeUntilNextRequest()); 195 196 CaptivePortalObserver observer(profile(), service()); 197 service()->DetectCaptivePortal(); 198 199 EXPECT_EQ(CaptivePortalService::STATE_TIMER_RUNNING, service()->state()); 200 EXPECT_FALSE(FetchingURL()); 201 ASSERT_TRUE(TimerRunning()); 202 203 base::RunLoop().RunUntilIdle(); 204 EXPECT_FALSE(FetchingURL()); 205 EXPECT_FALSE(TimerRunning()); 206 EXPECT_EQ(1, observer.num_results_received()); 207 EXPECT_EQ(RESULT_INTERNET_CONNECTED, observer.captive_portal_result()); 208 } 209 210 // Tests exponential backoff. Prior to calling, the relevant recheck settings 211 // must be set to have a minimum time of 100 seconds, with 2 checks before 212 // starting exponential backoff. 213 void RunBackoffTest(Result expected_result, int net_error, int status_code) { 214 RunTest(expected_result, net_error, status_code, 0, NULL); 215 RunTest(expected_result, net_error, status_code, 0, NULL); 216 RunTest(expected_result, net_error, status_code, 100, NULL); 217 RunTest(expected_result, net_error, status_code, 200, NULL); 218 RunTest(expected_result, net_error, status_code, 400, NULL); 219 RunTest(expected_result, net_error, status_code, 800, NULL); 220 RunTest(expected_result, net_error, status_code, 1600, NULL); 221 RunTest(expected_result, net_error, status_code, 1600, NULL); 222 } 223 224 // Changes test time for the service and service's captive portal 225 // detector. 226 void AdvanceTime(const base::TimeDelta& delta) { 227 service()->advance_time_ticks_for_testing(delta); 228 CaptivePortalDetectorTestBase::AdvanceTime(delta); 229 } 230 231 bool TimerRunning() { 232 return service()->TimerRunning(); 233 } 234 235 base::TimeDelta GetTimeUntilNextRequest() { 236 return service()->backoff_entry_->GetTimeUntilRelease(); 237 } 238 239 void set_initial_backoff_no_portal( 240 base::TimeDelta initial_backoff_no_portal) { 241 service()->recheck_policy().initial_backoff_no_portal_ms = 242 initial_backoff_no_portal.InMilliseconds(); 243 } 244 245 void set_initial_backoff_portal(base::TimeDelta initial_backoff_portal) { 246 service()->recheck_policy().initial_backoff_portal_ms = 247 initial_backoff_portal.InMilliseconds(); 248 } 249 250 void set_maximum_backoff(base::TimeDelta maximum_backoff) { 251 service()->recheck_policy().backoff_policy.maximum_backoff_ms = 252 maximum_backoff.InMilliseconds(); 253 } 254 255 void set_num_errors_to_ignore(int num_errors_to_ignore) { 256 service()->recheck_policy().backoff_policy.num_errors_to_ignore = 257 num_errors_to_ignore; 258 } 259 260 void set_multiply_factor(double multiply_factor) { 261 service()->recheck_policy().backoff_policy.multiply_factor = 262 multiply_factor; 263 } 264 265 void set_jitter_factor(double jitter_factor) { 266 service()->recheck_policy().backoff_policy.jitter_factor = jitter_factor; 267 } 268 269 TestingProfile* profile() { return profile_.get(); } 270 271 CaptivePortalService* service() { return service_.get(); } 272 273 private: 274 // Stores the initial CaptivePortalService::TestingState so it can be restored 275 // after the test. 276 const CaptivePortalService::TestingState old_captive_portal_testing_state_; 277 278 content::TestBrowserThreadBundle thread_bundle_; 279 280 // Note that the construction order of these matters. 281 scoped_ptr<TestingProfile> profile_; 282 scoped_ptr<CaptivePortalService> service_; 283 }; 284 285 // Verify that an observer doesn't get messages from the wrong profile. 286 TEST_F(CaptivePortalServiceTest, CaptivePortalTwoProfiles) { 287 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 288 TestingProfile profile2; 289 scoped_ptr<CaptivePortalService> service2( 290 new CaptivePortalService(&profile2)); 291 CaptivePortalObserver observer2(&profile2, service2.get()); 292 293 RunTest(RESULT_INTERNET_CONNECTED, net::OK, 204, 0, NULL); 294 EXPECT_EQ(0, observer2.num_results_received()); 295 } 296 297 // Checks exponential backoff when the Internet is connected. 298 TEST_F(CaptivePortalServiceTest, CaptivePortalRecheckInternetConnected) { 299 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 300 301 // This value should have no effect on this test, until the end. 302 set_initial_backoff_portal(base::TimeDelta::FromSeconds(1)); 303 304 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100)); 305 RunBackoffTest(RESULT_INTERNET_CONNECTED, net::OK, 204); 306 307 // Make sure that getting a new result resets the timer. 308 RunTest(RESULT_BEHIND_CAPTIVE_PORTAL, net::OK, 200, 1600, NULL); 309 RunTest(RESULT_BEHIND_CAPTIVE_PORTAL, net::OK, 200, 0, NULL); 310 RunTest(RESULT_BEHIND_CAPTIVE_PORTAL, net::OK, 200, 1, NULL); 311 RunTest(RESULT_BEHIND_CAPTIVE_PORTAL, net::OK, 200, 2, NULL); 312 } 313 314 // Checks exponential backoff when there's an HTTP error. 315 TEST_F(CaptivePortalServiceTest, CaptivePortalRecheckError) { 316 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 317 318 // This value should have no effect on this test. 319 set_initial_backoff_portal(base::TimeDelta::FromDays(1)); 320 321 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100)); 322 RunBackoffTest(RESULT_NO_RESPONSE, net::OK, 500); 323 324 // Make sure that getting a new result resets the timer. 325 RunTest(RESULT_INTERNET_CONNECTED, net::OK, 204, 1600, NULL); 326 RunTest(RESULT_INTERNET_CONNECTED, net::OK, 204, 0, NULL); 327 RunTest(RESULT_INTERNET_CONNECTED, net::OK, 204, 100, NULL); 328 } 329 330 // Checks exponential backoff when there's a captive portal. 331 TEST_F(CaptivePortalServiceTest, CaptivePortalRecheckBehindPortal) { 332 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 333 334 // This value should have no effect on this test, until the end. 335 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(250)); 336 337 set_initial_backoff_portal(base::TimeDelta::FromSeconds(100)); 338 RunBackoffTest(RESULT_BEHIND_CAPTIVE_PORTAL, net::OK, 200); 339 340 // Make sure that getting a new result resets the timer. 341 RunTest(RESULT_INTERNET_CONNECTED, net::OK, 204, 1600, NULL); 342 RunTest(RESULT_INTERNET_CONNECTED, net::OK, 204, 0, NULL); 343 RunTest(RESULT_INTERNET_CONNECTED, net::OK, 204, 250, NULL); 344 } 345 346 // Check that everything works as expected when captive portal checking is 347 // disabled, including throttling. Then enables it again and runs another test. 348 TEST_F(CaptivePortalServiceTest, CaptivePortalPrefDisabled) { 349 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 350 351 // This value should have no effect on this test. 352 set_initial_backoff_no_portal(base::TimeDelta::FromDays(1)); 353 354 set_initial_backoff_portal(base::TimeDelta::FromSeconds(100)); 355 356 EnableCaptivePortalDetectionPreference(false); 357 358 RunDisabledTest(0); 359 for (int i = 0; i < 6; ++i) 360 RunDisabledTest(100); 361 362 EnableCaptivePortalDetectionPreference(true); 363 364 RunTest(RESULT_BEHIND_CAPTIVE_PORTAL, net::OK, 200, 0, NULL); 365 } 366 367 // Check that disabling the captive portal service while a check is running 368 // works. 369 TEST_F(CaptivePortalServiceTest, CaptivePortalPrefDisabledWhileRunning) { 370 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 371 CaptivePortalObserver observer(profile(), service()); 372 373 // Needed to create the URLFetcher, even if it never returns any results. 374 service()->DetectCaptivePortal(); 375 376 base::RunLoop().RunUntilIdle(); 377 EXPECT_TRUE(FetchingURL()); 378 EXPECT_FALSE(TimerRunning()); 379 380 EnableCaptivePortalDetectionPreference(false); 381 EXPECT_FALSE(FetchingURL()); 382 EXPECT_TRUE(TimerRunning()); 383 EXPECT_EQ(0, observer.num_results_received()); 384 385 base::RunLoop().RunUntilIdle(); 386 387 EXPECT_FALSE(FetchingURL()); 388 EXPECT_FALSE(TimerRunning()); 389 EXPECT_EQ(1, observer.num_results_received()); 390 391 EXPECT_EQ(RESULT_INTERNET_CONNECTED, observer.captive_portal_result()); 392 } 393 394 // Check that disabling the captive portal service while a check is pending 395 // works. 396 TEST_F(CaptivePortalServiceTest, CaptivePortalPrefDisabledWhilePending) { 397 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 398 set_initial_backoff_no_portal(base::TimeDelta::FromDays(1)); 399 400 CaptivePortalObserver observer(profile(), service()); 401 service()->DetectCaptivePortal(); 402 EXPECT_FALSE(FetchingURL()); 403 EXPECT_TRUE(TimerRunning()); 404 405 EnableCaptivePortalDetectionPreference(false); 406 EXPECT_FALSE(FetchingURL()); 407 EXPECT_TRUE(TimerRunning()); 408 EXPECT_EQ(0, observer.num_results_received()); 409 410 base::RunLoop().RunUntilIdle(); 411 412 EXPECT_FALSE(FetchingURL()); 413 EXPECT_FALSE(TimerRunning()); 414 EXPECT_EQ(1, observer.num_results_received()); 415 416 EXPECT_EQ(RESULT_INTERNET_CONNECTED, observer.captive_portal_result()); 417 } 418 419 // Check that disabling the captive portal service while a check is pending 420 // works. 421 TEST_F(CaptivePortalServiceTest, CaptivePortalPrefEnabledWhilePending) { 422 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 423 424 EnableCaptivePortalDetectionPreference(false); 425 RunDisabledTest(0); 426 427 CaptivePortalObserver observer(profile(), service()); 428 service()->DetectCaptivePortal(); 429 EXPECT_FALSE(FetchingURL()); 430 EXPECT_TRUE(TimerRunning()); 431 432 EnableCaptivePortalDetectionPreference(true); 433 EXPECT_FALSE(FetchingURL()); 434 EXPECT_TRUE(TimerRunning()); 435 436 base::RunLoop().RunUntilIdle(); 437 ASSERT_TRUE(FetchingURL()); 438 EXPECT_FALSE(TimerRunning()); 439 440 CompleteURLFetch(net::OK, 200, NULL); 441 EXPECT_FALSE(FetchingURL()); 442 EXPECT_FALSE(TimerRunning()); 443 444 EXPECT_EQ(1, observer.num_results_received()); 445 EXPECT_EQ(RESULT_BEHIND_CAPTIVE_PORTAL, observer.captive_portal_result()); 446 } 447 448 // Checks that disabling for browser tests works as expected. 449 TEST_F(CaptivePortalServiceTest, CaptivePortalDisableForTests) { 450 Initialize(CaptivePortalService::DISABLED_FOR_TESTING); 451 RunDisabledTest(0); 452 } 453 454 // Checks that jitter gives us values in the correct range. 455 TEST_F(CaptivePortalServiceTest, CaptivePortalJitter) { 456 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 457 set_jitter_factor(0.3); 458 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100)); 459 RunTest(RESULT_INTERNET_CONNECTED, net::OK, 204, 0, NULL); 460 RunTest(RESULT_INTERNET_CONNECTED, net::OK, 204, 0, NULL); 461 462 for (int i = 0; i < 50; ++i) { 463 int interval_sec = GetTimeUntilNextRequest().InSeconds(); 464 // Allow for roundoff, though shouldn't be necessary. 465 EXPECT_LE(69, interval_sec); 466 EXPECT_LE(interval_sec, 101); 467 } 468 } 469 470 // Check a Retry-After header that contains a delay in seconds. 471 TEST_F(CaptivePortalServiceTest, CaptivePortalRetryAfterSeconds) { 472 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 473 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100)); 474 const char* retry_after = "HTTP/1.1 503 OK\nRetry-After: 101\n\n"; 475 476 // Check that Retry-After headers work both on the first request to return a 477 // result and on subsequent requests. 478 RunTest(RESULT_NO_RESPONSE, net::OK, 503, 0, retry_after); 479 RunTest(RESULT_NO_RESPONSE, net::OK, 503, 101, retry_after); 480 RunTest(RESULT_INTERNET_CONNECTED, net::OK, 204, 101, NULL); 481 482 // Make sure that there's no effect on the next captive portal check after 483 // login. 484 EXPECT_EQ(base::TimeDelta::FromSeconds(0), GetTimeUntilNextRequest()); 485 } 486 487 // Check that the RecheckPolicy is still respected on 503 responses with 488 // Retry-After headers. 489 TEST_F(CaptivePortalServiceTest, CaptivePortalRetryAfterSecondsTooShort) { 490 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 491 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100)); 492 const char* retry_after = "HTTP/1.1 503 OK\nRetry-After: 99\n\n"; 493 494 RunTest(RESULT_NO_RESPONSE, net::OK, 503, 0, retry_after); 495 // Normally would be no delay on the first check with a new result. 496 RunTest(RESULT_NO_RESPONSE, net::OK, 503, 99, retry_after); 497 EXPECT_EQ(base::TimeDelta::FromSeconds(100), GetTimeUntilNextRequest()); 498 } 499 500 // Check a Retry-After header that contains a date. 501 TEST_F(CaptivePortalServiceTest, CaptivePortalRetryAfterDate) { 502 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 503 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(50)); 504 505 // base has a function to get a time in the right format from a string, but 506 // not the other way around. 507 base::Time start_time; 508 ASSERT_TRUE( 509 base::Time::FromString("Tue, 17 Apr 2012 18:02:00 GMT", &start_time)); 510 SetTime(start_time); 511 512 RunTest(RESULT_NO_RESPONSE, 513 net::OK, 514 503, 515 0, 516 "HTTP/1.1 503 OK\nRetry-After: Tue, 17 Apr 2012 18:02:51 GMT\n\n"); 517 EXPECT_EQ(base::TimeDelta::FromSeconds(51), GetTimeUntilNextRequest()); 518 } 519 520 } // namespace captive_portal 521