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

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

#ifndef IOS_CHROME_BROWSER_SHARED_MODEL_PROFILE_TEST_TEST_PROFILE_IOS_H_
#define IOS_CHROME_BROWSER_SHARED_MODEL_PROFILE_TEST_TEST_PROFILE_IOS_H_

#include <memory>
#include <string_view>
#include <utility>
#include <vector>

#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
#include "components/keyed_service/ios/refcounted_browser_state_keyed_service_factory.h"
#include "ios/chrome/browser/net/model/net_types.h"
#include "ios/chrome/browser/policy/model/browser_state_policy_connector.h"
#include "ios/chrome/browser/shared/model/profile/profile_ios.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "third_party/abseil-cpp/absl/types/variant.h"

namespace sync_preferences {
class PrefServiceSyncable;
class TestingPrefServiceSyncable;
}  // namespace sync_preferences

namespace policy {
class UserCloudPolicyManager;
}

class EnterprisePolicyTestHelper;
class TestProfileManagerIOS;

// This class is the implementation of ChromeBrowserState used for testing.
class TestChromeBrowserState final : public ChromeBrowserState {
 public:
  // Wrapper over absl::variant to help type deduction when calling
  // AddTestingFactories(). See example call in the method's comment.
  struct TestingFactory {
    TestingFactory(
        BrowserStateKeyedServiceFactory* service_factory,
        BrowserStateKeyedServiceFactory::TestingFactory testing_factory);

    TestingFactory(RefcountedBrowserStateKeyedServiceFactory* service_factory,
                   RefcountedBrowserStateKeyedServiceFactory::TestingFactory
                       testing_factory);

    TestingFactory(TestingFactory&&);
    TestingFactory& operator=(TestingFactory&&);

    ~TestingFactory();

    absl::variant<
        std::pair<BrowserStateKeyedServiceFactory*,
                  BrowserStateKeyedServiceFactory::TestingFactory>,
        std::pair<RefcountedBrowserStateKeyedServiceFactory*,
                  RefcountedBrowserStateKeyedServiceFactory::TestingFactory>>
        service_factory_and_testing_factory;
  };

  // Wrapper around std::vector to simplify the migration to OnceCallback
  // for *BrowserStateKeyedServiceFactory::TestingFactory.
  class TestingFactories {
   public:
    TestingFactories();

    template <typename... Ts>
      requires(... && std::same_as<Ts, TestingFactory>)
    TestingFactories(Ts&&... ts) {
      (..., factories_.push_back(std::move(ts)));
    }

    TestingFactories(TestingFactories&&);
    TestingFactories& operator=(TestingFactories&&);

    ~TestingFactories();

    template <typename... Args>
    void emplace_back(Args&&... args) {
      factories_.emplace_back(std::forward<Args>(args)...);
    }

    using iterator = std::vector<TestingFactory>::iterator;
    iterator begin() { return factories_.begin(); }
    iterator end() { return factories_.end(); }

   private:
    std::vector<TestingFactory> factories_;
  };

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

  ~TestChromeBrowserState() override;

  // BrowserState:
  bool IsOffTheRecord() const override;

  // ChromeBrowserState:
  scoped_refptr<base::SequencedTaskRunner> GetIOTaskRunner() override;
  ChromeBrowserState* GetOriginalChromeBrowserState() override;
  bool HasOffTheRecordChromeBrowserState() const override;
  ChromeBrowserState* GetOffTheRecordChromeBrowserState() override;
  PrefProxyConfigTracker* GetProxyConfigTracker() override;
  BrowserStatePolicyConnector* GetPolicyConnector() override;
  sync_preferences::PrefServiceSyncable* GetSyncablePrefs() override;
  ProfileIOSIOData* GetIOData() override;
  void ClearNetworkingHistorySince(base::Time time,
                                   base::OnceClosure completion) override;
  net::URLRequestContextGetter* CreateRequestContext(
      ProtocolHandlerMap* protocol_handlers) override;
  base::WeakPtr<ChromeBrowserState> AsWeakPtr() override;
  scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory()
      override;
  policy::UserCloudPolicyManager* GetUserCloudPolicyManager() override;
  void DestroyOffTheRecordChromeBrowserState() override;

  // Creates an off-the-record TestChromeBrowserState for
  // the current object, installing `testing_factories`
  // first.
  //
  // This is an error to call this method if the current
  // TestChromeBrowserState already has a off-the-record
  // object, or is itself off-the-record.
  //
  // This method will be called without factories if the
  // method `GetOffTheRecordBrowserState()` is called on
  // this object.
  TestChromeBrowserState* CreateOffTheRecordBrowserStateWithTestingFactories(
      TestingFactories testing_factories = {});

