chromium/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl.h"

#include <memory>
#include <string>
#include <utility>

#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
#include "chrome/browser/device_identity/device_oauth2_token_service_factory.h"
#include "chrome/browser/invalidation/profile_invalidation_provider_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/ash/components/cryptohome/system_salt_getter.h"
#include "chromeos/ash/components/dbus/userdataauth/cryptohome_misc_client.h"
#include "components/invalidation/impl/fake_invalidation_handler.h"
#include "components/invalidation/impl/fake_invalidation_service.h"
#include "components/invalidation/impl/fcm_invalidation_service.h"
#include "components/invalidation/invalidation_listener.h"
#include "components/invalidation/profile_invalidation_provider.h"
#include "components/invalidation/public/invalidation_service.h"
#include "components/invalidation/public/invalidator_state.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_task_environment.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace policy {

namespace {

const char kAffiliatedUserID1[] = "[email protected]";
const char kAffiliatedUserID2[] = "[email protected]";
const char kUnaffiliatedUserID[] = "test@other_domain.test";

std::variant<std::unique_ptr<invalidation::InvalidationService>,
             std::unique_ptr<invalidation::InvalidationListener>>
CreateInvalidationServiceForSenderId(std::string, std::string, std::string) {
  std::unique_ptr<invalidation::FakeInvalidationService> invalidation_service(
      new invalidation::FakeInvalidationService);
  invalidation_service->SetInvalidatorState(
      invalidation::InvalidatorState::kDisabled);
  return invalidation_service;
}

std::unique_ptr<KeyedService> BuildProfileInvalidationProvider(
    content::BrowserContext* context) {
  return std::make_unique<invalidation::ProfileInvalidationProvider>(
      nullptr, base::BindRepeating(&CreateInvalidationServiceForSenderId));
}

void SendInvalidatorStateChangeNotification(
    invalidation::InvalidationService* service,
    invalidation::InvalidatorState state) {
  static_cast<invalidation::FCMInvalidationService*>(service)
      ->OnInvalidatorStateChange(state);
}

}  // namespace

// A simple AffiliatedInvalidationServiceProvider::Consumer that registers a
// invalidation::FakeInvalidationHandler with
// the invalidation::InvalidationService that is currently being made available.
class FakeConsumer : public AffiliatedInvalidationServiceProvider::Consumer {
 public:
  FakeConsumer(AffiliatedInvalidationServiceProviderImpl* provider,
               const std::string& invalidation_owner_name);

  FakeConsumer(const FakeConsumer&) = delete;
  FakeConsumer& operator=(const FakeConsumer&) = delete;

  ~FakeConsumer() override;

  // AffiliatedInvalidationServiceProvider::Consumer:
  void OnInvalidationServiceSet(
      invalidation::InvalidationService* invalidation_service) override;

  int GetAndClearInvalidationServiceSetCount();
  const invalidation::InvalidationService* GetInvalidationService() const;

 private:
  raw_ptr<AffiliatedInvalidationServiceProviderImpl> provider_;
  invalidation::FakeInvalidationHandler invalidation_handler_;

  int invalidation_service_set_count_ = 0;
  raw_ptr<invalidation::InvalidationService, DanglingUntriaged>
      invalidation_service_ = nullptr;
};

class AffiliatedInvalidationServiceProviderImplTest : public testing::Test {
 public:
  AffiliatedInvalidationServiceProviderImplTest();
  // testing::Test:
  void SetUp() override;
  void TearDown() override;

  // Both functions don't pass ownership of the profile. The Profile is owned
  // by the global ProfileManager.
  Profile* LogInAndReturnAffiliatedProfile(const std::string& user_id);
  Profile* LogInAndReturnNonAffiliatedProfile(const std::string& user_id);

  // Logs in as an affiliated user and indicates that the per-profile
  // invalidation service for this user connected. Verifies that this
  // invalidation service is made available to the |consumer_| and the
  // device-global invalidation service is destroyed.
  void LogInAsAffiliatedUserAndConnectInvalidationService();

