// Copyright 2012 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/extensions/extension_prefs_unittest.h"

#include <algorithm>
#include <memory>
#include <optional>
#include <utility>

#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/extensions/install_tracker_factory.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/content_settings_registry.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/mock_pref_change_callback.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/sync/model/string_ordinal.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "extensions/browser/blocklist_extension_prefs.h"
#include "extensions/browser/blocklist_state.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_pref_value_map.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_prefs_observer.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/install_flag.h"
#include "extensions/browser/install_prefs_helper.h"
#include "extensions/browser/install_tracker.h"
#include "extensions/browser/pref_names.h"
#include "extensions/browser/pref_types.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_info.h"
#include "testing/gmock/include/gmock/gmock.h"

#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "chrome/browser/extensions/chrome_app_sorting.h"
#endif

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

using base::Time;
using extensions::mojom::APIPermissionID;
using extensions::mojom::ManifestLocation;

namespace extensions {

static void AddPattern(URLPatternSet* extent, const std::string& pattern) {
  int schemes = URLPattern::SCHEME_ALL;
  extent->AddPattern(URLPattern(schemes, pattern));
}

ExtensionPrefsTest::ExtensionPrefsTest()
    : prefs_(base::SingleThreadTaskRunner::GetCurrentDefault(),
             std::make_unique<TestingProfile>()) {}

ExtensionPrefsTest::~ExtensionPrefsTest() = default;

void ExtensionPrefsTest::RegisterPreferences(
    user_prefs::PrefRegistrySyncable* registry) {}

void ExtensionPrefsTest::SetUp() {
  RegisterPreferences(prefs_.pref_registry().get());
  Initialize();
}

void ExtensionPrefsTest::TearDown() {
  Verify();

  // Shutdown the InstallTracker early, which is a dependency on some
  // ExtensionPrefTests (and depends on PrefService being available in
  // shutdown).
  InstallTrackerFactory::GetForBrowserContext(prefs_.browser_context())
      ->Shutdown();

  // Reset ExtensionPrefs, and re-verify.
  prefs_.ResetPrefRegistry();
  RegisterPreferences(prefs_.pref_registry().get());
  prefs_.RecreateExtensionPrefs();

  // Hack: After recreating ExtensionPrefs, the AppSorting also needs to be
  // recreated. (ExtensionPrefs is never recreated in non-test code.)
  static_cast<TestExtensionSystem*>(
      ExtensionSystem::Get(prefs_.browser_context()))
      ->RecreateAppSorting();

  Verify();
  prefs_.pref_service()->CommitPendingWrite();
  base::RunLoop().RunUntilIdle();

  testing::Test::TearDown();
}

ChromeAppSorting* ExtensionPrefsTest::app_sorting() {
#if BUILDFLAG(ENABLE_EXTENSIONS)
  return static_cast<ChromeAppSorting*>(
      ExtensionSystem::Get(prefs_.browser_context())->app_sorting());
#else
  // Android doesn't support Chrome Apps, hence has no app sorting.
  NOTREACHED();
#endif
}

// Tests the LastPingDay/SetLastPingDay functions.
class ExtensionPrefsLastPingDay : public ExtensionPrefsTest {
 public:
  ExtensionPrefsLastPingDay()
      : extension_time_(Time::Now() - base::Hours(4)),
        blocklist_time_(Time::Now() - base::Hours(2)) {}

  void Initialize() override {
    extension_id_ = prefs_.AddExtensionAndReturnId("last_ping_day");
    EXPECT_TRUE(prefs()->LastPingDay(extension_id_).is_null());
    prefs()->SetLastPingDay(extension_id_, extension_time_);
    prefs()->SetBlocklistLastPingDay(blocklist_time_);
  }

  void Verify() override {
    Time result = prefs()->LastPingDay(extension_id_);
    EXPECT_FALSE(result.is_null());
    EXPECT_TRUE(result == extension_time_);
    result = prefs()->BlocklistLastPingDay();
    EXPECT_FALSE(result.is_null());
    EXPECT_TRUE(result == blocklist_time_);
  }

 private:
  Time extension_time_;
  Time blocklist_time_;
  ExtensionId extension_id_;
};
TEST_F(ExtensionPrefsLastPingDay, LastPingDay) {}

// Tests the migration of a deprecated disable reason.
class ExtensionPrefsDeprecatedDisableReason : public ExtensionPrefsTest {
 public:
  void Initialize() override {
    extension1_ = prefs_.AddExtension("test1");
    DisableReasonSet disable_reasons = {
        disable_reason::DEPRECATED_DISABLE_UNKNOWN_FROM_SYNC};
    prefs()->AddDisableReasons(extension1_->id(), disable_reasons);
    extension2_ = prefs_.AddExtension("test2");
    disable_reasons.insert(disable_reason::DISABLE_PERMISSIONS_INCREASE);
    prefs()->AddDisableReasons(extension2_->id(), disable_reasons);
    prefs()->MigrateDeprecatedDisableReasons();
  }

  void Verify() override {
    EXPECT_THAT(
        prefs()->GetDisableReasons(extension1_->id()),
        testing::UnorderedElementsAre(disable_reason::DISABLE_USER_ACTION));
    // Verify that if an extension has a disable reason in addition to the
    // deprecated reason, we don't add the user action disable reason.
    EXPECT_THAT(prefs()->GetDisableReasons(extension2_->id()),
                testing::UnorderedElementsAre(
                    disable_reason::DISABLE_PERMISSIONS_INCREASE));
  }

 private:
  scoped_refptr<Extension> extension1_;
  scoped_refptr<Extension> extension2_;
};

TEST_F(ExtensionPrefsDeprecatedDisableReason, MigrateExtensionState) {}

class ExtensionPrefsEscalatePermissions : public ExtensionPrefsTest {
 public:
  void Initialize() override {
    extension = prefs_.AddExtension("test");
    prefs()->AddDisableReasons(extension->id(),
                               {disable_reason::DISABLE_PERMISSIONS_INCREASE});
  }

  void Verify() override {
    EXPECT_TRUE(prefs()->DidExtensionEscalatePermissions(extension->id()));
  }

 private:
  scoped_refptr<Extension> extension;
};
TEST_F(ExtensionPrefsEscalatePermissions, EscalatePermissions) {}

// Tests the AddGrantedPermissions / GetGrantedPermissions functions.
class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest {
 public:
  void Initialize() override {
    const APIPermissionInfo* permission_info =
        PermissionsInfo::GetInstance()->GetByID(
            mojom::APIPermissionID::kSocket);

    extension_id_ = prefs_.AddExtensionAndReturnId("test");

    api_perm_set1_.insert(APIPermissionID::kTab);
    api_perm_set1_.insert(APIPermissionID::kBookmark);
    std::unique_ptr<APIPermission> permission(
        permission_info->CreateAPIPermission());
    {
      base::ListValue list;
      list.Append("tcp-connect:*.example.com:80");
      list.Append("udp-bind::8080");
      list.Append("udp-send-to::8888");
      base::Value value(std::move(list));
      ASSERT_TRUE(permission->FromValue(&value, nullptr, nullptr));
    }
    api_perm_set1_.insert(std::move(permission));

    api_perm_set2_.insert(APIPermissionID::kHistory);

    AddPattern(&ehost_perm_set1_, "http://*.google.com/*");
    AddPattern(&ehost_perm_set1_, "http://example.com/*");
    AddPattern(&ehost_perm_set1_, "chrome://favicon/*");

    AddPattern(&ehost_perm_set2_, "https://*.google.com/*");
    // with duplicate:
    AddPattern(&ehost_perm_set2_, "http://*.google.com/*");

    AddPattern(&shost_perm_set1_, "http://reddit.com/r/test/*");
    AddPattern(&shost_perm_set2_, "http://reddit.com/r/test/*");
    AddPattern(&shost_perm_set2_, "http://somesite.com/*");
    AddPattern(&shost_perm_set2_, "http://example.com/*");

    APIPermissionSet expected_apis = api_perm_set1_.Clone();

    AddPattern(&ehost_permissions_, "http://*.google.com/*");
    AddPattern(&ehost_permissions_, "http://example.com/*");
    AddPattern(&ehost_permissions_, "chrome://favicon/*");
    AddPattern(&ehost_permissions_, "https://*.google.com/*");

    AddPattern(&shost_permissions_, "http://reddit.com/r/test/*");
    AddPattern(&shost_permissions_, "http://somesite.com/*");
    AddPattern(&shost_permissions_, "http://example.com/*");

    // Make sure both granted api and host permissions start empty.
    EXPECT_TRUE(prefs()->GetGrantedPermissions(extension_id_)->IsEmpty());

    {
      // Add part of the api permissions.
      prefs()->AddGrantedPermissions(
          extension_id_,
          PermissionSet(api_perm_set1_.Clone(), ManifestPermissionSet(),
                        URLPatternSet(), URLPatternSet()));
      std::unique_ptr<const PermissionSet> granted_permissions =
          prefs()->GetGrantedPermissions(extension_id_);
      EXPECT_TRUE(granted_permissions.get());
      EXPECT_FALSE(granted_permissions->IsEmpty());
      EXPECT_EQ(expected_apis, granted_permissions->apis());
      EXPECT_TRUE(granted_permissions->effective_hosts().is_empty());
    }

    {
      // Add part of the explicit host permissions.
      prefs()->AddGrantedPermissions(
          extension_id_,
          PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
                        ehost_perm_set1_.Clone(), URLPatternSet()));
      std::unique_ptr<const PermissionSet> granted_permissions =
          prefs()->GetGrantedPermissions(extension_id_);
      EXPECT_FALSE(granted_permissions->IsEmpty());
      EXPECT_EQ(expected_apis, granted_permissions->apis());
      EXPECT_EQ(ehost_perm_set1_, granted_permissions->explicit_hosts());
      EXPECT_EQ(ehost_perm_set1_, granted_permissions->effective_hosts());
    }

