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/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" 26 27using captive_portal::CaptivePortalDetectorTestBase; 28using captive_portal::CaptivePortalResult; 29 30namespace { 31 32// An observer watches the CaptivePortalDetector. It tracks the last 33// received result and the total number of received results. 34class CaptivePortalObserver : public content::NotificationObserver { 35 public: 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), 41 profile_(profile), 42 captive_portal_service_(captive_portal_service) { 43 registrar_.Add(this, 44 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, 45 content::Source<Profile>(profile_)); 46 } 47 48 CaptivePortalResult captive_portal_result() const { 49 return captive_portal_result_; 50 } 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 CaptivePortalResult 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 85class 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(CaptivePortalResult 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(captive_portal::RESULT_INTERNET_CONNECTED, 208 observer.captive_portal_result()); 209 } 210 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, 215 int net_error, 216 int status_code) { 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); 225 } 226 227 // Changes test time for the service and service's captive portal 228 // detector. 229 void AdvanceTime(const base::TimeDelta& delta) { 230 service()->advance_time_ticks_for_testing(delta); 231 CaptivePortalDetectorTestBase::AdvanceTime(delta); 232 } 233 234 bool TimerRunning() { 235 return service()->TimerRunning(); 236 } 237 238 base::TimeDelta GetTimeUntilNextRequest() { 239 return service()->backoff_entry_->GetTimeUntilRelease(); 240 } 241 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(); 246 } 247 248 void set_initial_backoff_portal(base::TimeDelta initial_backoff_portal) { 249 service()->recheck_policy().initial_backoff_portal_ms = 250 initial_backoff_portal.InMilliseconds(); 251 } 252 253 void set_maximum_backoff(base::TimeDelta maximum_backoff) { 254 service()->recheck_policy().backoff_policy.maximum_backoff_ms = 255 maximum_backoff.InMilliseconds(); 256 } 257 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; 261 } 262 263 void set_multiply_factor(double multiply_factor) { 264 service()->recheck_policy().backoff_policy.multiply_factor = 265 multiply_factor; 266 } 267 268 void set_jitter_factor(double jitter_factor) { 269 service()->recheck_policy().backoff_policy.jitter_factor = jitter_factor; 270 } 271 272 TestingProfile* profile() { return profile_.get(); } 273 274 CaptivePortalService* service() { return service_.get(); } 275 276 private: 277 // Stores the initial CaptivePortalService::TestingState so it can be restored 278 // after the test. 279 const CaptivePortalService::TestingState old_captive_portal_testing_state_; 280 281 content::TestBrowserThreadBundle thread_bundle_; 282 283 // Note that the construction order of these matters. 284 scoped_ptr<TestingProfile> profile_; 285 scoped_ptr<CaptivePortalService> service_; 286}; 287 288// Verify that an observer doesn't get messages from the wrong profile. 289TEST_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()); 295 296 RunTest(captive_portal::RESULT_INTERNET_CONNECTED, net::OK, 204, 0, NULL); 297 EXPECT_EQ(0, observer2.num_results_received()); 298} 299 300// Checks exponential backoff when the Internet is connected. 301TEST_F(CaptivePortalServiceTest, CaptivePortalRecheckInternetConnected) { 302 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 303 304 // This value should have no effect on this test, until the end. 305 set_initial_backoff_portal(base::TimeDelta::FromSeconds(1)); 306 307 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100)); 308 RunBackoffTest(captive_portal::RESULT_INTERNET_CONNECTED, net::OK, 204); 309 310 // Make sure that getting a new result resets the timer. 311 RunTest( 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); 316} 317 318// Checks exponential backoff when there's an HTTP error. 319TEST_F(CaptivePortalServiceTest, CaptivePortalRecheckError) { 320 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 321 322 // This value should have no effect on this test. 323 set_initial_backoff_portal(base::TimeDelta::FromDays(1)); 324 325 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(100)); 326 RunBackoffTest(captive_portal::RESULT_NO_RESPONSE, net::OK, 500); 327 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); 332} 333 334// Checks exponential backoff when there's a captive portal. 335TEST_F(CaptivePortalServiceTest, CaptivePortalRecheckBehindPortal) { 336 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 337 338 // This value should have no effect on this test, until the end. 339 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(250)); 340 341 set_initial_backoff_portal(base::TimeDelta::FromSeconds(100)); 342 RunBackoffTest(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL, net::OK, 200); 343 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); 348} 349 350// Check that everything works as expected when captive portal checking is 351// disabled, including throttling. Then enables it again and runs another test. 352TEST_F(CaptivePortalServiceTest, CaptivePortalPrefDisabled) { 353 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 354 355 // This value should have no effect on this test. 356 set_initial_backoff_no_portal(base::TimeDelta::FromDays(1)); 357 358 set_initial_backoff_portal(base::TimeDelta::FromSeconds(100)); 359 360 EnableCaptivePortalDetectionPreference(false); 361 362 RunDisabledTest(0); 363 for (int i = 0; i < 6; ++i) 364 RunDisabledTest(100); 365 366 EnableCaptivePortalDetectionPreference(true); 367 368 RunTest(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL, net::OK, 200, 0, NULL); 369} 370 371// Check that disabling the captive portal service while a check is running 372// works. 373TEST_F(CaptivePortalServiceTest, CaptivePortalPrefDisabledWhileRunning) { 374 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 375 CaptivePortalObserver observer(profile(), service()); 376 377 // Needed to create the URLFetcher, even if it never returns any results. 378 service()->DetectCaptivePortal(); 379 380 base::RunLoop().RunUntilIdle(); 381 EXPECT_TRUE(FetchingURL()); 382 EXPECT_FALSE(TimerRunning()); 383 384 EnableCaptivePortalDetectionPreference(false); 385 EXPECT_FALSE(FetchingURL()); 386 EXPECT_TRUE(TimerRunning()); 387 EXPECT_EQ(0, observer.num_results_received()); 388 389 base::RunLoop().RunUntilIdle(); 390 391 EXPECT_FALSE(FetchingURL()); 392 EXPECT_FALSE(TimerRunning()); 393 EXPECT_EQ(1, observer.num_results_received()); 394 395 EXPECT_EQ(captive_portal::RESULT_INTERNET_CONNECTED, 396 observer.captive_portal_result()); 397} 398 399// Check that disabling the captive portal service while a check is pending 400// works. 401TEST_F(CaptivePortalServiceTest, CaptivePortalPrefDisabledWhilePending) { 402 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 403 set_initial_backoff_no_portal(base::TimeDelta::FromDays(1)); 404 405 CaptivePortalObserver observer(profile(), service()); 406 service()->DetectCaptivePortal(); 407 EXPECT_FALSE(FetchingURL()); 408 EXPECT_TRUE(TimerRunning()); 409 410 EnableCaptivePortalDetectionPreference(false); 411 EXPECT_FALSE(FetchingURL()); 412 EXPECT_TRUE(TimerRunning()); 413 EXPECT_EQ(0, observer.num_results_received()); 414 415 base::RunLoop().RunUntilIdle(); 416 417 EXPECT_FALSE(FetchingURL()); 418 EXPECT_FALSE(TimerRunning()); 419 EXPECT_EQ(1, observer.num_results_received()); 420 421 EXPECT_EQ(captive_portal::RESULT_INTERNET_CONNECTED, 422 observer.captive_portal_result()); 423} 424 425// Check that disabling the captive portal service while a check is pending 426// works. 427TEST_F(CaptivePortalServiceTest, CaptivePortalPrefEnabledWhilePending) { 428 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 429 430 EnableCaptivePortalDetectionPreference(false); 431 RunDisabledTest(0); 432 433 CaptivePortalObserver observer(profile(), service()); 434 service()->DetectCaptivePortal(); 435 EXPECT_FALSE(FetchingURL()); 436 EXPECT_TRUE(TimerRunning()); 437 438 EnableCaptivePortalDetectionPreference(true); 439 EXPECT_FALSE(FetchingURL()); 440 EXPECT_TRUE(TimerRunning()); 441 442 base::RunLoop().RunUntilIdle(); 443 ASSERT_TRUE(FetchingURL()); 444 EXPECT_FALSE(TimerRunning()); 445 446 CompleteURLFetch(net::OK, 200, NULL); 447 EXPECT_FALSE(FetchingURL()); 448 EXPECT_FALSE(TimerRunning()); 449 450 EXPECT_EQ(1, observer.num_results_received()); 451 EXPECT_EQ(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL, 452 observer.captive_portal_result()); 453} 454 455// Checks that disabling for browser tests works as expected. 456TEST_F(CaptivePortalServiceTest, CaptivePortalDisableForTests) { 457 Initialize(CaptivePortalService::DISABLED_FOR_TESTING); 458 RunDisabledTest(0); 459} 460 461// Checks that jitter gives us values in the correct range. 462TEST_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); 468 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); 474 } 475} 476 477// Check a Retry-After header that contains a delay in seconds. 478TEST_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"; 482 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); 488 489 // Make sure that there's no effect on the next captive portal check after 490 // login. 491 EXPECT_EQ(base::TimeDelta::FromSeconds(0), GetTimeUntilNextRequest()); 492} 493 494// Check that the RecheckPolicy is still respected on 503 responses with 495// Retry-After headers. 496TEST_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"; 500 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()); 505} 506 507// Check a Retry-After header that contains a date. 508TEST_F(CaptivePortalServiceTest, CaptivePortalRetryAfterDate) { 509 Initialize(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); 510 set_initial_backoff_no_portal(base::TimeDelta::FromSeconds(50)); 511 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; 515 ASSERT_TRUE( 516 base::Time::FromString("Tue, 17 Apr 2012 18:02:00 GMT", &start_time)); 517 SetTime(start_time); 518 519 RunTest(captive_portal::RESULT_NO_RESPONSE, 520 net::OK, 521 503, 522 0, 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()); 525} 526