chromium/ios/chrome/browser/shared/model/profile/test/test_profile_ios.mm

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

#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"

#import <tuple>

#import "base/base_paths.h"
#import "base/files/file_util.h"
#import "base/functional/callback_helpers.h"
#import "base/location.h"
#import "base/logging.h"
#import "base/memory/ptr_util.h"
#import "base/path_service.h"
#import "base/task/sequenced_task_runner.h"
#import "base/task/single_thread_task_runner.h"
#import "base/task/thread_pool.h"
#import "base/test/test_file_util.h"
#import "components/keyed_service/core/service_access_type.h"
#import "components/keyed_service/ios/browser_state_dependency_manager.h"
#import "components/policy/core/common/cloud/user_cloud_policy_manager.h"
#import "components/profile_metrics/browser_profile_type.h"
#import "components/supervised_user/core/browser/supervised_user_settings_service.h"
#import "components/sync_preferences/pref_service_syncable.h"
#import "components/sync_preferences/testing_pref_service_syncable.h"
#import "components/user_prefs/user_prefs.h"
#import "ios/chrome/browser/browser_state/model/browser_state_keyed_service_factories.h"
#import "ios/chrome/browser/prefs/model/ios_chrome_pref_service_factory.h"
#import "ios/chrome/browser/shared/model/prefs/browser_prefs.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_settings_service_factory.h"
#import "ios/web/public/thread/web_task_traits.h"
#import "ios/web/public/thread/web_thread.h"
#import "net/url_request/url_request_test_util.h"

namespace {

// Assigns `testing_factories` to `browser_state`.
void AssignTestingFactories(
    TestChromeBrowserState* browser_state,
    TestChromeBrowserState::TestingFactories testing_factories) {
  for (auto& item : testing_factories) {
    absl::visit(
        [browser_state](auto& p) {
          p.first->SetTestingFactory(browser_state, std::move(p.second));
        },
        item.service_factory_and_testing_factory);
  }
}

}  // namespace

TestChromeBrowserState::TestingFactory::TestingFactory(
    BrowserStateKeyedServiceFactory* service_factory,
    BrowserStateKeyedServiceFactory::TestingFactory testing_factory)
    : service_factory_and_testing_factory(
          std::make_pair(service_factory, std::move(testing_factory))) {}

TestChromeBrowserState::TestingFactory::TestingFactory(
    RefcountedBrowserStateKeyedServiceFactory* service_factory,
    RefcountedBrowserStateKeyedServiceFactory::TestingFactory testing_factory)
    : service_factory_and_testing_factory(
          std::make_pair(service_factory, std::move(testing_factory))) {}

TestChromeBrowserState::TestingFactory::TestingFactory(TestingFactory&&) =
    default;

TestChromeBrowserState::TestingFactory&
TestChromeBrowserState::TestingFactory::operator=(TestingFactory&&) = default;

TestChromeBrowserState::TestingFactory::~TestingFactory() = default;

TestChromeBrowserState::TestingFactories::TestingFactories() = default;

TestChromeBrowserState::TestingFactories::TestingFactories(TestingFactories&&) =
    default;

TestChromeBrowserState::TestingFactories&
TestChromeBrowserState::TestingFactories::operator=(TestingFactories&&) =
    default;

TestChromeBrowserState::TestingFactories::~TestingFactories() = default;

TestChromeBrowserState::TestChromeBrowserState(
    const base::FilePath& state_path,
    TestChromeBrowserState* original_browser_state,
    TestingFactories testing_factories)
    : ChromeBrowserState(state_path,
                         /*browser_state_name=*/std::string(),
                         original_browser_state->GetIOTaskRunner()),
      testing_prefs_(nullptr),
      otr_browser_state_(nullptr),
      original_browser_state_(original_browser_state) {
  DCHECK(original_browser_state_);

  BrowserStateDependencyManager::GetInstance()->MarkBrowserStateLive(this);

  AssignTestingFactories(this, std::move(testing_factories));
  profile_metrics::SetBrowserProfileType(
      this, profile_metrics::BrowserProfileType::kIncognito);

  // Not calling Init() here as the bi-directional link between original and
  // off-the-record TestChromeBrowserState must be established before this
  // method can be called.
}