    {
      // Add part of the scriptable host permissions.
      prefs()->AddGrantedPermissions(
          extension_id_,
          PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
                        URLPatternSet(), shost_perm_set1_.Clone()));
      std::unique_ptr<const PermissionSet> granted_permissions =
          prefs()->GetGrantedPermissions(extension_id_);
      EXPECT_FALSE(granted_permissions->IsEmpty());
      EXPECT_EQ(expected_apis, granted_permissions->apis());
      EXPECT_EQ(ehost_perm_set1_, granted_permissions->explicit_hosts());
      EXPECT_EQ(shost_perm_set1_, granted_permissions->scriptable_hosts());

      effective_permissions_ =
          URLPatternSet::CreateUnion(ehost_perm_set1_, shost_perm_set1_);
      EXPECT_EQ(effective_permissions_, granted_permissions->effective_hosts());
    }

    {
      // Add the rest of the permissions.
      APIPermissionSet::Union(expected_apis, api_perm_set2_, &api_permissions_);
      prefs()->AddGrantedPermissions(
          extension_id_,
          PermissionSet(api_perm_set2_.Clone(), ManifestPermissionSet(),
                        ehost_perm_set2_.Clone(), shost_perm_set2_.Clone()));

      std::unique_ptr<const PermissionSet> granted_permissions =
          prefs()->GetGrantedPermissions(extension_id_);
      EXPECT_TRUE(granted_permissions.get());
      EXPECT_FALSE(granted_permissions->IsEmpty());
      EXPECT_EQ(api_permissions_, granted_permissions->apis());
      EXPECT_EQ(ehost_permissions_, granted_permissions->explicit_hosts());
      EXPECT_EQ(shost_permissions_, granted_permissions->scriptable_hosts());
      effective_permissions_ =
          URLPatternSet::CreateUnion(ehost_permissions_, shost_permissions_);
      EXPECT_EQ(effective_permissions_, granted_permissions->effective_hosts());
    }
  }

  void Verify() override {
    std::unique_ptr<const PermissionSet> permissions =
        prefs()->GetGrantedPermissions(extension_id_);
    EXPECT_TRUE(permissions.get());
    EXPECT_EQ(api_permissions_, permissions->apis());
    EXPECT_EQ(ehost_permissions_, permissions->explicit_hosts());
    EXPECT_EQ(shost_permissions_, permissions->scriptable_hosts());
  }

 private:
  ExtensionId extension_id_;
  APIPermissionSet api_perm_set1_;
  APIPermissionSet api_perm_set2_;
  URLPatternSet ehost_perm_set1_;
  URLPatternSet ehost_perm_set2_;
  URLPatternSet shost_perm_set1_;
  URLPatternSet shost_perm_set2_;

  APIPermissionSet api_permissions_;
  URLPatternSet ehost_permissions_;
  URLPatternSet shost_permissions_;
  URLPatternSet effective_permissions_;
};
TEST_F(ExtensionPrefsGrantedPermissions, GrantedPermissions) {}

// Tests the SetDesiredActivePermissions / GetDesiredActivePermissions
// functions.
class ExtensionPrefsActivePermissions : public ExtensionPrefsTest {
 public:
  void Initialize() override {
    extension_id_ = prefs_.AddExtensionAndReturnId("test");

    {
      APIPermissionSet api_perms;
      api_perms.insert(APIPermissionID::kTab);
      api_perms.insert(APIPermissionID::kBookmark);
      api_perms.insert(APIPermissionID::kHistory);

      URLPatternSet ehosts;
      AddPattern(&ehosts, "http://*.google.com/*");
      AddPattern(&ehosts, "http://example.com/*");
      AddPattern(&ehosts, "chrome://favicon/*");

      URLPatternSet shosts;
      AddPattern(&shosts, "https://*.google.com/*");
      AddPattern(&shosts, "http://reddit.com/r/test/*");

      active_perms_ = std::make_unique<PermissionSet>(
          std::move(api_perms), ManifestPermissionSet(), std::move(ehosts),
          std::move(shosts));
    }

    // Make sure the active permissions start empty.
    std::unique_ptr<const PermissionSet> active =
        prefs()->GetDesiredActivePermissions(extension_id_);
    EXPECT_TRUE(active->IsEmpty());

    // Set the desired active permissions.
    prefs()->SetDesiredActivePermissions(extension_id_, *active_perms_);
    active = prefs()->GetDesiredActivePermissions(extension_id_);
    EXPECT_EQ(active_perms_->apis(), active->apis());
    EXPECT_EQ(active_perms_->explicit_hosts(), active->explicit_hosts());
    EXPECT_EQ(active_perms_->scriptable_hosts(), active->scriptable_hosts());
    EXPECT_EQ(*active_perms_, *active);

    // Reset the desired active permissions.
    active_perms_ = std::make_unique<PermissionSet>();
    prefs()->SetDesiredActivePermissions(extension_id_, *active_perms_);
    active = prefs()->GetDesiredActivePermissions(extension_id_);
    EXPECT_EQ(*active_perms_, *active);
  }

  void Verify() override {
    std::unique_ptr<const PermissionSet> permissions =
        prefs()->GetDesiredActivePermissions(extension_id_);
    EXPECT_EQ(*active_perms_, *permissions);
  }

 private:
  ExtensionId extension_id_;
  std::unique_ptr<const PermissionSet> active_perms_;
};
TEST_F(ExtensionPrefsActivePermissions, SetAndGetDesiredActivePermissions) {}

// Tests the GetVersionString function.
class ExtensionPrefsVersionString : public ExtensionPrefsTest {
 public:
  void Initialize() override {
    extension = prefs_.AddExtension("test");
    EXPECT_EQ("0.1", prefs()->GetVersionString(extension->id()));
    prefs()->OnExtensionUninstalled(extension->id(),
                                    ManifestLocation::kInternal, false);
  }

  void Verify() override {
    EXPECT_EQ("", prefs()->GetVersionString(extension->id()));
  }

 private:
  scoped_refptr<Extension> extension;
};
TEST_F(ExtensionPrefsVersionString, VersionString) {}

class ExtensionPrefsAcknowledgment : public ExtensionPrefsTest {
 public:
  void Initialize() override {
    not_installed_id_ = "pghjnghklobnfoidcldiidjjjhkeeaoi";

    // Install some extensions.
    for (int i = 0; i < 5; i++) {
      std::string name = "test" + base::NumberToString(i);
      extensions_.push_back(prefs_.AddExtension(name));
    }
    EXPECT_EQ(std::nullopt,
              prefs()->GetInstalledExtensionInfo(not_installed_id_));

    ExtensionList::const_iterator iter;
    for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
      std::string id = (*iter)->id();
      EXPECT_FALSE(prefs()->IsExternalExtensionAcknowledged(id));
      EXPECT_FALSE(prefs()->IsBlocklistedExtensionAcknowledged(id));
      if (external_id_.empty()) {
        external_id_ = id;
        continue;
      }
      if (blocklisted_id_.empty()) {
        blocklisted_id_ = id;
        continue;
      }
    }
    // For each type of acknowledgment, acknowledge one installed and one
    // not-installed extension id.
    prefs()->AcknowledgeExternalExtension(external_id_);
    prefs()->AcknowledgeBlocklistedExtension(blocklisted_id_);
    prefs()->AcknowledgeExternalExtension(not_installed_id_);
    prefs()->AcknowledgeBlocklistedExtension(not_installed_id_);
  }

  void Verify() override {
    ExtensionList::const_iterator iter;
    for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
      std::string id = (*iter)->id();
      if (id == external_id_) {
        EXPECT_TRUE(prefs()->IsExternalExtensionAcknowledged(id));
      } else {
        EXPECT_FALSE(prefs()->IsExternalExtensionAcknowledged(id));
      }
      if (id == blocklisted_id_) {
        EXPECT_TRUE(prefs()->IsBlocklistedExtensionAcknowledged(id));
      } else {
        EXPECT_FALSE(prefs()->IsBlocklistedExtensionAcknowledged(id));
      }
    }
    EXPECT_TRUE(prefs()->IsExternalExtensionAcknowledged(not_installed_id_));
    EXPECT_TRUE(prefs()->IsBlocklistedExtensionAcknowledged(not_installed_id_));
  }

 private:
  ExtensionList extensions_;

  std::string not_installed_id_;
  std::string external_id_;
  std::string blocklisted_id_;
};
TEST_F(ExtensionPrefsAcknowledgment, Acknowledgment) {}

