chromium/chrome/installer/util/google_update_settings_unittest.cc

// 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.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chrome/installer/util/google_update_settings.h"

#include <windows.h>

#include <stddef.h>

#include <memory>
#include <string_view>

#include "base/base_paths.h"
#include "base/hash/hash.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_path_override.h"
#include "base/test/test_reg_util_win.h"
#include "base/win/registry.h"
#include "base/win/shlwapi.h"  // For SHDeleteKey.
#include "build/branding_buildflags.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/install_static/install_details.h"
#include "chrome/install_static/install_util.h"
#include "chrome/install_static/test/scoped_install_details.h"
#include "chrome/installer/util/additional_parameters.h"
#include "chrome/installer/util/fake_installation_state.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/helper.h"
#include "chrome/installer/util/util_constants.h"
#include "chrome/installer/util/work_item_list.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::win::RegKey;

namespace {

// This test fixture redirects the HKLM and HKCU registry hives for
// the duration of the test to make it independent of the machine
// and user settings.
class GoogleUpdateSettingsTest : public testing::Test {
 protected:
  enum SystemUserInstall {
    SYSTEM_INSTALL,
    USER_INSTALL,
  };

  GoogleUpdateSettingsTest()
      : program_files_override_(base::DIR_PROGRAM_FILES),
        program_files_x86_override_(base::DIR_PROGRAM_FILESX86) {}

  void SetUp() override {
    ASSERT_NO_FATAL_FAILURE(
        registry_overrides_.OverrideRegistry(HKEY_LOCAL_MACHINE));
    ASSERT_NO_FATAL_FAILURE(
        registry_overrides_.OverrideRegistry(HKEY_CURRENT_USER));
  }

  // Creates "ap" key with the value given as parameter. Also adds work
  // items to work_item_list given so that they can be rolled back later.
  bool CreateApKey(WorkItemList* work_item_list, const std::wstring& value) {
    HKEY reg_root = HKEY_CURRENT_USER;
    std::wstring reg_key = GetApKeyPath();
    work_item_list->AddCreateRegKeyWorkItem(reg_root, reg_key, KEY_WOW64_32KEY);
    work_item_list->AddSetRegValueWorkItem(reg_root, reg_key, KEY_WOW64_32KEY,
                                           google_update::kRegApField,
                                           value.c_str(), true);
    if (!work_item_list->Do()) {
      work_item_list->Rollback();
      return false;
    }
    return true;
  }

  static std::wstring GetProductGuid() { return install_static::GetAppGuid(); }

  // Returns the key path of "ap" key, e.g.:
  // Google\Update\ClientState\<kTestProductGuid>
  std::wstring GetApKeyPath() {
    return install_static::GetClientStateKeyPath();
  }

  // Utility method to read "ap" key value
  std::wstring ReadApKeyValue() {
    RegKey key;
    std::wstring ap_key_value;
    std::wstring reg_key = GetApKeyPath();
    if (key.Open(HKEY_CURRENT_USER, reg_key.c_str(),
                 KEY_WOW64_32KEY | KEY_QUERY_VALUE) == ERROR_SUCCESS) {
      key.ReadValue(google_update::kRegApField, &ap_key_value);
    }

    return ap_key_value;
  }

  bool SetUpdatePolicyForAppGuid(const std::wstring& app_guid,
                                 GoogleUpdateSettings::UpdatePolicy policy) {
    RegKey policy_key;
    if (policy_key.Create(HKEY_LOCAL_MACHINE,
                          GoogleUpdateSettings::kPoliciesKey,
                          KEY_SET_VALUE) == ERROR_SUCCESS) {
      std::wstring app_update_override(
          GoogleUpdateSettings::kUpdateOverrideValuePrefix);
      app_update_override.append(app_guid);
      return policy_key.WriteValue(app_update_override.c_str(),
                                   static_cast<DWORD>(policy)) == ERROR_SUCCESS;
    }
    return false;
  }

  GoogleUpdateSettings::UpdatePolicy GetUpdatePolicyForAppGuid(
      const std::wstring& app_guid) {
    RegKey policy_key;
    if (policy_key.Create(HKEY_LOCAL_MACHINE,
                          GoogleUpdateSettings::kPoliciesKey,
                          KEY_QUERY_VALUE) == ERROR_SUCCESS) {
      std::wstring app_update_override(
          GoogleUpdateSettings::kUpdateOverrideValuePrefix);
      app_update_override.append(app_guid);

      DWORD value;
      if (policy_key.ReadValueDW(app_update_override.c_str(), &value) ==
          ERROR_SUCCESS) {
        return static_cast<GoogleUpdateSettings::UpdatePolicy>(value);
      }
    }
    return GoogleUpdateSettings::UPDATE_POLICIES_COUNT;
  }

  bool SetGlobalUpdatePolicy(GoogleUpdateSettings::UpdatePolicy policy) {
    RegKey policy_key;
    return policy_key.Create(HKEY_LOCAL_MACHINE,
                             GoogleUpdateSettings::kPoliciesKey,
                             KEY_SET_VALUE) == ERROR_SUCCESS &&
           policy_key.WriteValue(GoogleUpdateSettings::kUpdatePolicyValue,
                                 static_cast<DWORD>(policy)) == ERROR_SUCCESS;
  }