  // Logs in as an unaffiliated user and indicates that the per-profile
  // invalidation service for this user connected. Verifies that this
  // invalidation service is ignored and the device-global invalidation service
  // is not destroyed.
  void LogInAsUnaffiliatedUserAndConnectInvalidationService();

  // Indicates that the device-global invalidation service connected. Verifies
  // that the |consumer_| is informed about this.
  void ConnectDeviceGlobalInvalidationService();

  // Indicates that the logged-in user's per-profile invalidation service
  // disconnected. Verifies that the |consumer_| is informed about this and a
  // device-global invalidation service is created.
  void DisconnectPerProfileInvalidationService();

  invalidation::FakeInvalidationService* GetProfileInvalidationService(
      Profile* profile,
      bool create);

 protected:
  // Ownership is not passed. The Profile is owned by the global ProfileManager.
  Profile* LogInAndReturnProfile(const std::string& user_id,
                                 bool is_affiliated);
  std::unique_ptr<AffiliatedInvalidationServiceProviderImpl> provider_;
  std::unique_ptr<FakeConsumer> consumer_;
  raw_ptr<invalidation::InvalidationService, DanglingUntriaged>
      device_invalidation_service_;
  raw_ptr<invalidation::FakeInvalidationService, DanglingUntriaged>
      profile_invalidation_service_;

 private:
  content::BrowserTaskEnvironment task_environment_;
  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
  raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged> fake_user_manager_;
  user_manager::ScopedUserManager user_manager_enabler_;
  ash::ScopedCrosSettingsTestHelper cros_settings_test_helper_;
  network::TestURLLoaderFactory test_url_loader_factory_;
  TestingProfileManager profile_manager_;
  session_manager::SessionManager session_manager_;
};

FakeConsumer::FakeConsumer(AffiliatedInvalidationServiceProviderImpl* provider,
                           const std::string& invalidation_owner_name)
    : provider_(provider), invalidation_handler_(invalidation_owner_name) {
  provider_->RegisterConsumer(this);
}

FakeConsumer::~FakeConsumer() {
  if (invalidation_service_) {
    invalidation_service_->RemoveObserver(&invalidation_handler_);
  }
  provider_->UnregisterConsumer(this);

  EXPECT_EQ(0, invalidation_service_set_count_);
}

void FakeConsumer::OnInvalidationServiceSet(
    invalidation::InvalidationService* invalidation_service) {
  ++invalidation_service_set_count_;

  if (invalidation_service_) {
    invalidation_service_->RemoveObserver(&invalidation_handler_);
  }

  invalidation_service_ = invalidation_service;

  if (invalidation_service_) {
    // Regression test for http://crbug.com/455504: The |invalidation_service|
    // was sometimes destroyed without notifying consumers and giving them a
    // chance to unregister their invalidation handlers. Register an
    // invalidation handler so that |invalidation_service| CHECK()s in its
    // destructor if this regresses.
    invalidation_service_->AddObserver(&invalidation_handler_);
  }
}

int FakeConsumer::GetAndClearInvalidationServiceSetCount() {
  const int invalidation_service_set_count = invalidation_service_set_count_;
  invalidation_service_set_count_ = 0;
  return invalidation_service_set_count;
}

const invalidation::InvalidationService* FakeConsumer::GetInvalidationService()
    const {
  return invalidation_service_;
}

AffiliatedInvalidationServiceProviderImplTest::
    AffiliatedInvalidationServiceProviderImplTest()
    : device_invalidation_service_(nullptr),
      profile_invalidation_service_(nullptr),
      fake_user_manager_(new ash::FakeChromeUserManager),
      user_manager_enabler_(base::WrapUnique(fake_user_manager_.get())),
      profile_manager_(TestingBrowserProcess::GetGlobal()) {
  cros_settings_test_helper_.InstallAttributes()->SetCloudManaged("example.com",
                                                                  "device_id");
}