// Tests the idle install information functions.
class ExtensionPrefsDelayedInstallInfo : public ExtensionPrefsTest {
 public:
  // Sets idle install information for one test extension.
  void SetIdleInfo(
      const std::string& id,
      int num,
      int install_flags = kInstallFlagNone,
      const syncer::StringOrdinal& page_ordinal = syncer::StringOrdinal(),
      const std::string& install_parameter = std::string()) {
    base::DictValue manifest;
    manifest.Set(manifest_keys::kName, "test");
    manifest.Set(manifest_keys::kVersion, "1." + base::NumberToString(num));
    manifest.Set(manifest_keys::kManifestVersion, 2);
    base::FilePath path =
        prefs_.extensions_dir().AppendASCII(base::NumberToString(num));
    std::u16string errors;
    scoped_refptr<Extension> extension =
        Extension::Create(path, ManifestLocation::kInternal, manifest,
                          Extension::NO_FLAGS, id, &errors);
    ASSERT_TRUE(extension.get()) << errors;
    ASSERT_EQ(id, extension->id());
    prefs()->SetDelayedInstallInfo(
        extension.get(),
        {install_flags, ExtensionPrefs::DelayReason::kWaitForIdle, page_ordinal,
         install_parameter});
  }

  // Verifies that we get back expected idle install information previously
  // set by SetIdleInfo.
  void VerifyIdleInfo(
      const std::string& id,
      int num,
      int expected_flags = kInstallFlagNone,
      const syncer::StringOrdinal& expected_page_ordinal =
          syncer::StringOrdinal(),
      const std::string& expected_install_parameter = std::string()) {
    std::optional<ExtensionPrefs::InstallRecord> info(
        prefs()->GetDelayedInstallExtensionInfo(id));
    ASSERT_TRUE(info);
    const std::string* version =
        info->extension_manifest->FindString("version");
    ASSERT_TRUE(version);
    ASSERT_EQ("1." + base::NumberToString(num), *version);
    ASSERT_EQ(base::NumberToString(num),
              info->extension_path.BaseName().MaybeAsASCII());

    ExtensionPrefs::DelayedInstallInfo delayed_info =
        prefs()->GetDelayedInstallInfo(id);
    EXPECT_EQ(expected_flags, delayed_info.install_flags);
    EXPECT_EQ(delayed_info.page_ordinal.IsValid(),
              expected_page_ordinal.IsValid());
    if (expected_page_ordinal.IsValid()) {
      EXPECT_TRUE(delayed_info.page_ordinal.Equals(expected_page_ordinal));
    }
    EXPECT_EQ(expected_install_parameter, delayed_info.install_parameter);
    EXPECT_TRUE(delayed_info.ruleset_install_prefs.empty());
  }

  bool HasInfoForId(const ExtensionPrefs::InstallRecords& info,
                    const std::string& id) {
    return std::ranges::find_if(
               info.begin(), info.end(),
               [&id](const ExtensionPrefs::InstallRecord& info) {
                 return info.extension_id == id;
               }) != info.end();
  }

  void Initialize() override {
    base::PathService::Get(chrome::DIR_TEST_DATA, &basedir_);
    now_ = Time::Now();
    id1_ = prefs_.AddExtensionAndReturnId("1");
    id2_ = prefs_.AddExtensionAndReturnId("2");
    id3_ = prefs_.AddExtensionAndReturnId("3");
    id4_ = prefs_.AddExtensionAndReturnId("4");

    // Set info for two extensions.
    SetIdleInfo(id1_, 1);
    SetIdleInfo(id2_, 2);
    VerifyIdleInfo(id1_, 1);
    VerifyIdleInfo(id2_, 2);
    ExtensionPrefs::InstallRecords info = prefs()->GetAllDelayedInstallInfo();
    EXPECT_EQ(2u, info.size());
    EXPECT_TRUE(HasInfoForId(info, id1_));
    EXPECT_TRUE(HasInfoForId(info, id2_));
    EXPECT_FALSE(prefs()->GetDelayedInstallExtensionInfo(id3_));

    // Set info for 2 more extensions.
    SetIdleInfo(id3_, 3);
    SetIdleInfo(id4_, 4, kInstallFlagDoNotSync, syncer::StringOrdinal(),
                "TestParam");
  }

  void Verify() override {
    // Make sure the info for the 4 extensions we expect is present.
    ExtensionPrefs::InstallRecords info = prefs()->GetAllDelayedInstallInfo();
    EXPECT_EQ(4u, info.size());
    EXPECT_TRUE(HasInfoForId(info, id1_));
    EXPECT_TRUE(HasInfoForId(info, id2_));
    EXPECT_TRUE(HasInfoForId(info, id3_));
    EXPECT_TRUE(HasInfoForId(info, id4_));
    VerifyIdleInfo(id1_, 1);
    VerifyIdleInfo(id2_, 2);
    VerifyIdleInfo(id3_, 3);
    VerifyIdleInfo(id4_, 4, kInstallFlagDoNotSync, syncer::StringOrdinal(),
                   "TestParam");
  }

 protected:
  Time now_;
  base::FilePath basedir_;
  std::string id1_;
  std::string id2_;
  std::string id3_;
  std::string id4_;
};
TEST_F(ExtensionPrefsDelayedInstallInfo, DelayedInstallInfo) {}

class ExtensionPrefsOnExtensionInstalled : public ExtensionPrefsTest {
 public:
  void Initialize() override {
    extension_ = prefs_.AddExtension("on_extension_installed");
    EXPECT_FALSE(prefs()->IsExtensionDisabled(extension_->id()));
    prefs()->OnExtensionInstalled(extension_.get(),
                                  {disable_reason::DISABLE_USER_ACTION},
                                  syncer::StringOrdinal(), "Param");
  }

  void Verify() override {
    EXPECT_TRUE(prefs()->IsExtensionDisabled(extension_->id()));
    EXPECT_EQ(std::string("Param"), GetInstallParam(prefs(), extension_->id()));
  }

 private:
  scoped_refptr<Extension> extension_;
};
TEST_F(ExtensionPrefsOnExtensionInstalled, ExtensionPrefsOnExtensionInstalled) {
}

class ExtensionPrefsPopulatesInstallTimePrefs : public ExtensionPrefsTest {
 public:
  void Initialize() override {
    extension_ = prefs_.AddExtension("test1");
    // Cache the first install time.
    first_install_time_ = GetFirstInstallTime(prefs(), extension_->id());
    auto last_update_time = GetLastUpdateTime(prefs(), extension_->id());
    // First time install will result in same value for both first_install_time
    // and last_update_time prefs.
    EXPECT_NE(base::Time(), first_install_time_);
    EXPECT_NE(base::Time(), last_update_time);
    EXPECT_EQ(first_install_time_, last_update_time);

    // Update the extension.
    extension_ = prefs_.AddExtension("test1");
  }

  void Verify() override {
    auto first_install_time = GetFirstInstallTime(prefs(), extension_->id());
    auto last_update_time = GetLastUpdateTime(prefs(), extension_->id());
    EXPECT_NE(base::Time(), first_install_time);
    EXPECT_NE(base::Time(), last_update_time);
    // Verify that the first_install_time remains unchanged after the extension
    // update.
    EXPECT_EQ(first_install_time, first_install_time_);
    // Verify that the last_update_time is no longer the same as the
    // first_install_time after the extension update.
    EXPECT_NE(first_install_time, last_update_time);
  }

 private:
  scoped_refptr<Extension> extension_;
  base::Time first_install_time_;
};
TEST_F(ExtensionPrefsPopulatesInstallTimePrefs,
       ExtensionPrefsPopulatesInstallTimePrefs) {}