  GoogleUpdateSettings::UpdatePolicy GetGlobalUpdatePolicy() {
    RegKey policy_key;
    DWORD value;
    return (policy_key.Create(HKEY_LOCAL_MACHINE,
                              GoogleUpdateSettings::kPoliciesKey,
                              KEY_QUERY_VALUE) == ERROR_SUCCESS &&
            policy_key.ReadValueDW(GoogleUpdateSettings::kUpdatePolicyValue,
                                   &value) == ERROR_SUCCESS)
               ? static_cast<GoogleUpdateSettings::UpdatePolicy>(value)
               : GoogleUpdateSettings::UPDATE_POLICIES_COUNT;
  }

  bool SetUpdateTimeoutOverride(DWORD time_in_minutes) {
    RegKey policy_key;
    return policy_key.Create(HKEY_LOCAL_MACHINE,
                             GoogleUpdateSettings::kPoliciesKey,
                             KEY_SET_VALUE) == ERROR_SUCCESS &&
           policy_key.WriteValue(
               GoogleUpdateSettings::kCheckPeriodOverrideMinutes,
               time_in_minutes) == ERROR_SUCCESS;
  }

  // Path overrides so that SHGetFolderPath isn't needed after the registry
  // is overridden.
  base::ScopedPathOverride program_files_override_;
  base::ScopedPathOverride program_files_x86_override_;
  registry_util::RegistryOverrideManager registry_overrides_;
};

}  // namespace

// Run through all combinations of diff vs. full install, success and failure
// results, and a fistful of initial "ap" values checking that the expected
// final "ap" value is generated by
// GoogleUpdateSettings::UpdateGoogleUpdateApKey.
TEST_F(GoogleUpdateSettingsTest, UpdateGoogleUpdateApKey) {
  const installer::ArchiveType archive_types[] = {
      installer::UNKNOWN_ARCHIVE_TYPE, installer::FULL_ARCHIVE_TYPE,
      installer::INCREMENTAL_ARCHIVE_TYPE};
  const int results[] = {installer::FIRST_INSTALL_SUCCESS,
                         installer::INSTALL_FAILED};
  const wchar_t* const plain[] = {L"", L"1.1", L"1.1-dev"};
  const wchar_t* const full[] = {L"-full", L"1.1-full", L"1.1-dev-full"};
  static_assert(std::size(full) == std::size(plain), "bad full array size");
  const wchar_t* const* input_arrays[] = {plain, full};
  for (const installer::ArchiveType archive_type : archive_types) {
    SCOPED_TRACE(::testing::Message()
                 << "archive_type="
                 << (archive_type == installer::UNKNOWN_ARCHIVE_TYPE
                         ? "UNKNOWN"
                         : (archive_type == installer::FULL_ARCHIVE_TYPE
                                ? "FULL"
                                : "INCREMENTAL")));
    for (const int result : results) {
      SCOPED_TRACE(::testing::Message()
                   << "result="
                   << (result == installer::FIRST_INSTALL_SUCCESS ? "SUCCESS"
                                                                  : "FAILED"));
      // The archive type will/must always be known on install success.
      if (archive_type == installer::UNKNOWN_ARCHIVE_TYPE &&
          result == installer::FIRST_INSTALL_SUCCESS) {
        continue;
      }
      const wchar_t* const* outputs = nullptr;
      if (result == installer::FIRST_INSTALL_SUCCESS ||
          archive_type == installer::FULL_ARCHIVE_TYPE) {
        outputs = plain;
      } else if (archive_type == installer::INCREMENTAL_ARCHIVE_TYPE) {
        outputs = full;
      }  // else if (archive_type == UNKNOWN) see below

      for (const wchar_t* const* inputs : input_arrays) {
        if (archive_type == installer::UNKNOWN_ARCHIVE_TYPE) {
          // "-full" is untouched if the archive type is unknown.
          if (inputs == full)
            outputs = full;
          else
            outputs = plain;
        }
        for (size_t input_idx = 0; input_idx < std::size(plain); ++input_idx) {
          const wchar_t* input = inputs[input_idx];
          const wchar_t* output = outputs[input_idx];
          SCOPED_TRACE(::testing::Message() << "input=\"" << input << "\"");
          SCOPED_TRACE(::testing::Message() << "output=\"" << output << "\"");

          std::unique_ptr<WorkItemList> work_item_list(
              WorkItem::CreateWorkItemList());

          ASSERT_TRUE(CreateApKey(work_item_list.get(), input));
          installer::AdditionalParameters ap;
          if (std::wstring_view(output) == ap.value()) {
            EXPECT_FALSE(GoogleUpdateSettings::UpdateGoogleUpdateApKey(
                archive_type, result, &ap));
          } else {
            EXPECT_TRUE(GoogleUpdateSettings::UpdateGoogleUpdateApKey(
                archive_type, result, &ap));
          }
          EXPECT_STREQ(output, ap.value());
        }
      }
    }
  }
}