  // Returns the preferences as a TestingPrefServiceSyncable if possible or
  // null. Returns null for off-the-record TestChromeBrowserState and also
  // for TestChromeBrowserState initialized with a custom pref service.
  sync_preferences::TestingPrefServiceSyncable* GetTestingPrefService();

  // Sets a SharedURLLoaderFactory for test.
  void SetSharedURLLoaderFactory(
      scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory);

  // Helper class that allows for parameterizing the building
  // of TestChromeBrowserStates.
  class Builder {
   public:
    Builder();

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

    Builder(Builder&&);
    Builder& operator=(Builder&&);

    ~Builder();

    // Adds a testing factory to the TestChromeBrowserState. These testing
    // factories are installed before the BrowserStateKeyedServices are created.
    Builder& AddTestingFactory(
        BrowserStateKeyedServiceFactory* service_factory,
        BrowserStateKeyedServiceFactory::TestingFactory testing_factory);
    Builder& AddTestingFactory(
        RefcountedBrowserStateKeyedServiceFactory* service_factory,
        RefcountedBrowserStateKeyedServiceFactory::TestingFactory
            testing_factory);

    // Adds multiple testing factories to TestChromeBrowserState. These testing
    // factories are installed before the BrowserStateKeyedServices are created.
    // Example use:
    //
    // AddTestingFactories(
    //     {TestChromeBrowserState::TestingFactory{
    //          RegularServiceFactory::GetInstance(),
    //          RegularServiceFactory::GetDefaultFactory(),
    //      },
    //      TestChromeBrowserState::TestingFactory{
    //          RefcountedServiceFactory::GetInstance(),
    //          RefcountedServiceFactory::GetDefaultFactory(),
    //      }});
    Builder& AddTestingFactories(TestingFactories testing_factories);

    // Sets the name of the ChromeBrowserState. If not set, then will be
    // derived from the path passed to `SetPath()` or use an arbitrary
    // value if `SetPath()` is not called.
    Builder& SetName(const std::string& name);

    // Sets the PrefService to be used by the ChromeBrowserState.
    Builder& SetPrefService(
        std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs);

    Builder& SetPolicyConnector(
        std::unique_ptr<BrowserStatePolicyConnector> policy_connector);

    // Sets a UserCloudPolicyManager for test.
    Builder& SetUserCloudPolicyManager(
        std::unique_ptr<policy::UserCloudPolicyManager>
            user_cloud_policy_manager);

    // Creates the TestChromeBrowserState using previously-set settings.
    std::unique_ptr<TestChromeBrowserState> Build() &&;

   private:
    friend class EnterprisePolicyTestHelper;
    friend class TestProfileManagerIOS;

    // Creates the TestChromeBrowserState using `data_dir` as base directory
    // for the storage, and other previously-set settings.
    std::unique_ptr<TestChromeBrowserState> Build(
        const base::FilePath& data_dir) &&;

    // Various staging variables where values are held until Build() is invoked.
    std::string browser_state_name_;
    std::unique_ptr<sync_preferences::PrefServiceSyncable> pref_service_;

    std::unique_ptr<policy::UserCloudPolicyManager> user_cloud_policy_manager_;
    std::unique_ptr<BrowserStatePolicyConnector> policy_connector_;

    TestingFactories testing_factories_;
  };

 private:
  friend class Builder;

  // Used to create the principal 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);

  // Used to create the incognito TestChromeBrowserState.
  TestChromeBrowserState(const base::FilePath& state_path,
                         TestChromeBrowserState* original_browser_state,
                         TestingFactories testing_factories);

  // Initialization of the TestChromeBrowserState. This is a separate method
  // as it needs to be called after the bi-directional link between original
  // and off-the-record TestChromeBrowserState has been created.
  void Init();

  // If non-null, `testing_prefs_` points to `prefs_`. It is there to avoid
  // casting as `prefs_` may not be a TestingPrefServiceSyncable.
  std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs_;
  raw_ptr<sync_preferences::TestingPrefServiceSyncable> testing_prefs_;

  std::unique_ptr<policy::UserCloudPolicyManager> user_cloud_policy_manager_;
  std::unique_ptr<BrowserStatePolicyConnector> policy_connector_;

  // A SharedURLLoaderFactory for test.
  scoped_refptr<network::SharedURLLoaderFactory>
      test_shared_url_loader_factory_;

  // The incognito ChromeBrowserState instance that is associated with this
  // non-incognito ChromeBrowserState instance.
  std::unique_ptr<TestChromeBrowserState> otr_browser_state_;
  raw_ptr<TestChromeBrowserState> original_browser_state_;

  base::WeakPtrFactory<TestChromeBrowserState> weak_ptr_factory_{this};
};

using TestProfileIOS = TestChromeBrowserState;

#endif  // IOS_CHROME_BROWSER_SHARED_MODEL_PROFILE_TEST_TEST_PROFILE_IOS_H_