class ExtensionPrefsMigratesToLastUpdateTime : public ExtensionPrefsTest {
 public:
  void Initialize() override {
    extension_ = prefs_.AddExtension("test1");
    // Re-create migration scenario by removing the new first_install_time,
    // last_update_time pref keys and adding back the legacy install_time key.
    prefs()->UpdateExtensionPref(extension_->id(), kLastUpdateTimePrefKey,
                                 std::nullopt);
    prefs()->UpdateExtensionPref(extension_->id(), kFirstInstallTimePrefKey,
                                 std::nullopt);
    time_str_ = base::NumberToString(
        base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
    prefs()->SetStringPref(extension_->id(), kOldInstallTimePrefMap, time_str_);

    // Run the migration routine.
    prefs()->BackfillAndMigrateInstallTimePrefs();
  }

  void Verify() override {
    auto* dict = prefs()->GetExtensionPref(extension_->id());

    // Verify the legacy install_time key has been removed and replaced by
    // the last_update_time key. Also verify that the first_install_time key
    // has been added and has the same value as the last_update_time key.
    EXPECT_FALSE(dict->FindString(kOldInstallTimePrefKey));
    const std::string* first_install_time =
        dict->FindString(kFirstInstallTimePrefKey);
    ASSERT_TRUE(first_install_time);
    EXPECT_EQ(*first_install_time, time_str_);
    const std::string* last_update_time =
        dict->FindString(kLastUpdateTimePrefKey);
    ASSERT_TRUE(last_update_time);
    EXPECT_EQ(*last_update_time, time_str_);
  }

 private:
  scoped_refptr<Extension> extension_;
  std::string time_str_;
  static constexpr char kFirstInstallTimePrefKey[] = "first_install_time";
  static constexpr char kLastUpdateTimePrefKey[] = "last_update_time";
  static constexpr char kOldInstallTimePrefKey[] = "install_time";
  static constexpr PrefMap kOldInstallTimePrefMap = {
      kOldInstallTimePrefKey, PrefType::kString, PrefScope::kExtensionSpecific};
};
TEST_F(ExtensionPrefsMigratesToLastUpdateTime,
       ExtensionPrefsMigratesToLastUpdateTime) {}

// Tests that the bit map pref value is cleared if the value matches the default
// bit.
class ExtensionPrefsBitMapPrefValueClearedIfEqualsDefaultValue
    : public ExtensionPrefsTest {
 public:
  void Initialize() override {
    extension_ = prefs_.AddExtension("test1");
    prefs()->ModifyBitMapPrefBits(
        extension_->id(), disable_reason::DISABLE_PERMISSIONS_INCREASE,
        ExtensionPrefs::BitMapPrefOperation::kAdd, "disable_reasons",
        disable_reason::DISABLE_USER_ACTION);
    // Set the bit map pref value to the default value, it should clear the
    // pref.
    prefs()->ModifyBitMapPrefBits(
        extension_->id(), disable_reason::DISABLE_USER_ACTION,
        ExtensionPrefs::BitMapPrefOperation::kReplace, "disable_reasons",
        disable_reason::DISABLE_USER_ACTION);
  }

  void Verify() override {
    const base::DictValue* ext = prefs()->GetExtensionPref(extension_->id());
    ASSERT_TRUE(ext);
    // The pref value should be cleared.
    EXPECT_FALSE(ext->FindInt("disable_reasons"));
  }

 private:
  scoped_refptr<Extension> extension_;
};

TEST_F(ExtensionPrefsBitMapPrefValueClearedIfEqualsDefaultValue,
       ExtensionPrefsBitMapPrefValueClearedIfEqualsDefaultValue) {}

class ExtensionPrefsFlags : public ExtensionPrefsTest {
 public:
  void Initialize() override {
    {
      base::DictValue dictionary;
      dictionary.Set(manifest_keys::kName, "from_webstore");
      dictionary.Set(manifest_keys::kVersion, "0.1");
      dictionary.Set(manifest_keys::kManifestVersion, 2);
      webstore_extension_ = prefs_.AddExtensionWithManifestAndFlags(
          dictionary, ManifestLocation::kInternal, Extension::FROM_WEBSTORE);
    }

    {
      base::DictValue dictionary;
      dictionary.Set(manifest_keys::kName, "was_installed_by_default");
      dictionary.Set(manifest_keys::kVersion, "0.1");
      dictionary.Set(manifest_keys::kManifestVersion, 2);
      default_extension_ = prefs_.AddExtensionWithManifestAndFlags(
          dictionary, ManifestLocation::kInternal,
          Extension::WAS_INSTALLED_BY_DEFAULT);
    }

    {
      base::DictValue dictionary;
      dictionary.Set(manifest_keys::kName, "was_installed_by_oem");
      dictionary.Set(manifest_keys::kVersion, "0.1");
      dictionary.Set(manifest_keys::kManifestVersion, 2);
      oem_extension_ = prefs_.AddExtensionWithManifestAndFlags(
          dictionary, ManifestLocation::kInternal,
          Extension::WAS_INSTALLED_BY_OEM);
    }
  }

  void Verify() override {
    EXPECT_TRUE(IsFromWebStore(prefs(), webstore_extension_->id()));
    EXPECT_TRUE(WasInstalledByDefault(prefs(), default_extension_->id()));
    EXPECT_TRUE(WasInstalledByOem(prefs(), oem_extension_->id()));
  }

 private:
  scoped_refptr<Extension> webstore_extension_;
  scoped_refptr<Extension> default_extension_;
  scoped_refptr<Extension> oem_extension_;
};
TEST_F(ExtensionPrefsFlags, ExtensionPrefsFlags) {}

PrefsPrepopulatedTestBase::PrefsPrepopulatedTestBase() {
  base::DictValue simple_dict;
  std::u16string error;

  simple_dict.Set(manifest_keys::kVersion, "1.0.0.0");
  simple_dict.Set(manifest_keys::kManifestVersion, 2);
  simple_dict.Set(manifest_keys::kName, "unused");

  extension1_ = Extension::Create(prefs_.temp_dir().AppendASCII("ext1_"),
                                  ManifestLocation::kExternalPref, simple_dict,
                                  Extension::NO_FLAGS, &error);
  extension2_ = Extension::Create(prefs_.temp_dir().AppendASCII("ext2_"),
                                  ManifestLocation::kExternalPref, simple_dict,
                                  Extension::NO_FLAGS, &error);
  extension3_ = Extension::Create(prefs_.temp_dir().AppendASCII("ext3_"),
                                  ManifestLocation::kExternalPref, simple_dict,
                                  Extension::NO_FLAGS, &error);
  extension4_ = Extension::Create(prefs_.temp_dir().AppendASCII("ext4_"),
                                  ManifestLocation::kExternalPref, simple_dict,
                                  Extension::NO_FLAGS, &error);

  internal_extension_ = Extension::Create(
      prefs_.temp_dir().AppendASCII("internal extension"),
      ManifestLocation::kInternal, simple_dict, Extension::NO_FLAGS, &error);
}

PrefsPrepopulatedTestBase::~PrefsPrepopulatedTestBase() = default;

// Tests clearing the last launched preference.
class ExtensionPrefsClearLastLaunched : public ExtensionPrefsTest {
 public:
  ~ExtensionPrefsClearLastLaunched() override = default;

  void Initialize() override {
    extension_a_ = prefs_.AddExtension("a");
    extension_b_ = prefs_.AddExtension("b");
  }

  void Verify() override {
    // Set last launched times for each extension.
    prefs()->SetLastLaunchTime(extension_a_->id(), base::Time::Now());
    prefs()->SetLastLaunchTime(extension_b_->id(), base::Time::Now());

    // Also set some other preference for one of the extensions.
    prefs()->SetAllowFileAccess(extension_a_->id(), true);

    // Now clear the launch times.
    prefs()->ClearLastLaunchTimes();

    // All launch times should be gone.
    EXPECT_EQ(base::Time(), prefs()->GetLastLaunchTime(extension_a_->id()));
    EXPECT_EQ(base::Time(), prefs()->GetLastLaunchTime(extension_b_->id()));

    // Other preferences should be untouched.
    EXPECT_TRUE(prefs()->AllowFileAccess(extension_a_->id()));
  }

 private:
  scoped_refptr<const Extension> extension_a_;
  scoped_refptr<const Extension> extension_b_;
};
TEST_F(ExtensionPrefsClearLastLaunched, ExtensionPrefsClearLastLaunched) {}

class ExtensionPrefsComponentExtension : public ExtensionPrefsTest {
 public:
  ~ExtensionPrefsComponentExtension() override = default;
  void Initialize() override {
    // Adding a component extension.
    component_extension_ =
        ExtensionBuilder("a")
            .SetLocation(ManifestLocation::kComponent)
            .SetPath(prefs_.extensions_dir().AppendASCII("a"))
            .Build();
    prefs_.AddExtension(component_extension_.get());

    // Adding a non component extension.
    no_component_extension_ =
        ExtensionBuilder("b")
            .SetLocation(ManifestLocation::kInternal)
            .SetPath(prefs_.extensions_dir().AppendASCII("b"))
            .Build();
    prefs_.AddExtension(no_component_extension_.get());

    APIPermissionSet api_perms;
    api_perms.insert(APIPermissionID::kTab);
    api_perms.insert(APIPermissionID::kBookmark);
    api_perms.insert(APIPermissionID::kHistory);

    URLPatternSet shosts;
    AddPattern(&shosts, "chrome://print/*");

    active_perms_ = std::make_unique<PermissionSet>(
        std::move(api_perms), ManifestPermissionSet(), URLPatternSet(),
        std::move(shosts));
    // Set the desired active permissions.
    prefs()->SetDesiredActivePermissions(component_extension_->id(),
                                         *active_perms_);
    prefs()->SetDesiredActivePermissions(no_component_extension_->id(),
                                         *active_perms_);
  }