TestChromeBrowserState::TestChromeBrowserState(
    const base::FilePath& state_path,
    std::string_view browser_state_name,
    std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs,
    TestingFactories testing_factories,
    std::unique_ptr<BrowserStatePolicyConnector> policy_connector,
    std::unique_ptr<policy::UserCloudPolicyManager> user_cloud_policy_manager)
    : ChromeBrowserState(
          state_path,
          browser_state_name,
          base::ThreadPool::CreateSequencedTaskRunner(
              {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
      prefs_(std::move(prefs)),
      testing_prefs_(nullptr),
      user_cloud_policy_manager_(std::move(user_cloud_policy_manager)),
      policy_connector_(std::move(policy_connector)),
      otr_browser_state_(nullptr),
      original_browser_state_(nullptr) {
  DCHECK(!browser_state_name.empty());

  BrowserStateDependencyManager::GetInstance()->MarkBrowserStateLive(this);

  AssignTestingFactories(this, std::move(testing_factories));
  profile_metrics::SetBrowserProfileType(
      this, profile_metrics::BrowserProfileType::kRegular);

  Init();
}

TestChromeBrowserState::~TestChromeBrowserState() {
  // Allows blocking in this scope for testing.
  base::ScopedAllowBlockingForTesting allow_bocking;

  // If this TestChromeBrowserState owns an incognito TestChromeBrowserState,
  // tear it down first.
  otr_browser_state_.reset();

  // Here, (1) the browser state services may
  // depend on `policy_connector_` and `user_cloud_policy_manager_`, and (2)
  // `policy_connector_` depends on `user_cloud_policy_manager_`. The
  // dependencies have to be shut down backward.
  policy_connector_->Shutdown();
  if (user_cloud_policy_manager_) {
    user_cloud_policy_manager_->Shutdown();
  }

  BrowserStateDependencyManager::GetInstance()->DestroyBrowserStateServices(
      this);
}

void TestChromeBrowserState::Init() {
  // Allows blocking in this scope so directory manipulation can happen in this
  // scope for testing.
  base::ScopedAllowBlockingForTesting allow_bocking;

  // If threads have been initialized, we should be on the UI thread.
  DCHECK(!web::WebThread::IsThreadInitialized(web::WebThread::UI) ||
         web::WebThread::CurrentlyOn(web::WebThread::UI));

  const base::FilePath state_path = GetStatePath();
  if (!base::PathExists(state_path)) {
    base::CreateDirectory(state_path);
  }

  // Normally this would happen during browser startup, but for tests we need to
  // trigger creation of BrowserState-related services.
  EnsureBrowserStateKeyedServiceFactoriesBuilt();

  if (prefs_) {
    // If user passed a custom PrefServiceSyncable, then leave `testing_prefs_`
    // unset as it is not possible to determine its type.
  } else if (IsOffTheRecord()) {
    // This leaves `testing_prefs_` unset as CreateIncognitoBrowserStatePrefs()
    // does not return a TestingPrefServiceSyncable.
    DCHECK(original_browser_state_);
    prefs_ =
        CreateIncognitoBrowserStatePrefs(original_browser_state_->prefs_.get());
  } else {
    testing_prefs_ = new sync_preferences::TestingPrefServiceSyncable();
    RegisterBrowserStatePrefs(testing_prefs_->registry());
    prefs_.reset(testing_prefs_);
  }
  user_prefs::UserPrefs::Set(this, prefs_.get());

  // Prefs for incognito TestChromeBrowserState are set in
  // CreateIncognitoBrowserStatePrefs().
  if (!IsOffTheRecord()) {
    user_prefs::PrefRegistrySyncable* pref_registry =
        static_cast<user_prefs::PrefRegistrySyncable*>(
            prefs_->DeprecatedGetPrefRegistry());
    BrowserStateDependencyManager::GetInstance()
        ->RegisterBrowserStatePrefsForServices(pref_registry);
  }

  BrowserStateDependencyManager::GetInstance()
      ->CreateBrowserStateServicesForTest(this);
  // `SupervisedUserSettingsService` needs to be initialized for SyncService.
  SupervisedUserSettingsServiceFactory::GetForBrowserState(this)->Init(
      GetStatePath(), GetIOTaskRunner().get(),
      /*load_synchronously=*/true);
}

bool TestChromeBrowserState::IsOffTheRecord() const {
  return original_browser_state_ != nullptr;
}

scoped_refptr<base::SequencedTaskRunner>
TestChromeBrowserState::GetIOTaskRunner() {
  return base::SingleThreadTaskRunner::GetCurrentDefault();
}

ChromeBrowserState* TestChromeBrowserState::GetOriginalChromeBrowserState() {
  if (IsOffTheRecord()) {
    return original_browser_state_;
  }
  return this;
}

bool TestChromeBrowserState::HasOffTheRecordChromeBrowserState() const {
  return otr_browser_state_ != nullptr;
}

ChromeBrowserState*
TestChromeBrowserState::GetOffTheRecordChromeBrowserState() {
  if (IsOffTheRecord()) {
    return this;
  }

  if (otr_browser_state_) {
    return otr_browser_state_.get();
  }

  return CreateOffTheRecordBrowserStateWithTestingFactories();
}

TestChromeBrowserState*
TestChromeBrowserState::CreateOffTheRecordBrowserStateWithTestingFactories(
    TestingFactories testing_factories) {
  DCHECK(!IsOffTheRecord());
  DCHECK(!otr_browser_state_);
  otr_browser_state_.reset(new TestChromeBrowserState(
      GetOffTheRecordStatePath(), this, std::move(testing_factories)));
  otr_browser_state_->Init();
  return otr_browser_state_.get();
}

PrefProxyConfigTracker* TestChromeBrowserState::GetProxyConfigTracker() {
  return nullptr;
}

BrowserStatePolicyConnector* TestChromeBrowserState::GetPolicyConnector() {
  return policy_connector_.get();
}

sync_preferences::PrefServiceSyncable*
TestChromeBrowserState::GetSyncablePrefs() {
  return prefs_.get();
}

ProfileIOSIOData* TestChromeBrowserState::GetIOData() {
  return nullptr;
}

void TestChromeBrowserState::ClearNetworkingHistorySince(
    base::Time time,
    base::OnceClosure completion) {
  if (!completion.is_null()) {
    std::move(completion).Run();
  }
}

net::URLRequestContextGetter* TestChromeBrowserState::CreateRequestContext(
    ProtocolHandlerMap* protocol_handlers) {
  return new net::TestURLRequestContextGetter(web::GetIOThreadTaskRunner({}));
}

base::WeakPtr<ChromeBrowserState> TestChromeBrowserState::AsWeakPtr() {
  return weak_ptr_factory_.GetWeakPtr();
}

sync_preferences::TestingPrefServiceSyncable*
TestChromeBrowserState::GetTestingPrefService() {
  DCHECK(prefs_);
  DCHECK(testing_prefs_);
  return testing_prefs_;
}

policy::UserCloudPolicyManager*
TestChromeBrowserState::GetUserCloudPolicyManager() {
  return user_cloud_policy_manager_.get();
}

void TestChromeBrowserState::DestroyOffTheRecordChromeBrowserState() {
  DCHECK(!IsOffTheRecord());
  otr_browser_state_.reset();
}

scoped_refptr<network::SharedURLLoaderFactory>
TestChromeBrowserState::GetSharedURLLoaderFactory() {
  return test_shared_url_loader_factory_
             ? test_shared_url_loader_factory_
             : BrowserState::GetSharedURLLoaderFactory();
}

void TestChromeBrowserState::SetSharedURLLoaderFactory(
    scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory) {
  test_shared_url_loader_factory_ = std::move(shared_url_loader_factory);
}

TestChromeBrowserState::Builder::Builder() = default;

TestChromeBrowserState::Builder::Builder(Builder&&) = default;

TestChromeBrowserState::Builder& TestChromeBrowserState::Builder::operator=(
    Builder&&) = default;

TestChromeBrowserState::Builder::~Builder() = default;

TestChromeBrowserState::Builder&
TestChromeBrowserState::Builder::AddTestingFactory(
    BrowserStateKeyedServiceFactory* service_factory,
    BrowserStateKeyedServiceFactory::TestingFactory testing_factory) {
  testing_factories_.emplace_back(service_factory, std::move(testing_factory));
  return *this;
}

TestChromeBrowserState::Builder&
TestChromeBrowserState::Builder::AddTestingFactory(
    RefcountedBrowserStateKeyedServiceFactory* service_factory,
    RefcountedBrowserStateKeyedServiceFactory::TestingFactory testing_factory) {
  testing_factories_.emplace_back(service_factory, std::move(testing_factory));
  return *this;
}

TestChromeBrowserState::Builder&
TestChromeBrowserState::Builder::AddTestingFactories(
    TestingFactories testing_factories) {
  for (auto& item : testing_factories) {
    testing_factories.emplace_back(std::move(item));
  }
  return *this;
}

TestChromeBrowserState::Builder& TestChromeBrowserState::Builder::SetName(
    const std::string& name) {
  browser_state_name_ = name;
  return *this;
}

TestChromeBrowserState::Builder&
TestChromeBrowserState::Builder::SetPrefService(
    std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs) {
  pref_service_ = std::move(prefs);
  return *this;
}

TestChromeBrowserState::Builder&
TestChromeBrowserState::Builder::SetPolicyConnector(
    std::unique_ptr<BrowserStatePolicyConnector> policy_connector) {
  policy_connector_ = std::move(policy_connector);
  return *this;
}

TestChromeBrowserState::Builder&
TestChromeBrowserState::Builder::SetUserCloudPolicyManager(
    std::unique_ptr<policy::UserCloudPolicyManager> user_cloud_policy_manager) {
  user_cloud_policy_manager_ = std::move(user_cloud_policy_manager);
  return *this;
}

std::unique_ptr<TestChromeBrowserState>
TestChromeBrowserState::Builder::Build() && {
  return std::move(*this).Build(base::CreateUniqueTempDirectoryScopedToTest());
}

std::unique_ptr<TestChromeBrowserState> TestChromeBrowserState::Builder::Build(
    const base::FilePath& data_dir) && {
  CHECK(!data_dir.empty());

  // Ensure that the name is not empty.
  if (browser_state_name_.empty()) {
    browser_state_name_ = "Test";
  }

  return base::WrapUnique(new TestChromeBrowserState(
      data_dir.Append(browser_state_name_), browser_state_name_,
      std::move(pref_service_), std::move(testing_factories_),
      std::move(policy_connector_), std::move(user_cloud_policy_manager_)));
}