TEST_F(GoogleUpdateSettingsTest, UpdateInstallStatusTest) {
  std::unique_ptr<WorkItemList> work_item_list(WorkItem::CreateWorkItemList());
  // Test incremental install failure
  ASSERT_TRUE(CreateApKey(work_item_list.get(), L""))
      << "Failed to create ap key.";
  GoogleUpdateSettings::UpdateInstallStatus(
      false, installer::INCREMENTAL_ARCHIVE_TYPE, installer::INSTALL_FAILED);
  EXPECT_STREQ(ReadApKeyValue().c_str(), L"-full");
  work_item_list->Rollback();

  work_item_list.reset(WorkItem::CreateWorkItemList());
  // Test incremental install success
  ASSERT_TRUE(CreateApKey(work_item_list.get(), L""))
      << "Failed to create ap key.";
  GoogleUpdateSettings::UpdateInstallStatus(false,
                                            installer::INCREMENTAL_ARCHIVE_TYPE,
                                            installer::FIRST_INSTALL_SUCCESS);
  EXPECT_STREQ(ReadApKeyValue().c_str(), L"");
  work_item_list->Rollback();

  work_item_list.reset(WorkItem::CreateWorkItemList());
  // Test full install failure
  ASSERT_TRUE(CreateApKey(work_item_list.get(), L"-full"))
      << "Failed to create ap key.";
  GoogleUpdateSettings::UpdateInstallStatus(false, installer::FULL_ARCHIVE_TYPE,
                                            installer::INSTALL_FAILED);
  EXPECT_STREQ(ReadApKeyValue().c_str(), L"");
  work_item_list->Rollback();

  work_item_list.reset(WorkItem::CreateWorkItemList());
  // Test full install success
  ASSERT_TRUE(CreateApKey(work_item_list.get(), L"-full"))
      << "Failed to create ap key.";
  GoogleUpdateSettings::UpdateInstallStatus(false, installer::FULL_ARCHIVE_TYPE,
                                            installer::FIRST_INSTALL_SUCCESS);
  EXPECT_STREQ(ReadApKeyValue().c_str(), L"");
  work_item_list->Rollback();

  work_item_list.reset(WorkItem::CreateWorkItemList());
  // Test the case of when "ap" key doesnt exist at all
  std::wstring ap_key_value = ReadApKeyValue();
  std::wstring reg_key = GetApKeyPath();
  HKEY reg_root = HKEY_CURRENT_USER;
  bool ap_key_deleted = false;
  RegKey key;
  if (key.Open(HKEY_CURRENT_USER, reg_key.c_str(),
               KEY_WOW64_32KEY | KEY_SET_VALUE) != ERROR_SUCCESS) {
    work_item_list->AddCreateRegKeyWorkItem(reg_root, reg_key, KEY_WOW64_32KEY);
    ASSERT_TRUE(work_item_list->Do()) << "Failed to create ClientState key.";
  } else if (key.DeleteValue(google_update::kRegApField) == ERROR_SUCCESS) {
    ap_key_deleted = true;
  }
  // try differential installer
  GoogleUpdateSettings::UpdateInstallStatus(
      false, installer::INCREMENTAL_ARCHIVE_TYPE, installer::INSTALL_FAILED);
  EXPECT_STREQ(ReadApKeyValue().c_str(), L"-full");
  // try full installer now
  GoogleUpdateSettings::UpdateInstallStatus(false, installer::FULL_ARCHIVE_TYPE,
                                            installer::INSTALL_FAILED);
  EXPECT_STREQ(ReadApKeyValue().c_str(), L"");
  // Now cleanup to leave the system in unchanged state.
  // - Diff installer creates an ap key if it didn't exist, so delete this ap
  // key
  // - If we created any reg key path for ap, roll it back
  // - Finally restore the original value of ap key.
  if (key.Open(HKEY_CURRENT_USER, reg_key.c_str(),
               KEY_WOW64_32KEY | KEY_SET_VALUE) == ERROR_SUCCESS) {
    key.DeleteValue(google_update::kRegApField);
  }
  work_item_list->Rollback();
  if (ap_key_deleted) {
    work_item_list.reset(WorkItem::CreateWorkItemList());
    ASSERT_TRUE(CreateApKey(work_item_list.get(), ap_key_value))
        << "Failed to restore ap key.";
  }
}

TEST_F(GoogleUpdateSettingsTest, SetEulaConsent) {
  using installer::FakeInstallationState;

  const bool system_level = true;
  FakeInstallationState machine_state;

  // Chrome is installed.
  machine_state.AddChrome(system_level,
                          new base::Version(chrome::kChromeVersion));

  RegKey key;
  DWORD value;

  // eulaconsent is set on the product.
  EXPECT_TRUE(GoogleUpdateSettings::SetEulaConsent(machine_state, true));
  EXPECT_EQ(ERROR_SUCCESS,
            key.Open(HKEY_LOCAL_MACHINE,
                     install_static::GetClientStateMediumKeyPath().c_str(),
                     KEY_QUERY_VALUE));
  EXPECT_EQ(ERROR_SUCCESS,
            key.ReadValueDW(google_update::kRegEulaAceptedField, &value));
  EXPECT_EQ(1U, value);
}