  void Verify() override {
    // Component extension can access chrome://print/*.
    std::unique_ptr<const PermissionSet> component_permissions =
        prefs()->GetDesiredActivePermissions(component_extension_->id());
    EXPECT_EQ(1u, component_permissions->scriptable_hosts().size());

    // Non Component extension can not access chrome://print/*.
    std::unique_ptr<const PermissionSet> no_component_permissions =
        prefs()->GetDesiredActivePermissions(no_component_extension_->id());
    EXPECT_EQ(0u, no_component_permissions->scriptable_hosts().size());

    // |URLPattern::SCHEME_CHROMEUI| scheme will be added in valid_schemes for
    // component extensions.
    URLPatternSet scriptable_hosts;
    std::string pref_key = "active_permissions.scriptable_host";
    int valid_schemes = URLPattern::SCHEME_ALL & ~URLPattern::SCHEME_CHROMEUI;

    EXPECT_TRUE(prefs()->ReadPrefAsURLPatternSet(component_extension_->id(),
                                                 pref_key, &scriptable_hosts,
                                                 valid_schemes));

    EXPECT_FALSE(prefs()->ReadPrefAsURLPatternSet(no_component_extension_->id(),
                                                  pref_key, &scriptable_hosts,
                                                  valid_schemes));

    // Both extensions should be registered with the ExtensionPrefValueMap.
    // See https://crbug.com/40403830.
    EXPECT_TRUE(prefs_.extension_pref_value_map()->CanExtensionControlPref(
        component_extension_->id(), "a_pref", false));
    EXPECT_TRUE(prefs_.extension_pref_value_map()->CanExtensionControlPref(
        no_component_extension_->id(), "a_pref", false));
  }

 private:
  std::unique_ptr<const PermissionSet> active_perms_;
  scoped_refptr<const Extension> component_extension_;
  scoped_refptr<const Extension> no_component_extension_;
};
TEST_F(ExtensionPrefsComponentExtension, ExtensionPrefsComponentExtension) {}

// Tests reading and writing runtime granted permissions.
class ExtensionPrefsRuntimeGrantedPermissions : public ExtensionPrefsTest {
 public:
  ExtensionPrefsRuntimeGrantedPermissions() = default;

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

  ~ExtensionPrefsRuntimeGrantedPermissions() override = default;

  void Initialize() override {
    extension_a_ = prefs_.AddExtension("a");
    extension_b_ = prefs_.AddExtension("b");

    // By default, runtime-granted permissions are empty.
    EXPECT_TRUE(
        prefs()->GetRuntimeGrantedPermissions(extension_a_->id())->IsEmpty());
    EXPECT_TRUE(
        prefs()->GetRuntimeGrantedPermissions(extension_b_->id())->IsEmpty());

    URLPattern example_com(URLPattern::SCHEME_ALL, "https://example.com/*");
    URLPattern chromium_org(URLPattern::SCHEME_ALL, "https://chromium.org/*");

    {
      // Add two hosts to the runtime granted permissions. Verify they were
      // correctly added.
      URLPatternSet added_urls({example_com, chromium_org});
      PermissionSet added_permissions(APIPermissionSet(),
                                      ManifestPermissionSet(),
                                      std::move(added_urls), URLPatternSet());
      prefs()->AddRuntimeGrantedPermissions(extension_a_->id(),
                                            added_permissions);

      std::unique_ptr<const PermissionSet> retrieved_permissions =
          prefs()->GetRuntimeGrantedPermissions(extension_a_->id());
      ASSERT_TRUE(retrieved_permissions);
      EXPECT_EQ(added_permissions, *retrieved_permissions);
    }

    {
      // Remove one of the hosts. The only remaining host should be
      // example.com
      URLPatternSet removed_urls({chromium_org});
      PermissionSet removed_permissions(
          APIPermissionSet(), ManifestPermissionSet(), std::move(removed_urls),
          URLPatternSet());
      prefs()->RemoveRuntimeGrantedPermissions(extension_a_->id(),
                                               removed_permissions);

      URLPatternSet remaining_urls({example_com});
      PermissionSet remaining_permissions(
          APIPermissionSet(), ManifestPermissionSet(),
          std::move(remaining_urls), URLPatternSet());
      std::unique_ptr<const PermissionSet> retrieved_permissions =
          prefs()->GetRuntimeGrantedPermissions(extension_a_->id());
      ASSERT_TRUE(retrieved_permissions);
      EXPECT_EQ(remaining_permissions, *retrieved_permissions);
    }

    // The second extension should still have no runtime-granted permissions.
    EXPECT_TRUE(
        prefs()->GetRuntimeGrantedPermissions(extension_b_->id())->IsEmpty());
  }

  void Verify() override {
    {
      // The first extension should still have example.com as the granted
      // permission.
      URLPattern example_com(URLPattern::SCHEME_ALL, "https://example.com/*");
      URLPatternSet remaining_urls({example_com});
      PermissionSet remaining_permissions(
          APIPermissionSet(), ManifestPermissionSet(),
          std::move(remaining_urls), URLPatternSet());
      std::unique_ptr<const PermissionSet> retrieved_permissions =
          prefs()->GetRuntimeGrantedPermissions(extension_a_->id());
      ASSERT_TRUE(retrieved_permissions);
      EXPECT_EQ(remaining_permissions, *retrieved_permissions);
    }

    EXPECT_TRUE(
        prefs()->GetRuntimeGrantedPermissions(extension_b_->id())->IsEmpty());
  }

 private:
  scoped_refptr<const Extension> extension_a_;
  scoped_refptr<const Extension> extension_b_;
};
TEST_F(ExtensionPrefsRuntimeGrantedPermissions,
       ExtensionPrefsRuntimeGrantedPermissions) {}

// Tests the removal of obsolete keys from extension pref entries.
class ExtensionPrefsObsoletePrefRemoval : public ExtensionPrefsTest {
 public:
  ExtensionPrefsObsoletePrefRemoval() = default;

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

  ~ExtensionPrefsObsoletePrefRemoval() override = default;

  void Initialize() override {
    extension_ = prefs_.AddExtension("a");
    constexpr char kTestValue[] = "test_value";
    prefs()->UpdateExtensionPref(extension_->id(),
                                 ExtensionPrefs::kFakeObsoletePrefForTesting,
                                 base::Value(kTestValue));
    std::string str_value;
    EXPECT_TRUE(prefs()->ReadPrefAsString(
        extension_->id(), ExtensionPrefs::kFakeObsoletePrefForTesting,
        &str_value));
    EXPECT_EQ(kTestValue, str_value);

    prefs()->MigrateObsoleteExtensionPrefs();
  }

  void Verify() override {
    std::string str_value;
    EXPECT_FALSE(prefs()->ReadPrefAsString(
        extension_->id(), ExtensionPrefs::kFakeObsoletePrefForTesting,
        &str_value));
  }

 private:
  scoped_refptr<const Extension> extension_;
};

TEST_F(ExtensionPrefsObsoletePrefRemoval, ExtensionPrefsObsoletePrefRemoval) {}

// Tests the migration of renamed keys from extension pref entries.
class ExtensionPrefsMigratedPref : public ExtensionPrefsTest {
 public:
  ExtensionPrefsMigratedPref() = default;

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

  ~ExtensionPrefsMigratedPref() override = default;

  void Initialize() override {
    extension_ = prefs_.AddExtension("a");
    prefs()->MigrateObsoleteExtensionPrefs();
  }

  void Verify() override {}

 private:
  scoped_refptr<const Extension> extension_;
};

TEST_F(ExtensionPrefsMigratedPref, ExtensionPrefsMigratedPref) {}

// Tests the removal of obsolete keys from extension pref entries.
class ExtensionPrefsIsExternalExtensionUninstalled : public ExtensionPrefsTest {
 public:
  ExtensionPrefsIsExternalExtensionUninstalled() = default;
  ExtensionPrefsIsExternalExtensionUninstalled(
      const ExtensionPrefsIsExternalExtensionUninstalled& other) = delete;
  ExtensionPrefsIsExternalExtensionUninstalled& operator=(
      const ExtensionPrefsIsExternalExtensionUninstalled& other) = delete;
  ~ExtensionPrefsIsExternalExtensionUninstalled() override = default;

  void Initialize() override {
    uninstalled_external_id_ =
        prefs_
            .AddExtensionWithLocation("external uninstall",
                                      ManifestLocation::kExternalPref)
            ->id();
    uninstalled_by_program_external_id_ =
        prefs_
            .AddExtensionWithLocation("external uninstall by program",
                                      ManifestLocation::kExternalPref)
            ->id();
    installed_external_id_ =
        prefs_
            .AddExtensionWithLocation("external install",
                                      ManifestLocation::kExternalPref)
            ->id();
    uninstalled_internal_id_ =
        prefs_
            .AddExtensionWithLocation("internal uninstall",
                                      ManifestLocation::kInternal)
            ->id();
    installed_internal_id_ =
        prefs_
            .AddExtensionWithLocation("internal install",
                                      ManifestLocation::kInternal)
            ->id();

    prefs()->OnExtensionUninstalled(uninstalled_external_id_,
                                    ManifestLocation::kExternalPref, false);
    prefs()->OnExtensionUninstalled(uninstalled_by_program_external_id_,
                                    ManifestLocation::kExternalPref, true);
    prefs()->OnExtensionUninstalled(uninstalled_internal_id_,
                                    ManifestLocation::kInternal, false);
  }

  void Verify() override {
    EXPECT_TRUE(
        prefs()->IsExternalExtensionUninstalled(uninstalled_external_id_));
    EXPECT_FALSE(prefs()->IsExternalExtensionUninstalled(
        uninstalled_by_program_external_id_));
    EXPECT_FALSE(
        prefs()->IsExternalExtensionUninstalled(installed_external_id_));
    EXPECT_FALSE(
        prefs()->IsExternalExtensionUninstalled(uninstalled_internal_id_));
    EXPECT_FALSE(
        prefs()->IsExternalExtensionUninstalled(installed_internal_id_));
  }