void AffiliatedInvalidationServiceProviderImplTest::SetUp() {
  ash::CryptohomeMiscClient::InitializeFake();
  ash::SystemSaltGetter::Initialize();
  ASSERT_TRUE(profile_manager_.SetUp());

  DeviceOAuth2TokenServiceFactory::Initialize(
      test_url_loader_factory_.GetSafeWeakWrapper(),
      TestingBrowserProcess::GetGlobal()->local_state());

  invalidation::ProfileInvalidationProviderFactory::GetInstance()
      ->RegisterTestingFactory(
          base::BindRepeating(&BuildProfileInvalidationProvider));

  provider_ = std::make_unique<AffiliatedInvalidationServiceProviderImpl>();
}

void AffiliatedInvalidationServiceProviderImplTest::TearDown() {
  consumer_.reset();
  provider_->Shutdown();
  provider_.reset();

  invalidation::ProfileInvalidationProviderFactory::GetInstance()
      ->RegisterTestingFactory(
          invalidation::ProfileInvalidationProviderFactory::
              GlobalTestingFactory());
  DeviceOAuth2TokenServiceFactory::Shutdown();
  ash::SystemSaltGetter::Shutdown();
  ash::CryptohomeMiscClient::Shutdown();
}

Profile*
AffiliatedInvalidationServiceProviderImplTest::LogInAndReturnAffiliatedProfile(
    const std::string& user_id) {
  return LogInAndReturnProfile(user_id, true);
}

Profile* AffiliatedInvalidationServiceProviderImplTest::
    LogInAndReturnNonAffiliatedProfile(const std::string& user_id) {
  return LogInAndReturnProfile(user_id, false);
}

Profile* AffiliatedInvalidationServiceProviderImplTest::LogInAndReturnProfile(
    const std::string& user_id,
    bool is_affiliated) {
  TestingProfile* profile = profile_manager_.CreateTestingProfile(user_id);
  AccountId account_id = AccountId::FromUserEmail(user_id);
  fake_user_manager_->AddUserWithAffiliationAndTypeAndProfile(
      account_id, is_affiliated, user_manager::UserType::kRegular, profile);
  session_manager_.NotifyUserProfileLoaded(account_id);
  return profile;
}

void AffiliatedInvalidationServiceProviderImplTest::
    LogInAsAffiliatedUserAndConnectInvalidationService() {
  // Log in as an affiliated user.
  Profile* profile = LogInAndReturnAffiliatedProfile(kAffiliatedUserID1);
  EXPECT_TRUE(profile);

  // Verify that a per-profile invalidation service has been created.
  profile_invalidation_service_ =
      GetProfileInvalidationService(profile, false /* create */);
  ASSERT_TRUE(profile_invalidation_service_);

  // Verify that the device-global invalidation service still exists.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());

  // Indicate that the per-profile invalidation service has connected. Verify
  // that the consumer is informed about this.
  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());
  profile_invalidation_service_->SetInvalidatorState(
      invalidation::InvalidatorState::kEnabled);
  EXPECT_EQ(1, consumer_->GetAndClearInvalidationServiceSetCount());
  EXPECT_EQ(profile_invalidation_service_, consumer_->GetInvalidationService());

  // Verify that the device-global invalidation service has been destroyed.
  EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());
}

void AffiliatedInvalidationServiceProviderImplTest::
    LogInAsUnaffiliatedUserAndConnectInvalidationService() {
  // Log in as an unaffiliated user.
  Profile* profile = LogInAndReturnNonAffiliatedProfile(kUnaffiliatedUserID);
  EXPECT_TRUE(profile);

  // Verify that a per-profile invalidation service has been created.
  profile_invalidation_service_ =
      GetProfileInvalidationService(profile, false /* create */);
  ASSERT_TRUE(profile_invalidation_service_);

  // Verify that the device-global invalidation service still exists.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());

  // Indicate that the per-profile invalidation service has connected. Verify
  // that the consumer is not called back.
  profile_invalidation_service_->SetInvalidatorState(
      invalidation::InvalidatorState::kEnabled);
  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());

  // Verify that the device-global invalidation service still exists.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());
}