// Test that the appropriate default is returned if no update override is
// present.
TEST_F(GoogleUpdateSettingsTest, GetAppUpdatePolicyNoOverride) {
  // There are no policies at all.
  EXPECT_EQ(ERROR_FILE_NOT_FOUND,
            RegKey().Open(HKEY_LOCAL_MACHINE,
                          GoogleUpdateSettings::kPoliciesKey, KEY_QUERY_VALUE));
  bool is_overridden = true;
  EXPECT_EQ(GoogleUpdateSettings::kDefaultUpdatePolicy,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_FALSE(is_overridden);

  // The policy key exists, but there are no values of interest present.
  EXPECT_EQ(ERROR_SUCCESS,
            RegKey().Create(HKEY_LOCAL_MACHINE,
                            GoogleUpdateSettings::kPoliciesKey, KEY_SET_VALUE));
  EXPECT_EQ(ERROR_SUCCESS,
            RegKey().Open(HKEY_LOCAL_MACHINE,
                          GoogleUpdateSettings::kPoliciesKey, KEY_QUERY_VALUE));
  is_overridden = true;
  EXPECT_EQ(GoogleUpdateSettings::kDefaultUpdatePolicy,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_FALSE(is_overridden);
}

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)

// Test that the default override is returned if no app-specific override is
// present.
TEST_F(GoogleUpdateSettingsTest, GetAppUpdatePolicyDefaultOverride) {
  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(GoogleUpdateSettings::kUpdatePolicyValue,
                            static_cast<DWORD>(0)));
  bool is_overridden = true;
  EXPECT_EQ(GoogleUpdateSettings::UPDATES_DISABLED,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_FALSE(is_overridden);

  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(GoogleUpdateSettings::kUpdatePolicyValue,
                            static_cast<DWORD>(1)));
  is_overridden = true;
  EXPECT_EQ(GoogleUpdateSettings::AUTOMATIC_UPDATES,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_FALSE(is_overridden);

  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(GoogleUpdateSettings::kUpdatePolicyValue,
                            static_cast<DWORD>(2)));
  is_overridden = true;
  EXPECT_EQ(GoogleUpdateSettings::MANUAL_UPDATES_ONLY,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_FALSE(is_overridden);

  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(GoogleUpdateSettings::kUpdatePolicyValue,
                            static_cast<DWORD>(3)));
  is_overridden = true;
  EXPECT_EQ(GoogleUpdateSettings::AUTO_UPDATES_ONLY,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_FALSE(is_overridden);

  // The default policy should be in force for bogus values.
  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(GoogleUpdateSettings::kUpdatePolicyValue,
                            static_cast<DWORD>(4)));
  is_overridden = true;
  EXPECT_EQ(GoogleUpdateSettings::kDefaultUpdatePolicy,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_FALSE(is_overridden);
}

// Test that an app-specific override is used if present.
TEST_F(GoogleUpdateSettingsTest, GetAppUpdatePolicyAppOverride) {
  std::wstring app_policy_value(
      GoogleUpdateSettings::kUpdateOverrideValuePrefix);
  app_policy_value.append(GetProductGuid());

  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(GoogleUpdateSettings::kUpdatePolicyValue,
                            static_cast<DWORD>(1)));
  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(app_policy_value.c_str(), static_cast<DWORD>(0)));
  bool is_overridden = false;
  EXPECT_EQ(GoogleUpdateSettings::UPDATES_DISABLED,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_TRUE(is_overridden);

  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(GoogleUpdateSettings::kUpdatePolicyValue,
                            static_cast<DWORD>(0)));
  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(app_policy_value.c_str(), static_cast<DWORD>(1)));
  is_overridden = false;
  EXPECT_EQ(GoogleUpdateSettings::AUTOMATIC_UPDATES,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_TRUE(is_overridden);

  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(app_policy_value.c_str(), static_cast<DWORD>(2)));
  is_overridden = false;
  EXPECT_EQ(GoogleUpdateSettings::MANUAL_UPDATES_ONLY,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_TRUE(is_overridden);

  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(app_policy_value.c_str(), static_cast<DWORD>(3)));
  is_overridden = false;
  EXPECT_EQ(GoogleUpdateSettings::AUTO_UPDATES_ONLY,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_TRUE(is_overridden);

  // The default policy should be in force for bogus values.
  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                   KEY_SET_VALUE)
                .WriteValue(app_policy_value.c_str(), static_cast<DWORD>(4)));
  is_overridden = true;
  EXPECT_EQ(GoogleUpdateSettings::UPDATES_DISABLED,
            GoogleUpdateSettings::GetAppUpdatePolicy(GetProductGuid(),
                                                     &is_overridden));
  EXPECT_FALSE(is_overridden);
}

TEST_F(GoogleUpdateSettingsTest, PerAppUpdatesDisabledByPolicy) {
  const wchar_t* app_guid = install_static::GetAppGuid();
  EXPECT_TRUE(SetUpdatePolicyForAppGuid(
      app_guid, GoogleUpdateSettings::UPDATES_DISABLED));
  bool is_overridden = false;
  GoogleUpdateSettings::UpdatePolicy update_policy =
      GoogleUpdateSettings::GetAppUpdatePolicy(app_guid, &is_overridden);
  EXPECT_TRUE(is_overridden);
  EXPECT_EQ(GoogleUpdateSettings::UPDATES_DISABLED, update_policy);
  EXPECT_FALSE(GoogleUpdateSettings::AreAutoupdatesEnabled());

  EXPECT_TRUE(GoogleUpdateSettings::ReenableAutoupdates());
  update_policy =
      GoogleUpdateSettings::GetAppUpdatePolicy(app_guid, &is_overridden);
  // Should still have a policy but now that policy should explicitly enable
  // updates.
  EXPECT_TRUE(is_overridden);
  EXPECT_EQ(GoogleUpdateSettings::AUTOMATIC_UPDATES, update_policy);
  EXPECT_TRUE(GoogleUpdateSettings::AreAutoupdatesEnabled());
}