 private:
  std::string uninstalled_external_id_;
  std::string uninstalled_by_program_external_id_;
  std::string installed_external_id_;
  std::string uninstalled_internal_id_;
  std::string installed_internal_id_;
};

TEST_F(ExtensionPrefsIsExternalExtensionUninstalled,
       ExtensionPrefsIsExternalExtensionUninstalled) {}

#if BUILDFLAG(IS_CHROMEOS)
class ExtensionPrefsApplyPendingUpdates
    : public ExtensionPrefsTest,
      public testing::WithParamInterface<std::tuple<std::string, bool>> {
 public:
  void Initialize() override {
    auto [pref, value] = GetParam();
    extension_ = prefs_.AddExtension("apply_pending_updates");
    prefs()->UpdateExtensionPref(extension_->id(), pref + "-pending",
                                 base::Value(value));
  }

  void Verify() override {}

 protected:
  scoped_refptr<Extension> extension_;
};

INSTANTIATE_TEST_SUITE_P(
    PendingUpdates,
    ExtensionPrefsApplyPendingUpdates,
    ::testing::Combine(::testing::Values("newAllowFileAccess", "incognito"),
                       ::testing::Bool()));

TEST_P(ExtensionPrefsApplyPendingUpdates, ExtensionPrefsApplyPendingUpdates) {
  auto id = extension_->id();
  bool actual = false;
  auto [pref, value] = GetParam();

  ASSERT_TRUE(prefs()->HasPrefForExtension(id));
  ASSERT_TRUE(prefs()->ReadPrefAsBoolean(id, pref + "-pending", &actual));
  ASSERT_EQ(actual, value);
  ASSERT_FALSE(prefs()->ReadPrefAsBoolean(id, pref, &actual));

  prefs()->ApplyPendingUpdates();

  ASSERT_FALSE(prefs()->ReadPrefAsBoolean(id, pref + "-pending", &actual));
  ASSERT_TRUE(prefs()->ReadPrefAsBoolean(id, pref, &actual));
  ASSERT_EQ(actual, value);
}
#endif  // BUILDFLAG(IS_CHROMEOS)

////////////////////////////////////////////////////////////////////////////////
// The following are ExtensionPrefs tests that don't use the same
// Initialize(), Verify(), <recreate>, Verify() flow that the others do, and
// instead just use a normal testing::Test setup.

using ExtensionPrefsSimpleTest = testing::Test;

// Tests that raw manipulation of extension disable reasons works and unknown
// values can be written / read back. This also also tests that the non-raw
// getter collapses unknown values to DISABLE_UNKNOWN.
TEST_F(ExtensionPrefsSimpleTest, DisableReasonsRawManipulation) {
  content::BrowserTaskEnvironment task_environment;
  TestExtensionPrefs prefs(base::SingleThreadTaskRunner::GetCurrentDefault(),
                           std::make_unique<TestingProfile>());
  std::string extension_id = prefs.AddExtension("Test Extension")->id();

  ExtensionPrefs* extension_prefs = prefs.prefs();
  ASSERT_FALSE(extension_prefs->IsExtensionDisabled(extension_id));

  auto passkey = ExtensionPrefs::DisableReasonRawManipulationPasskey();
  constexpr int kUnknownReason_1 = disable_reason::DISABLE_REASON_LAST + 1;
  constexpr int kUnknownReason_2 = disable_reason::DISABLE_REASON_LAST + 2;
  constexpr int kUnknownReason_3 = disable_reason::DISABLE_REASON_LAST + 3;
  constexpr disable_reason::DisableReason kKnownReason_1 =
      disable_reason::DISABLE_USER_ACTION;
  constexpr disable_reason::DisableReason kKnownReason_2 =
      disable_reason::DISABLE_PERMISSIONS_INCREASE;

  // Disable the extension with known and unknown reasons.
  extension_prefs->ReplaceRawDisableReasons(passkey, extension_id,
                                            {kKnownReason_1, kUnknownReason_1});
  EXPECT_THAT(extension_prefs->GetRawDisableReasons(passkey, extension_id),
              testing::UnorderedElementsAre(kKnownReason_1, kUnknownReason_1));
  EXPECT_THAT(extension_prefs->GetDisableReasons(extension_id),
              testing::UnorderedElementsAre(kKnownReason_1,
                                            disable_reason::DISABLE_UNKNOWN));

  // Add one known and one unknown reason.
  extension_prefs->AddRawDisableReasons(passkey, extension_id,
                                        {kKnownReason_2, kUnknownReason_2});
  EXPECT_THAT(extension_prefs->GetRawDisableReasons(passkey, extension_id),
              testing::UnorderedElementsAre(kKnownReason_1, kUnknownReason_1,
                                            kKnownReason_2, kUnknownReason_2));
  EXPECT_THAT(extension_prefs->GetDisableReasons(extension_id),
              testing::UnorderedElementsAre(kKnownReason_1, kKnownReason_2,
                                            disable_reason::DISABLE_UNKNOWN));

  // Try replacing the disable reason set.
  extension_prefs->ReplaceRawDisableReasons(passkey, extension_id,
                                            {kUnknownReason_3, kKnownReason_1});
  EXPECT_THAT(extension_prefs->GetRawDisableReasons(passkey, extension_id),
              testing::UnorderedElementsAre(kUnknownReason_3, kKnownReason_1));
  EXPECT_THAT(extension_prefs->GetDisableReasons(extension_id),
              testing::UnorderedElementsAre(kKnownReason_1,
                                            disable_reason::DISABLE_UNKNOWN));

  // Try replacing the disable reason set with only known reasons.
  extension_prefs->ReplaceRawDisableReasons(passkey, extension_id,
                                            {kKnownReason_1, kKnownReason_2});
  EXPECT_THAT(extension_prefs->GetRawDisableReasons(passkey, extension_id),
              testing::UnorderedElementsAre(kKnownReason_1, kKnownReason_2));
  EXPECT_THAT(extension_prefs->GetDisableReasons(extension_id),
              testing::UnorderedElementsAre(kKnownReason_1, kKnownReason_2));
}

TEST_F(ExtensionPrefsSimpleTest, DoesNotLoadCdpInstalledExtensions) {
  content::BrowserTaskEnvironment task_environment;
  TestExtensionPrefs prefs(base::SingleThreadTaskRunner::GetCurrentDefault(),
                           std::make_unique<TestingProfile>());

  base::DictValue dictionary;
  dictionary.Set(manifest_keys::kName, "cdp_extension");
  dictionary.Set(manifest_keys::kVersion, "0.1");
  dictionary.Set(manifest_keys::kManifestVersion, 3);
  scoped_refptr<const Extension> cdp_extension =
      prefs.AddExtensionWithManifestAndFlags(dictionary,
                                             ManifestLocation::kUnpacked,
                                             Extension::INSTALLED_VIA_CDP);

  EXPECT_TRUE(prefs.prefs()->HasPrefForExtension(cdp_extension->id()));

  prefs.RecreateExtensionPrefs();

  EXPECT_FALSE(prefs.prefs()->HasPrefForExtension(cdp_extension->id()));
}

TEST_F(ExtensionPrefsSimpleTest, CleanUpCdpInstalledExtensions) {
  content::BrowserTaskEnvironment task_environment;
  TestExtensionPrefs prefs(base::SingleThreadTaskRunner::GetCurrentDefault(),
                           std::make_unique<TestingProfile>());

  scoped_refptr<const Extension> extension =
      prefs.AddExtension("normal_extension");

  base::DictValue dictionary;
  dictionary.Set(manifest_keys::kName, "cdp_extension");
  dictionary.Set(manifest_keys::kVersion, "0.1");
  dictionary.Set(manifest_keys::kManifestVersion, 2);
  scoped_refptr<const Extension> cdp_extension =
      prefs.AddExtensionWithManifestAndFlags(dictionary,
                                             ManifestLocation::kUnpacked,
                                             Extension::INSTALLED_VIA_CDP);

  ExtensionPrefs* extension_prefs = prefs.prefs();
  EXPECT_TRUE(extension_prefs->HasPrefForExtension(extension->id()));
  EXPECT_TRUE(extension_prefs->HasPrefForExtension(cdp_extension->id()));

  extension_prefs->CleanUpCdpInstalledExtensions();

  EXPECT_TRUE(extension_prefs->HasPrefForExtension(extension->id()));
  EXPECT_FALSE(extension_prefs->HasPrefForExtension(cdp_extension->id()));
}

// Tests that duplicate sub-event filters accumulated in prefs from prior
// sessions are deduplicated, keeping the last filter. See crbug.com/502402731.
// TODO(andreaorru): remove this after M156, once non-duplicating webRequest
// behavior has been stable for a while.
TEST_F(ExtensionPrefsSimpleTest, CleanUpDuplicateSubEventFilters) {
  content::BrowserTaskEnvironment task_environment;
  TestExtensionPrefs prefs(base::SingleThreadTaskRunner::GetCurrentDefault(),
                           std::make_unique<TestingProfile>());
  std::string extension_id = prefs.AddExtension("Test Extension")->id();
  ExtensionPrefs* extension_prefs = prefs.prefs();
  const std::string kEventName = "webRequest.onBeforeRequest/s0";

  for (const char* pref_key : {EventRouter::kFilteredEvents,
                               EventRouter::kFilteredServiceWorkerEvents}) {
    // Populate prefs with two filters under the sub-event name.
    {
      ExtensionPrefs::ScopedDictionaryUpdate update(extension_prefs,
                                                    extension_id, pref_key);
      auto filtered_events = update.Create();
      base::ListValue filter_list;
      base::DictValue filter1;
      filter1.Set("hostSuffix", "foo.com");
      base::DictValue filter2;
      filter2.Set("hostSuffix", "bar.com");
      filter_list.Append(std::move(filter1));
      filter_list.Append(std::move(filter2));
      filtered_events->SetKey(kEventName, base::Value(std::move(filter_list)));
    }

    extension_prefs->CleanUpDuplicateSubEventFilters();

    // Verify that only the last filter (bar.com) remains.
    const base::DictValue* dict =
        extension_prefs->ReadPrefAsDict(extension_id, pref_key);
    ASSERT_TRUE(dict);
    const base::Value* value = dict->Find(kEventName);
    ASSERT_TRUE(value);
    ASSERT_TRUE(value->is_list());
    const auto& filter_list = value->GetList();
    EXPECT_EQ(1u, filter_list.size());
    const std::string* host_suffix =
        filter_list[0].GetDict().FindString("hostSuffix");
    EXPECT_TRUE(host_suffix);
    EXPECT_EQ("bar.com", *host_suffix);
  }
}

// Tests that ExtensionPrefs initialization prunes empty filter lists and
// malformed non-list values from preferences, preserves non-empty lists, and
// removes the preference dictionary entirely when no valid entries remain (for
// both installed and stale extensions).
// Regression test for crbug.com/526929792.
// TODO(andreaorru): Remove this after M157 alongside
// CleanUpEmptyFilteredEventLists().
TEST_F(ExtensionPrefsSimpleTest, CleanUpEmptyFilteredEventLists) {
  content::BrowserTaskEnvironment task_environment;
  TestExtensionPrefs prefs(base::SingleThreadTaskRunner::GetCurrentDefault(),
                           std::make_unique<TestingProfile>());
  const std::string installed_id = prefs.AddExtension("Test Extension")->id();
  // A valid ID with no installed extension to test cleanup of stale entries.
  const std::string stale_id(32, 'a');
  ExtensionPrefs* extension_prefs = prefs.prefs();

  for (const char* pref_key : {EventRouter::kFilteredEvents,
                               EventRouter::kFilteredServiceWorkerEvents}) {
    // Populate the installed extension with two empty filter lists, one
    // non-empty list, and one malformed non-list value; populate the stale
    // entry with only an empty list.
    {
      ExtensionPrefs::ScopedDictionaryUpdate update(extension_prefs,
                                                    installed_id, pref_key);
      auto filtered_events = update.Create();
      filtered_events->SetKey("webRequest.onBeforeRequest/s0",
                              base::Value(base::ListValue()));
      base::ListValue filter_list;
      base::DictValue filter;
      filter.Set("hostSuffix", "foo.com");
      filter_list.Append(std::move(filter));
      filtered_events->SetKey("webRequest.onBeforeRequest/s1",
                              base::Value(std::move(filter_list)));
      filtered_events->SetKey("webNavigation.onCommitted",
                              base::Value(base::ListValue()));
      filtered_events->SetKey("webRequest.onCompleted/s2", base::Value(42));
    }
    {
      ExtensionPrefs::ScopedDictionaryUpdate update(extension_prefs, stale_id,
                                                    pref_key);
      update.Create()->SetKey("webRequest.onBeforeRequest/s0",
                              base::Value(base::ListValue()));
    }
  }

  // Recreating ExtensionPrefs triggers cleanup in the constructor.
  prefs.RecreateExtensionPrefs();
  extension_prefs = prefs.prefs();

  for (const char* pref_key : {EventRouter::kFilteredEvents,
                               EventRouter::kFilteredServiceWorkerEvents}) {
    // Only the non-empty list of the installed extension remains.
    const base::DictValue* dict =
        extension_prefs->ReadPrefAsDict(installed_id, pref_key);
    ASSERT_TRUE(dict);
    EXPECT_FALSE(dict->contains("webRequest.onBeforeRequest/s0"));
    EXPECT_FALSE(dict->contains("webNavigation.onCommitted"));
    EXPECT_FALSE(dict->contains("webRequest.onCompleted/s2"));
    const base::ListValue* filter_list =
        dict->FindList("webRequest.onBeforeRequest/s1");
    ASSERT_TRUE(filter_list);
    ASSERT_EQ(1u, filter_list->size());
    const std::string* host_suffix =
        (*filter_list)[0].GetDict().FindString("hostSuffix");
    ASSERT_TRUE(host_suffix);
    EXPECT_EQ("foo.com", *host_suffix);

    // The stale entry had only an empty list, so its preference was deleted.
    EXPECT_FALSE(extension_prefs->ReadPrefAsDict(stale_id, pref_key));
  }
  // Unrelated preferences should remain intact.
  EXPECT_TRUE(extension_prefs->HasPrefForExtension(installed_id));
}

// Tests the generic Get/Set functions for profile wide extension prefs.
TEST_F(ExtensionPrefsSimpleTest, ProfileExtensionPrefsMapTest) {
  constexpr PrefMap kTestBooleanPref = {"test.boolean", PrefType::kBool,
                                        PrefScope::kProfile};
  constexpr PrefMap kTestIntegerPref = {"test.integer", PrefType::kInteger,
                                        PrefScope::kProfile};
  constexpr PrefMap kTestStringPref = {"test.string", PrefType::kString,
                                       PrefScope::kProfile};
  constexpr PrefMap kTestTimePref = {"test.time", PrefType::kTime,
                                     PrefScope::kProfile};
  constexpr PrefMap kTestGURLPref = {"test.gurl", PrefType::kGURL,
                                     PrefScope::kProfile};
  constexpr PrefMap kTestDictPref = {"test.dict", PrefType::kDictionary,
                                     PrefScope::kProfile};

  content::BrowserTaskEnvironment task_environment_;
  TestExtensionPrefs prefs(base::SingleThreadTaskRunner::GetCurrentDefault(),
                           std::make_unique<TestingProfile>());

  auto* registry = prefs.pref_registry().get();
  registry->RegisterBooleanPref(kTestBooleanPref.name, false);
  registry->RegisterIntegerPref(kTestIntegerPref.name, 0);
  registry->RegisterStringPref(kTestStringPref.name, std::string());
  registry->RegisterTimePref(kTestTimePref.name, base::Time());
  registry->RegisterStringPref(kTestGURLPref.name, std::string());
  registry->RegisterDictionaryPref(kTestDictPref.name);

  prefs.prefs()->SetBooleanPref(kTestBooleanPref, true);
  prefs.prefs()->SetIntegerPref(kTestIntegerPref, 1);
  prefs.prefs()->SetStringPref(kTestStringPref, "foo");
  base::Time time = base::Time::Now();
  prefs.prefs()->SetTimePref(kTestTimePref, time);
  GURL url = GURL("https://example/com");
  prefs.prefs()->SetGURLPref(kTestGURLPref, url);
  base::DictValue dict;
  dict.Set("key", "val");
  prefs.prefs()->SetDictionaryPref(kTestDictPref, std::move(dict));

  EXPECT_TRUE(prefs.prefs()->GetPrefAsBoolean(kTestBooleanPref));
  EXPECT_EQ(prefs.prefs()->GetPrefAsInteger(kTestIntegerPref), 1);
  EXPECT_EQ(prefs.prefs()->GetPrefAsString(kTestStringPref), "foo");
  EXPECT_EQ(prefs.prefs()->GetPrefAsTime(kTestTimePref), time);
  EXPECT_EQ(prefs.prefs()->GetPrefAsGURL(kTestGURLPref), url);
  const std::string* string_ptr =
      prefs.prefs()->GetPrefAsDictionary(kTestDictPref).FindString("key");
  EXPECT_TRUE(string_ptr);
  EXPECT_EQ(*string_ptr, "val");
}

TEST_F(ExtensionPrefsSimpleTest, DisableReasonsObserverTest) {
  content::BrowserTaskEnvironment task_environment;
  TestExtensionPrefs test_prefs(
      base::SingleThreadTaskRunner::GetCurrentDefault(),
      std::make_unique<TestingProfile>());
  const ExtensionId extension_id = test_prefs.AddExtensionAndReturnId("test");

  class Observer : public ExtensionPrefsObserver {
   public:
    explicit Observer(ExtensionPrefs* prefs) {
      scoped_observation_.Observe(prefs);
    }
    ~Observer() override = default;

    MOCK_METHOD(void,
                OnExtensionDisableReasonsChanged,
                (const ExtensionId& extension_id,
                 DisableReasonSet disabled_reasons),
                (override));
    MOCK_METHOD(void,
                OnExtensionStateChanged,
                (const ExtensionId& extension_id, bool state),
                (override));

   private:
    base::ScopedObservation<ExtensionPrefs, ExtensionPrefsObserver>
        scoped_observation_{this};
  };

  ExtensionPrefs* extension_prefs = test_prefs.prefs();
  ASSERT_FALSE(extension_prefs->IsExtensionDisabled(extension_id));

  const disable_reason::DisableReason disable_reason_1 =
      disable_reason::DISABLE_USER_ACTION;
  const disable_reason::DisableReason disable_reason_2 =
      disable_reason::DISABLE_PERMISSIONS_INCREASE;

  // The extension is initially enabled. This test sequentially adds and then
  // removes two disable reasons. This is how the disable reason change:
  //
  // S0: {}
  // S1: {disable_reason_1}
  // S2: {disable_reason_1, disable_reason_2}
  // S3: {disable_reason_1}
  // S4: {}
  //
  // OnExtensionDisableReasonsChanged() should be called for every state, except
  // the initial state (S0).
  //
  // OnExtensionStateChanged() should be called when the first disable reason is
  // added (S1) and when the last disable reason is removed (S4).
  Observer observer(extension_prefs);
  ::testing::InSequence sequence;

  // S1.
  EXPECT_CALL(observer, OnExtensionDisableReasonsChanged(
                            extension_id,
                            testing::UnorderedElementsAre(disable_reason_1)))
      .Times(1);
  EXPECT_CALL(observer, OnExtensionStateChanged(extension_id, false)).Times(1);

  // S2.
  EXPECT_CALL(observer,
              OnExtensionDisableReasonsChanged(
                  extension_id, testing::UnorderedElementsAre(
                                    disable_reason_1, disable_reason_2)))
      .Times(1);

  // S3.
  EXPECT_CALL(observer, OnExtensionDisableReasonsChanged(
                            extension_id,
                            testing::UnorderedElementsAre(disable_reason_1)))
      .Times(1);

  // S4.
  EXPECT_CALL(observer, OnExtensionDisableReasonsChanged(extension_id,
                                                         testing::IsEmpty()))
      .Times(1);
  EXPECT_CALL(observer, OnExtensionStateChanged(extension_id, true)).Times(1);

  extension_prefs->AddDisableReason(extension_id, disable_reason_1);
  extension_prefs->AddDisableReason(extension_id, disable_reason_2);
  extension_prefs->RemoveDisableReason(extension_id, disable_reason_2);
  extension_prefs->RemoveDisableReason(extension_id, disable_reason_1);
}

TEST_F(ExtensionPrefsSimpleTest, ExtensionSpecificPrefsMapTest) {
  constexpr PrefMap kTestBooleanPref = {"test.boolean", PrefType::kBool,
                                        PrefScope::kExtensionSpecific};
  constexpr PrefMap kTestIntegerPref = {"test.integer", PrefType::kInteger,
                                        PrefScope::kExtensionSpecific};
  constexpr PrefMap kTestStringPref = {"test.string", PrefType::kString,
                                       PrefScope::kExtensionSpecific};
  constexpr PrefMap kTestDictPref = {"test.dict", PrefType::kDictionary,
                                     PrefScope::kExtensionSpecific};
  constexpr PrefMap kTestListPref = {"test.list", PrefType::kList,
                                     PrefScope::kExtensionSpecific};
  constexpr PrefMap kTestTimePref = {"test.time", PrefType::kTime,
                                     PrefScope::kExtensionSpecific};

  content::BrowserTaskEnvironment task_environment_;
  TestExtensionPrefs prefs(base::SingleThreadTaskRunner::GetCurrentDefault(),
                           std::make_unique<TestingProfile>());

  std::string extension_id = prefs.AddExtensionAndReturnId("1");
  prefs.prefs()->SetBooleanPref(extension_id, kTestBooleanPref, true);
  prefs.prefs()->SetIntegerPref(extension_id, kTestIntegerPref, 1);
  prefs.prefs()->SetStringPref(extension_id, kTestStringPref, "foo");
  base::DictValue dict;
  dict.Set("key", "val");
  prefs.prefs()->SetDictionaryPref(extension_id, kTestDictPref,
                                   std::move(dict));
  base::ListValue list;
  list.Append("list_val");
  prefs.prefs()->SetListPref(extension_id, kTestListPref, std::move(list));
  base::Time time = base::Time::Now();
  prefs.prefs()->SetTimePref(extension_id, kTestTimePref, time);

  bool bool_value = false;
  EXPECT_TRUE(prefs.prefs()->ReadPrefAsBoolean(extension_id, kTestBooleanPref,
                                               &bool_value));
  EXPECT_TRUE(bool_value);
  int int_value = 0;
  EXPECT_TRUE(prefs.prefs()->ReadPrefAsInteger(extension_id, kTestIntegerPref,
                                               &int_value));
  EXPECT_EQ(int_value, 1);
  std::string string_value;
  EXPECT_TRUE(prefs.prefs()->ReadPrefAsString(extension_id, kTestStringPref,
                                              &string_value));
  EXPECT_EQ(string_value, "foo");

  const base::DictValue* dict_val =
      prefs.prefs()->ReadPrefAsDictionary(extension_id, kTestDictPref);
  ASSERT_TRUE(dict_val);
  const std::string* string_ptr = dict_val->FindString("key");
  ASSERT_TRUE(string_ptr);
  EXPECT_EQ(*string_ptr, "val");

  const base::ListValue* list_val =
      prefs.prefs()->ReadPrefAsList(extension_id, kTestListPref);
  ASSERT_TRUE(list_val);
  ASSERT_FALSE(list_val->empty());
  ASSERT_TRUE((*list_val)[0].is_string());
  EXPECT_EQ((*list_val)[0].GetString(), "list_val");

  EXPECT_EQ(time, prefs.prefs()->ReadPrefAsTime(extension_id, kTestTimePref));
}

TEST_F(ExtensionPrefsSimpleTest, HasOnlyDisableReasonTest) {
  content::BrowserTaskEnvironment task_environment;
  TestExtensionPrefs prefs(base::SingleThreadTaskRunner::GetCurrentDefault(),
                           std::make_unique<TestingProfile>());
  std::string extension_id = prefs.AddExtension("Test Extension")->id();
  ExtensionPrefs* extension_prefs = prefs.prefs();

  // No disable reasons to begin with.
  EXPECT_FALSE(extension_prefs->HasOnlyDisableReason(
      extension_id, disable_reason::DISABLE_USER_ACTION));

  // Add a disable reason.
  extension_prefs->AddDisableReason(extension_id,
                                    disable_reason::DISABLE_USER_ACTION);
  EXPECT_TRUE(extension_prefs->HasOnlyDisableReason(
      extension_id, disable_reason::DISABLE_USER_ACTION));

  // Add another disable reason.
  extension_prefs->AddDisableReason(extension_id,
                                    disable_reason::DISABLE_EXTERNAL_EXTENSION);
  EXPECT_FALSE(extension_prefs->HasOnlyDisableReason(
      extension_id, disable_reason::DISABLE_USER_ACTION));
  EXPECT_FALSE(extension_prefs->HasOnlyDisableReason(
      extension_id, disable_reason::DISABLE_EXTERNAL_EXTENSION));

  // Remove the first disable reason.
  extension_prefs->RemoveDisableReason(extension_id,
                                       disable_reason::DISABLE_USER_ACTION);
  EXPECT_TRUE(extension_prefs->HasOnlyDisableReason(
      extension_id, disable_reason::DISABLE_EXTERNAL_EXTENSION));
}

TEST_F(ExtensionPrefsSimpleTest, RemoveDisableReasons) {
  content::BrowserTaskEnvironment task_environment;
  TestExtensionPrefs prefs(base::SingleThreadTaskRunner::GetCurrentDefault(),
                           std::make_unique<TestingProfile>());
  std::string extension_id = prefs.AddExtension("Test Extension")->id();
  ExtensionPrefs* extension_prefs = prefs.prefs();
  extension_prefs->AddDisableReasons(
      extension_id, {disable_reason::DISABLE_USER_ACTION,
                     disable_reason::DISABLE_EXTERNAL_EXTENSION,
                     disable_reason::DISABLE_CORRUPTED});

  // Remove one disable reason.
  extension_prefs->RemoveDisableReason(extension_id,
                                       disable_reason::DISABLE_USER_ACTION);
  EXPECT_THAT(
      extension_prefs->GetDisableReasons(extension_id),
      testing::UnorderedElementsAre(disable_reason::DISABLE_EXTERNAL_EXTENSION,
                                    disable_reason::DISABLE_CORRUPTED));

  // Try removing a disable reason that doesn't exist.
  extension_prefs->RemoveDisableReasons(extension_id,
                                        {disable_reason::DISABLE_RELOAD});
  EXPECT_THAT(
      extension_prefs->GetDisableReasons(extension_id),
      testing::UnorderedElementsAre(disable_reason::DISABLE_EXTERNAL_EXTENSION,
                                    disable_reason::DISABLE_CORRUPTED));

  // Remove the remaining disable reasons.
  extension_prefs->RemoveDisableReasons(
      extension_id, {disable_reason::DISABLE_EXTERNAL_EXTENSION,
                     disable_reason::DISABLE_CORRUPTED});
  EXPECT_TRUE(extension_prefs->GetDisableReasons(extension_id).empty());
}

}  // namespace extensions
