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(¤t_time_, &tv, ¤t_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