TEST_F(GoogleUpdateSettingsTest, PerAppUpdatesEnabledWithGlobalDisabled) {
  // Disable updates globally but enable them for Chrome (the app-specific
  // setting should take precedence).
  const wchar_t* app_guid = install_static::GetAppGuid();
  EXPECT_TRUE(SetUpdatePolicyForAppGuid(
      app_guid, GoogleUpdateSettings::AUTOMATIC_UPDATES));
  EXPECT_TRUE(SetGlobalUpdatePolicy(GoogleUpdateSettings::UPDATES_DISABLED));

  // Make sure we read this as still having updates enabled.
  EXPECT_TRUE(GoogleUpdateSettings::AreAutoupdatesEnabled());

  // Make sure that the reset action returns true and is a no-op.
  EXPECT_TRUE(GoogleUpdateSettings::ReenableAutoupdates());
  EXPECT_EQ(GoogleUpdateSettings::AUTOMATIC_UPDATES,
            GetUpdatePolicyForAppGuid(app_guid));
  EXPECT_EQ(GoogleUpdateSettings::UPDATES_DISABLED, GetGlobalUpdatePolicy());
}

TEST_F(GoogleUpdateSettingsTest, GlobalUpdatesDisabledByPolicy) {
  const wchar_t* app_guid = install_static::GetAppGuid();
  EXPECT_TRUE(SetGlobalUpdatePolicy(GoogleUpdateSettings::UPDATES_DISABLED));
  bool is_overridden = false;

  // The contract for GetAppUpdatePolicy states that |is_overridden| should be
  // set to false when updates are disabled on a non-app-specific basis.
  GoogleUpdateSettings::UpdatePolicy update_policy =
      GoogleUpdateSettings::GetAppUpdatePolicy(app_guid, &is_overridden);
  EXPECT_FALSE(is_overridden);
  EXPECT_EQ(GoogleUpdateSettings::UPDATES_DISABLED, update_policy);
  EXPECT_FALSE(GoogleUpdateSettings::AreAutoupdatesEnabled());

  EXPECT_TRUE(GoogleUpdateSettings::ReenableAutoupdates());
  update_policy =
      GoogleUpdateSettings::GetAppUpdatePolicy(app_guid, &is_overridden);
  // Policy should now be to enable updates, |is_overridden| should still be
  // false.
  EXPECT_FALSE(is_overridden);
  EXPECT_EQ(GoogleUpdateSettings::AUTOMATIC_UPDATES, update_policy);
  EXPECT_TRUE(GoogleUpdateSettings::AreAutoupdatesEnabled());
}

TEST_F(GoogleUpdateSettingsTest, UpdatesDisabledByTimeout) {
  // Disable updates altogether.
  EXPECT_TRUE(SetUpdateTimeoutOverride(0));
  EXPECT_FALSE(GoogleUpdateSettings::AreAutoupdatesEnabled());
  EXPECT_TRUE(GoogleUpdateSettings::ReenableAutoupdates());
  EXPECT_TRUE(GoogleUpdateSettings::AreAutoupdatesEnabled());

  // Set the update period to something unreasonable.
  EXPECT_TRUE(SetUpdateTimeoutOverride(
      GoogleUpdateSettings::kCheckPeriodOverrideMinutesMax + 1));
  EXPECT_FALSE(GoogleUpdateSettings::AreAutoupdatesEnabled());
  EXPECT_TRUE(GoogleUpdateSettings::ReenableAutoupdates());
  EXPECT_TRUE(GoogleUpdateSettings::AreAutoupdatesEnabled());
}

#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)

TEST_F(GoogleUpdateSettingsTest, GetDownloadPreference) {
  RegKey policy_key;

  if (policy_key.Open(HKEY_LOCAL_MACHINE, GoogleUpdateSettings::kPoliciesKey,
                      KEY_SET_VALUE) == ERROR_SUCCESS) {
    policy_key.DeleteValue(
        GoogleUpdateSettings::kDownloadPreferencePolicyValue);
  }
  policy_key.Close();

  // When no policy is present expect to return an empty string.
  EXPECT_TRUE(GoogleUpdateSettings::GetDownloadPreference().empty());

  // Expect "cacheable" when the correct policy is present.
  EXPECT_EQ(ERROR_SUCCESS, policy_key.Create(HKEY_LOCAL_MACHINE,
                                             GoogleUpdateSettings::kPoliciesKey,
                                             KEY_SET_VALUE));
  EXPECT_EQ(
      ERROR_SUCCESS,
      policy_key.WriteValue(
          GoogleUpdateSettings::kDownloadPreferencePolicyValue, L"cacheable"));
  EXPECT_STREQ(L"cacheable",
               GoogleUpdateSettings::GetDownloadPreference().c_str());

  EXPECT_EQ(ERROR_SUCCESS,
            policy_key.WriteValue(
                GoogleUpdateSettings::kDownloadPreferencePolicyValue,
                std::wstring(32, L'a').c_str()));
  EXPECT_STREQ(std::wstring(32, L'a').c_str(),
               GoogleUpdateSettings::GetDownloadPreference().c_str());

  // Expect an empty string when an unsupported policy is set.
  // It contains spaces.
  EXPECT_EQ(ERROR_SUCCESS,
            policy_key.WriteValue(
                GoogleUpdateSettings::kDownloadPreferencePolicyValue, L"a b"));
  EXPECT_TRUE(GoogleUpdateSettings::GetDownloadPreference().empty());

  // It contains non alphanumeric characters.
  EXPECT_EQ(ERROR_SUCCESS,
            policy_key.WriteValue(
                GoogleUpdateSettings::kDownloadPreferencePolicyValue, L"<a>"));
  EXPECT_TRUE(GoogleUpdateSettings::GetDownloadPreference().empty());

  // It is too long.
  EXPECT_EQ(ERROR_SUCCESS,
            policy_key.WriteValue(
                GoogleUpdateSettings::kDownloadPreferencePolicyValue,
                std::wstring(33, L'a').c_str()));
  EXPECT_TRUE(GoogleUpdateSettings::GetDownloadPreference().empty());
}

