blob: badce529de724d33a52cdff7f6c53a8c76c565c4 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/captive_portal/captive_portal_detector.h"
#include <memory>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "components/captive_portal/captive_portal_testing_utils.h"
#include "net/base/net_errors.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace captive_portal {
namespace {
class CaptivePortalClient {
public:
explicit CaptivePortalClient(CaptivePortalDetector* captive_portal_detector)
: num_results_received_(0) {
}
void OnPortalDetectionCompleted(
const CaptivePortalDetector::Results& results) {
results_ = results;
++num_results_received_;
}
const CaptivePortalDetector::Results& captive_portal_results() const {
return results_;
}
int num_results_received() const { return num_results_received_; }
private:
CaptivePortalDetector::Results results_;
int num_results_received_;
DISALLOW_COPY_AND_ASSIGN(CaptivePortalClient);
};
} // namespace
class CaptivePortalDetectorTest : public testing::Test,
public CaptivePortalDetectorTestBase {
public:
CaptivePortalDetectorTest() {}
~CaptivePortalDetectorTest() override {}
void SetUp() override {
detector_.reset(new CaptivePortalDetector(test_loader_factory()));
set_detector(detector_.get());
}
void TearDown() override { detector_.reset(); }
void RunTest(const CaptivePortalDetector::Results& expected_results,
int net_error,
int status_code,
const char* response_headers) {
ASSERT_FALSE(FetchingURL());
GURL url(CaptivePortalDetector::kDefaultURL);
CaptivePortalClient client(detector());
detector()->DetectCaptivePortal(
url,
base::BindOnce(&CaptivePortalClient::OnPortalDetectionCompleted,
base::Unretained(&client)),
TRAFFIC_ANNOTATION_FOR_TESTS);
ASSERT_TRUE(FetchingURL());
base::RunLoop().RunUntilIdle();
CompleteURLFetch(net_error, status_code, response_headers);
EXPECT_FALSE(FetchingURL());
EXPECT_EQ(1, client.num_results_received());
EXPECT_EQ(expected_results.result, client.captive_portal_results().result);
EXPECT_EQ(expected_results.response_code,
client.captive_portal_results().response_code);
EXPECT_EQ(expected_results.retry_after_delta,
client.captive_portal_results().retry_after_delta);
}
void RunCancelTest() {
ASSERT_FALSE(FetchingURL());
GURL url(CaptivePortalDetector::kDefaultURL);
CaptivePortalClient client(detector());
detector()->DetectCaptivePortal(
url,
base::BindOnce(&CaptivePortalClient::OnPortalDetectionCompleted,
base::Unretained(&client)),
TRAFFIC_ANNOTATION_FOR_TESTS);
ASSERT_TRUE(FetchingURL());
base::RunLoop().RunUntilIdle();
detector()->Cancel();
ASSERT_FALSE(FetchingURL());
EXPECT_EQ(0, client.num_results_received());
}
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<CaptivePortalDetector> detector_;
};
// Test that the CaptivePortalDetector returns the expected result
// codes in response to a variety of probe results.
TEST_F(CaptivePortalDetectorTest, CaptivePortalResultCodes) {
CaptivePortalDetector::Results results;
results.result = captive_portal::RESULT_INTERNET_CONNECTED;
results.response_code = 204;
RunTest(results, net::OK, 204, nullptr);
// The server may return an HTTP error when it's acting up.
results.result = captive_portal::RESULT_NO_RESPONSE;
results.response_code = 500;
RunTest(results, net::OK, 500, nullptr);
// Generic network error case.
results.result = captive_portal::RESULT_NO_RESPONSE;
results.response_code = 0;
RunTest(results, net::ERR_TIMED_OUT, 0, nullptr);
// In the general captive portal case, the portal will return a page with a
// 200 status.
results.result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL;
results.response_code = 200;
RunTest(results, net::OK, 200, nullptr);
// Some captive portals return 511 instead, to advertise their captive
// portal-ness.
results.result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL;
results.response_code = 511;
RunTest(results, net::OK, 511, nullptr);
}
// Check a Retry-After header that contains a delay in seconds.
TEST_F(CaptivePortalDetectorTest, CaptivePortalRetryAfterSeconds) {
const char* retry_after = "HTTP/1.1 503 OK\nRetry-After: 101\n\n";
CaptivePortalDetector::Results results;
// Check that Retry-After headers work both on the first request to return a
// result and on subsequent requests.
results.result = captive_portal::RESULT_NO_RESPONSE;
results.response_code = 503;
results.retry_after_delta = base::TimeDelta::FromSeconds(101);
RunTest(results, net::OK, 503, retry_after);
results.result = captive_portal::RESULT_INTERNET_CONNECTED;
results.response_code = 204;
results.retry_after_delta = base::TimeDelta();
RunTest(results, net::OK, 204, nullptr);
}
// Check a Retry-After header that contains a date.
TEST_F(CaptivePortalDetectorTest, CaptivePortalRetryAfterDate) {
const char* retry_after =
"HTTP/1.1 503 OK\nRetry-After: Tue, 17 Apr 2012 18:02:51 GMT\n\n";
CaptivePortalDetector::Results results;
// base has a function to get a time in the right format from a string, but
// not the other way around.
base::Time start_time;
ASSERT_TRUE(base::Time::FromString("Tue, 17 Apr 2012 18:02:00 GMT",
&start_time));
base::Time retry_after_time;
ASSERT_TRUE(base::Time::FromString("Tue, 17 Apr 2012 18:02:51 GMT",
&retry_after_time));
SetTime(start_time);
results.result = captive_portal::RESULT_NO_RESPONSE;
results.response_code = 503;
results.retry_after_delta = retry_after_time - start_time;
RunTest(results, net::OK, 503, retry_after);
}
// Check invalid Retry-After headers are ignored.
TEST_F(CaptivePortalDetectorTest, CaptivePortalRetryAfterInvalid) {
const char* retry_after = "HTTP/1.1 503 OK\nRetry-After: Christmas\n\n";
CaptivePortalDetector::Results results;
results.result = captive_portal::RESULT_NO_RESPONSE;
results.response_code = 503;
RunTest(results, net::OK, 503, retry_after);
}
TEST_F(CaptivePortalDetectorTest, Cancel) {
RunCancelTest();
CaptivePortalDetector::Results results;
results.result = captive_portal::RESULT_INTERNET_CONNECTED;
results.response_code = 204;
RunTest(results, net::OK, 204, nullptr);
}
} // namespace captive_portal