void AffiliatedInvalidationServiceProviderImplTest::
    ConnectDeviceGlobalInvalidationService() {
  // Verify that a device-global invalidation service has been created.
  device_invalidation_service_ =
      provider_->GetDeviceInvalidationServiceForTest();
  ASSERT_TRUE(device_invalidation_service_);

  // Indicate that the device-global invalidation service has connected. Verify
  // that the consumer is informed about this.
  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());
  SendInvalidatorStateChangeNotification(
      device_invalidation_service_, invalidation::InvalidatorState::kEnabled);
  EXPECT_EQ(1, consumer_->GetAndClearInvalidationServiceSetCount());
  EXPECT_EQ(device_invalidation_service_, consumer_->GetInvalidationService());
}

void AffiliatedInvalidationServiceProviderImplTest::
    DisconnectPerProfileInvalidationService() {
  ASSERT_TRUE(profile_invalidation_service_);

  // Indicate that the per-profile invalidation service has disconnected. Verify
  // that the consumer is informed about this.
  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());
  profile_invalidation_service_->SetInvalidatorState(
      invalidation::InvalidatorState::kDisabled);
  EXPECT_EQ(1, consumer_->GetAndClearInvalidationServiceSetCount());
  EXPECT_EQ(nullptr, consumer_->GetInvalidationService());

  // Verify that a device-global invalidation service has been created.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());
}

invalidation::FakeInvalidationService*
AffiliatedInvalidationServiceProviderImplTest::GetProfileInvalidationService(
    Profile* profile,
    bool create) {
  invalidation::ProfileInvalidationProvider* invalidation_provider;
  invalidation_provider =
      static_cast<invalidation::ProfileInvalidationProvider*>(
          invalidation::ProfileInvalidationProviderFactory::GetInstance()
              ->GetServiceForBrowserContext(profile, create));
  if (!invalidation_provider)
    return nullptr;
  auto invalidation_service =
      invalidation_provider->GetInvalidationServiceOrListener(
          kPolicyFCMInvalidationSenderID,
          invalidation::InvalidationListener::kProjectNumberEnterprise);
  CHECK(std::holds_alternative<invalidation::InvalidationService*>(
      invalidation_service));
  return static_cast<invalidation::FakeInvalidationService*>(
      std::get<invalidation::InvalidationService*>(invalidation_service));
}

// No consumers are registered with the
// AffiliatedInvalidationServiceProviderImpl. Verifies that no device-global
// invalidation service is created, whether an affiliated user is logged in or
// not.
TEST_F(AffiliatedInvalidationServiceProviderImplTest, NoConsumers) {
  // Verify that no device-global invalidation service has been created.
  EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());

  // Log in as an affiliated user.
  EXPECT_TRUE(LogInAndReturnAffiliatedProfile(kAffiliatedUserID1));

  // Verify that no device-global invalidation service has been created.
  EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());
}

// Verifies that when no connected invalidation service is available for use,
// none is made available to consumers.
TEST_F(AffiliatedInvalidationServiceProviderImplTest,
       NoInvalidationServiceAvailable) {
  // Register a consumer. Verify that the consumer is not called back
  // immediately as no connected invalidation service exists yet.
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());
}

// A consumer is registered with the AffiliatedInvalidationServiceProviderImpl.
// Verifies that when no per-profile invalidation service belonging to an
// affiliated user is available, a device-global invalidation service is
// created. Further verifies that when the device-global invalidation service
// connects, it is made available to the consumer.
TEST_F(AffiliatedInvalidationServiceProviderImplTest,
       UseDeviceInvalidationService) {
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");

  // Indicate that the device-global invalidation service connected. Verify that
  // that the consumer is informed about this.
  ConnectDeviceGlobalInvalidationService();

  // Indicate that the device-global invalidation service has disconnected.
  // Verify that the consumer is informed about this.
  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());
  SendInvalidatorStateChangeNotification(
      device_invalidation_service_, invalidation::InvalidatorState::kDisabled);
  EXPECT_EQ(1, consumer_->GetAndClearInvalidationServiceSetCount());
  EXPECT_EQ(nullptr, consumer_->GetInvalidationService());

  // Verify that the device-global invalidation service still exists.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());
}