class SetProgressTest : public GoogleUpdateSettingsTest,
                        public testing::WithParamInterface<bool> {
 protected:
  SetProgressTest()
      : system_install_(GetParam()),
        root_key_(system_install_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER) {}

  const bool system_install_;
  const HKEY root_key_;
};

TEST_P(SetProgressTest, SetProgress) {
  std::wstring path(google_update::kRegPathClientState);
  path += L"\\";
  path += GetProductGuid();

  constexpr int kValues[] = {0, 25, 50, 99, 100};
  for (int value : kValues) {
    GoogleUpdateSettings::SetProgress(system_install_, path, value);
    DWORD progress = 0;
    base::win::RegKey key(root_key_, path.c_str(),
                          KEY_QUERY_VALUE | KEY_WOW64_32KEY);
    ASSERT_TRUE(key.Valid());
    ASSERT_EQ(ERROR_SUCCESS,
              key.ReadValueDW(google_update::kRegInstallerProgress, &progress));
    EXPECT_EQ(static_cast<DWORD>(value), progress);
  }
}

INSTANTIATE_TEST_SUITE_P(SetProgressUserLevel,
                         SetProgressTest,
                         testing::Values(false));
INSTANTIATE_TEST_SUITE_P(SetProgressSystemLevel,
                         SetProgressTest,
                         testing::Values(true));

// Test GoogleUpdateSettings::GetUninstallCommandLine at system- or user-level,
// according to the param.
class GetUninstallCommandLine : public GoogleUpdateSettingsTest,
                                public testing::WithParamInterface<bool> {
 protected:
  static const wchar_t kDummyCommand[];

  void SetUp() override {
    GoogleUpdateSettingsTest::SetUp();
    system_install_ = GetParam();
    root_key_ = system_install_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  }

  HKEY root_key_;
  bool system_install_;
};

const wchar_t GetUninstallCommandLine::kDummyCommand[] =
    L"\"goopdate.exe\" /spam";

// Tests that GetUninstallCommandLine returns an empty string if there's no
// Software\Google\Update key.
TEST_P(GetUninstallCommandLine, TestNoKey) {
  EXPECT_EQ(std::wstring(),
            GoogleUpdateSettings::GetUninstallCommandLine(system_install_));
}

// Tests that GetUninstallCommandLine returns an empty string if there's no
// UninstallCmdLine value in the Software\Google\Update key.
TEST_P(GetUninstallCommandLine, TestNoValue) {
  RegKey(root_key_, google_update::kRegPathGoogleUpdate, KEY_SET_VALUE);
  EXPECT_EQ(std::wstring(),
            GoogleUpdateSettings::GetUninstallCommandLine(system_install_));
}

// Tests that GetUninstallCommandLine returns an empty string if there's an
// empty UninstallCmdLine value in the Software\Google\Update key.
TEST_P(GetUninstallCommandLine, TestEmptyValue) {
  RegKey(root_key_, google_update::kRegPathGoogleUpdate, KEY_SET_VALUE)
      .WriteValue(google_update::kRegUninstallCmdLine, L"");
  EXPECT_EQ(std::wstring(),
            GoogleUpdateSettings::GetUninstallCommandLine(system_install_));
}

// Tests that GetUninstallCommandLine returns the correct string if there's an
// UninstallCmdLine value in the Software\Google\Update key.
TEST_P(GetUninstallCommandLine, TestRealValue) {
  RegKey(root_key_, google_update::kRegPathGoogleUpdate, KEY_SET_VALUE)
      .WriteValue(google_update::kRegUninstallCmdLine, kDummyCommand);
  EXPECT_EQ(std::wstring(kDummyCommand),
            GoogleUpdateSettings::GetUninstallCommandLine(system_install_));
  // Make sure that there's no value in the other level (user or system).
  EXPECT_EQ(std::wstring(),
            GoogleUpdateSettings::GetUninstallCommandLine(!system_install_));
}

INSTANTIATE_TEST_SUITE_P(GetUninstallCommandLineAtLevel,
                         GetUninstallCommandLine,
                         testing::Bool());

// Test GoogleUpdateSettings::GetGoogleUpdateVersion at system- or user-level,
// according to the param.
class GetGoogleUpdateVersion : public GoogleUpdateSettingsTest,
                               public testing::WithParamInterface<bool> {
 protected:
  static const wchar_t kDummyVersion[];

  void SetUp() override {
    GoogleUpdateSettingsTest::SetUp();
    system_install_ = GetParam();
    root_key_ = system_install_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  }

  HKEY root_key_;
  bool system_install_;
};

const wchar_t GetGoogleUpdateVersion::kDummyVersion[] = L"1.2.3.4";

// Tests that GetGoogleUpdateVersion returns an empty string if there's no
// Software\Google\Update key.
TEST_P(GetGoogleUpdateVersion, TestNoKey) {
  EXPECT_FALSE(
      GoogleUpdateSettings::GetGoogleUpdateVersion(system_install_).IsValid());
}

// Tests that GetGoogleUpdateVersion returns an empty string if there's no
// version value in the Software\Google\Update key.
TEST_P(GetGoogleUpdateVersion, TestNoValue) {
  RegKey(root_key_, google_update::kRegPathGoogleUpdate, KEY_SET_VALUE);
  EXPECT_FALSE(
      GoogleUpdateSettings::GetGoogleUpdateVersion(system_install_).IsValid());
}

