blob: 26c249e90c93a12141e3ec6a6fa15e915d958829 [file] [log] [blame]
// Copyright 2017 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 "chrome/browser/signin/force_signin_verifier.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "services/identity/public/cpp/identity_test_environment.h"
#include "services/network/test/test_network_connection_tracker.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
class MockForceSigninVerifier : public ForceSigninVerifier {
public:
explicit MockForceSigninVerifier(Profile* profile)
: ForceSigninVerifier(profile) {}
bool IsDelayTaskPosted() { return GetOneShotTimerForTesting()->IsRunning(); }
int FailureCount() { return GetBackoffEntryForTesting()->failure_count(); }
identity::PrimaryAccountAccessTokenFetcher* access_token_fetcher() {
return GetAccessTokenFetcherForTesting();
}
MOCK_METHOD0(CloseAllBrowserWindows, void(void));
};
class ForceSigninVerifierTest
: public ::testing::Test,
public network::NetworkConnectionTracker::NetworkConnectionObserver {
public:
void SetUp() override {
profile_ = IdentityTestEnvironmentProfileAdaptor::
CreateProfileForIdentityTestEnvironment();
identity_test_env_profile_adaptor_ =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile_.get());
account_info_ =
identity_test_env()->MakePrimaryAccountAvailable("email@test.com");
}
void TearDown() override { verifier_.reset(); }
void OnConnectionChanged(network::mojom::ConnectionType type) override {
wait_for_network_type_change_.QuitWhenIdle();
}
identity::IdentityTestEnvironment* identity_test_env() {
return identity_test_env_profile_adaptor_->identity_test_env();
}
std::unique_ptr<MockForceSigninVerifier> verifier_;
content::TestBrowserThreadBundle bundle_;
std::unique_ptr<TestingProfile> profile_;
std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
identity_test_env_profile_adaptor_;
AccountInfo account_info_;
base::RunLoop wait_for_network_type_change_;
base::RunLoop wait_for_network_type_async_return_;
GoogleServiceAuthError persistent_error_ = GoogleServiceAuthError(
GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS);
GoogleServiceAuthError transient_error_ =
GoogleServiceAuthError(GoogleServiceAuthError::State::CONNECTION_FAILED);
base::HistogramTester histogram_tester_;
};
TEST_F(ForceSigninVerifierTest, OnGetTokenSuccess) {
verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
ASSERT_NE(nullptr, verifier_->access_token_fetcher());
ASSERT_FALSE(verifier_->HasTokenBeenVerified());
ASSERT_FALSE(verifier_->IsDelayTaskPosted());
EXPECT_CALL(*verifier_.get(), CloseAllBrowserWindows()).Times(0);
identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
account_info_.account_id, /*token=*/"", base::Time());
ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
ASSERT_TRUE(verifier_->HasTokenBeenVerified());
ASSERT_FALSE(verifier_->IsDelayTaskPosted());
ASSERT_EQ(0, verifier_->FailureCount());
histogram_tester_.ExpectBucketCount(kForceSigninVerificationMetricsName, 1,
1);
histogram_tester_.ExpectTotalCount(
kForceSigninVerificationSuccessTimeMetricsName, 1);
histogram_tester_.ExpectTotalCount(
kForceSigninVerificationFailureTimeMetricsName, 0);
}
TEST_F(ForceSigninVerifierTest, OnGetTokenPersistentFailure) {
verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
ASSERT_NE(nullptr, verifier_->access_token_fetcher());
ASSERT_FALSE(verifier_->HasTokenBeenVerified());
ASSERT_FALSE(verifier_->IsDelayTaskPosted());
EXPECT_CALL(*verifier_.get(), CloseAllBrowserWindows()).Times(1);
identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
persistent_error_);
ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
ASSERT_TRUE(verifier_->HasTokenBeenVerified());
ASSERT_FALSE(verifier_->IsDelayTaskPosted());
ASSERT_EQ(0, verifier_->FailureCount());
histogram_tester_.ExpectBucketCount(kForceSigninVerificationMetricsName, 1,
1);
histogram_tester_.ExpectTotalCount(
kForceSigninVerificationSuccessTimeMetricsName, 0);
histogram_tester_.ExpectTotalCount(
kForceSigninVerificationFailureTimeMetricsName, 1);
}
TEST_F(ForceSigninVerifierTest, OnGetTokenTransientFailure) {
verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
ASSERT_NE(nullptr, verifier_->access_token_fetcher());
ASSERT_FALSE(verifier_->HasTokenBeenVerified());
ASSERT_FALSE(verifier_->IsDelayTaskPosted());
EXPECT_CALL(*verifier_.get(), CloseAllBrowserWindows()).Times(0);
identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
transient_error_);
ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
ASSERT_FALSE(verifier_->HasTokenBeenVerified());
ASSERT_TRUE(verifier_->IsDelayTaskPosted());
ASSERT_EQ(1, verifier_->FailureCount());
histogram_tester_.ExpectBucketCount(kForceSigninVerificationMetricsName, 1,
1);
histogram_tester_.ExpectTotalCount(
kForceSigninVerificationSuccessTimeMetricsName, 0);
histogram_tester_.ExpectTotalCount(
kForceSigninVerificationFailureTimeMetricsName, 0);
}
TEST_F(ForceSigninVerifierTest, OnLostConnection) {
verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
transient_error_);
ASSERT_EQ(1, verifier_->FailureCount());
ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
ASSERT_TRUE(verifier_->IsDelayTaskPosted());
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_NONE);
wait_for_network_type_change_.Run();
content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
ASSERT_EQ(0, verifier_->FailureCount());
ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
ASSERT_FALSE(verifier_->IsDelayTaskPosted());
}
TEST_F(ForceSigninVerifierTest, OnReconnected) {
verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
transient_error_);
ASSERT_EQ(1, verifier_->FailureCount());
ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
ASSERT_TRUE(verifier_->IsDelayTaskPosted());
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_WIFI);
wait_for_network_type_change_.Run();
content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
ASSERT_EQ(0, verifier_->FailureCount());
ASSERT_NE(nullptr, verifier_->access_token_fetcher());
ASSERT_FALSE(verifier_->IsDelayTaskPosted());
}
TEST_F(ForceSigninVerifierTest, GetNetworkStatusAsync) {
network::TestNetworkConnectionTracker::GetInstance()->SetRespondSynchronously(
false);
verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
// There is no network type at first.
ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
// Waiting for the network type returns.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, wait_for_network_type_async_return_.QuitClosure());
wait_for_network_type_async_return_.Run();
// Get the type and send the request.
ASSERT_NE(nullptr, verifier_->access_token_fetcher());
}
TEST_F(ForceSigninVerifierTest, LaunchVerifierWithoutNetwork) {
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_NONE);
network::TestNetworkConnectionTracker::GetInstance()->SetRespondSynchronously(
false);
verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
// There is no network type.
ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
// Waiting for the network type returns.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, wait_for_network_type_async_return_.QuitClosure());
wait_for_network_type_async_return_.Run();
// Get the type, there is no network connection, don't send the request.
ASSERT_EQ(nullptr, verifier_->access_token_fetcher());
// Network is resumed.
content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_WIFI);
wait_for_network_type_change_.Run();
content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
// Send the request.
ASSERT_NE(nullptr, verifier_->access_token_fetcher());
}
TEST_F(ForceSigninVerifierTest, ChangeNetworkFromWIFITo4GWithOnGoingRequest) {
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_WIFI);
network::TestNetworkConnectionTracker::GetInstance()->SetRespondSynchronously(
false);
verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
EXPECT_EQ(nullptr, verifier_->access_token_fetcher());
// Waiting for the network type returns.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, wait_for_network_type_async_return_.QuitClosure());
wait_for_network_type_async_return_.Run();
// The network type if wifi, send the request.
auto* first_request = verifier_->access_token_fetcher();
EXPECT_NE(nullptr, first_request);
// Network is changed to 4G.
content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_4G);
wait_for_network_type_change_.Run();
content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
// There is still one on-going request.
EXPECT_EQ(first_request, verifier_->access_token_fetcher());
identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
account_info_.account_id, /*token=*/"", base::Time());
}
TEST_F(ForceSigninVerifierTest, ChangeNetworkFromWIFITo4GWithFinishedRequest) {
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_WIFI);
network::TestNetworkConnectionTracker::GetInstance()->SetRespondSynchronously(
false);
verifier_ = std::make_unique<MockForceSigninVerifier>(profile_.get());
EXPECT_EQ(nullptr, verifier_->access_token_fetcher());
// Waiting for the network type returns.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, wait_for_network_type_async_return_.QuitClosure());
wait_for_network_type_async_return_.Run();
// The network type if wifi, send the request.
EXPECT_NE(nullptr, verifier_->access_token_fetcher());
// Finishes the request.
identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
account_info_.account_id, /*token=*/"", base::Time());
EXPECT_EQ(nullptr, verifier_->access_token_fetcher());
// Network is changed to 4G.
content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_4G);
wait_for_network_type_change_.Run();
content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
// No more request because it's verfied already.
EXPECT_EQ(nullptr, verifier_->access_token_fetcher());
}