// A consumer is registered with the AffiliatedInvalidationServiceProviderImpl.
// Verifies that when a per-profile invalidation service belonging to an
// affiliated user connects, it is made available to the consumer.
TEST_F(AffiliatedInvalidationServiceProviderImplTest,
       UseAffiliatedProfileInvalidationService) {
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");

  // Verify that a device-global invalidation service has been created.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());

  // Log in as an affiliated user and indicate that the per-profile invalidation
  // service for this user connected. Verify that this invalidation service is
  // made available to the |consumer_| and the device-global invalidation
  // service is destroyed.
  LogInAsAffiliatedUserAndConnectInvalidationService();

  // Indicate that the logged-in user's per-profile invalidation service
  // disconnected. Verify that the consumer is informed about this and a
  // device-global invalidation service is created.
  DisconnectPerProfileInvalidationService();
}

// A consumer is registered with the AffiliatedInvalidationServiceProviderImpl.
// Verifies that when a per-profile invalidation service belonging to an
// unaffiliated user connects, it is ignored.
TEST_F(AffiliatedInvalidationServiceProviderImplTest,
       DoNotUseUnaffiliatedProfileInvalidationService) {
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");

  // Verify that a device-global invalidation service has been created.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());

  // Log in as an unaffiliated user and indicate that the per-profile
  // invalidation service for this user connected. Verify that this invalidation
  // service is ignored and the device-global invalidation service is not
  // destroyed.
  LogInAsUnaffiliatedUserAndConnectInvalidationService();
}

// A consumer is registered with the AffiliatedInvalidationServiceProviderImpl.
// A device-global invalidation service exists, is connected and is made
// available to the consumer. Verifies that when a per-profile invalidation
// service belonging to an affiliated user connects, it is made available to the
// consumer instead and the device-global invalidation service is destroyed.
TEST_F(AffiliatedInvalidationServiceProviderImplTest,
       SwitchToAffiliatedProfileInvalidationService) {
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");

  // Indicate that the device-global invalidation service connected. Verify that
  // that the consumer is informed about this.
  ConnectDeviceGlobalInvalidationService();

  // Log in as an affiliated user and indicate that the per-profile invalidation
  // service for this user connected. Verify that this invalidation service is
  // made available to the |consumer_| and the device-global invalidation
  // service is destroyed.
  LogInAsAffiliatedUserAndConnectInvalidationService();
}

// Verifies that every InvalidationService state except
// |invalidation::InvalidatorState::kEnabled| are treated as disconnected.
TEST_F(AffiliatedInvalidationServiceProviderImplTest,
       FlipInvalidationServiceState) {
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");

  // Create and make |profile_invalidation_service_| enabled.
  LogInAsAffiliatedUserAndConnectInvalidationService();

  profile_invalidation_service_->SetInvalidatorState(
      invalidation::InvalidatorState::kDisabled);
  EXPECT_EQ(1, consumer_->GetAndClearInvalidationServiceSetCount());
  EXPECT_FALSE(consumer_->GetInvalidationService());

  profile_invalidation_service_->SetInvalidatorState(
      invalidation::InvalidatorState::kEnabled);
  EXPECT_EQ(1, consumer_->GetAndClearInvalidationServiceSetCount());
  EXPECT_EQ(profile_invalidation_service_, consumer_->GetInvalidationService());
}

// A consumer is registered with the AffiliatedInvalidationServiceProviderImpl.
// A device-global invalidation service exists, is connected and is made
// available to the consumer. Verifies that when a per-profile invalidation
// service belonging to an unaffiliated user connects, it is ignored and the
// device-global invalidation service continues to be made available to the
// consumer.
TEST_F(AffiliatedInvalidationServiceProviderImplTest,
       DoNotSwitchToUnaffiliatedProfileInvalidationService) {
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");

  // Indicate that the device-global invalidation service connected. Verify that
  // that the consumer is informed about this.
  ConnectDeviceGlobalInvalidationService();

  // Log in as an unaffiliated user and indicate that the per-profile
  // invalidation service for this user connected. Verify that this invalidation
  // service is ignored and the device-global invalidation service is not
  // destroyed.
  LogInAsUnaffiliatedUserAndConnectInvalidationService();
}