// Tests that GetGoogleUpdateVersion returns an empty string if there's an
// empty version value in the Software\Google\Update key.
TEST_P(GetGoogleUpdateVersion, TestEmptyValue) {
  RegKey(root_key_, google_update::kRegPathGoogleUpdate, KEY_SET_VALUE)
      .WriteValue(google_update::kRegGoogleUpdateVersion, L"");
  EXPECT_FALSE(
      GoogleUpdateSettings::GetGoogleUpdateVersion(system_install_).IsValid());
}

// Tests that GetGoogleUpdateVersion returns the correct string if there's a
// version value in the Software\Google\Update key.
TEST_P(GetGoogleUpdateVersion, TestRealValue) {
  RegKey(root_key_, google_update::kRegPathGoogleUpdate, KEY_SET_VALUE)
      .WriteValue(google_update::kRegGoogleUpdateVersion, kDummyVersion);
  base::Version expected(base::WideToASCII(kDummyVersion));
  EXPECT_EQ(expected,
            GoogleUpdateSettings::GetGoogleUpdateVersion(system_install_));
  // Make sure that there's no value in the other level (user or system).
  EXPECT_FALSE(
      GoogleUpdateSettings::GetGoogleUpdateVersion(!system_install_).IsValid());
}

INSTANTIATE_TEST_SUITE_P(GetGoogleUpdateVersionAtLevel,
                         GetGoogleUpdateVersion,
                         testing::Bool());

// Tests that GetHashedCohortId returns an empty optional if there's no cohort
// key.
TEST_F(GoogleUpdateSettingsTest, GetHashedCohortIdTestNoKey) {
  EXPECT_FALSE(GoogleUpdateSettings::GetHashedCohortId());
}

// Tests that GetHashedCohortId returns an empty optional if there's no "id"
// value in the cohort key.
TEST_F(GoogleUpdateSettingsTest, GetHashedCohortIdTestNoValue) {
  RegKey(install_static::InstallDetails::Get().system_level()
             ? HKEY_LOCAL_MACHINE
             : HKEY_CURRENT_USER,
         install_static::GetClientStateKeyPath(
             install_static::InstallDetails::Get().app_guid())
             .append(L"\\cohort")
             .c_str(),
         KEY_SET_VALUE);
  EXPECT_FALSE(GoogleUpdateSettings::GetHashedCohortId());
}

TEST_F(GoogleUpdateSettingsTest, GetHashedCohortIdTestEmptyValue) {
  RegKey(install_static::InstallDetails::Get().system_level()
             ? HKEY_LOCAL_MACHINE
             : HKEY_CURRENT_USER,
         install_static::GetClientStateKeyPath(
             install_static::InstallDetails::Get().app_guid())
             .append(L"\\cohort")
             .c_str(),
         KEY_SET_VALUE)
      .WriteValue(google_update::kRegDefaultField, L"");
  EXPECT_FALSE(GoogleUpdateSettings::GetHashedCohortId());
}

TEST_F(GoogleUpdateSettingsTest, GetHashedCohortIdTestRealValue) {
  RegKey(install_static::InstallDetails::Get().system_level()
             ? HKEY_LOCAL_MACHINE
             : HKEY_CURRENT_USER,
         install_static::GetClientStateKeyPath(
             install_static::InstallDetails::Get().app_guid())
             .append(L"\\cohort")
             .c_str(),
         KEY_SET_VALUE)
      .WriteValue(google_update::kRegDefaultField, L"1:qesc2/qesff:[email protected]");
  EXPECT_TRUE(GoogleUpdateSettings::GetHashedCohortId());
  EXPECT_EQ(*GoogleUpdateSettings::GetHashedCohortId(),
            base::PersistentHash("1:qesc2/qesff"));
}

// Test values for use by the CollectStatsConsent test fixture.
class StatsState {
 public:
  enum StateSetting {
    NO_SETTING,
    FALSE_SETTING,
    TRUE_SETTING,
  };
  struct UserLevelState {};
  struct SystemLevelState {};
  static const UserLevelState kUserLevel;
  static const SystemLevelState kSystemLevel;

  StatsState(const UserLevelState&, StateSetting state_value)
      : system_level_(false),
        state_value_(state_value),
        state_medium_value_(NO_SETTING) {}
  StatsState(const SystemLevelState&,
             StateSetting state_value,
             StateSetting state_medium_value)
      : system_level_(true),
        state_value_(state_value),
        state_medium_value_(state_medium_value) {}
  bool system_level() const { return system_level_; }
  HKEY root_key() const {
    return system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  }
  StateSetting state_value() const { return state_value_; }
  StateSetting state_medium_value() const { return state_medium_value_; }
  bool is_consent_granted() const {
    return (system_level_ && state_medium_value_ != NO_SETTING)
               ? (state_medium_value_ == TRUE_SETTING)
               : (state_value_ == TRUE_SETTING);
  }

 private:
  bool system_level_;
  StateSetting state_value_;
  StateSetting state_medium_value_;
};

const StatsState::UserLevelState StatsState::kUserLevel = {};
const StatsState::SystemLevelState StatsState::kSystemLevel = {};

// A value parameterized test for testing the stats collection consent setting.
class CollectStatsConsent : public ::testing::TestWithParam<StatsState> {
 protected:
  CollectStatsConsent();
  void SetUp() override;
  void ApplySetting(StatsState::StateSetting setting,
                    HKEY root_key,
                    const std::wstring& reg_key);

  registry_util::RegistryOverrideManager override_manager_;
  std::unique_ptr<install_static::ScopedInstallDetails> scoped_install_details_;
};

CollectStatsConsent::CollectStatsConsent() = default;

// Install the registry override and apply the settings to the registry.
void CollectStatsConsent::SetUp() {
  // Override both HKLM and HKCU as tests may touch either/both.
  ASSERT_NO_FATAL_FAILURE(
      override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE));
  ASSERT_NO_FATAL_FAILURE(
      override_manager_.OverrideRegistry(HKEY_CURRENT_USER));

  const StatsState& stats_state = GetParam();
  scoped_install_details_ =
      std::make_unique<install_static::ScopedInstallDetails>(
          stats_state.system_level(), 0 /* install_mode_index */);
  const HKEY root_key = stats_state.root_key();
  ASSERT_NO_FATAL_FAILURE(
      ApplySetting(stats_state.state_value(), root_key,
                   install_static::GetClientStateKeyPath()));
  ASSERT_NO_FATAL_FAILURE(
      ApplySetting(stats_state.state_medium_value(), root_key,
                   install_static::GetClientStateMediumKeyPath()));
}

// Write the correct value to represent |setting| in the registry.
void CollectStatsConsent::ApplySetting(StatsState::StateSetting setting,
                                       HKEY root_key,
                                       const std::wstring& reg_key) {
  if (setting != StatsState::NO_SETTING) {
    DWORD value = setting != StatsState::FALSE_SETTING ? 1 : 0;
    ASSERT_EQ(ERROR_SUCCESS,
              RegKey(root_key, reg_key.c_str(), KEY_SET_VALUE)
                  .WriteValue(google_update::kRegUsageStatsField, value));
  }
}

// Test that stats consent can be read.
TEST_P(CollectStatsConsent, GetCollectStatsConsent) {
  if (GetParam().is_consent_granted())
    EXPECT_TRUE(GoogleUpdateSettings::GetCollectStatsConsent());
  else
    EXPECT_FALSE(GoogleUpdateSettings::GetCollectStatsConsent());
}

// Test that stats consent can be flipped to the opposite setting, that the new
// setting takes affect, and that the correct registry location is modified.
TEST_P(CollectStatsConsent, SetCollectStatsConsent) {
  // When testing revoking consent, verify that backup client info is cleared.
  // To do so, first add some backup client info.
  if (GetParam().is_consent_granted()) {
    metrics::ClientInfo client_info;
    client_info.client_id = "01234567-89ab-cdef-fedc-ba9876543210";
    client_info.installation_date = 123;
    client_info.reporting_enabled_date = 345;
    GoogleUpdateSettings::StoreMetricsClientInfo(client_info);
  }

  EXPECT_TRUE(GoogleUpdateSettings::SetCollectStatsConsent(
      !GetParam().is_consent_granted()));

  const std::wstring reg_key =
      GetParam().system_level() ? install_static::GetClientStateMediumKeyPath()
                                : install_static::GetClientStateKeyPath();
  DWORD value = 0;
  EXPECT_EQ(ERROR_SUCCESS,
            RegKey(GetParam().root_key(), reg_key.c_str(), KEY_QUERY_VALUE)
                .ReadValueDW(google_update::kRegUsageStatsField, &value));
  if (GetParam().is_consent_granted()) {
    EXPECT_FALSE(GoogleUpdateSettings::GetCollectStatsConsent());
    EXPECT_EQ(0UL, value);
  } else {
    EXPECT_TRUE(GoogleUpdateSettings::GetCollectStatsConsent());
    EXPECT_EQ(1UL, value);
    // Verify that backup client info has been cleared.
    EXPECT_FALSE(GoogleUpdateSettings::LoadMetricsClientInfo());
  }
}

INSTANTIATE_TEST_SUITE_P(
    UserLevel,
    CollectStatsConsent,
    ::testing::Values(
        StatsState(StatsState::kUserLevel, StatsState::NO_SETTING),
        StatsState(StatsState::kUserLevel, StatsState::FALSE_SETTING),
        StatsState(StatsState::kUserLevel, StatsState::TRUE_SETTING)));
INSTANTIATE_TEST_SUITE_P(
    SystemLevel,
    CollectStatsConsent,
    ::testing::Values(StatsState(StatsState::kSystemLevel,
                                 StatsState::NO_SETTING,
                                 StatsState::NO_SETTING),
                      StatsState(StatsState::kSystemLevel,
                                 StatsState::NO_SETTING,
                                 StatsState::FALSE_SETTING),
                      StatsState(StatsState::kSystemLevel,
                                 StatsState::NO_SETTING,
                                 StatsState::TRUE_SETTING),
                      StatsState(StatsState::kSystemLevel,
                                 StatsState::FALSE_SETTING,
                                 StatsState::NO_SETTING),
                      StatsState(StatsState::kSystemLevel,
                                 StatsState::FALSE_SETTING,
                                 StatsState::FALSE_SETTING),
                      StatsState(StatsState::kSystemLevel,
                                 StatsState::FALSE_SETTING,
                                 StatsState::TRUE_SETTING),
                      StatsState(StatsState::kSystemLevel,
                                 StatsState::TRUE_SETTING,
                                 StatsState::NO_SETTING),
                      StatsState(StatsState::kSystemLevel,
                                 StatsState::TRUE_SETTING,
                                 StatsState::FALSE_SETTING),
                      StatsState(StatsState::kSystemLevel,
                                 StatsState::TRUE_SETTING,
                                 StatsState::TRUE_SETTING)));