// A consumer is registered with the AffiliatedInvalidationServiceProviderImpl.
// A per-profile invalidation service belonging to an affiliated user exists, is
// connected and is made available to the consumer. Verifies that when the
// per-profile invalidation service disconnects, a device-global invalidation
// service is created. Further verifies that when the device-global invalidation
// service connects, it is made available to the consumer.
TEST_F(AffiliatedInvalidationServiceProviderImplTest,
       SwitchToDeviceInvalidationService) {
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");

  // Verify that a device-global invalidation service has been created.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());

  // Log in as an affiliated user and indicate that the per-profile invalidation
  // service for this user connected. Verify that this invalidation service is
  // made available to the |consumer_| and the device-global invalidation
  // service is destroyed.
  LogInAsAffiliatedUserAndConnectInvalidationService();

  // Indicate that the logged-in user's per-profile invalidation service
  // disconnected. Verify that the consumer is informed about this and a
  // device-global invalidation service is created.
  DisconnectPerProfileInvalidationService();

  // Indicate that the device-global invalidation service connected. Verify that
  // that the consumer is informed about this.
  ConnectDeviceGlobalInvalidationService();
}

// A consumer is registered with the AffiliatedInvalidationServiceProviderImpl.
// A per-profile invalidation service belonging to a first affiliated user
// exists, is connected and is made available to the consumer. A per-profile
// invalidation service belonging to a second affiliated user also exists and is
// connected. Verifies that when the per-profile invalidation service belonging
// to the first user disconnects, the per-profile invalidation service belonging
// to the second user is made available to the consumer instead.
TEST_F(AffiliatedInvalidationServiceProviderImplTest,
       SwitchBetweenAffiliatedProfileInvalidationServices) {
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");

  // Verify that a device-global invalidation service has been created.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());

  // Log in as a first affiliated user and indicate that the per-profile
  // invalidation service for this user connected. Verify that this invalidation
  // service is made available to the |consumer_| and the device-global
  // invalidation service is destroyed.
  LogInAsAffiliatedUserAndConnectInvalidationService();

  // Log in as a second affiliated user.
  Profile* second_profile = LogInAndReturnAffiliatedProfile(kAffiliatedUserID2);
  EXPECT_TRUE(second_profile);

  // Verify that the device-global invalidation service still does not exist.
  EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());

  // Verify that a per-profile invalidation service for the second user has been
  // created.
  invalidation::FakeInvalidationService* second_profile_invalidation_service =
      GetProfileInvalidationService(second_profile, false /* create */);
  ASSERT_TRUE(second_profile_invalidation_service);

  // Indicate that the second user's per-profile invalidation service has
  // connected. Verify that the consumer is not called back.
  second_profile_invalidation_service->SetInvalidatorState(
      invalidation::InvalidatorState::kEnabled);
  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());

  // Indicate that the first user's per-profile invalidation service has
  // disconnected. Verify that the consumer is informed that the second user's
  // per-profile invalidation service should be used instead of the first
  // user's.
  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());
  profile_invalidation_service_->SetInvalidatorState(
      invalidation::InvalidatorState::kDisabled);
  EXPECT_EQ(1, consumer_->GetAndClearInvalidationServiceSetCount());
  EXPECT_EQ(second_profile_invalidation_service,
            consumer_->GetInvalidationService());

  // Verify that the device-global invalidation service still does not exist.
  EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());
}

// A consumer is registered with the AffiliatedInvalidationServiceProviderImpl.
// A device-global invalidation service exists, is connected and is made
// available to the consumer. Verifies that when a second consumer registers,
// the device-global invalidation service is made available to it as well.
// Further verifies that when the first consumer unregisters, the device-global
// invalidation service is not destroyed and remains available to the second
// consumer. Further verifies that when the second consumer also unregisters,
// the device-global invalidation service is destroyed.
TEST_F(AffiliatedInvalidationServiceProviderImplTest, MultipleConsumers) {
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");

  // Indicate that the device-global invalidation service connected. Verify that
  // that the consumer is informed about this.
  ConnectDeviceGlobalInvalidationService();

  // Register a second consumer. Verify that the consumer is called back
  // immediately as a connected invalidation service is available.
  std::unique_ptr<FakeConsumer> second_consumer(
      new FakeConsumer(provider_.get(), "second_consumer"));
  EXPECT_EQ(1, second_consumer->GetAndClearInvalidationServiceSetCount());
  EXPECT_EQ(device_invalidation_service_,
            second_consumer->GetInvalidationService());

  // Unregister the first consumer.
  consumer_.reset();

  // Verify that the device-global invalidation service still exists.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());

  // Unregister the second consumer.
  second_consumer.reset();

  // Verify that the device-global invalidation service has been destroyed.
  EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());
}

// A consumer is registered with the AffiliatedInvalidationServiceProviderImpl.
// A per-profile invalidation service belonging to a first affiliated user
// exists, is connected and is made available to the consumer. Verifies that
// when the provider is shut down, the consumer is informed that no
// invalidation service is available for use anymore. Also verifies that no
// device-global invalidation service is created and a per-profile invalidation
// service belonging to a second affiliated user that subsequently connects is
// ignored.
TEST_F(AffiliatedInvalidationServiceProviderImplTest, NoServiceAfterShutdown) {
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");

  // Verify that a device-global invalidation service has been created.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());

  // Log in as a first affiliated user and indicate that the per-profile
  // invalidation service for this user connected. Verify that this invalidation
  // service is made available to the |consumer_| and the device-global
  // invalidation service is destroyed.
  LogInAsAffiliatedUserAndConnectInvalidationService();

  // Shut down the |provider_|. Verify that the |consumer_| is informed that no
  // invalidation service is available for use anymore.
  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());
  provider_->Shutdown();
  EXPECT_EQ(1, consumer_->GetAndClearInvalidationServiceSetCount());
  EXPECT_EQ(nullptr, consumer_->GetInvalidationService());

  // Verify that the device-global invalidation service still does not exist.
  EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());

  // Log in as a second affiliated user.
  Profile* second_profile = LogInAndReturnAffiliatedProfile(kAffiliatedUserID2);
  EXPECT_TRUE(second_profile);

  // Verify that the device-global invalidation service still does not exist.
  EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());

  // Create a per-profile invalidation service for the second user.
  invalidation::FakeInvalidationService* second_profile_invalidation_service =
      GetProfileInvalidationService(second_profile, true /* create */);
  ASSERT_TRUE(second_profile_invalidation_service);

  // Indicate that the second user's per-profile invalidation service has
  // connected. Verify that the consumer is not called back.
  second_profile_invalidation_service->SetInvalidatorState(
      invalidation::InvalidatorState::kEnabled);
  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());

  // Verify that the device-global invalidation service still does not exist.
  EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());
}

// A consumer is registered with the AffiliatedInvalidationServiceProviderImpl.
// A device-global invalidation service exists, is connected and is made
// available to the consumer. Verifies that when the provider is shut down, the
// consumer is informed that no invalidation service is available for use
// anymore before the device-global invalidation service is destroyed.
// This is a regression test for http://crbug.com/455504.
TEST_F(AffiliatedInvalidationServiceProviderImplTest,
       ConnectedDeviceGlobalInvalidationServiceOnShutdown) {
  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");

  // Verify that a device-global invalidation service has been created.
  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());

  // Indicate that the device-global invalidation service connected. Verify that
  // that the consumer is informed about this.
  ConnectDeviceGlobalInvalidationService();

  // Shut down the |provider_|. Verify that the |consumer_| is informed that no
  // invalidation service is available for use anymore. This also serves as a
  // regression test which verifies that the invalidation service is not
  // destroyed until the |consumer_| has been informed: If the invalidation
  // service was destroyed too early, the |consumer_| would still be registered
  // as an observer and the invalidation service's destructor would DCHECK().
  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());
  provider_->Shutdown();
  EXPECT_EQ(1, consumer_->GetAndClearInvalidationServiceSetCount());
  EXPECT_EQ(nullptr, consumer_->GetInvalidationService());

  // Verify that the device-global invalidation service has been destroyed.
  EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());
}

}  // namespace policy