chromium/chrome/updater/test/integration_tests.cc

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

#include <cstdlib>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/files/scoped_temp_file.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/functional/function_ref.h"
#include "base/json/json_file_value_serializer.h"
#include "base/json/json_reader.h"
#include "base/memory/scoped_refptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "base/uuid.h"
#include "base/values.h"
#include "base/version.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "chrome/enterprise_companion/device_management_storage/dm_storage.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/device_management/dm_policy_builder_for_testing.h"
#include "chrome/updater/ipc/ipc_support.h"
#include "chrome/updater/policy/dm_policy_manager.h"
#include "chrome/updater/protos/omaha_settings.pb.h"
#include "chrome/updater/registration_data.h"
#include "chrome/updater/service_proxy_factory.h"
#include "chrome/updater/test/integration_test_commands.h"
#include "chrome/updater/test/integration_tests_impl.h"
#include "chrome/updater/test/request_matcher.h"
#include "chrome/updater/test/server.h"
#include "chrome/updater/test/test_scope.h"
#include "chrome/updater/test/unit_test_util.h"
#include "chrome/updater/update_service.h"
#include "chrome/updater/updater_branding.h"
#include "chrome/updater/updater_version.h"
#include "chrome/updater/util/util.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/update_client/protocol_definition.h"
#include "components/update_client/update_client.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_LINUX)
#include <unistd.h>

#include "base/environment.h"
#include "base/strings/strcat.h"
#include "chrome/updater/util/posix_util.h"
#endif

#if BUILDFLAG(IS_MAC)
#include "chrome/updater/test/integration_tests_mac.h"
#include "chrome/updater/util/mac_util.h"
#endif

#if BUILDFLAG(IS_WIN)
#include <shlobj.h>
#include <wrl/client.h>

#include "base/strings/utf_string_conversions.h"
#include "base/win/com_init_util.h"
#include "base/win/registry.h"
#include "base/win/scoped_bstr.h"
#include "chrome/updater/app/server/win/updater_idl.h"
#include "chrome/updater/app/server/win/updater_internal_idl.h"
#include "chrome/updater/app/server/win/updater_legacy_idl.h"
#include "chrome/updater/test/test_scope.h"
#include "chrome/updater/util/win_util.h"
#include "chrome/updater/win/setup/setup_util.h"
#include "chrome/updater/win/ui/l10n_util.h"
#include "chrome/updater/win/ui/resources/updater_installer_strings.h"
#include "chrome/updater/win/win_constants.h"
#endif  // BUILDFLAG(IS_WIN)

namespace updater::test {
namespace {

namespace enterprise_management =
    ::wireless_android_enterprise_devicemanagement;
using enterprise_management::ApplicationSettings;
using enterprise_management::OmahaSettingsClientProto;

void ExpectNoUpdateSequence(ScopedServer* test_server,
                            const std::string& app_id) {
  test_server->ExpectOnce({request::GetUpdaterUserAgentMatcher(),
                           request::GetContentMatcher({base::StringPrintf(
                               R"(.*"appid":"%s".*)", app_id.c_str())})},
                          base::StringPrintf(")]}'\n"
                                             R"({"response":{)"
                                             R"(  "protocol":"3.1",)"
                                             R"(  "app":[)"
                                             R"(    {)"
                                             R"(      "appid":"%s",)"
                                             R"(      "status":"ok",)"
                                             R"(      "updatecheck":{)"
                                             R"(        "status":"noupdate")"
                                             R"(      })"
                                             R"(    })"
                                             R"(  ])"
                                             R"(}})",
                                             app_id.c_str()));
}

base::FilePath GetInstallerPath(const std::string& installer) {
  return base::FilePath::FromASCII("test_installer").AppendASCII(installer);
}

struct TestApp {
  std::string appid;
  base::Version v1;
  std::string v1_crx;
  base::Version v2;
  std::string v2_crx;

  base::CommandLine GetInstallCommandSwitches(bool install_v1) const {
    base::CommandLine command(base::CommandLine::NO_PROGRAM);
    if (IsSystemInstall(GetUpdaterScopeForTesting())) {
      command.AppendArg("--system");
    }
    command.AppendSwitchASCII("--appid", appid);
    command.AppendSwitchASCII("--company", COMPANY_SHORTNAME_STRING);
    command.AppendSwitchASCII("--product_version",
                              install_v1 ? v1.GetString() : v2.GetString());
    return command;
  }

  std::string GetInstallCommandLineArgs(bool install_v1) const {
#if BUILDFLAG(IS_WIN)
    return base::WideToASCII(
        GetInstallCommandSwitches(install_v1).GetCommandLineString());
#else
    return GetInstallCommandSwitches(install_v1).GetCommandLineString();
#endif
  }

  base::CommandLine GetInstallCommandLine(bool install_v1) const {
    base::FilePath exe_path;
    base::PathService::Get(base::DIR_EXE, &exe_path);
    const base::FilePath installer_path =
        GetInstallerPath(install_v1 ? v1_crx : v2_crx);
    base::CommandLine command = GetInstallCommandSwitches(install_v1);
#if BUILDFLAG(IS_WIN)
    command.SetProgram(exe_path.Append(
        installer_path.ReplaceExtension(FILE_PATH_LITERAL(".exe"))));
#else
    command.SetProgram(exe_path.Append(
        installer_path.DirName().AppendASCII("test_app_setup.sh")));
#endif
    return command;
  }
};

}  // namespace

class IntegrationTest : public ::testing::Test {
 public:
  IntegrationTest() : test_commands_(CreateIntegrationTestCommands()) {}
  ~IntegrationTest() override = default;

 protected:
  void SetUp() override {
    VLOG(2) << __func__ << " entered.";

    ASSERT_NO_FATAL_FAILURE(CleanProcesses());
    ASSERT_TRUE(WaitForUpdaterExit());
    ASSERT_NO_FATAL_FAILURE(Clean());
    ASSERT_NO_FATAL_FAILURE(ExpectClean());
    ASSERT_NO_FATAL_FAILURE(EnterTestMode(
        GURL("http://localhost:1234"), GURL("http://localhost:1235"),
        GURL("http://localhost:1236"), {}, base::Minutes(5)));
    ASSERT_NO_FATAL_FAILURE(SetMachineManaged(false));
#if BUILDFLAG(IS_LINUX)
    // On LUCI the XDG_RUNTIME_DIR and DBUS_SESSION_BUS_ADDRESS environment
    // variables may not be set. These are required for systemctl to connect to
    // its bus in user mode.
    std::unique_ptr<base::Environment> env = base::Environment::Create();
    const std::string xdg_runtime_dir =
        base::StrCat({"/run/user/", base::NumberToString(getuid())});
    if (!env->HasVar("XDG_RUNTIME_DIR")) {
      ASSERT_TRUE(env->SetVar("XDG_RUNTIME_DIR", xdg_runtime_dir));
    }
    if (!env->HasVar("DBUS_SESSION_BUS_ADDRESS")) {
      ASSERT_TRUE(
          env->SetVar("DBUS_SESSION_BUS_ADDRESS",
                      base::StrCat({"unix:path=", xdg_runtime_dir, "/bus"})));
    }
#endif

    // Mark the device as de-registered. This stops sending DM requests
    // that mess up the request expectations in the mock server.
    ASSERT_NO_FATAL_FAILURE(DMDeregisterDevice());

    VLOG(2) << __func__ << "completed.";
  }

  void TearDown() override {
    VLOG(2) << __func__ << " entered.";

    ExitTestMode();
    if (!HasFailure()) {
      ExpectClean();
    }
    ExpectNoCrashes();

    PrintLog();
    CopyLog();

    DMCleanup();

    // Updater process must not be running for `Clean()` to succeed.
    ASSERT_TRUE(WaitForUpdaterExit());
    Clean();

    VLOG(2) << __func__ << "completed.";
  }

  void ExpectNoCrashes() { test_commands_->ExpectNoCrashes(); }

  void CopyLog() { test_commands_->CopyLog(/*infix=*/""); }

  void PrintLog() { test_commands_->PrintLog(); }

  void Install(const base::Value::List& switches = {}) {
    test_commands_->Install(switches);
  }

  void InstallUpdaterAndApp(const std::string& app_id,
                            const bool is_silent_install,
                            const std::string& tag,
                            const std::string& child_window_text_to_find = {},
                            const bool always_launch_cmd = false,
                            const bool verify_app_logo_loaded = false,
                            const bool expect_success = true,
                            const bool wait_for_the_installer = true) {
    test_commands_->InstallUpdaterAndApp(
        app_id, is_silent_install, tag, child_window_text_to_find,
        always_launch_cmd, verify_app_logo_loaded, expect_success,
        wait_for_the_installer);
  }

  void ExpectInstalled() { test_commands_->ExpectInstalled(); }

  void Uninstall() {
    ASSERT_TRUE(WaitForUpdaterExit());
    ExpectNoCrashes();
    PrintLog();
    CopyLog();
    test_commands_->Uninstall();
    ASSERT_TRUE(WaitForUpdaterExit());
  }

  void ExpectCandidateUninstalled() {
    test_commands_->ExpectCandidateUninstalled();
  }

  void Clean() { test_commands_->Clean(); }

  void ExpectClean() { test_commands_->ExpectClean(); }

  void EnterTestMode(const GURL& update_url,
                     const GURL& crash_upload_url,
                     const GURL& device_management_url,
                     const GURL& app_logo_url,
                     const base::TimeDelta& idle_timeout) {
    test_commands_->EnterTestMode(update_url, crash_upload_url,
                                  device_management_url, app_logo_url,
                                  idle_timeout);
  }

  void ExitTestMode() { test_commands_->ExitTestMode(); }

  void SetGroupPolicies(const base::Value::Dict& values) {
    test_commands_->SetGroupPolicies(values);
  }

  void SetPlatformPolicies(const base::Value::Dict& values) {
    test_commands_->SetPlatformPolicies(values);
  }

  void SetMachineManaged(bool is_managed_device) {
    test_commands_->SetMachineManaged(is_managed_device);
  }

  void ExpectVersionActive(const std::string& version) {
    test_commands_->ExpectVersionActive(version);
  }

  void ExpectVersionNotActive(const std::string& version) {
    test_commands_->ExpectVersionNotActive(version);
  }

#if BUILDFLAG(IS_WIN)
  void ExpectInterfacesRegistered() {
    test_commands_->ExpectInterfacesRegistered();
  }

  void ExpectMarshalInterfaceSucceeds() {
    test_commands_->ExpectMarshalInterfaceSucceeds();
  }

  void ExpectLegacyUpdate3WebSucceeds(
      const std::string& app_id,
      AppBundleWebCreateMode app_bundle_web_create_mode,
      int expected_final_state,
      int expected_error_code,
      bool cancel_when_downloading = false) {
    test_commands_->ExpectLegacyUpdate3WebSucceeds(
        app_id, app_bundle_web_create_mode, expected_final_state,
        expected_error_code, cancel_when_downloading);
  }

  void ExpectLegacyProcessLauncherSucceeds() {
    test_commands_->ExpectLegacyProcessLauncherSucceeds();
  }

  void ExpectLegacyAppCommandWebSucceeds(const std::string& app_id,
                                         const std::string& command_id,
                                         const base::Value::List& parameters,
                                         int expected_exit_code) {
    test_commands_->ExpectLegacyAppCommandWebSucceeds(
        app_id, command_id, parameters, expected_exit_code);
  }

  void ExpectLegacyPolicyStatusSucceeds() {
    test_commands_->ExpectLegacyPolicyStatusSucceeds();
  }

  void RunUninstallCmdLine() { test_commands_->RunUninstallCmdLine(); }

  void RunHandoff(const std::string& app_id) {
    test_commands_->RunHandoff(app_id);
  }

#endif  // BUILDFLAG(IS_WIN)

  void InstallAppViaService(
      const std::string& app_id,
      const base::Value::Dict& expected_final_values = {}) {
    test_commands_->InstallAppViaService(app_id, expected_final_values);
  }

  void SetupFakeUpdaterHigherVersion() {
    test_commands_->SetupFakeUpdaterHigherVersion();
  }

  void SetupFakeUpdaterLowerVersion() {
    test_commands_->SetupFakeUpdaterLowerVersion();
  }

  void SetupRealUpdaterLowerVersion() {
    test_commands_->SetupRealUpdaterLowerVersion();
  }

  void SetActive(const std::string& app_id) {
    test_commands_->SetActive(app_id);
  }

  void ExpectActive(const std::string& app_id) {
    test_commands_->ExpectActive(app_id);
  }

  void ExpectNotActive(const std::string& app_id) {
    test_commands_->ExpectNotActive(app_id);
  }

  void SetExistenceCheckerPath(const std::string& app_id,
                               const base::FilePath& path) {
    test_commands_->SetExistenceCheckerPath(app_id, path);
  }

  void SetServerStarts(int value) { test_commands_->SetServerStarts(value); }

  void FillLog() { test_commands_->FillLog(); }

  void ExpectLogRotated() { test_commands_->ExpectLogRotated(); }

  void ExpectRegistered(const std::string& app_id) {
    test_commands_->ExpectRegistered(app_id);
  }

  void ExpectNotRegistered(const std::string& app_id) {
    test_commands_->ExpectNotRegistered(app_id);
  }

  void ExpectAppTag(const std::string& app_id, const std::string& tag) {
    test_commands_->ExpectAppTag(app_id, tag);
  }

  void SetAppTag(const std::string& app_id, const std::string& tag) {
    test_commands_->SetAppTag(app_id, tag);
  }

  void ExpectAppVersion(const std::string& app_id,
                        const base::Version& version) {
    test_commands_->ExpectAppVersion(app_id, version);
  }

  void InstallApp(const std::string& app_id,
                  const base::Version& version = base::Version("0.1")) {
    test_commands_->InstallApp(app_id, version);
  }

  void UninstallApp(const std::string& app_id) {
    test_commands_->UninstallApp(app_id);
  }

  void RunWake(int exit_code) {
    ASSERT_TRUE(WaitForUpdaterExit());
    test_commands_->RunWake(exit_code);
  }

  void RunWakeAll() {
    ASSERT_TRUE(WaitForUpdaterExit());
    test_commands_->RunWakeAll();
  }

  void RunCrashMe() { test_commands_->RunCrashMe(); }

  void RunWakeActive(int exit_code) {
    ASSERT_TRUE(WaitForUpdaterExit());
    test_commands_->RunWakeActive(exit_code);
  }

  void RunServer(int exit_code, bool internal) {
    ASSERT_TRUE(WaitForUpdaterExit());
    test_commands_->RunServer(exit_code, internal);
  }

  void CheckForUpdate(const std::string& app_id) {
    test_commands_->CheckForUpdate(app_id);
  }

  void Update(const std::string& app_id,
              const std::string& install_data_index) {
    test_commands_->Update(app_id, install_data_index);
  }

  void UpdateAll() { test_commands_->UpdateAll(); }

  void GetAppStates(const base::Value::Dict& expected_app_states) {
    test_commands_->GetAppStates(expected_app_states);
  }

  void DeleteUpdaterDirectory() { test_commands_->DeleteUpdaterDirectory(); }

  void DeleteActiveUpdaterExecutable() {
    test_commands_->DeleteActiveUpdaterExecutable();
  }

  void DeleteFile(const base::FilePath& path) {
    test_commands_->DeleteFile(path);
  }

  base::FilePath GetDifferentUserPath() {
    return test_commands_->GetDifferentUserPath();
  }

  void ExpectUpdateCheckRequest(ScopedServer* test_server) {
    test_commands_->ExpectUpdateCheckRequest(test_server);
  }

  void ExpectUpdateCheckSequence(ScopedServer* test_server,
                                 const std::string& app_id,
                                 UpdateService::Priority priority,
                                 const base::Version& from_version,
                                 const base::Version& to_version) {
    test_commands_->ExpectUpdateCheckSequence(test_server, app_id, priority,
                                              from_version, to_version);
  }

  void ExpectUninstallPing(ScopedServer* test_server,
                           std::optional<GURL> target_url = {}) {
    test_commands_->ExpectPing(test_server,
                               update_client::protocol_request::kEventUninstall,
                               target_url);
  }

  void ExpectAppCommandPing(ScopedServer* test_server,
                            const std::string& appid,
                            const std::string& appcommandid,
                            int errorcode,
                            int eventresult,
                            int event_type,
                            const base::Version& version) {
    test_commands_->ExpectAppCommandPing(test_server, appid, appcommandid,
                                         errorcode, eventresult, event_type,
                                         version);
  }

  void ExpectUpdateSequence(ScopedServer* test_server,
                            const std::string& app_id,
                            const std::string& install_data_index,
                            UpdateService::Priority priority,
                            const base::Version& from_version,
                            const base::Version& to_version,
                            bool do_fault_injection = false) {
    test_commands_->ExpectUpdateSequence(
        test_server, app_id, install_data_index, priority, from_version,
        to_version, do_fault_injection);
  }

  void ExpectUpdateSequenceBadHash(ScopedServer* test_server,
                                   const std::string& app_id,
                                   const std::string& install_data_index,
                                   UpdateService::Priority priority,
                                   const base::Version& from_version,
                                   const base::Version& to_version) {
    test_commands_->ExpectUpdateSequenceBadHash(test_server, app_id,
                                                install_data_index, priority,
                                                from_version, to_version);
  }

  void ExpectSelfUpdateSequence(ScopedServer* test_server) {
    test_commands_->ExpectSelfUpdateSequence(test_server);
  }

  void ExpectInstallSequence(ScopedServer* test_server,
                             const std::string& app_id,
                             const std::string& install_data_index,
                             UpdateService::Priority priority,
                             const base::Version& from_version,
                             const base::Version& to_version,
                             bool do_fault_injection = false) {
    test_commands_->ExpectInstallSequence(
        test_server, app_id, install_data_index, priority, from_version,
        to_version, do_fault_injection);
  }

  void StressUpdateService() { test_commands_->StressUpdateService(); }

  void CallServiceUpdate(
      const std::string& app_id,
      const std::string& install_data_index,
      UpdateService::PolicySameVersionUpdate policy_same_version_update) {
    test_commands_->CallServiceUpdate(app_id, install_data_index,
                                      policy_same_version_update);
  }

  void SetupFakeLegacyUpdater() { test_commands_->SetupFakeLegacyUpdater(); }

#if BUILDFLAG(IS_WIN)
  void RunFakeLegacyUpdater() { test_commands_->RunFakeLegacyUpdater(); }
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_MAC)
  void PrivilegedHelperInstall() { test_commands_->PrivilegedHelperInstall(); }
  void DeleteLegacyUpdater() { test_commands_->DeleteLegacyUpdater(); }
  void ExpectPrepareToRunBundleSuccess(const base::FilePath& bundle_path) {
    test_commands_->ExpectPrepareToRunBundleSuccess(bundle_path);
  }

  void ExpectKSAdminFetchTag(bool elevate,
                             const std::string& product_id,
                             const base::FilePath& xc_path,
                             std::optional<UpdaterScope> store_flag,
                             std::optional<std::string> want_tag) {
    test_commands_->ExpectKSAdminFetchTag(elevate, product_id, xc_path,
                                          store_flag, want_tag);
  }

#endif  // BUILDFLAG(IS_MAC)

  void ExpectAppInstalled(const std::string& appid,
                          const base::Version& expected_version) {
    ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(appid, expected_version));

    // Verify installed app artifacts.
#if BUILDFLAG(IS_WIN)
    std::wstring pv;
    EXPECT_EQ(
        ERROR_SUCCESS,
        base::win::RegKey(UpdaterScopeToHKeyRoot(GetUpdaterScopeForTesting()),
                          GetAppClientsKey(appid).c_str(), Wow6432(KEY_READ))
            .ReadValue(kRegValuePV, &pv));
    EXPECT_EQ(pv, base::ASCIIToWide(expected_version.GetString()));
#else
    const base::FilePath app_json_path =
        GetInstallDirectory(GetUpdaterScopeForTesting())
            ->DirName()
            .AppendASCII(appid)
            .AppendASCII("app.json");
    JSONFileValueDeserializer parser(app_json_path,
                                     base::JSON_ALLOW_TRAILING_COMMAS);
    int error_code = 0;
    std::string error_message;
    std::unique_ptr<base::Value> app_data(
        parser.Deserialize(&error_code, &error_message));
    EXPECT_EQ(error_code, 0)
        << "Failed to load app json file at: " << app_json_path;
    EXPECT_TRUE(app_data);
    EXPECT_TRUE(app_data->is_dict());
    const base::Value::Dict& app_info = app_data->GetDict();
    EXPECT_EQ(*app_info.FindString("app"), appid);
    EXPECT_EQ(*app_info.FindString("company"), COMPANY_SHORTNAME_STRING);
    EXPECT_EQ(*app_info.FindString("pv"), expected_version.GetString());
#endif  // BUILDFLAG(IS_WIN)
  }

  void InstallTestApp(const TestApp& app, bool install_v1 = true) {
    const base::Version version = install_v1 ? app.v1 : app.v2;
    InstallApp(app.appid, version);
    base::FilePath exe_path;
    ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &exe_path));
    const base::CommandLine command = app.GetInstallCommandLine(install_v1);
    VLOG(2) << "Launch app setup command: " << command.GetCommandLineString();
    const base::Process process = base::LaunchProcess(
        IsSystemInstall(GetUpdaterScopeForTesting()) ? MakeElevated(command)
                                                     : command,
        {});
    if (!process.IsValid()) {
      VLOG(2) << "Failed to launch the app setup command.";
    }
    int exit_code = -1;
    EXPECT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_timeout(),
                                               &exit_code));
    EXPECT_EQ(0, exit_code);
#if !BUILDFLAG(IS_WIN)
    SetExistenceCheckerPath(app.appid,
                            GetInstallDirectory(GetUpdaterScopeForTesting())
                                ->DirName()
                                .AppendASCII(app.appid));
#endif

    ExpectAppInstalled(app.appid, version);
  }

  void ExpectLegacyUpdaterMigrated() {
    test_commands_->ExpectLegacyUpdaterMigrated();
  }

  void RunRecoveryComponent(const std::string& app_id,
                            const base::Version& version) {
    test_commands_->RunRecoveryComponent(app_id, version);
  }

  void SetLastChecked(const base::Time& time) {
    test_commands_->SetLastChecked(time);
  }

  void ExpectLastChecked() { test_commands_->ExpectLastChecked(); }

  void ExpectLastStarted() { test_commands_->ExpectLastStarted(); }

  void RunOfflineInstall(bool is_legacy_install, bool is_silent_install) {
    test_commands_->RunOfflineInstall(is_legacy_install, is_silent_install);
  }

  void RunOfflineInstallOsNotSupported(bool is_legacy_install,
                                       bool is_silent_install) {
    test_commands_->RunOfflineInstallOsNotSupported(is_legacy_install,
                                                    is_silent_install);
  }

  void DMPushEnrollmentToken(const std::string& enrollment_token) {
    test_commands_->DMPushEnrollmentToken(enrollment_token);
  }

  void DMDeregisterDevice() { test_commands_->DMDeregisterDevice(); }

  void DMCleanup() { test_commands_->DMCleanup(); }

  scoped_refptr<IntegrationTestCommands> test_commands_;

#if BUILDFLAG(IS_WIN)
  static constexpr char kGlobalPolicyKey[] = "";
  const TestApp kApp1 = {"test1", base::Version("1.0.0.0"),
                         "Testapp2Setup.crx3", base::Version("2.0.0.0"),
                         "Testapp2Setup.crx3"};
  const TestApp kApp2 = {"test2", base::Version("100.0.0.0"),
                         "Testapp2Setup.crx3", base::Version("101.0.0.0"),
                         "Testapp2Setup.crx3"};
  const TestApp kApp3 = {"test3", base::Version("1.0"), "Testapp2Setup.crx3",
                         base::Version("1.1"), "Testapp2Setup.crx3"};
#else
  static constexpr char kGlobalPolicyKey[] = "global";
  const TestApp kApp1 = {
      "test1", base::Version("1.0.0.0"), "test_installer_test1_v1.crx3",
      base::Version("2.0.0.0"), "test_installer_test1_v2.crx3"};
  const TestApp kApp2 = {
      "test2", base::Version("100.0.0.0"), "test_installer_test2_v1.crx3",
      base::Version("101.0.0.0"), "test_installer_test2_v2.crx3"};
  const TestApp kApp3 = {"test3", base::Version("1.0"),
                         "test_installer_test3_v1.crx3", base::Version("1.1"),
                         "test_installer_test3_v2.crx3"};
#endif  // BUILDFLAG(IS_WIN)

 private:
  base::test::TaskEnvironment environment_;
  ScopedIPCSupportWrapper ipc_support_;
};

#if defined(ADDRESS_SANITIZER)
#define MAYBE_UpdateServiceStress DISABLED_UpdateServiceStress
#else
#define MAYBE_UpdateServiceStress UpdateServiceStress
#endif

// Tests the setup and teardown of the fixture.
TEST_F(IntegrationTest, DoNothing) {}

TEST_F(IntegrationTest, Install) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));
#if BUILDFLAG(IS_WIN)
  // Tests the COM registration after the install. For now, tests that the
  // COM interfaces are registered, which is indirectly testing the type
  // library separation for the public, private, and legacy interfaces.
  ASSERT_NO_FATAL_FAILURE(ExpectInterfacesRegistered());
#endif  // BUILDFLAG(IS_WIN)
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

// Tests running the installer when the updater is already installed at the
// same version. It should have no notable effect.
TEST_F(IntegrationTest, OverinstallRedundant) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(InstallApp("test"));

  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test"));

  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test"));

  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, OverinstallWorking) {
  ASSERT_NO_FATAL_FAILURE(SetupRealUpdaterLowerVersion());
  ASSERT_NO_FATAL_FAILURE(InstallApp("test"));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionNotActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test"));

  // A new version hands off installation to the old version, and doesn't
  // change the active version of the updater.
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionNotActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test"));

  // After two wakes, the new updater is active.
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(
      ExpectUpdateSequence(&test_server, kQualificationAppId, "",
                           UpdateService::Priority::kBackground,
                           base::Version("0.1"), base::Version("0.2")));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectNoUpdateSequence(&test_server, kUpdaterAppId));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test"));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, OverinstallBroken) {
  ASSERT_NO_FATAL_FAILURE(SetupRealUpdaterLowerVersion());
  ASSERT_NO_FATAL_FAILURE(InstallApp("test"));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(DeleteActiveUpdaterExecutable());

  // Since the old version is not working, the new version should install and
  // become active.
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test"));

  ASSERT_NO_FATAL_FAILURE(Uninstall());

  // Cleanup the older version by reinstalling and uninstalling.
  ASSERT_NO_FATAL_FAILURE(SetupRealUpdaterLowerVersion());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, OverinstallBrokenSameVersion) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallApp("test"));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(DeleteActiveUpdaterExecutable());

  // Since the existing version is now not working, it should reinstall. This
  // will ultimately result in no visible change to the prefs file since the
  // new active version number will be the same as the old one.
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test"));

  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, SelfUninstallOutdatedUpdater) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(InstallApp("test"));
  ASSERT_NO_FATAL_FAILURE(SetupFakeUpdaterHigherVersion());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionNotActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test"));

  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(ExpectCandidateUninstalled());
  // The candidate uninstall should not have altered global prefs.
  ASSERT_NO_FATAL_FAILURE(ExpectVersionNotActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectVersionNotActive("0.0.0.0"));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test"));

  // Do not call `Uninstall()` since the outdated updater uninstalled itself.
  // Additional clean up is needed because of how this test is set up. After
  // the outdated instance uninstalls, a few files are left in the product
  // directory: prefs.json, updater.log, and overrides.json. These files are
  // owned by the active instance of the updater but in this case there is
  // no active instance left; therefore, explicit clean up is required.
  PrintLog();
  CopyLog();
  Clean();
}

TEST_F(IntegrationTest, QualifyUpdater) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(SetupFakeUpdaterLowerVersion());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionNotActive(kUpdaterVersion));

  ASSERT_NO_FATAL_FAILURE(
      ExpectUpdateSequence(&test_server, kQualificationAppId, "",
                           UpdateService::Priority::kBackground,
                           base::Version("0.1"), base::Version("0.2")));

  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  // This instance is now qualified and should activate itself and check itself
  // for updates on the next check.
  test_server.ExpectOnce({request::GetUpdaterUserAgentMatcher(),
                          request::GetContentMatcher(
                              {base::StringPrintf(".*%s.*", kUpdaterAppId)})},
                         ")]}'\n");
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, CleanupOldVersion) {
  ASSERT_NO_FATAL_FAILURE(SetupFakeUpdaterLowerVersion());

  // Since the old version is not working, the new version should install and
  // become active.
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));

  // Waking the new version should clean up the old.
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  std::optional<base::FilePath> path =
      GetInstallDirectory(GetUpdaterScopeForTesting());
  ASSERT_TRUE(path);
  int dirs = 0;
  base::FileEnumerator(*path, false, base::FileEnumerator::DIRECTORIES)
      .ForEach([&dirs](const base::FilePath& path) {
        if (base::Version(path.BaseName().MaybeAsASCII()).IsValid()) {
          ++dirs;
        }
      });
  EXPECT_EQ(dirs, 1);

  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, SelfUpdate) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallApp("test"));

  base::Version next_version(base::StringPrintf("%s1", kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      &test_server, kUpdaterAppId, "", UpdateService::Priority::kBackground,
      base::Version(kUpdaterVersion), next_version));

  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kUpdaterAppId, next_version));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test"));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, SelfUpdateWithWakeAll) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallApp("test"));

  base::Version next_version(base::StringPrintf("%s1", kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      &test_server, kUpdaterAppId, "", UpdateService::Priority::kBackground,
      base::Version(kUpdaterVersion), next_version));

  ASSERT_NO_FATAL_FAILURE(RunWakeAll());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kUpdaterAppId, next_version));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test"));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, NoSelfUpdateIfNoEula) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(
      Install(base::Value::List().Append(kEulaRequiredSwitch)));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(
      ExpectAppVersion(kUpdaterAppId, base::Version(kUpdaterVersion)));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

#if BUILDFLAG(IS_WIN)
TEST_F(IntegrationTest, UninstallWithoutPingIfNoEula) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(
      Install(base::Value::List().Append(kEulaRequiredSwitch)));
  ASSERT_NO_FATAL_FAILURE(RunOfflineInstall(/*is_legacy_install=*/false,
                                            /*is_silent_install=*/false));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(SetServerStarts(24));
  ASSERT_NO_FATAL_FAILURE(
      UninstallApp("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}"));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectClean());
}

TEST_F(IntegrationTest, SelfUpdateAfterEulaAcceptedViaRegistry) {
  if (!IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP() << "HKLM/CSM only exists in system scope.";
  }
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(
      Install(base::Value::List().Append(kEulaRequiredSwitch)));

  // Set EULA accepted on the updater app itself.
  ASSERT_EQ(
      base::win::RegKey(UpdaterScopeToHKeyRoot(GetUpdaterScopeForTesting()),
                        base::StrCat({CLIENT_STATE_MEDIUM_KEY,
                                      base::UTF8ToWide(kUpdaterAppId)})
                            .c_str(),
                        Wow6432(KEY_WRITE))
          .WriteValue(L"eulaaccepted", 1),
      ERROR_SUCCESS);

  base::Version next_version(base::StringPrintf("%s1", kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      &test_server, kUpdaterAppId, "", UpdateService::Priority::kBackground,
      base::Version(kUpdaterVersion), next_version));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kUpdaterAppId, next_version));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}
#endif  // BUILDFLAG(IS_WIN)

#if !BUILDFLAG(IS_LINUX)
// InstallAppViaService does not work on Linux.
TEST_F(IntegrationTest, SelfUpdateAfterEulaAcceptedViaInstall) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(
      Install(base::Value::List().Append(kEulaRequiredSwitch)));

  // Installing an app implies EULA accepted.
  ASSERT_NO_FATAL_FAILURE(ExpectAppsUpdateSequence(
      GetUpdaterScopeForTesting(), &test_server,
      /*request_attributes=*/{},
      {
          AppUpdateExpectation(
              kApp1.GetInstallCommandLineArgs(/*install_v1=*/true), kApp1.appid,
              base::Version({0, 0, 0, 0}), kApp1.v1,
              /*is_install=*/true,
              /*should_update=*/true, false, "", "",
              GetInstallerPath(kApp1.v1_crx)),
      }));

  ASSERT_NO_FATAL_FAILURE(InstallAppViaService(kApp1.appid));

  base::Version next_version(base::StringPrintf("%s1", kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      &test_server, kUpdaterAppId, "", UpdateService::Priority::kBackground,
      base::Version(kUpdaterVersion), next_version));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kUpdaterAppId, next_version));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}
#endif  // !BUILDFLAG(IS_LINUX)

TEST_F(IntegrationTest, ReportsActive) {
  // A longer than usual timeout is needed for this test because the macOS
  // UpdateServiceInternal server takes at least 10 seconds to shut down after
  // Install, and InstallApp cannot make progress until it shut downs and
  // releases the global prefs lock.
  ASSERT_GE(TestTimeouts::action_timeout(), base::Seconds(18));
  base::test::ScopedRunLoopTimeout timeout(FROM_HERE,
                                           TestTimeouts::action_timeout());

  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  // Register apps test1 and test2. Expect pings for each.
  ASSERT_NO_FATAL_FAILURE(InstallApp("test1"));
  ASSERT_NO_FATAL_FAILURE(InstallApp("test2"));

  // Set test1 to be active and do a background updatecheck.
  ASSERT_NO_FATAL_FAILURE(SetActive("test1"));
  ASSERT_NO_FATAL_FAILURE(ExpectActive("test1"));
  ASSERT_NO_FATAL_FAILURE(ExpectNotActive("test2"));
  test_server.ExpectOnce(
      {request::GetUpdaterUserAgentMatcher(),
       request::GetContentMatcher(
           {R"(.*"appid":"test1","enabled":true,"installdate":-1,)",
            R"("ping":{"ad":-1,.*)"})},
      R"()]}')"
      "\n"
      R"({"response":{"protocol":"3.1","daystart":{"elapsed_)"
      R"(days":5098}},"app":[{"appid":"test1","status":"ok",)"
      R"("updatecheck":{"status":"noupdate"}},{"appid":"test2",)"
      R"("status":"ok","updatecheck":{"status":"noupdate"}}]})");
  ASSERT_NO_FATAL_FAILURE(RunWake(0));

  // The updater has cleared the active bits.
  ASSERT_NO_FATAL_FAILURE(ExpectNotActive("test1"));
  ASSERT_NO_FATAL_FAILURE(ExpectNotActive("test2"));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

// Tests calling `CheckForUpdate` when the updater is not installed.
TEST_F(IntegrationTest, CheckForUpdate_UpdaterNotInstalled) {
  scoped_refptr<UpdateService> update_service =
      CreateUpdateServiceProxy(GetUpdaterScopeForTesting());
  base::RunLoop loop;
  update_service->CheckForUpdate(
      "test", UpdateService::Priority::kForeground,
      UpdateService::PolicySameVersionUpdate::kNotAllowed, base::DoNothing(),
      base::BindLambdaForTesting([&loop](UpdateService::Result result) {
        EXPECT_TRUE(result == UpdateService::Result::kServiceFailed ||
                    result == UpdateService::Result::kIPCConnectionFailed)
            << "result == " << result;
        loop.Quit();
      }));
  loop.Run();
}

TEST_F(IntegrationTest, CheckForUpdate) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());

  const std::string kAppId("test");
  ASSERT_NO_FATAL_FAILURE(InstallApp(kAppId));
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateCheckSequence(
      &test_server, kAppId, UpdateService::Priority::kForeground,
      base::Version("0.1"), base::Version("1")));
  ASSERT_NO_FATAL_FAILURE(CheckForUpdate(kAppId));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, UpdateBadHash) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());

  const std::string kAppId("test");
  ASSERT_NO_FATAL_FAILURE(InstallApp(kAppId));
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequenceBadHash(
      &test_server, kAppId, "", UpdateService::Priority::kBackground,
      base::Version("0.1"), base::Version("1")));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, UpdateErrorStatus) {
  ScopedServer test_server(test_commands_);

  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp1, /*install_v1=*/true));

  for (const char* app_response_status :
       {"noupdate", "error-internal", "error-hash", "error-osnotsupported",
        "error-hwnotsupported", "error-unsupportedprotocol"}) {
    ExpectAppsUpdateSequence(
        GetUpdaterScopeForTesting(), &test_server, {},
        {
            AppUpdateExpectation(
                kApp1.GetInstallCommandLineArgs(/*install_v1=*/false),
                kApp1.appid, kApp1.v1, kApp1.v2,
                /*is_install=*/false,
                /*should_update=*/false, false, "", "",
                GetInstallerPath(kApp1.v2_crx),
                /*always_serve_crx=*/false,
                /*error_category=*/UpdateService::ErrorCategory::kNone,
                /*error_code=*/0,
                /*event_type=*/0,
                /*custom_app_response=*/{}, app_response_status),
        });
    ASSERT_NO_FATAL_FAILURE(RunWake(0));
    ASSERT_TRUE(WaitForUpdaterExit());
    ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp1.appid, kApp1.v1))
        << "App is unexpectedly updated with update check status: "
        << app_response_status;
    ASSERT_NO_FATAL_FAILURE(SetLastChecked(base::Time::Now() - base::Hours(9)))
        << "Failed to set last-checked to force next update check.";
  }

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, UpdateApp) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());

  const std::string kAppId("test");
  ASSERT_NO_FATAL_FAILURE(InstallApp(kAppId));
  base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      &test_server, kAppId, "", UpdateService::Priority::kBackground,
      base::Version("0.1"), v1));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));

  base::Version v2("2");
  const std::string kInstallDataIndex("test_install_data_index");
  ASSERT_NO_FATAL_FAILURE(
      ExpectUpdateSequence(&test_server, kAppId, kInstallDataIndex,
                           UpdateService::Priority::kForeground, v1, v2));
  ASSERT_NO_FATAL_FAILURE(Update(kAppId, kInstallDataIndex));

  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v2));
  ASSERT_NO_FATAL_FAILURE(ExpectLastChecked());
  ASSERT_NO_FATAL_FAILURE(ExpectLastStarted());

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

#if BUILDFLAG(IS_WIN)
TEST_F(IntegrationTest, GZipUpdateResponses) {
  ScopedServer test_server(test_commands_);
  test_server.set_gzip_response(true);
  ASSERT_NO_FATAL_FAILURE(Install());

  const std::string kAppId("test");
  ASSERT_NO_FATAL_FAILURE(InstallApp(kAppId));
  base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      &test_server, kAppId, "", UpdateService::Priority::kBackground,
      base::Version("0.1"), v1));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));

  base::Version v2("2");
  const std::string kInstallDataIndex("test_install_data_index");
  ASSERT_NO_FATAL_FAILURE(
      ExpectUpdateSequence(&test_server, kAppId, kInstallDataIndex,
                           UpdateService::Priority::kForeground, v1, v2));
  ASSERT_NO_FATAL_FAILURE(Update(kAppId, kInstallDataIndex));

  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v2));
  ASSERT_NO_FATAL_FAILURE(ExpectLastChecked());
  ASSERT_NO_FATAL_FAILURE(ExpectLastStarted());

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, UpdateAppSucceedsEvenAfterDeletingInterfaces) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());

  const UpdaterScope scope = GetUpdaterScopeForTesting();
  ASSERT_TRUE(AreComInterfacesPresent(scope, true));
  ASSERT_TRUE(AreComInterfacesPresent(scope, false));
  // Delete IUpdaterXXX, used by `InstallApp` via `RegisterApp`.
  // Delete IUpdaterInternal, used by the `wake` task.
  {
    for (const IID& iid : [&scope]() -> std::vector<IID> {
           switch (scope) {
             case UpdaterScope::kUser:
               return {
                   __uuidof(IUpdaterUser),
                   __uuidof(IUpdaterCallbackUser),
                   __uuidof(IUpdaterInternalUser),
                   __uuidof(IUpdaterInternalCallbackUser),
               };
             case UpdaterScope::kSystem:
               return {
                   __uuidof(IUpdaterSystem),
                   __uuidof(IUpdaterCallbackSystem),
                   __uuidof(IUpdaterInternalSystem),
                   __uuidof(IUpdaterInternalCallbackSystem),
               };
           }
         }()) {
      LONG result =
          base::win::RegKey(UpdaterScopeToHKeyRoot(scope), L"", DELETE)
              .DeleteKey(GetComIidRegistryPath(iid).c_str());
      ASSERT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
    }
  }
  ASSERT_FALSE(AreComInterfacesPresent(scope, true));
  ASSERT_FALSE(AreComInterfacesPresent(scope, false));

  const std::string kAppId("test");
  ASSERT_NO_FATAL_FAILURE(InstallApp(kAppId));
  base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      &test_server, kAppId, "", UpdateService::Priority::kBackground,
      base::Version("0.1"), v1));

  ASSERT_NO_FATAL_FAILURE(RunWake(0));

  base::Version v2("2");
  const std::string kInstallDataIndex("test_install_data_index");
  ASSERT_NO_FATAL_FAILURE(
      ExpectUpdateSequence(&test_server, kAppId, kInstallDataIndex,
                           UpdateService::Priority::kForeground, v1, v2));
  ASSERT_NO_FATAL_FAILURE(Update(kAppId, kInstallDataIndex));

  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v2));
  ASSERT_NO_FATAL_FAILURE(ExpectLastChecked());
  ASSERT_NO_FATAL_FAILURE(ExpectLastStarted());

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}
#endif  // BUILDFLAG(IS_WIN)

TEST_F(IntegrationTest, NoCheckWhenLastCheckedRecently) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(SetLastChecked(base::Time::Now() - base::Minutes(5)));
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallApp("test"));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, NoCheckWhenLastCheckedRecentlyPolicy) {
  ScopedServer test_server(test_commands_);
  base::Value::Dict group_policies;
  group_policies.Set("autoupdatecheckperiodminutes", 60 * 18);
  ASSERT_NO_FATAL_FAILURE(SetLastChecked(base::Time::Now() - base::Hours(12)));
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(SetGroupPolicies(group_policies));
  ASSERT_NO_FATAL_FAILURE(InstallApp("test"));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, NoCheckWhenSuppressed) {
  ScopedServer test_server(test_commands_);
  base::Time::Exploded now;
  base::Time::Now().LocalExplode(&now);
  base::Value::Dict group_policies;
  group_policies.Set("updatessuppressedstarthour", (now.hour - 1 + 24) % 24);
  group_policies.Set("updatessuppressedstartmin", 0);
  group_policies.Set("updatessuppresseddurationmin", 120);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(SetGroupPolicies(group_policies));
  ASSERT_NO_FATAL_FAILURE(InstallApp("test"));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, InstallUpdaterAndApp) {
  ScopedServer test_server(test_commands_);
  const std::string kAppId("test");
  const base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_server, kAppId, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1));

  ASSERT_NO_FATAL_FAILURE(
      InstallUpdaterAndApp(kAppId, /*is_silent_install=*/true, "usagestats=1"));
  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, InstallUpdaterAndTwoApps) {
  ScopedServer test_server(test_commands_);
  const std::string kAppId("test");
  const std::string kAppId2("test2");
  const base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_server, kAppId, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1));
  ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
      kAppId, /*is_silent_install=*/true,
      base::StrCat({"appguid=", kAppId, "&ap=foo&usagestats=1"})));
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_server, kAppId2, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1));
  ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
      kAppId2, /*is_silent_install=*/true,
      base::StrCat({"appguid=", kAppId2, "&ap=foo2&usagestats=1"})));
  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId2, v1));
  ASSERT_NO_FATAL_FAILURE(ExpectAppTag(kAppId, "foo"));
  ASSERT_NO_FATAL_FAILURE(ExpectAppTag(kAppId2, "foo2"));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, ChangeTag) {
  ScopedServer test_server(test_commands_);
  const std::string kAppId("test");
  const base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_server, kAppId, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1));
  ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
      kAppId, /*is_silent_install=*/true,
      base::StrCat({"appguid=", kAppId, "&ap=foo&usagestats=1"})));
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_server, kAppId, "", UpdateService::Priority::kForeground,
      base::Version({1}), v1));
  ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
      kAppId, /*is_silent_install=*/true,
      base::StrCat({"appguid=", kAppId, "&ap=foo2&usagestats=1"})));
  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));
  ASSERT_NO_FATAL_FAILURE(ExpectAppTag(kAppId, "foo2"));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, SetTagRoundTrip) {
  ASSERT_NO_FATAL_FAILURE(Install());

  ASSERT_NO_FATAL_FAILURE(InstallApp("test"));
  ASSERT_NO_FATAL_FAILURE(ExpectAppTag("test", ""));

  ASSERT_NO_FATAL_FAILURE(SetAppTag("test", "abc"));
  ASSERT_NO_FATAL_FAILURE(ExpectAppTag("test", "abc"));

  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, MultipleWakesOneNetRequest) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());

  // Only one sequence visible to the server despite multiple wakes.
  ASSERT_NO_FATAL_FAILURE(ExpectNoUpdateSequence(&test_server, kUpdaterAppId));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, MultipleUpdateAllsMultipleNetRequests) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());

  ASSERT_NO_FATAL_FAILURE(ExpectNoUpdateSequence(&test_server, kUpdaterAppId));
  ASSERT_NO_FATAL_FAILURE(UpdateAll());
  ASSERT_NO_FATAL_FAILURE(ExpectNoUpdateSequence(&test_server, kUpdaterAppId));
  ASSERT_NO_FATAL_FAILURE(UpdateAll());

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, GetAppStates) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());

  const std::string kAppId("test");
  const base::Version v1("0.1");
  ASSERT_NO_FATAL_FAILURE(InstallApp(kAppId));

  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));

  base::Value::Dict expected_app_state;
  expected_app_state.Set("app_id", kAppId);
  expected_app_state.Set("version", v1.GetString());
  expected_app_state.Set("ap", "");
  expected_app_state.Set("brand_code", "");
  expected_app_state.Set("brand_path", "");
  expected_app_state.Set("ecp", "");
  expected_app_state.Set("cohort", "");
  base::Value::Dict expected_app_states;
  expected_app_states.Set(kAppId, std::move(expected_app_state));

  ASSERT_NO_FATAL_FAILURE(GetAppStates(expected_app_states));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, UnregisterUninstalledApp) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(InstallApp("test1"));
  ASSERT_NO_FATAL_FAILURE(InstallApp("test2"));

  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(UninstallApp("test1"));

  ASSERT_NO_FATAL_FAILURE(RunWake(0));

  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectNotRegistered("test1"));
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test2"));

  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, UninstallIfMaxServerWakesBeforeRegistrationExceeded) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(SetServerStarts(24));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
}

TEST_F(IntegrationTest, UninstallUpdaterWhenAllAppsUninstalled) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallApp("test1"));
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(SetServerStarts(24));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(UninstallApp("test1"));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
}

TEST_F(IntegrationTest, RotateLog) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(FillLog());
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectLogRotated());
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

#if BUILDFLAG(CHROMIUM_BRANDING) || BUILDFLAG(GOOGLE_CHROME_BRANDING)

TEST_F(IntegrationTest, SelfUpdateFromOldReal) {
  ScopedServer test_server(test_commands_);

  ASSERT_NO_FATAL_FAILURE(SetupRealUpdaterLowerVersion());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionNotActive(kUpdaterVersion));

  // Trigger an old instance update check.
  ASSERT_NO_FATAL_FAILURE(ExpectSelfUpdateSequence(&test_server));
  ASSERT_NO_FATAL_FAILURE(RunWakeActive(0));

  // Qualify the new instance.
  ASSERT_NO_FATAL_FAILURE(
      ExpectUpdateSequence(&test_server, kQualificationAppId, "",
                           UpdateService::Priority::kBackground,
                           base::Version("0.1"), base::Version("0.2")));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  // Activate the new instance. (It should not check itself for updates.)
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, UninstallIfUnusedSelfAndOldReal) {
  ScopedServer test_server(test_commands_);

  ASSERT_NO_FATAL_FAILURE(SetupRealUpdaterLowerVersion());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionNotActive(kUpdaterVersion));

  // Trigger an old instance update check.
  ASSERT_NO_FATAL_FAILURE(ExpectSelfUpdateSequence(&test_server));
  ASSERT_NO_FATAL_FAILURE(RunWakeActive(0));

  // Qualify the new instance.
  ASSERT_NO_FATAL_FAILURE(
      ExpectUpdateSequence(&test_server, kQualificationAppId, "",
                           UpdateService::Priority::kBackground,
                           base::Version("0.1"), base::Version("0.2")));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  // Activate the new instance. (It should not check itself for updates.)
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(SetServerStarts(24));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  // Expect that the updater uninstalled itself as well as the lower version.
}

// Tests that installing and uninstalling an old version of the updater from
// CIPD is possible.
TEST_F(IntegrationTest, InstallLowerVersion) {
  ASSERT_NO_FATAL_FAILURE(SetupRealUpdaterLowerVersion());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionNotActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(Uninstall());

#if BUILDFLAG(IS_WIN)
  // This deletes a tree of empty subdirectories corresponding to the crash
  // handler of the lower version updater installed above. `Uninstall` runs
  // `updater --uninstall` from the out directory of the build, which attempts
  // to launch the `uninstall.cmd` script corresponding to this version of the
  // updater from the install directory. However, there is no such script
  // because this version was never installed, and the script is not found
  // there.
  ASSERT_NO_FATAL_FAILURE(DeleteUpdaterDirectory());
#endif  // IS_WIN
}

#endif  // BUILDFLAG(CHROMIUM_BRANDING) || BUILDFLAG(GOOGLE_CHROME_BRANDING)

TEST_F(IntegrationTest, MAYBE_UpdateServiceStress) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(StressUpdateService());
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, IdleServerExits) {
#if BUILDFLAG(IS_WIN)
  if (IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP() << "System server startup is complicated on Windows.";
  }
#endif
  ASSERT_NO_FATAL_FAILURE(EnterTestMode(
      GURL("http://localhost:1234"), GURL("http://localhost:1234"),
      GURL("http://localhost:1234"), {}, base::Seconds(1)));
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(RunServer(kErrorIdle, true));
  ASSERT_NO_FATAL_FAILURE(RunServer(kErrorIdle, false));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, SameVersionUpdate) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  const std::string app_id = "test-appid";
  ASSERT_NO_FATAL_FAILURE(InstallApp(app_id));

  const std::string response = base::StringPrintf(
      ")]}'\n"
      R"({"response":{)"
      R"(  "protocol":"3.1",)"
      R"(  "app":[)"
      R"(    {)"
      R"(      "appid":"%s",)"
      R"(      "status":"ok",)"
      R"(      "updatecheck":{)"
      R"(        "status":"noupdate")"
      R"(      })"
      R"(    })"
      R"(  ])"
      R"(}})",
      app_id.c_str());
  test_server.ExpectOnce(
      {request::GetUpdaterUserAgentMatcher(),
       request::GetContentMatcher(
           {R"("updatecheck":{"sameversionupdate":true},"version":"0.1"}.*)"})},
      response);
  ASSERT_NO_FATAL_FAILURE(CallServiceUpdate(
      app_id, "", UpdateService::PolicySameVersionUpdate::kAllowed));

  test_server.ExpectOnce({request::GetUpdaterUserAgentMatcher(),
                          request::GetContentMatcher(
                              {R"(.*"updatecheck":{},"version":"0.1"}.*)"})},
                         response);
  ASSERT_NO_FATAL_FAILURE(CallServiceUpdate(
      app_id, "", UpdateService::PolicySameVersionUpdate::kNotAllowed));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, InstallDataIndex) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  const std::string app_id = "test-appid";
  const std::string install_data_index = "test-install-data-index";

  ASSERT_NO_FATAL_FAILURE(InstallApp(app_id));

  const std::string response = base::StringPrintf(
      ")]}'\n"
      R"({"response":{)"
      R"(  "protocol":"3.1",)"
      R"(  "app":[)"
      R"(    {)"
      R"(      "appid":"%s",)"
      R"(      "status":"ok",)"
      R"(      "updatecheck":{)"
      R"(        "status":"noupdate")"
      R"(      })"
      R"(    })"
      R"(  ])"
      R"(}})",
      app_id.c_str());

  test_server.ExpectOnce(
      {request::GetUpdaterUserAgentMatcher(),
       request::GetContentMatcher({base::StringPrintf(
           R"(.*"data":\[{"index":"%s","name":"install"}],.*)",
           install_data_index.c_str())})},
      response);

  ASSERT_NO_FATAL_FAILURE(
      CallServiceUpdate(app_id, install_data_index,
                        UpdateService::PolicySameVersionUpdate::kAllowed));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, MigrateLegacyUpdater) {
  ASSERT_NO_FATAL_FAILURE(SetupFakeLegacyUpdater());
#if BUILDFLAG(IS_WIN)
  ASSERT_NO_FATAL_FAILURE(RunFakeLegacyUpdater());
#endif  // BUILDFLAG(IS_WIN)
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectLegacyUpdaterMigrated());
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, RecoveryNoUpdater) {
  const std::string appid = "test1";
  const base::Version version("0.1");
  ASSERT_NO_FATAL_FAILURE(RunRecoveryComponent(appid, version));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(appid, version));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, RegisterApp) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_TRUE(WaitForUpdaterExit());

  RegistrationRequest registration;
  registration.app_id = "e595682b-02d5-46d1-b7ab-90034bd6be0f";
  registration.brand_code = "TSBD";
  registration.brand_path = base::FilePath::FromASCII("/bp");
  registration.ap = "TestAp";
  registration.version = base::Version("11.22.33.44");
  registration.existence_checker_path = base::FilePath::FromASCII("/tmp");
  registration.cohort = "cohort_test";
  test_commands_->RegisterApp(registration);

  base::Value::Dict expected_app_state;
  expected_app_state.Set("app_id", "e595682b-02d5-46d1-b7ab-90034bd6be0f");
  expected_app_state.Set("brand_code", "TSBD");
  expected_app_state.Set("brand_path", "/bp");
  expected_app_state.Set("ap", "TestAp");
  expected_app_state.Set("version", "11.22.33.44");
  expected_app_state.Set("ecp", "/tmp");
#if BUILDFLAG(IS_POSIX)
  // Cohort is only communicated over IPC on POSIX. Refer to crbug.com/40283110.
  expected_app_state.Set("cohort", "cohort_test");
#endif
  base::Value::Dict expected_app_states;
  expected_app_states.Set("e595682b-02d5-46d1-b7ab-90034bd6be0f",
                          std::move(expected_app_state));
  ASSERT_NO_FATAL_FAILURE(GetAppStates(expected_app_states));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, CrashUsageStatsEnabled) {
#if BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER)
  GTEST_SKIP() << "Crash tests disabled for Win ASAN.";
#else
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_TRUE(WaitForUpdaterExit());

  const std::string response;
  test_server.ExpectOnce(
      {
          request::GetPathMatcher(
              base::StringPrintf(R"(%s\?product=%s&version=%s&guid=.*)",
                                 test_server.crash_report_path().c_str(),
                                 CRASH_PRODUCT_NAME, kUpdaterVersion)),
          request::GetHeaderMatcher({{"User-Agent", R"(Crashpad/.*)"}}),
          request::GetMultipartContentMatcher({
              {"guid", std::vector<std::string>({})},  // Crash guid.
              {"process_type", std::vector<std::string>({R"(updater)"})},
              {"prod", std::vector<std::string>({CRASH_PRODUCT_NAME})},
              {"ver", std::vector<std::string>({kUpdaterVersion})},
              {"upload_file_minidump",  // Dump file name and its content.
               std::vector<std::string>(
                   {R"(filename=".*dmp")",
                    R"(Content-Type: application/octet-stream)", R"(MDMP)"})},
          }),
      },
      response);
  ExpectUninstallPing(&test_server);
  RunCrashMe();
  ASSERT_TRUE(WaitForUpdaterExit());

  // Delete the dmp files generated by this test, so `ExpectNoCrashes` won't
  // complain at TearDown.
  std::optional<base::FilePath> database_path(
      GetCrashDatabasePath(GetUpdaterScopeForTesting()));
  if (database_path && base::PathExists(*database_path)) {
    base::FileEnumerator(*database_path, true, base::FileEnumerator::FILES,
                         FILE_PATH_LITERAL("*.dmp"),
                         base::FileEnumerator::FolderSearchPolicy::ALL)
        .ForEach([](const base::FilePath& name) {
          VLOG(0) << "Deleting file at: " << name;
          EXPECT_TRUE(base::DeleteFile(name));
        });
  }
  ASSERT_NO_FATAL_FAILURE(Uninstall());
#endif
}

class IntegrationTestDeviceManagement : public IntegrationTest {
 public:
  IntegrationTestDeviceManagement() = default;
  ~IntegrationTestDeviceManagement() override = default;

 protected:
  void SetUp() override {
    IntegrationTest::SetUp();
    test_server_ = std::make_unique<ScopedServer>(test_commands_);
    if (!IsSystemInstall(GetUpdaterScopeForTesting())) {
      GTEST_SKIP();
    }
    DMCleanup();
    ASSERT_NO_FATAL_FAILURE(SetMachineManaged(true));
  }

  void TearDown() override {
    DMCleanup();
    IntegrationTest::TearDown();
  }

  void SetCloudPolicyOverridesPlatformPolicy() {
// Cloud policy overrides platform policy default, except on Windows.
#if BUILDFLAG(IS_WIN)
    EXPECT_EQ(ERROR_SUCCESS,
              base::win::RegKey(HKEY_LOCAL_MACHINE, UPDATER_POLICIES_KEY,
                                Wow6432(KEY_WRITE))
                  .WriteValue(L"CloudPolicyOverridesPlatformPolicy", 1));
#endif  // BUILDFLAG(IS_WIN)
  }

  std::unique_ptr<ScopedServer> test_server_;
  static constexpr char kEnrollmentToken[] =
      "00001111-beef-f00d-2222-333344445555";
  static constexpr char kDMToken[] = "integration-dm-token";

#if BUILDFLAG(IS_WIN)
  static constexpr char kGlobalPolicyKey[] = "";
#else
  static constexpr char kGlobalPolicyKey[] = "global";
#endif  // BUILDFLAG(IS_WIN)
};

// Tests the setup and teardown of the fixture.
TEST_F(IntegrationTestDeviceManagement, Nothing) {}

TEST_F(IntegrationTestDeviceManagement, PolicyFetchBeforeInstall) {
  OmahaSettingsClientProto omaha_settings;
  omaha_settings.set_install_default(
      enterprise_management::INSTALL_DEFAULT_DISABLED);
  omaha_settings.set_download_preference("not-cacheable");
  omaha_settings.set_proxy_mode("system");
  omaha_settings.set_proxy_server("test.proxy.server");
  ApplicationSettings app;
  app.set_app_guid(kApp1.appid);
  app.set_update(enterprise_management::AUTOMATIC_UPDATES_ONLY);
  app.set_target_version_prefix("0.1");
  app.set_rollback_to_target_version(
      enterprise_management::ROLLBACK_TO_TARGET_VERSION_ENABLED);
  omaha_settings.mutable_application_settings()->Add(std::move(app));

  DMPushEnrollmentToken(kEnrollmentToken);

  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  scoped_refptr<device_management_storage::DMStorage> dm_storage =
      device_management_storage::GetDefaultDMStorage();
  ASSERT_NE(dm_storage, nullptr);
  std::optional<OmahaSettingsClientProto> omaha_policy =
      GetOmahaPolicySettings(dm_storage);
  ASSERT_TRUE(omaha_policy);
  EXPECT_EQ(omaha_policy->download_preference(), "not-cacheable");
  EXPECT_EQ(omaha_policy->proxy_mode(), "system");
  EXPECT_EQ(omaha_policy->proxy_server(), "test.proxy.server");
  ASSERT_GT(omaha_policy->application_settings_size(), 0);
  const ApplicationSettings& app_policy =
      omaha_policy->application_settings()[0];
  EXPECT_EQ(app_policy.app_guid(), kApp1.appid);
  EXPECT_EQ(app_policy.update(), enterprise_management::AUTOMATIC_UPDATES_ONLY);
  EXPECT_EQ(app_policy.target_version_prefix(), "0.1");
  EXPECT_EQ(app_policy.rollback_to_target_version(),
            enterprise_management::ROLLBACK_TO_TARGET_VERSION_ENABLED);
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

#if BUILDFLAG(IS_MAC)
TEST_F(IntegrationTestDeviceManagement, FallbackToOutOfProcessFetcher) {
  OmahaSettingsClientProto omaha_settings;
  omaha_settings.set_install_default(
      enterprise_management::INSTALL_DEFAULT_DISABLED);
  omaha_settings.set_download_preference("not-cacheable");
  ApplicationSettings app;
  app.set_app_guid(kApp1.appid);
  app.set_update(enterprise_management::AUTOMATIC_UPDATES_ONLY);
  omaha_settings.mutable_application_settings()->Add(std::move(app));

  DMPushEnrollmentToken(kEnrollmentToken);

  // Verify that a single HTTP error from DM server is recovered by the
  // fallback fetcher.
  test_server_->ExpectOnce({}, "", net::HTTP_INTERNAL_SERVER_ERROR);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  test_server_->ExpectOnce({}, "", net::HTTP_INTERNAL_SERVER_ERROR);
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  scoped_refptr<device_management_storage::DMStorage> dm_storage =
      device_management_storage::GetDefaultDMStorage();
  ASSERT_NE(dm_storage, nullptr);
  std::optional<OmahaSettingsClientProto> omaha_policy =
      GetOmahaPolicySettings(dm_storage);
  ASSERT_TRUE(omaha_policy);
  EXPECT_EQ(omaha_policy->download_preference(), "not-cacheable");
  ASSERT_GT(omaha_policy->application_settings_size(), 0);
  const ApplicationSettings& app_policy =
      omaha_policy->application_settings()[0];
  EXPECT_EQ(app_policy.app_guid(), kApp1.appid);
  EXPECT_EQ(app_policy.update(), enterprise_management::AUTOMATIC_UPDATES_ONLY);
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}
#endif  // BUILDFLAG(IS_MAC)

TEST_F(IntegrationTestDeviceManagement, AppInstall) {
  const base::Version kApp1Version = base::Version("1.0.0.0");
  OmahaSettingsClientProto omaha_settings;
  omaha_settings.set_install_default(
      enterprise_management::INSTALL_DEFAULT_DISABLED);
  ApplicationSettings app;
  app.set_app_guid(kApp1.appid);
  app.set_install(enterprise_management::INSTALL_ENABLED);
  omaha_settings.mutable_application_settings()->Add(std::move(app));

  DMPushEnrollmentToken(kEnrollmentToken);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  ASSERT_NO_FATAL_FAILURE(ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/{},
      {
          AppUpdateExpectation(
              kApp1.GetInstallCommandLineArgs(/*install_v1=*/true), kApp1.appid,
              base::Version({0, 0, 0, 0}), kApp1.v1,
              /*is_install=*/true,
              /*should_update=*/true, false, "", "",
              GetInstallerPath(kApp1.v1_crx)),
      }));

  ASSERT_NO_FATAL_FAILURE(InstallAppViaService(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(InstallAppViaService(kApp2.appid));
  ExpectAppInstalled(kApp1.appid, kApp1.v1);
  ASSERT_NO_FATAL_FAILURE(ExpectNotRegistered(kApp2.appid));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestDeviceManagement, ForceInstall) {
  const base::Version kApp1Version = base::Version("1.0.0.0");

  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  // Force-install app1, enable install app2.
  OmahaSettingsClientProto omaha_settings;
  omaha_settings.set_install_default(
      enterprise_management::INSTALL_DEFAULT_DISABLED);
  ApplicationSettings app1;
  app1.set_app_guid(kApp1.appid);
  app1.set_install(enterprise_management::INSTALL_FORCED);
  omaha_settings.mutable_application_settings()->Add(std::move(app1));
  ApplicationSettings app2;
  app2.set_app_guid(kApp2.appid);
  app2.set_install(enterprise_management::INSTALL_ENABLED);
  omaha_settings.mutable_application_settings()->Add(std::move(app2));

  DMPushEnrollmentToken(kEnrollmentToken);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);
  ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/{},
      {
          AppUpdateExpectation(
              kApp1.GetInstallCommandLineArgs(/*install_v1=*/true), kApp1.appid,
              base::Version({0, 0, 0, 0}), kApp1.v1,
              /*is_install=*/true,
              /*should_update=*/true, false, "", "",
              GetInstallerPath(kApp1.v1_crx)),
      });
  ExpectUpdateCheckRequest(test_server_.get());

  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ExpectAppInstalled(kApp1.appid, kApp1.v1);
  ASSERT_NO_FATAL_FAILURE(ExpectNotRegistered(kApp2.appid));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestDeviceManagement, QualifyUpdaterWhenUpdateDisabled) {
  // Disable global update via CBCM.
  DMPushEnrollmentToken(kEnrollmentToken);
  OmahaSettingsClientProto omaha_settings;
  omaha_settings.set_update_default(enterprise_management::UPDATES_DISABLED);
  omaha_settings.set_cloud_policy_overrides_platform_policy(true);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(SetupFakeUpdaterLowerVersion());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionNotActive(kUpdaterVersion));

  ASSERT_NO_FATAL_FAILURE(
      ExpectUpdateSequence(test_server_.get(), kQualificationAppId, "",
                           UpdateService::Priority::kBackground,
                           base::Version("0.1"), base::Version("0.2")));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  // Verify the new instance is qualified and activated itself.
  ExpectDeviceManagementPolicyFetchRequest(
      test_server_.get(), kDMToken, omaha_settings, /*first_request=*/false);
  test_server_->ExpectOnce({request::GetUpdaterUserAgentMatcher(),
                            request::GetContentMatcher(
                                {base::StringPrintf(".*%s.*", kUpdaterAppId)})},
                           ")]}'\n");
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

#if BUILDFLAG(IS_WIN)
// RuntimeEnrollmentToken is supported on Windows only.
TEST_F(IntegrationTestDeviceManagement, RuntimeEnrollmentToken) {
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           OmahaSettingsClientProto());
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      test_server_.get(), kApp1.appid, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), kApp1.v1));
  ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
      kApp1.appid, /*is_silent_install=*/true,
      base::StrCat({"etoken=", kEnrollmentToken, "&appguid=", kApp1.appid,
                    "&usagestats=1"})));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kApp1.appid, kApp1.v1));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

// This test depends on platform policy overriding cloud policy, which is not
// the default on POSIX. Therefore, this test is Windows only.
TEST_F(IntegrationTestDeviceManagement, AppUpdateConflictPolicies) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp1, /*install_v1=*/true));
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp2, /*install_v1=*/true));
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp3, /*install_v1=*/true));

  base::Value::Dict policies;
  policies.Set(kApp2.appid, base::Value::Dict().Set("Update", kPolicyEnabled));
  ASSERT_NO_FATAL_FAILURE(SetPlatformPolicies(policies));

  // Cloud policy sets update default to disabled, app1 to auto-update, and
  // app2 to manual-update.
  DMPushEnrollmentToken(kEnrollmentToken);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  OmahaSettingsClientProto omaha_settings;
  omaha_settings.set_update_default(enterprise_management::UPDATES_DISABLED);
  ApplicationSettings app1;
  app1.set_app_guid(kApp1.appid);
  app1.set_update(enterprise_management::AUTOMATIC_UPDATES_ONLY);
  omaha_settings.mutable_application_settings()->Add(std::move(app1));
  ApplicationSettings app2;
  app2.set_app_guid(kApp2.appid);
  app2.set_update(enterprise_management::MANUAL_UPDATES_ONLY);
  omaha_settings.mutable_application_settings()->Add(std::move(app2));
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);

  ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/{},
      {
          AppUpdateExpectation(
              kApp1.GetInstallCommandLineArgs(/*install_v1=*/false),
              kApp1.appid, kApp1.v1, kApp1.v2,
              /*is_install=*/false,
              /*should_update=*/true, false, "", "",
              GetInstallerPath(kApp1.v2_crx)),
          AppUpdateExpectation(
              kApp2.GetInstallCommandLineArgs(/*install_v1=*/false),
              kApp2.appid, kApp2.v1, kApp2.v2,
              /*is_install=*/false,
              /*should_update=*/true, false, "", "",
              GetInstallerPath(kApp2.v2_crx)),
          AppUpdateExpectation(
              kApp3.GetInstallCommandLineArgs(/*install_v1=*/false),
              kApp3.appid, kApp3.v1, kApp3.v2,
              /*is_install=*/false,
              /*should_update=*/false, false, "", "",
              GetInstallerPath(kApp3.v2_crx)),
      });
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp1.appid, kApp1.v2));
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp2.appid, kApp2.v2));
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp3.appid, kApp3.v1));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp2.appid));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp3.appid));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestDeviceManagement, IPolicyStatus) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp1, /*install_v1=*/true));

  base::Value::Dict policies;
  policies.Set(kApp2.appid, base::Value::Dict().Set("Update", kPolicyEnabled));
  ASSERT_NO_FATAL_FAILURE(SetPlatformPolicies(policies));
  DMPushEnrollmentToken(kEnrollmentToken);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  OmahaSettingsClientProto omaha_settings;
  omaha_settings.set_download_preference("cacheable");
  omaha_settings.set_update_default(enterprise_management::UPDATES_DISABLED);
  omaha_settings.set_cloud_policy_overrides_platform_policy(true);
  ApplicationSettings app1;
  app1.set_app_guid(kApp1.appid);
  app1.set_target_channel("stable");
  app1.set_update(enterprise_management::AUTOMATIC_UPDATES_ONLY);
  app1.set_rollback_to_target_version(
      enterprise_management::ROLLBACK_TO_TARGET_VERSION_ENABLED);
  app1.set_target_version_prefix("2.0.");
  omaha_settings.mutable_application_settings()->Add(std::move(app1));
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);
  ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/{},
      {
          AppUpdateExpectation(
              kApp1.GetInstallCommandLineArgs(/*install_v1=*/false),
              kApp1.appid, kApp1.v1, kApp1.v2,
              /*is_install=*/false,
              /*should_update=*/true, false, "", "",
              GetInstallerPath(kApp1.v2_crx)),
      });
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp1.appid, kApp1.v2));

  {
    const bool is_system_install = IsSystemInstall(GetUpdaterScopeForTesting());
    base::win::AssertComInitialized();
    Microsoft::WRL::ComPtr<IUnknown> unknown;
    ASSERT_HRESULT_SUCCEEDED(
        ::CoCreateInstance(is_system_install ? CLSID_PolicyStatusSystemClass
                                             : CLSID_PolicyStatusUserClass,
                           nullptr, CLSCTX_ALL, IID_PPV_ARGS(&unknown)));

    const base::win::ScopedBstr app_id(base::ASCIIToWide(kApp1.appid));
    Microsoft::WRL::ComPtr<IPolicyStatus4> policy_status;
    ASSERT_TRUE(SUCCEEDED(unknown.CopyTo(is_system_install
                                             ? __uuidof(IPolicyStatus4System)
                                             : __uuidof(IPolicyStatus4User),
                                         IID_PPV_ARGS_Helper(&policy_status))));
    {
      Microsoft::WRL::ComPtr<IPolicyStatusValue> policy;
      EXPECT_HRESULT_SUCCEEDED(
          policy_status->get_downloadPreferenceGroupPolicy(&policy));
      ExpectPolicyStatusValues(policy, L"Device Management", L"cacheable",
                               VARIANT_FALSE);
    }
    {
      Microsoft::WRL::ComPtr<IPolicyStatusValue> policy;
      EXPECT_HRESULT_SUCCEEDED(
          policy_status->get_cloudPolicyOverridesPlatformPolicy(&policy));
      ExpectPolicyStatusValues(policy, L"Device Management", L"true",
                               VARIANT_FALSE);
    }
    {
      Microsoft::WRL::ComPtr<IPolicyStatusValue> policy;
      EXPECT_HRESULT_SUCCEEDED(policy_status->get_effectivePolicyForAppInstalls(
          app_id.Get(), &policy));
      ExpectPolicyStatusValues(policy, L"Default", L"1", VARIANT_FALSE);
    }
    {
      Microsoft::WRL::ComPtr<IPolicyStatusValue> policy;
      EXPECT_HRESULT_SUCCEEDED(policy_status->get_effectivePolicyForAppUpdates(
          app_id.Get(), &policy));
      ExpectPolicyStatusValues(policy, L"Device Management", L"3",
                               VARIANT_TRUE);
    }
    {
      Microsoft::WRL::ComPtr<IPolicyStatusValue> policy;
      EXPECT_HRESULT_SUCCEEDED(
          policy_status->get_targetChannel(app_id.Get(), &policy));
      ExpectPolicyStatusValues(policy, L"Device Management", L"stable",
                               VARIANT_FALSE);
    }
    {
      Microsoft::WRL::ComPtr<IPolicyStatusValue> policy;
      EXPECT_HRESULT_SUCCEEDED(
          policy_status->get_isRollbackToTargetVersionAllowed(app_id.Get(),
                                                              &policy));
      ExpectPolicyStatusValues(policy, L"Device Management", L"true",
                               VARIANT_TRUE);
    }
    {
      Microsoft::WRL::ComPtr<IPolicyStatusValue> policy;
      EXPECT_HRESULT_SUCCEEDED(
          policy_status->get_targetVersionPrefix(app_id.Get(), &policy));
      ExpectPolicyStatusValues(policy, L"Device Management", L"2.0.",
                               VARIANT_FALSE);
    }
    {
      const base::win::ScopedBstr app_id2(base::ASCIIToWide(kApp2.appid));
      Microsoft::WRL::ComPtr<IPolicyStatusValue> policy;
      EXPECT_HRESULT_SUCCEEDED(policy_status->get_effectivePolicyForAppUpdates(
          app_id2.Get(), &policy));
      ExpectPolicyStatusValues(policy, L"Device Management", L"0",
                               VARIANT_TRUE);
    }
  }
  ASSERT_TRUE(WaitForUpdaterExit());

  // Uninstall
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}
#endif  // BUILDFLAG(IS_WIN)

class IntegrationTestCloudPolicyOverridesPlatformPolicy
    : public ::testing::WithParamInterface<bool>,
      public IntegrationTestDeviceManagement {};

TEST_P(IntegrationTestCloudPolicyOverridesPlatformPolicy, UseCloudPolicy) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp1, /*install_v1=*/true));
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp2, /*install_v1=*/true));
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp3, /*install_v1=*/true));

  base::Value::Dict policies;
  policies.Set(kGlobalPolicyKey, base::Value::Dict()
                                     .Set("UpdateDefault", kPolicyDisabled)
                                     .Set("DownloadPreference", "cacheable"));
  policies.Set(kApp1.appid, base::Value::Dict()
                                .Set("Update", kPolicyDisabled)
                                .Set("TargetChannel", "beta"));
  policies.Set(kApp2.appid, base::Value::Dict().Set("Update", kPolicyEnabled));
  policies.Set(kApp3.appid, base::Value::Dict()
                                .Set("Update", kPolicyEnabled)
                                .Set("TargetChannel", "canary"));
  ASSERT_NO_FATAL_FAILURE(SetPlatformPolicies(policies));

  // Overrides app1 to auto-update, app2 to manual-update with cloud policy.
  DMPushEnrollmentToken(kEnrollmentToken);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  OmahaSettingsClientProto omaha_settings;
  ApplicationSettings app1;
  app1.set_app_guid(kApp1.appid);
  app1.set_update(enterprise_management::AUTOMATIC_UPDATES_ONLY);
  app1.set_target_channel("beta_canary");
  omaha_settings.mutable_application_settings()->Add(std::move(app1));
  ApplicationSettings app2;
  app2.set_app_guid(kApp2.appid);
  app2.set_update(enterprise_management::MANUAL_UPDATES_ONLY);
  omaha_settings.mutable_application_settings()->Add(std::move(app2));
  if (GetParam()) {
    omaha_settings.set_cloud_policy_overrides_platform_policy(true);
  } else {
    ASSERT_NO_FATAL_FAILURE(SetCloudPolicyOverridesPlatformPolicy());
  }

  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);
  ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/base::Value::Dict().Set("dlpref", "cacheable"),
      {
          AppUpdateExpectation(
              kApp1.GetInstallCommandLineArgs(/*install_v1=*/false),
              kApp1.appid, kApp1.v1, kApp1.v2,
              /*is_install=*/false,
              /*should_update=*/true, false, "", "beta_canary",
              GetInstallerPath(kApp1.v2_crx)),
          AppUpdateExpectation(
              kApp2.GetInstallCommandLineArgs(/*install_v1=*/false),
              kApp2.appid, kApp2.v1, kApp2.v1,
              /*is_install=*/false,
              /*should_update=*/false, false, "", "",
              GetInstallerPath(kApp2.v2_crx)),
          AppUpdateExpectation(
              kApp3.GetInstallCommandLineArgs(/*install_v1=*/false),
              kApp3.appid, kApp3.v1, kApp3.v2,
              /*is_install=*/false,
              /*should_update=*/true, false, "", "canary",
              GetInstallerPath(kApp3.v2_crx)),
      });
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp1.appid, kApp1.v2));
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp2.appid, kApp2.v1));
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp3.appid, kApp3.v2));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp2.appid));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp3.appid));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

INSTANTIATE_TEST_SUITE_P(
    IntegrationTestCloudPolicyOverridesPlatformPolicyTestCases,
    IntegrationTestCloudPolicyOverridesPlatformPolicy,
    ::testing::Bool());

TEST_F(IntegrationTestDeviceManagement, RollbackToTargetVersion) {
  constexpr char kTargetVersionPrefix[] = "1.0.";

  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp1, /*install_v1=*/false));

  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp1.appid, kApp1.v2));

  DMPushEnrollmentToken(kEnrollmentToken);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  OmahaSettingsClientProto omaha_settings;
  ApplicationSettings app;
  app.set_app_guid(kApp1.appid);
  app.set_target_version_prefix(kTargetVersionPrefix);
  app.set_rollback_to_target_version(
      enterprise_management::ROLLBACK_TO_TARGET_VERSION_ENABLED);
  omaha_settings.mutable_application_settings()->Add(std::move(app));
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);

  ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/{},
      {AppUpdateExpectation(
          kApp1.GetInstallCommandLineArgs(/*install_v1=*/true), kApp1.appid,
          kApp1.v2, kApp1.v1,
          /*is_install=*/false,
          /*should_update=*/true, /*allow_rollback=*/true, kTargetVersionPrefix,
          "", GetInstallerPath(kApp1.v1_crx))});
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp1.appid, kApp1.v1));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestDeviceManagement, DMTokenDeletion) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp1, /*install_v1=*/true));
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp1.appid, kApp1.v1));

  // Do a policy fetch to update the DM token.
  DMPushEnrollmentToken(kEnrollmentToken);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken, {});
  ASSERT_NO_FATAL_FAILURE(
      ExpectNoUpdateSequence(test_server_.get(), kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  EXPECT_EQ(device_management_storage::GetDefaultDMStorage()->GetDmToken(),
            kDMToken);

  // Run a second policy fetch and delete the DM token.
  ExpectDeviceManagementTokenDeletionRequest(test_server_.get(), kDMToken,
                                             /*invalidate_token=*/false);
#if BUILDFLAG(IS_MAC)
  // A second response for fallback fetcher.
  ExpectDeviceManagementTokenDeletionRequest(test_server_.get(), kDMToken,
                                             /*invalidate_token=*/false);
#endif
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  EXPECT_TRUE(
      device_management_storage::GetDefaultDMStorage()->GetDmToken().empty());

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestDeviceManagement, DMTokenInvalidation) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp1, /*install_v1=*/true));
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp1.appid, kApp1.v1));

  // Do a policy fetch to update the DM token.
  DMPushEnrollmentToken(kEnrollmentToken);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken, {});
  ASSERT_NO_FATAL_FAILURE(
      ExpectNoUpdateSequence(test_server_.get(), kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  EXPECT_EQ(device_management_storage::GetDefaultDMStorage()->GetDmToken(),
            kDMToken);

  // Run a second policy fetch and invalidate the DM token.
  ExpectDeviceManagementTokenDeletionRequest(test_server_.get(), kDMToken,
                                             /*invalidate_token=*/true);
#if BUILDFLAG(IS_MAC)
  // A second response for fallback fetcher.
  ExpectDeviceManagementTokenDeletionRequest(test_server_.get(), kDMToken,
                                             /*invalidate_token=*/true);
#endif  // BUILDFLAG(IS_MAC)
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  EXPECT_TRUE(
      device_management_storage::GetDefaultDMStorage()->IsDeviceDeregistered());

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestDeviceManagement, PublicKeyRotation) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp1, /*install_v1=*/false));

  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp1.appid, kApp1.v2));

  // Do a policy fetch to download the policy info with the initial public key.
  DMPushEnrollmentToken(kEnrollmentToken);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  OmahaSettingsClientProto omaha_settings;
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);
  ASSERT_NO_FATAL_FAILURE(
      ExpectNoUpdateSequence(test_server_.get(), kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  scoped_refptr<device_management_storage::DMStorage> dm_storage =
      device_management_storage::GetDefaultDMStorage();
  std::unique_ptr<device_management_storage::CachedPolicyInfo> cached_info =
      dm_storage->GetCachedPolicyInfo();
  ASSERT_NE(cached_info, nullptr);
  int64_t initial_key_timestamp = cached_info->timestamp();

  // Run a second policy fetch and verify the cached policy info is updated
  // to the one with the new public key.
  ApplicationSettings app;
  app.set_app_guid(kApp1.appid);
  app.set_target_version_prefix("1.0");
  app.set_rollback_to_target_version(
      enterprise_management::ROLLBACK_TO_TARGET_VERSION_ENABLED);
  omaha_settings.mutable_application_settings()->Add(std::move(app));
  ExpectDeviceManagementPolicyFetchWithNewPublicKeyRequest(
      test_server_.get(), kDMToken, omaha_settings);
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  cached_info = dm_storage->GetCachedPolicyInfo();
  ASSERT_NE(cached_info, nullptr);
  ASSERT_GT(cached_info->timestamp(), initial_key_timestamp);

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

// Tests that interact with state in both system and user updater configuration
// are run as part of the system-scope tests.
class IntegrationTestUserInSystem : public IntegrationTest {
 public:
  ~IntegrationTestUserInSystem() override = default;

 protected:
  void SetUp() override {
    if (SkipTest()) {
      GTEST_SKIP() << "The test is skipped in this configuration";
    }

    IntegrationTest::SetUp();
    test_server_ = std::make_unique<ScopedServer>();
    test_server_->ConfigureTestMode(user_test_commands_.get());
    test_server_->ConfigureTestMode(test_commands_.get());
  }

  void TearDown() override {
    if (!SkipTest()) {
      IntegrationTest::TearDown();
    }
  }

  void InstallUserUpdater(const base::Value::List& switches = {}) {
    user_test_commands_->Install(switches);
  }

  void UninstallUserUpdater() {
    ASSERT_TRUE(WaitForUpdaterExit());
    ExpectNoCrashes();
    PrintUserLog();
    CopyUserLog();
    user_test_commands_->Uninstall();
    ASSERT_TRUE(WaitForUpdaterExit());
  }

  void ExpectUserUpdaterInstalled() { user_test_commands_->ExpectInstalled(); }

  void InstallUserApp(const std::string& app_id, const base::Version& version) {
    user_test_commands_->InstallApp(app_id, version);
  }

  void ExpectUserAppVersion(const std::string& app_id,
                            const base::Version& version) {
    user_test_commands_->ExpectAppVersion(app_id, version);
  }

  void SetUserAppExistenceCheckerPath(const std::string& app_id,
                                      const base::FilePath& path) {
    user_test_commands_->SetExistenceCheckerPath(app_id, path);
  }

  void SetUserAppTag(const std::string& app_id, const std::string& tag) {
    user_test_commands_->SetAppTag(app_id, tag);
  }

  void ExpectUserAppTag(const std::string& app_id, const std::string& tag) {
    user_test_commands_->ExpectAppTag(app_id, tag);
  }

  void PrintUserLog() { user_test_commands_->PrintLog(); }

  void CopyUserLog() { user_test_commands_->CopyLog("user"); }

  void ExpectUserUninstallPing(ScopedServer* test_server,
                               std::optional<GURL> target_url = {}) {
    user_test_commands_->ExpectPing(
        test_server, update_client::protocol_request::kEventUninstall,
        target_url);
  }

  void ExpectUserInstallSequence(ScopedServer* test_server,
                                 const std::string& app_id,
                                 const std::string& install_data_index,
                                 UpdateService::Priority priority,
                                 const base::Version& from_version,
                                 const base::Version& to_version) {
    user_test_commands_->ExpectInstallSequence(test_server, app_id,
                                               install_data_index, priority,
                                               from_version, to_version,
                                               /*do_fault_injection=*/false);
  }

  void InstallUserUpdaterAndApp(
      const std::string& app_id,
      const bool is_silent_install,
      const std::string& tag,
      const std::string& child_window_text_to_find = {},
      const bool always_launch_cmd = false,
      const bool verify_app_logo_loaded = false) {
    user_test_commands_->InstallUpdaterAndApp(
        app_id, is_silent_install, tag, child_window_text_to_find,
        always_launch_cmd, verify_app_logo_loaded,
        /*expect_success=*/true, /*wait_for_the_installer=*/true);
  }

  scoped_refptr<IntegrationTestCommands> user_test_commands_ =
      CreateIntegrationTestCommandsUser(UpdaterScope::kUser);
  std::unique_ptr<ScopedServer> test_server_;

 private:
  // Even though the updater itself supports installing per-user applications at
  // high integrity, most of the tests in the `IntegrationTestUserInSystem` test
  // suite cannot run on Windows with UAC on, because the integration test
  // driver does not fully support installing per-user applications at high
  // integrity. For instance, it functions as a COM client running at high
  // integrity to create the user updater COM server, which is not supported on
  // Windows with UAC on.
  bool SkipTest() const {
    return !IsSystemInstall(GetUpdaterScopeForTesting()) ||
           (WrongUser(UpdaterScope::kUser) &&
            (GetTestName() !=
             "IntegrationTestUserInSystem.ElevatedInstallOfUserUpdaterAndApp"));
  }
};

// Tests the updater's functionality of installing per-user applications at high
// integrity. This test uses integration test driver APIs that support
// installing per-user applications at high integrity. For instance, it runs
// `UpdaterSetup --install --app-id=test` and `UpdaterSetup --uninstall`
// elevated via the command line, so that it directly uses the updater's
// functionality of de-elevating.
TEST_F(IntegrationTestUserInSystem, ElevatedInstallOfUserUpdaterAndApp) {
  const std::string kAppId("test");
  const base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectUserInstallSequence(
      test_server_.get(), kAppId, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1));

  ASSERT_NO_FATAL_FAILURE(InstallUserUpdaterAndApp(
      kAppId, /*is_silent_install=*/true, "usagestats=1"));
  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(ExpectUserAppVersion(kAppId, v1));

  ASSERT_NO_FATAL_FAILURE(ExpectUserUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(UninstallUserUpdater());
}

TEST_F(IntegrationTestUserInSystem, TagNonInterference) {
  ASSERT_NO_FATAL_FAILURE(InstallUserUpdater());
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectUserUpdaterInstalled());

  base::Version v("1.0.0.0");
  ASSERT_NO_FATAL_FAILURE(InstallApp("test_app", v));
  ExpectAppVersion("test_app", v);
  ExpectAppTag("test_app", "");
  ASSERT_NO_FATAL_FAILURE(InstallUserApp("test_app", v));
  ExpectUserAppVersion("test_app", v);
  ExpectUserAppTag("test_app", "");

  ASSERT_NO_FATAL_FAILURE(SetAppTag("test_app", "system"));
  ExpectAppTag("test_app", "system");
  ExpectUserAppTag("test_app", "");
  ASSERT_NO_FATAL_FAILURE(SetUserAppTag("test_app", "user"));
  ExpectUserAppTag("test_app", "user");
  ExpectAppTag("test_app", "system");

  ExpectUninstallPing(test_server_.get());
  Uninstall();
  ExpectUserUninstallPing(test_server_.get());
  UninstallUserUpdater();
}

// macOS specific tests.
#if BUILDFLAG(IS_MAC)

// The CRURegistration library exists only on macOS. It runs ksadmin. It should
// not find ksadmin before the updater is installed or after it is uninstalled,
// but should find the scope-suitable ksadmin while the updater is installed.
TEST_F(IntegrationTest, CRURegistrationFindKSAdmin) {
  EXPECT_NO_FATAL_FAILURE(ExpectCRURegistrationCannotFindKSAdmin())
      << "ksadmin found before first installation.";
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  EXPECT_NO_FATAL_FAILURE(
      ExpectCRURegistrationFindsKSAdmin(GetUpdaterScopeForTesting()));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
  EXPECT_NO_FATAL_FAILURE(ExpectCRURegistrationCannotFindKSAdmin())
      << "ksadmin found after uninstall.";
}

TEST_F(IntegrationTest, CRURegistrationCannotGetTagWithoutUpdater) {
  base::ScopedTempFile xc_path;
  ASSERT_TRUE(xc_path.Create());
  EXPECT_NO_FATAL_FAILURE(
      ExpectCRURegistrationCannotFetchTag(kApp1.appid, xc_path.path()));
}

TEST_F(IntegrationTest, CRURegistrationCannotGetTagWithoutApp) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());

  base::ScopedTempFile xc_path;
  ASSERT_TRUE(xc_path.Create());
  EXPECT_NO_FATAL_FAILURE(
      ExpectCRURegistrationCannotFetchTag(kApp1.appid, xc_path.path()));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

#if !defined(ADDRESS_SANITIZER)
TEST_F(IntegrationTest, CRURegistrationFindsBlankTag) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());

  base::ScopedTempFile xc_path;
  ASSERT_TRUE(xc_path.Create());
  ASSERT_NO_FATAL_FAILURE(InstallApp(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(SetExistenceCheckerPath(kApp1.appid, xc_path.path()));

  EXPECT_NO_FATAL_FAILURE(
      ExpectCRURegistrationFetchesTag(kApp1.appid, xc_path.path(), ""));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, CRURegistrationFindsTag) {
  ScopedServer test_server(test_commands_);
  const std::string kAppId("test");
  const base::Version v1("1");
  base::ScopedTempFile xc_path;
  ASSERT_TRUE(xc_path.Create());

  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_server, kAppId, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1));
  ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
      kAppId, /*is_silent_install=*/true,
      base::StrCat({"appguid=", kAppId, "&ap=tagvalue&usagestats=1"})));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(SetExistenceCheckerPath(kAppId, xc_path.path()));

  EXPECT_NO_FATAL_FAILURE(
      ExpectCRURegistrationFetchesTag(kAppId, xc_path.path(), "tagvalue"));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}
#endif  // !defined(ADDRESS_SANITIZER)

// App ownership feature only exists on macOS.
TEST_F(IntegrationTest, UnregisterUnownedApp) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));

  ASSERT_NO_FATAL_FAILURE(InstallApp("test1"));
  ASSERT_NO_FATAL_FAILURE(InstallApp("test2"));
  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(SetExistenceCheckerPath(
      "test1", IsSystemInstall(GetUpdaterScopeForTesting())
                   ? temp_dir.GetPath()
                   : GetDifferentUserPath()));

  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  // Since the updater may have chowned the temp dir, we may need to elevate to
  // delete it.
  ASSERT_NO_FATAL_FAILURE(DeleteFile(temp_dir.GetPath()));

  if (IsSystemInstall(GetUpdaterScopeForTesting())) {
    ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test1"));
  } else {
    ASSERT_NO_FATAL_FAILURE(ExpectNotRegistered("test1"));
  }

  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test2"));

  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

// The updater shims are only repaired by the server on macOS.
TEST_F(IntegrationTest, RepairUpdater) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(DeleteLegacyUpdater());
  std::optional<base::FilePath> ksadmin_path =
      GetKSAdminPath(GetUpdaterScopeForTesting());
  ASSERT_TRUE(ksadmin_path.has_value());
  ASSERT_FALSE(base::PathExists(*ksadmin_path));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_TRUE(base::PathExists(*ksadmin_path));
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

// Only macOS software needs to try to suppress user-visible Gatekeeper popups.
TEST_F(IntegrationTest, SmokeTestPrepareToRunBundle) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));
  ASSERT_TRUE(WaitForUpdaterExit());

  std::optional<base::FilePath> updater_path =
      GetUpdaterAppBundlePath(GetUpdaterScopeForTesting());
  ASSERT_TRUE(updater_path);
  ASSERT_NO_FATAL_FAILURE(ExpectPrepareToRunBundleSuccess(*updater_path));

  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

// The privileged helper only exists on macOS. This does not test installation
// of the helper itself, but is meant to cover its core functionality.
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
TEST_F(IntegrationTest, PrivilegedHelperInstall) {
  if (GetUpdaterScopeForTesting() != UpdaterScope::kSystem) {
    return;  // Test is only applicable to system scope.
  }
  ASSERT_NO_FATAL_FAILURE(PrivilegedHelperInstall());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectRegistered("test1"));
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion("test1", base::Version("1.2.3.4")));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}
#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)

TEST_F(IntegrationTest, FallbackToOutOfProcessFetcher) {
  const std::string kAppId1("test1");
  const base::Version v1("1");
  // Injects an HTTP error before each network fetch to activate the fallback
  // fetcher. The installation should still succeed.
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_server, kAppId1, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1, /*do_fault_injection=*/true));
  ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
      kAppId1, /*is_silent_install=*/true,
      base::StrCat({"appguid=", kAppId1, "&ap=foo&usagestats=1"})));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId1, v1));
  ASSERT_NO_FATAL_FAILURE(ExpectAppTag(kAppId1, "foo"));

  const std::string kAppId2("test2");
  const base::Version v2("2.0");
  // Consecutive HTTP errors should fail the installation, given the fact that
  // updater has only one fallback for each network task.
  test_server.ExpectOnce({}, "", net::HTTP_INTERNAL_SERVER_ERROR);
  test_server.ExpectOnce({}, "", net::HTTP_GONE);
  ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
      kAppId2, /*is_silent_install=*/true,
      base::StrCat({"appguid=", kAppId2, "&ap=foo2&usagestats=1"}),
      /*child_window_text_to_find=*/{}, /*always_launch_cmd=*/false,
      /*verify_app_logo_loaded=*/false,
      /*expect_success=*/false));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId2, base::Version()));
  ASSERT_NO_FATAL_FAILURE(ExpectAppTag(kAppId2, ""));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, KSAdminNoAppNoTag) {
#if defined(ADDRESS_SANITIZER)
  if (IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP() << "User->System launcher can't load macOS ASAN dylib.";
    // Actually, since this test expects ksadmin to fail, it passes under these
    // conditions, but for the wrong reason.
  }
#else
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ExpectKSAdminFetchTag(false, "no.such.app", {}, {}, {});
  ASSERT_NO_FATAL_FAILURE(Uninstall());
#endif  // defined(ADDRESS_SANITIZER)
}

TEST_F(IntegrationTest, KSAdminUntaggedApp) {
#if defined(ADDRESS_SANITIZER)
  if (IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP() << "User->System launcher can't load macOS ASAN dylib.";
  }
#else
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(InstallApp("org.chromium.testapp"));
  ExpectKSAdminFetchTag(false, "org.chromium.testapp", {}, {}, "");
  ASSERT_NO_FATAL_FAILURE(UninstallApp("org.chromium.testapp"));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
#endif  // defined(ADDRESS_SANITIZER)
}

TEST_F(IntegrationTest, KSAdminTaggedApp) {
#if defined(ADDRESS_SANITIZER)
  if (IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP() << "User->System launcher can't load macOS ASAN dylib.";
  }
#else
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(InstallApp("org.chromium.testapp"));
  ASSERT_NO_FATAL_FAILURE(SetAppTag("org.chromium.testapp", "some-tag"));
  ExpectKSAdminFetchTag(false, "org.chromium.testapp", {}, {}, "some-tag");
  ASSERT_NO_FATAL_FAILURE(UninstallApp("org.chromium.testapp"));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
#endif  // defined(ADDRESS_SANITIZER)
}

TEST_F(IntegrationTest, CRURegistrationInstallsUpdater) {
  if (IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP();
  }
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(ExpectRegistrationTestAppUserUpdaterInstallSuccess());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  ExpectUninstallPing(&test_server);
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, CRURegistrationIdempotentInstallSuccess) {
  if (IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP();
  }
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  ASSERT_NO_FATAL_FAILURE(ExpectRegistrationTestAppUserUpdaterInstallSuccess());
  ExpectInstalled();

  ExpectUninstallPing(&test_server);
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, CRURegistrationRegister) {
  if (IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP();
  }
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  ASSERT_NO_FATAL_FAILURE(ExpectRegistrationTestAppRegisterSuccess());
  ExpectAppVersion("org.chromium.CRURegistration.testing.RegisterMe",
                   base::Version({1, 0, 0, 0}));

  ExpectUninstallPing(&test_server);
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, CRURegistrationInstallAndRegister) {
  if (IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP();
  }
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(ExpectRegistrationTestAppInstallAndRegisterSuccess());
  ExpectInstalled();
  ExpectAppVersion("org.chromium.CRURegistration.testing.RegisterMe",
                   base::Version({2, 0, 0, 0}));

  ExpectUninstallPing(&test_server);
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

// This is a copy of ReportsActive, but it uses CRURegistration to mark the
// app active. If both this test and ReportsActive fail, suspect an issue with
// actives reporting; if only this test fails, suspect CRURegistration.
TEST_F(IntegrationTest, CRURegistrationReportsActive) {
  if (IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP();
  }
  // A longer than usual timeout is needed for this test because the macOS
  // UpdateServiceInternal server takes at least 10 seconds to shut down after
  // Install, and InstallApp cannot make progress until it shut downs and
  // releases the global prefs lock.
  ASSERT_GE(TestTimeouts::action_timeout(), base::Seconds(18));
  base::test::ScopedRunLoopTimeout timeout(FROM_HERE,
                                           TestTimeouts::action_timeout());

  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  // Register apps test1 and test2. Expect pings for each.
  ASSERT_NO_FATAL_FAILURE(InstallApp("test1"));
  ASSERT_NO_FATAL_FAILURE(InstallApp("test2"));

  // Set test1 to be active via CRURegistration and do a background updatecheck.
  ASSERT_NO_FATAL_FAILURE(ExpectCRURegistrationMarksActive("test1"));
  ASSERT_NO_FATAL_FAILURE(ExpectActive("test1"));
  ASSERT_NO_FATAL_FAILURE(ExpectNotActive("test2"));
  test_server.ExpectOnce(
      {request::GetUpdaterUserAgentMatcher(),
       request::GetContentMatcher(
           {R"(.*"appid":"test1","enabled":true,"installdate":-1,)",
            R"("ping":{"ad":-1,.*)"})},
      R"()]}')"
      "\n"
      R"({"response":{"protocol":"3.1","daystart":{"elapsed_)"
      R"(days":5098}},"app":[{"appid":"test1","status":"ok",)"
      R"("updatecheck":{"status":"noupdate"}},{"appid":"test2",)"
      R"("status":"ok","updatecheck":{"status":"noupdate"}}]})");
  ASSERT_NO_FATAL_FAILURE(RunWake(0));

  // The updater has cleared the active bits.
  ASSERT_NO_FATAL_FAILURE(ExpectNotActive("test1"));
  ASSERT_NO_FATAL_FAILURE(ExpectNotActive("test2"));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

#if !defined(ADDRESS_SANITIZER)

TEST_F(IntegrationTestUserInSystem, CRURegistrationRegistersApp) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallUserUpdater());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectUserUpdaterInstalled());
  base::ScopedTempFile xc_file;
  ASSERT_TRUE(xc_file.Create());

  ExpectCRURegistrationRegisters("test", xc_file.path(), "0.0.0.1");
  ExpectUserAppVersion("test", base::Version({0, 0, 0, 1}));
  ExpectNotRegistered("test");

  ExpectUserUninstallPing(test_server_.get());
  ASSERT_NO_FATAL_FAILURE(UninstallUserUpdater());
  ExpectUninstallPing(test_server_.get());
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestUserInSystem, CRURegistrationUpdatesVersion) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallUserUpdater());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectUserUpdaterInstalled());
  base::ScopedTempFile xc_file;
  ASSERT_TRUE(xc_file.Create());

  InstallUserApp("test", base::Version({0, 0, 0, 1}));
  ExpectCRURegistrationRegisters("test", xc_file.path(), "0.0.0.2");
  ExpectUserAppVersion("test", base::Version({0, 0, 0, 2}));
  ExpectNotRegistered("test");

  ExpectUserUninstallPing(test_server_.get());
  ASSERT_NO_FATAL_FAILURE(UninstallUserUpdater());
  ExpectUninstallPing(test_server_.get());
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestUserInSystem, CRURegistrationCannotRegisterMissingAppID) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallUserUpdater());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectUserUpdaterInstalled());
  base::ScopedTempFile xc_file;
  ASSERT_TRUE(xc_file.Create());

  ExpectCRURegistrationCannotRegister("", xc_file.path(), "0.0.0.1");

  ExpectUserUninstallPing(test_server_.get());
  ASSERT_NO_FATAL_FAILURE(UninstallUserUpdater());
  ExpectUninstallPing(test_server_.get());
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestUserInSystem, CRURegistrationNeedsUpdater) {
  base::ScopedTempFile xc_file;
  ASSERT_TRUE(xc_file.Create());

  ExpectCRURegistrationCannotRegister("test", xc_file.path(), "0.0.0.1");
}

class IntegrationTestKSAdminUserInSystem : public IntegrationTestUserInSystem {
 protected:
  void ExpectUserKSAdminFetchTag(bool elevate,
                                 const std::string& product_id,
                                 const base::FilePath& xc_path,
                                 std::optional<UpdaterScope> store_flag,
                                 std::optional<std::string> want_tag) {
    user_test_commands_->ExpectKSAdminFetchTag(elevate, product_id, xc_path,
                                               store_flag, want_tag);
  }

  void ExpectBothKSAdminFetchTag(bool elevate,
                                 const std::string& product_id,
                                 const base::FilePath xc_path,
                                 std::optional<UpdaterScope> store_flag,
                                 std::optional<std::string> want_tag) {
    ExpectUserKSAdminFetchTag(elevate, product_id, xc_path, store_flag,
                              want_tag);
    ExpectKSAdminFetchTag(elevate, product_id, xc_path, store_flag, want_tag);
  }
};

TEST_F(IntegrationTestKSAdminUserInSystem, KSAdminNoAppNoTagNoMatterWhat) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallUserUpdater());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectUserUpdaterInstalled());

  ExpectBothKSAdminFetchTag(false, "no.such.app", {}, {}, {});
  ExpectBothKSAdminFetchTag(true, "no.such.app", {}, {}, {});

  ExpectUserUninstallPing(test_server_.get());
  ASSERT_NO_FATAL_FAILURE(UninstallUserUpdater());
  ExpectUninstallPing(test_server_.get());
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

// A set of KSAdmin tests that require apps to be installed in a specific way:
//
// * product ID `system-app`, tag `system-tag`, installed at system scope
// * product ID `user-app`, tag `user-tag`, installed at user scope
// * product ID `repeat-app`, tag `repeat-system-tag`, installed at system scope
// * product ID `repeat-app`, tag `repeat-user-tag`, installed at user scope
//
// Each installation has a unique existence checker path referring to a temp
// file created during test setup and deleted during teardown. Test setup and
// teardown also installs and uninstalls updaters at both user and system scope.
//
// Tests may also rely on `nonexistent-app` to test product IDs not registered
// with any updater. The class also provides an extra temp file that is not
// the existence checker path of anything, for similar reasons.
class IntegrationTestKSAdminFourApps
    : public IntegrationTestKSAdminUserInSystem {
 protected:
  void SetUp() override {
    IntegrationTestKSAdminUserInSystem::SetUp();
    if (IsSkipped() || HasFailure()) {
      // If the test should not run, stop without installing the updater.
      return;
    }

    ASSERT_NO_FATAL_FAILURE(Install());
    ASSERT_NO_FATAL_FAILURE(InstallUserUpdater());
    ASSERT_TRUE(WaitForUpdaterExit());
    ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
    ASSERT_NO_FATAL_FAILURE(ExpectUserUpdaterInstalled());

    base::Version v("1.0.0.0");

    ASSERT_NO_FATAL_FAILURE(InstallApp(kSystemAppID, v));
    ASSERT_NO_FATAL_FAILURE(SetAppTag(kSystemAppID, kSystemAppTag));
    ASSERT_TRUE(system_app_xcfile_.Create());
    ASSERT_NO_FATAL_FAILURE(
        SetExistenceCheckerPath(kSystemAppID, system_app_xcfile_.path()));

    ASSERT_NO_FATAL_FAILURE(InstallApp(kRepeatAppID, v));
    ASSERT_NO_FATAL_FAILURE(SetAppTag(kRepeatAppID, kRepeatAppSystemTag));
    ASSERT_TRUE(repeat_app_system_xcfile_.Create());
    ASSERT_NO_FATAL_FAILURE(SetExistenceCheckerPath(
        kRepeatAppID, repeat_app_system_xcfile_.path()));

    ASSERT_NO_FATAL_FAILURE(InstallUserApp(kUserAppID, v));
    ASSERT_NO_FATAL_FAILURE(SetUserAppTag(kUserAppID, kUserAppTag));
    ASSERT_TRUE(user_app_xcfile_.Create());
    ASSERT_NO_FATAL_FAILURE(
        SetUserAppExistenceCheckerPath(kUserAppID, user_app_xcfile_.path()));

    ASSERT_NO_FATAL_FAILURE(InstallUserApp(kRepeatAppID, v));
    ASSERT_NO_FATAL_FAILURE(SetUserAppTag(kRepeatAppID, kRepeatAppUserTag));
    ASSERT_TRUE(repeat_app_user_xcfile_.Create());
    ASSERT_NO_FATAL_FAILURE(SetUserAppExistenceCheckerPath(
        kRepeatAppID, repeat_app_user_xcfile_.path()));

    ASSERT_TRUE(no_app_xcfile_.Create());
  }

  void TearDown() override {
    if (IsSkipped()) {
      // Did not set up; no setup actions to reverse.
      return;
    }
    if (test_server_) {
      ExpectUserUninstallPing(test_server_.get());
    }
    ASSERT_NO_FATAL_FAILURE(UninstallUserUpdater());
    if (test_server_) {
      ExpectUninstallPing(test_server_.get());
    }
    ASSERT_NO_FATAL_FAILURE(Uninstall());

    IntegrationTestKSAdminUserInSystem::TearDown();
  }

  static constexpr char kSystemAppID[] = "system-app";
  static constexpr char kSystemAppTag[] = "system-tag";
  base::ScopedTempFile system_app_xcfile_;

  static constexpr char kRepeatAppID[] = "repeat-app";
  static constexpr char kRepeatAppSystemTag[] = "repeat-system-tag";
  base::ScopedTempFile repeat_app_system_xcfile_;
  static constexpr char kRepeatAppUserTag[] = "repeat-user-tag";
  base::ScopedTempFile repeat_app_user_xcfile_;

  static constexpr char kUserAppID[] = "user-app";
  static constexpr char kUserAppTag[] = "user-tag";
  base::ScopedTempFile user_app_xcfile_;

  static constexpr char kNonexistentAppID[] = "nonexistent-app";
  base::ScopedTempFile no_app_xcfile_;
};

TEST_F(IntegrationTestKSAdminFourApps, ServiceTagSmokeTest) {
  ExpectAppTag(kSystemAppID, kSystemAppTag);
  ExpectAppTag(kRepeatAppID, kRepeatAppSystemTag);
  ExpectUserAppTag(kUserAppID, kUserAppTag);
  ExpectUserAppTag(kRepeatAppID, kRepeatAppUserTag);
}

TEST_F(IntegrationTestKSAdminFourApps, UserLookupNoHints) {
  ExpectBothKSAdminFetchTag(false, kSystemAppID, {}, {}, kSystemAppTag);
  ExpectBothKSAdminFetchTag(false, kRepeatAppID, {}, {}, kRepeatAppSystemTag);
  ExpectBothKSAdminFetchTag(false, kUserAppID, {}, {}, kUserAppTag);
  ExpectBothKSAdminFetchTag(false, kNonexistentAppID, {}, {}, {});
}

TEST_F(IntegrationTestKSAdminFourApps, ElevatedLookupNoHints) {
  ExpectBothKSAdminFetchTag(true, kSystemAppID, {}, {}, kSystemAppTag);
  ExpectBothKSAdminFetchTag(true, kRepeatAppID, {}, {}, kRepeatAppSystemTag);
  ExpectBothKSAdminFetchTag(true, kUserAppID, {}, {}, {});
  ExpectBothKSAdminFetchTag(true, kNonexistentAppID, {}, {}, {});
}

TEST_F(IntegrationTestKSAdminFourApps, UserStoreFlag) {
  // When running elevated, ksadmin refuses to use a user store.
  ExpectBothKSAdminFetchTag(true, kSystemAppID, {}, UpdaterScope::kUser, {});
  ExpectBothKSAdminFetchTag(true, kRepeatAppID, {}, UpdaterScope::kUser, {});
  ExpectBothKSAdminFetchTag(true, kUserAppID, {}, UpdaterScope::kUser, {});
  ExpectBothKSAdminFetchTag(true, kNonexistentAppID, {}, UpdaterScope::kUser,
                            {});

  // In the presence of a user store flag, only search the user store.
  ExpectBothKSAdminFetchTag(false, kSystemAppID, {}, UpdaterScope::kUser, {});
  ExpectBothKSAdminFetchTag(false, kRepeatAppID, {}, UpdaterScope::kUser,
                            kRepeatAppUserTag);
  ExpectBothKSAdminFetchTag(false, kUserAppID, {}, UpdaterScope::kUser,
                            kUserAppTag);
  ExpectBothKSAdminFetchTag(false, kNonexistentAppID, {}, UpdaterScope::kUser,
                            {});

  // Existence checker path hinting does not alter any part of this result.
  ExpectBothKSAdminFetchTag(false, kRepeatAppID, repeat_app_user_xcfile_.path(),
                            UpdaterScope::kUser, kRepeatAppUserTag);
  ExpectBothKSAdminFetchTag(false, kRepeatAppID,
                            repeat_app_system_xcfile_.path(),
                            UpdaterScope::kUser, kRepeatAppUserTag);
  ExpectBothKSAdminFetchTag(false, kRepeatAppID, no_app_xcfile_.path(),
                            UpdaterScope::kUser, kRepeatAppUserTag);
  ExpectBothKSAdminFetchTag(true, kRepeatAppID,
                            repeat_app_system_xcfile_.path(),
                            UpdaterScope::kUser, {});
  ExpectBothKSAdminFetchTag(true, kRepeatAppID, repeat_app_user_xcfile_.path(),
                            UpdaterScope::kUser, {});
  ExpectBothKSAdminFetchTag(true, kRepeatAppID, no_app_xcfile_.path(),
                            UpdaterScope::kUser, {});
}

TEST_F(IntegrationTestKSAdminFourApps, SystemStoreFlag) {
  // In the presence of a system store flag, only search the system store.
  ExpectBothKSAdminFetchTag(false, kSystemAppID, {}, UpdaterScope::kSystem,
                            kSystemAppTag);
  ExpectBothKSAdminFetchTag(false, kRepeatAppID, {}, UpdaterScope::kSystem,
                            kRepeatAppSystemTag);
  ExpectBothKSAdminFetchTag(false, kUserAppID, {}, UpdaterScope::kSystem, {});
  ExpectBothKSAdminFetchTag(false, kNonexistentAppID, {}, UpdaterScope::kUser,
                            {});
  ExpectBothKSAdminFetchTag(true, kSystemAppID, {}, UpdaterScope::kSystem,
                            kSystemAppTag);
  ExpectBothKSAdminFetchTag(true, kRepeatAppID, {}, UpdaterScope::kSystem,
                            kRepeatAppSystemTag);
  ExpectBothKSAdminFetchTag(true, kUserAppID, {}, UpdaterScope::kSystem, {});
  ExpectBothKSAdminFetchTag(true, kNonexistentAppID, {}, UpdaterScope::kUser,
                            {});

  // Existence checker path hinting does not alter elevated results.
  ExpectBothKSAdminFetchTag(true, kRepeatAppID,
                            repeat_app_system_xcfile_.path(),
                            UpdaterScope::kSystem, kRepeatAppSystemTag);
  ExpectBothKSAdminFetchTag(true, kRepeatAppID, repeat_app_user_xcfile_.path(),
                            UpdaterScope::kSystem, kRepeatAppSystemTag);
  ExpectBothKSAdminFetchTag(true, kRepeatAppID, no_app_xcfile_.path(),
                            UpdaterScope::kSystem, kRepeatAppSystemTag);
}

// TODO: crbug/355246092 - Fix ksadmin's handling of this scenario and enable
//     this test. Currently, ksadmin will see the `--system-store` switch and
//     retrieve the registration from the system store, but not check further
//     to verify the existence checker path match.
TEST_F(IntegrationTestKSAdminFourApps,
       DISABLED_SystemStoreFlagXCPathMismatchAsUser) {
  // Because a non-elevated user can't "fix" a mismatched path for a system
  // app registration, a mismatching existence checker path causes lookup
  // to fail; because the store was explicitly specified, ksadmin will not
  // consider the user store.
  ExpectBothKSAdminFetchTag(false, kRepeatAppID,
                            repeat_app_system_xcfile_.path(),
                            UpdaterScope::kSystem, kRepeatAppSystemTag);
  ExpectBothKSAdminFetchTag(false, kRepeatAppID, repeat_app_user_xcfile_.path(),
                            UpdaterScope::kSystem, {});
  ExpectBothKSAdminFetchTag(false, kRepeatAppID, no_app_xcfile_.path(),
                            UpdaterScope::kSystem, {});
}

TEST_F(IntegrationTestKSAdminFourApps, XCPathMatch) {
  ExpectBothKSAdminFetchTag(true, kSystemAppID, system_app_xcfile_.path(), {},
                            kSystemAppTag);
  ExpectBothKSAdminFetchTag(false, kSystemAppID, system_app_xcfile_.path(), {},
                            kSystemAppTag);

  // Root can't see user stores.
  ExpectBothKSAdminFetchTag(true, kUserAppID, user_app_xcfile_.path(), {}, {});
  ExpectBothKSAdminFetchTag(false, kUserAppID, user_app_xcfile_.path(), {},
                            kUserAppTag);

  // When running as user, XC path disambiguates.
  ExpectBothKSAdminFetchTag(false, kRepeatAppID, repeat_app_user_xcfile_.path(),
                            {}, kRepeatAppUserTag);
  ExpectBothKSAdminFetchTag(false, kRepeatAppID,
                            repeat_app_system_xcfile_.path(), {},
                            kRepeatAppSystemTag);

  // Root can't see user stores, but it doesn't see the mismatching XC path
  // as a reason not to retrieve the entry in the system store, because -- since
  // the user is root -- the user would be able to fix this registration.
  ExpectBothKSAdminFetchTag(true, kRepeatAppID,
                            repeat_app_system_xcfile_.path(), {},
                            kRepeatAppSystemTag);
  ExpectBothKSAdminFetchTag(true, kRepeatAppID, repeat_app_user_xcfile_.path(),
                            {}, kRepeatAppSystemTag);
}

TEST_F(IntegrationTestKSAdminFourApps, XCPathMismatchElevated) {
  // When running as root, ksadmin only considers the system store, and doesn't
  // consider existence checking path mismatches to stop retrieval.
  ExpectBothKSAdminFetchTag(true, kSystemAppID, no_app_xcfile_.path(), {},
                            kSystemAppTag);
  ExpectBothKSAdminFetchTag(true, kUserAppID, no_app_xcfile_.path(), {}, {});
  ExpectBothKSAdminFetchTag(true, kRepeatAppID, no_app_xcfile_.path(), {},
                            kRepeatAppSystemTag);
  ExpectBothKSAdminFetchTag(true, kNonexistentAppID, no_app_xcfile_.path(), {},
                            {});
}

TEST_F(IntegrationTestKSAdminFourApps, XCPathMismatchUser) {
  // ksadmin knows a user can "fix" the existence checker path in a user
  // registration (and attempting to re-register the app will overwrite that
  // registration), but cannot "fix" (and therefore does not match) a system
  // registration with a different existence checking path.
  ExpectBothKSAdminFetchTag(false, kSystemAppID, no_app_xcfile_.path(), {}, {});
  ExpectBothKSAdminFetchTag(false, kUserAppID, no_app_xcfile_.path(), {},
                            kUserAppTag);
  ExpectBothKSAdminFetchTag(false, kRepeatAppID, no_app_xcfile_.path(), {},
                            kRepeatAppUserTag);
  ExpectBothKSAdminFetchTag(false, kNonexistentAppID, no_app_xcfile_.path(), {},
                            {});
}

TEST_F(IntegrationTestKSAdminFourApps, CRURegistrationFetchTag) {
  // Direct, unambiguous matches (or nothing matching).
  ExpectCRURegistrationFetchesTag(kSystemAppID, system_app_xcfile_.path(),
                                  kSystemAppTag);
  ExpectCRURegistrationFetchesTag(kUserAppID, user_app_xcfile_.path(),
                                  kUserAppTag);
  ExpectCRURegistrationCannotFetchTag(kNonexistentAppID, no_app_xcfile_.path());

  // Ambiguous app ID, direct XCFile path matches.
  ExpectCRURegistrationFetchesTag(
      kRepeatAppID, repeat_app_system_xcfile_.path(), kRepeatAppSystemTag);
  ExpectCRURegistrationFetchesTag(kRepeatAppID, repeat_app_user_xcfile_.path(),
                                  kRepeatAppUserTag);

  // Non-matching XCFile path can still match user apps, but only user apps.
  ExpectCRURegistrationFetchesTag(kUserAppID, no_app_xcfile_.path(),
                                  kUserAppTag);
  ExpectCRURegistrationFetchesTag(kRepeatAppID, no_app_xcfile_.path(),
                                  kRepeatAppUserTag);
  ExpectCRURegistrationCannotFetchTag(kSystemAppID, no_app_xcfile_.path());
}
#endif  // !defined(ADDRESS_SANITIZER)
#endif  // BUILDFLAG(IS_MAC)

// Windows specific tests.
#if BUILDFLAG(IS_WIN)
namespace {

void SetAuditMode() {
  ASSERT_EQ(base::win::RegKey(HKEY_LOCAL_MACHINE, kSetupStateKey, KEY_SET_VALUE)
                .WriteValue(L"ImageState", L"IMAGE_STATE_UNDEPLOYABLE"),
            ERROR_SUCCESS);
}

void ResetOemMode() {
  ASSERT_TRUE(ResetOemInstallState());
  ASSERT_EQ(base::win::RegKey(HKEY_LOCAL_MACHINE, kSetupStateKey, KEY_SET_VALUE)
                .DeleteValue(L"ImageState"),
            ERROR_SUCCESS);
}

void RewindOemState72PlusHours() {
  DWORD oem_install_time_minutes = 0;
  ASSERT_EQ(
      base::win::RegKey(HKEY_LOCAL_MACHINE, CLIENTS_KEY,
                        Wow6432(KEY_QUERY_VALUE))
          .ReadValueDW(kRegValueOemInstallTimeMin, &oem_install_time_minutes),
      ERROR_SUCCESS);

  // Rewind to 72 hours and 2 minutes before now.
  ASSERT_EQ(
      base::win::RegKey(HKEY_LOCAL_MACHINE, CLIENTS_KEY, Wow6432(KEY_SET_VALUE))
          .WriteValue(
              kRegValueOemInstallTimeMin,
              (base::Minutes(oem_install_time_minutes - 2) - kMinOemModeTime)
                  .InMinutes()),
      ERROR_SUCCESS);
}

}  // namespace

TEST_F(IntegrationTest, NoSelfUpdateIfOemMode) {
  if (!IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP();
  }
  ASSERT_NO_FATAL_FAILURE(SetAuditMode());
  absl::Cleanup reset_oem_mode = [] {
    ASSERT_NO_FATAL_FAILURE(ResetOemMode());
  };

  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install(base::Value::List().Append(kOemSwitch)));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(
      ExpectAppVersion(kUpdaterAppId, base::Version(kUpdaterVersion)));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, SelfUpdateIfNoAuditModeWithOemSwitch) {
  if (!IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP();
  }
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install(base::Value::List().Append(kOemSwitch)));
  base::Version next_version(base::StringPrintf("%s1", kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      &test_server, kUpdaterAppId, "", UpdateService::Priority::kBackground,
      base::Version(kUpdaterVersion), next_version));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kUpdaterAppId, next_version));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, SelfUpdateIfOemModeMoreThan72Hours) {
  if (!IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP();
  }
  ASSERT_NO_FATAL_FAILURE(SetAuditMode());
  absl::Cleanup reset_oem_mode = [] {
    ASSERT_NO_FATAL_FAILURE(ResetOemMode());
  };

  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install(base::Value::List().Append(kOemSwitch)));
  ASSERT_NO_FATAL_FAILURE(RewindOemState72PlusHours());
  base::Version next_version(base::StringPrintf("%s1", kUpdaterVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      &test_server, kUpdaterAppId, "", UpdateService::Priority::kBackground,
      base::Version(kUpdaterVersion), next_version));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kUpdaterAppId, next_version));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest,
       NoSelfUpdateIfOemModeMoreThan72HoursButEulaNotAccepted) {
  if (!IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP();
  }
  ASSERT_NO_FATAL_FAILURE(SetAuditMode());
  absl::Cleanup reset_oem_mode = [] {
    ASSERT_NO_FATAL_FAILURE(ResetOemMode());
  };

  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install(
      base::Value::List().Append(kOemSwitch).Append(kEulaRequiredSwitch)));
  ASSERT_NO_FATAL_FAILURE(RewindOemState72PlusHours());
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(
      ExpectAppVersion(kUpdaterAppId, base::Version(kUpdaterVersion)));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, Handoff) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());

  const std::string kAppId("test");
  const base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_server, kAppId, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1));
  ASSERT_NO_FATAL_FAILURE(RunHandoff(kAppId));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, ForceInstallApp) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());

  base::Value::Dict group_policies;
  group_policies.Set("installtest1",
                     IsSystemInstall(GetUpdaterScopeForTesting())
                         ? kPolicyForceInstallMachine
                         : kPolicyForceInstallUser);
  ASSERT_NO_FATAL_FAILURE(SetGroupPolicies(group_policies));

  const std::string kAppId("test1");
  base::Version v0point1("0.1");
  base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_server, kAppId, "", UpdateService::Priority::kForeground,
      base::Version("0.0.0.0"), v0point1));
  ASSERT_NO_FATAL_FAILURE(
      ExpectUpdateSequence(&test_server, kAppId, "",
                           UpdateService::Priority::kBackground, v0point1, v1));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));

  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, NeedsAdminPrefers) {
  if (::IsUserAnAdmin() && !IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP();
  }

  ScopedServer test_server(test_commands_);
  const std::string kAppId("test");
  const base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_server, kAppId, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1));

  ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
      {}, /*is_silent_install=*/true,
      base::StrCat({"appguid=", kAppId, "&needsadmin=Prefers&usagestats=1"})));
  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, MarshalInterface) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectMarshalInterfaceSucceeds());
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, LegacyProcessLauncher) {
  if (!IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP() << "Process launcher is only registered for system installs.";
  }
  ScopedServer test_server(test_commands_);

  ASSERT_NO_FATAL_FAILURE(Install());

  // `ExpectLegacyProcessLauncherSucceeds` runs the process launcher once with
  // usagestats enabled, and twice without, so only a single ping is expected.
  ASSERT_NO_FATAL_FAILURE(ExpectAppCommandPing(
      &test_server, "{831EF4D0-B729-4F61-AA34-91526481799D}", "cmd", 5420, 1,
      update_client::protocol_request::kEventAppCommandComplete, {}));
  ASSERT_NO_FATAL_FAILURE(ExpectLegacyProcessLauncherSucceeds());

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, LegacyAppCommandWeb_NoUsageStats_NoPing) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());

  const char kAppId[] = "test1";
  ASSERT_NO_FATAL_FAILURE(InstallApp(kAppId));

  base::Value::List parameters;
  parameters.Append("5432");
  ASSERT_NO_FATAL_FAILURE(
      ExpectLegacyAppCommandWebSucceeds(kAppId, "command1", parameters, 5432));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, LegacyAppCommandWeb_UsageStatsEnabled_ExpectPing) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());

  const std::string kAppId("test");
  // Enable usagestats.
  InstallApp(kAppId, base::Version("0.1"));
  ASSERT_EQ(
      base::win::RegKey(
          UpdaterScopeToHKeyRoot(GetUpdaterScopeForTesting()),
          base::StrCat({CLIENT_STATE_KEY, base::UTF8ToWide(kAppId)}).c_str(),
          Wow6432(KEY_WRITE))
          .WriteValue(L"usagestats", 1),
      ERROR_SUCCESS);

  base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      &test_server, kAppId, "", UpdateService::Priority::kBackground,
      base::Version("0.1"), v1));

  // Run wake to pick up the usage stats.
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));

  // The test runs the appcommand twice, so two pings of
  // `kEventAppCommandComplete`.
  for (int i = 0; i <= 1; ++i) {
    ASSERT_NO_FATAL_FAILURE(ExpectAppCommandPing(
        &test_server, kAppId, "command1", 5432, 1,
        update_client::protocol_request::kEventAppCommandComplete, v1));
  }

  base::Value::List parameters;
  parameters.Append("5432");
  ASSERT_NO_FATAL_FAILURE(
      ExpectLegacyAppCommandWebSucceeds(kAppId, "command1", parameters, 5432));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest,
       LegacyAppCommandWeb_InstallUpdaterAndApp_UsageStatsEnabled_ExpectPings) {
  ScopedServer test_server(test_commands_);
  const std::string kAppId("test");
  const base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_server, kAppId, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1));

  ASSERT_NO_FATAL_FAILURE(
      InstallUpdaterAndApp(kAppId, /*is_silent_install=*/true, "usagestats=1"));
  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));

  // The test runs the appcommand twice, so two pings of
  // `kEventAppCommandComplete`.
  for (int i = 0; i <= 1; ++i) {
    ASSERT_NO_FATAL_FAILURE(ExpectAppCommandPing(
        &test_server, kAppId, "command1", 5432, 1,
        update_client::protocol_request::kEventAppCommandComplete, v1));
  }

  base::Value::List parameters;
  parameters.Append("5432");
  ASSERT_NO_FATAL_FAILURE(
      ExpectLegacyAppCommandWebSucceeds(kAppId, "command1", parameters, 5432));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, LegacyPolicyStatus) {
  ScopedServer test_server(test_commands_);
  ASSERT_NO_FATAL_FAILURE(Install());

  const std::string kAppId("test");
  ASSERT_NO_FATAL_FAILURE(InstallApp(kAppId));
  base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      &test_server, kAppId, "", UpdateService::Priority::kBackground,
      base::Version("0.1"), v1));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));

  ASSERT_NO_FATAL_FAILURE(ExpectLegacyPolicyStatusSucceeds());

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, UninstallCmdLine) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));

  // Running the uninstall command does not uninstall this instance of the
  // updater right after installing it (not enough server starts).
  ASSERT_NO_FATAL_FAILURE(RunUninstallCmdLine());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  ASSERT_NO_FATAL_FAILURE(SetServerStarts(24));

  // Uninstall the idle updater.
  ASSERT_NO_FATAL_FAILURE(RunUninstallCmdLine());
  ASSERT_TRUE(WaitForUpdaterExit());
}

TEST_F(IntegrationTest, LogFileInTmpAfterUninstall) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectVersionActive(kUpdaterVersion));

  // Running the uninstall command does not uninstall this instance of the
  // updater right after installing it (not enough server starts).
  ASSERT_NO_FATAL_FAILURE(RunUninstallCmdLine());
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  // Expect no updater logs in %TMP%.
  EXPECT_EQ(GetUpdaterLogFilesInTmp().size(), 0u);

  ASSERT_NO_FATAL_FAILURE(SetServerStarts(24));

  // Uninstall the idle updater.
  ASSERT_NO_FATAL_FAILURE(RunUninstallCmdLine());
  ASSERT_TRUE(WaitForUpdaterExit());

  // Expect a single updater log in %TMP%, and confirm that the file name has an
  // uuid embedded within it.
  int invocation_count = 0;
  for (const auto& file : GetUpdaterLogFilesInTmp()) {
    ++invocation_count;
    if (invocation_count == 1) {
      std::wstring file_base = file.BaseName().value();
      EXPECT_TRUE(base::StartsWith(file_base, L"updater"));
      EXPECT_TRUE(base::EndsWith(file_base, L".log"));
      base::ReplaceSubstringsAfterOffset(&file_base, 0, L"updater", {});
      base::ReplaceSubstringsAfterOffset(&file_base, 0, L".log", {});
      EXPECT_TRUE(
          base::Uuid::ParseLowercase(base::WideToUTF8(file_base)).is_valid());
    } else {
      ADD_FAILURE() << "Unexpected, more than one updater log found: " << file;
    }
  }
  EXPECT_EQ(invocation_count, 1);
}

TEST_F(IntegrationTest, AppLogoUrl) {
  ScopedServer test_update_server(test_commands_);
  ScopedServer test_logo_server(test_commands_);
  EnterTestMode(test_update_server.update_url(),
                test_update_server.crash_upload_url(),
                test_update_server.device_management_url(),
                test_logo_server.app_logo_url(), base::Minutes(5));

  const std::string kAppId("googletest");
  const base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_update_server, kAppId, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1));

  std::string app_logo_bytes;
  ASSERT_TRUE(base::ReadFileToString(
      test::GetTestFilePath("app_logos")
          .AppendASCII(base::StringPrintf("%s.bmp", kAppId.c_str())),
      &app_logo_bytes));
  test_logo_server.ExpectOnce(
      {
          request::GetPathMatcher(base::StringPrintf(
              "%s%s.bmp\\?lang=%s", test_logo_server.app_logo_path().c_str(),
              kAppId.c_str(),
              base::WideToUTF8(GetPreferredLanguage()).c_str())),
      },
      app_logo_bytes);
  ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
      kAppId, /*is_silent_install=*/false, "usagestats=1",
      base::WideToUTF8(
          GetLocalizedString(IDS_BUNDLE_INSTALLED_SUCCESSFULLY_BASE)),
      /*always_launch_cmd=*/false, /*verify_app_logo_loaded=*/true));
  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_update_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, OfflineInstall) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(RunOfflineInstall(/*is_legacy_install=*/false,
                                            /*is_silent_install=*/false));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, OfflineInstallOsNotSupported) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(
      RunOfflineInstallOsNotSupported(/*is_legacy_install=*/false,
                                      /*is_silent_install=*/false));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, OfflineInstallSilent) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(RunOfflineInstall(/*is_legacy_install=*/false,
                                            /*is_silent_install=*/true));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, OfflineInstallOsNotSupportedSilent) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(
      RunOfflineInstallOsNotSupported(/*is_legacy_install=*/false,
                                      /*is_silent_install=*/true));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, OfflineInstallSilentLegacy) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(RunOfflineInstall(/*is_legacy_install=*/true,
                                            /*is_silent_install=*/true));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, OfflineInstallOsNotSupportedSilentLegacy) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(
      RunOfflineInstallOsNotSupported(/*is_legacy_install=*/true,
                                      /*is_silent_install=*/true));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, OfflineInstallEulaRequired) {
  ASSERT_NO_FATAL_FAILURE(
      Install(base::Value::List().Append(kEulaRequiredSwitch)));
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(RunOfflineInstall(/*is_legacy_install=*/false,
                                            /*is_silent_install=*/false));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, OfflineInstallOemMode) {
  if (!IsSystemInstall(GetUpdaterScopeForTesting())) {
    GTEST_SKIP();
  }
  ASSERT_NO_FATAL_FAILURE(SetAuditMode());
  absl::Cleanup reset_oem_mode = [] {
    ASSERT_NO_FATAL_FAILURE(ResetOemMode());
  };

  ASSERT_NO_FATAL_FAILURE(Install(base::Value::List().Append(kOemSwitch)));
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(RunOfflineInstall(/*is_legacy_install=*/false,
                                            /*is_silent_install=*/false));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTest, ExpectErrorUIWhenGetSetupLockFails) {
  ScopedServer test_update_server(test_commands_);
  const std::string kAppId("googletest");
  const base::Version v1("1");
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      &test_update_server, kAppId, "", UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), v1));

  // The test runs the installer twice. One installer succeeds, and the other
  // installer times out on the setup lock.
  for (int i = 0; i <= 1; ++i) {
    ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
        kAppId, /*is_silent_install=*/false, "usagestats=1",
        /*child_window_text_to_find=*/{},
        /*always_launch_cmd=*/false,
        /*verify_app_logo_loaded=*/false,
        /*expect_success=*/true,
        /*wait_for_the_installer=*/false));
    base::PlatformThread::Sleep(base::Seconds(1));
  }

  // Dismiss the setup lock error dialog, and then the success dialog.
  for (const auto message_id : {IDS_UNABLE_TO_GET_SETUP_LOCK_BASE,
                                IDS_BUNDLE_INSTALLED_SUCCESSFULLY_BASE}) {
    ASSERT_NO_FATAL_FAILURE(
        CloseInstallCompleteDialog(GetLocalizedString(message_id)));
  }

  ASSERT_TRUE(WaitForUpdaterExit());

  ASSERT_NO_FATAL_FAILURE(ExpectAppVersion(kAppId, v1));

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_update_server));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

class IntegrationTestLegacyUpdate3WebNewInstall : public IntegrationTest {
 public:
  IntegrationTestLegacyUpdate3WebNewInstall() = default;
  ~IntegrationTestLegacyUpdate3WebNewInstall() override = default;

 protected:
  void SetUp() override {
    if (!::IsUserAnAdmin() && IsSystemInstall(GetUpdaterScopeForTesting())) {
      GTEST_SKIP();
    }

    IntegrationTest::SetUp();

    test_server_ = std::make_unique<ScopedServer>(test_commands_);
    ASSERT_NO_FATAL_FAILURE(Install());
  }

  void TearDown() override {
    ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
    ASSERT_NO_FATAL_FAILURE(Uninstall());

    IntegrationTest::TearDown();
  }

  std::unique_ptr<ScopedServer> test_server_;
  static constexpr char kAppId[] = "test1";
};

TEST_F(IntegrationTestLegacyUpdate3WebNewInstall, CheckForInstall) {
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateCheckSequence(
      test_server_.get(), kAppId, UpdateService::Priority::kForeground,
      base::Version(kNullVersion), base::Version("0.1")));
  ASSERT_NO_FATAL_FAILURE(
      ExpectLegacyUpdate3WebSucceeds(kAppId, AppBundleWebCreateMode::kCreateApp,
                                     STATE_UPDATE_AVAILABLE, S_OK));
}

TEST_F(IntegrationTestLegacyUpdate3WebNewInstall, Install) {
  const base::Version v1("0.1");
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateCheckSequence(
      test_server_.get(), kAppId, UpdateService::Priority::kForeground,
      base::Version(kNullVersion), v1));

  // "expected_install_data_index" is set in `integration_tests_win.cc`,
  // `DoUpdate`.
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      test_server_.get(), kAppId, "expected_install_data_index",
      UpdateService::Priority::kForeground, base::Version(kNullVersion), v1));

  ASSERT_NO_FATAL_FAILURE(
      ExpectLegacyUpdate3WebSucceeds(kAppId, AppBundleWebCreateMode::kCreateApp,
                                     STATE_INSTALL_COMPLETE, S_OK));
  base::Value::Dict expected_app_state;
  expected_app_state.Set("app_id", kAppId);
  expected_app_state.Set("version", v1.GetString());

  // These values are set in `integration_tests_win.cc`, `DoUpdate`, in the call
  // to `createApp`:
  expected_app_state.Set("ap", "DoUpdateAP");
  expected_app_state.Set("brand_code", "BRND");

  expected_app_state.Set("brand_path", "");
  expected_app_state.Set("ecp", "");
  base::Value::Dict expected_app_states;
  expected_app_states.Set(kAppId, std::move(expected_app_state));

  ASSERT_NO_FATAL_FAILURE(GetAppStates(expected_app_states));
}

class IntegrationTestLegacyUpdate3Web : public IntegrationTest {
 public:
  IntegrationTestLegacyUpdate3Web() = default;
  ~IntegrationTestLegacyUpdate3Web() override = default;

 protected:
  void SetUp() override {
    IntegrationTest::SetUp();

    test_server_ = std::make_unique<ScopedServer>(test_commands_);
    ASSERT_NO_FATAL_FAILURE(Install());
    ASSERT_NO_FATAL_FAILURE(InstallApp(kAppId));
  }

  void TearDown() override {
    ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
    ASSERT_NO_FATAL_FAILURE(Uninstall());

    IntegrationTest::TearDown();
  }

  std::unique_ptr<ScopedServer> test_server_;
  static constexpr char kAppId[] = "test1";
};

TEST_F(IntegrationTestLegacyUpdate3Web, NoUpdate) {
  ASSERT_NO_FATAL_FAILURE(ExpectNoUpdateSequence(test_server_.get(), kAppId));
  ASSERT_NO_FATAL_FAILURE(ExpectLegacyUpdate3WebSucceeds(
      kAppId, AppBundleWebCreateMode::kCreateInstalledApp, STATE_NO_UPDATE,
      S_OK));
}

TEST_F(IntegrationTestLegacyUpdate3Web, DisabledPolicyManual) {
  ASSERT_TRUE(WaitForUpdaterExit());
  base::Value::Dict group_policies;
  group_policies.Set("updatetest1", kPolicyAutomaticUpdatesOnly);
  ASSERT_NO_FATAL_FAILURE(SetGroupPolicies(group_policies));
  ASSERT_NO_FATAL_FAILURE(ExpectLegacyUpdate3WebSucceeds(
      kAppId, AppBundleWebCreateMode::kCreateInstalledApp, STATE_ERROR,
      GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY_MANUAL));
}

TEST_F(IntegrationTestLegacyUpdate3Web, DisabledPolicy) {
  ASSERT_TRUE(WaitForUpdaterExit());
  base::Value::Dict group_policies;
  group_policies.Set("updatetest1", kPolicyDisabled);
  ASSERT_NO_FATAL_FAILURE(SetGroupPolicies(group_policies));
  ExpectLegacyUpdate3WebSucceeds(
      kAppId, AppBundleWebCreateMode::kCreateInstalledApp, STATE_ERROR,
      GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY);
}

TEST_F(IntegrationTestLegacyUpdate3Web, CheckForUpdate) {
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateCheckSequence(
      test_server_.get(), kAppId, UpdateService::Priority::kForeground,
      base::Version("0.1"), base::Version("0.2")));
  ASSERT_NO_FATAL_FAILURE(ExpectLegacyUpdate3WebSucceeds(
      kAppId, AppBundleWebCreateMode::kCreateInstalledApp,
      STATE_UPDATE_AVAILABLE, S_OK));
}

TEST_F(IntegrationTestLegacyUpdate3Web, Update) {
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateCheckSequence(
      test_server_.get(), kAppId, UpdateService::Priority::kForeground,
      base::Version("0.1"), base::Version("0.2")));
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateSequence(
      test_server_.get(), kAppId, "", UpdateService::Priority::kForeground,
      base::Version("0.1"), base::Version("0.2")));
  ASSERT_NO_FATAL_FAILURE(ExpectLegacyUpdate3WebSucceeds(
      kAppId, AppBundleWebCreateMode::kCreateInstalledApp,
      STATE_INSTALL_COMPLETE, S_OK));
}

TEST_F(IntegrationTestLegacyUpdate3Web, CheckForInstall) {
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateCheckSequence(
      test_server_.get(), kAppId, UpdateService::Priority::kForeground,
      base::Version("0.1"), base::Version("0.1")));
  ASSERT_NO_FATAL_FAILURE(
      ExpectLegacyUpdate3WebSucceeds(kAppId, AppBundleWebCreateMode::kCreateApp,
                                     STATE_UPDATE_AVAILABLE, S_OK));
}

TEST_F(IntegrationTestLegacyUpdate3Web, Install) {
  ASSERT_NO_FATAL_FAILURE(ExpectUpdateCheckSequence(
      test_server_.get(), kAppId, UpdateService::Priority::kForeground,
      base::Version("0.1"), base::Version("0.1")));
  ASSERT_NO_FATAL_FAILURE(ExpectInstallSequence(
      test_server_.get(), kAppId, "", UpdateService::Priority::kForeground,
      base::Version("0.1"), base::Version("0.1")));
  ASSERT_NO_FATAL_FAILURE(
      ExpectLegacyUpdate3WebSucceeds(kAppId, AppBundleWebCreateMode::kCreateApp,
                                     STATE_INSTALL_COMPLETE, S_OK));
}
class IntegrationTestMsi : public IntegrationTest {
 public:
  IntegrationTestMsi() = default;
  ~IntegrationTestMsi() override = default;

  static constexpr char kMsiAppId[] = "{c28fcf72-bcf2-45c5-8def-31a74ac02012}";

 protected:
  void SetUp() override {
    if (!IsSystemInstall(GetUpdaterScopeForTesting())) {
      GTEST_SKIP();
    }
    IntegrationTest::SetUp();
    test_server_ = std::make_unique<ScopedServer>(test_commands_);
    ASSERT_NO_FATAL_FAILURE(RemoveMsiProductData(kMsiProductIdInitialVersion));
    ASSERT_NO_FATAL_FAILURE(RemoveMsiProductData(kMsiProductIdUpdatedVersion));
  }

  void TearDown() override {
    if (!IsSystemInstall(GetUpdaterScopeForTesting())) {
      return;
    }
    ASSERT_NO_FATAL_FAILURE(RemoveMsiProductData(kMsiProductIdInitialVersion));
    ASSERT_NO_FATAL_FAILURE(RemoveMsiProductData(kMsiProductIdUpdatedVersion));
    IntegrationTest::TearDown();
  }

  void InstallMsiWithVersion(const base::Version& version) {
    InstallApp(kMsiAppId, version);
    base::FilePath msi_path;
    ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &msi_path));
    msi_path = msi_path.Append(
        GetInstallerPath(base::StrCat({kMsiAppId, ".", version.GetString()}))
            .AppendASCII(kMsiCrx)
            .RemoveExtension());
    const std::wstring command = BuildMsiCommandLine({}, {}, msi_path);
    base::Process process = base::LaunchProcess(command, {});
    if (!process.IsValid()) {
      LOG(ERROR) << "Invalid process launching command: " << command;
    }
    int exit_code = -1;
    EXPECT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_timeout(),
                                               &exit_code));
    EXPECT_EQ(0, exit_code);

    ExpectAppInstalled(kMsiAppId, version);
  }

  void RemoveMsiProductData(const std::wstring& msi_product_id) {
    ASSERT_FALSE(msi_product_id.empty());
    for (const auto& [root, key] :
         {std::make_pair(HKEY_LOCAL_MACHINE,
                         L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Instal"
                         L"ler\\UserData\\S-1-5-18\\Products"),
          std::make_pair(HKEY_CLASSES_ROOT, L"Installer\\Products")}) {
      for (const auto& access_mask : {KEY_WOW64_32KEY, KEY_WOW64_64KEY}) {
        base::win::RegKey(root, key, DELETE | access_mask)
            .DeleteKey(msi_product_id.c_str());
      }
    }
  }

  std::unique_ptr<ScopedServer> test_server_;
  static constexpr char kMsiCrx[] = "TestSystemMsiInstaller.msi.crx3";
  static constexpr wchar_t kMsiProductIdInitialVersion[] =
      L"40C670A26D240095081B31C3EDEF2BD2";
  static constexpr wchar_t kMsiProductIdUpdatedVersion[] =
      L"D2B2AC298EFCE2757A975961532CDE7D";
  const base::Version kMsiInitialVersion = base::Version("1.0.0.0");
  const base::Version kMsiUpdatedVersion = base::Version("2.0.0.0");
};

TEST_F(IntegrationTestMsi, Install) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());

  const base::FilePath crx_path = GetInstallerPath(kMsiCrx);
  ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/{},
      {
          AppUpdateExpectation({}, kMsiAppId, base::Version({0, 0, 0, 0}),
                               kMsiUpdatedVersion,
                               /*is_install=*/true,
                               /*should_update=*/true, false, "", "", crx_path),
      });

  ASSERT_NO_FATAL_FAILURE(InstallAppViaService(kMsiAppId));
  ExpectAppInstalled(kMsiAppId, kMsiUpdatedVersion);
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestMsi, InstallViaCommandLine) {
  const base::FilePath crx_path = GetInstallerPath(kMsiCrx);
  ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/{},
      {
          AppUpdateExpectation({}, kMsiAppId, base::Version({0, 0, 0, 0}),
                               kMsiUpdatedVersion,
                               /*is_install=*/true,
                               /*should_update=*/true, false, "", "", crx_path),
      });

  ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
      kMsiAppId, /*is_silent_install=*/true, "usagestats=1"));
  ASSERT_TRUE(WaitForUpdaterExit());

  ExpectAppInstalled(kMsiAppId, kMsiUpdatedVersion);

  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestMsi, Upgrade) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  InstallMsiWithVersion(kMsiInitialVersion);

  const base::FilePath crx_path = GetInstallerPath(kMsiCrx);
  ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/{},
      {
          AppUpdateExpectation({}, kMsiAppId, kMsiInitialVersion,
                               kMsiUpdatedVersion,
                               /*is_install=*/false,
                               /*should_update=*/true, false, "", "", crx_path),
      });
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kMsiAppId, kMsiUpdatedVersion));
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

struct IntegrationInstallerResultsTestCase {
  const bool interactive_install;
  const std::string command_line_args;
  const UpdateService::ErrorCategory error_category;
  const int error_code;
  const std::string installer_text;
  const std::string installer_cmd_line;
  const std::string custom_app_response;
  const std::optional<bool> always_launch_cmd;
};

class IntegrationInstallerResultsTest
    : public ::testing::WithParamInterface<IntegrationInstallerResultsTestCase>,
      public IntegrationTestMsi {};

INSTANTIATE_TEST_SUITE_P(
    IntegrationInstallerResultsTestCases,
    IntegrationInstallerResultsTest,
    ::testing::ValuesIn(std::vector<IntegrationInstallerResultsTestCase>{
        // InstallerResult::kMsiError, explicit error code.
        {
            false,
            "INSTALLER_RESULT=2 INSTALLER_ERROR=1603",
            UpdateService::ErrorCategory::kInstaller,
            1603,
            "Installer error: Fatal error during installation. ",
            {},
            {},
        },

        // `InstallerResult::kCustomError`, implicit error code
        // `kErrorApplicationInstallerFailed`.
        {
            false,
            "INSTALLER_RESULT=1 INSTALLER_RESULT_UI_STRING=TestUIString",
            UpdateService::ErrorCategory::kInstaller,
            kErrorApplicationInstallerFailed,
            "TestUIString",
            {},
            {},
        },

        // InstallerResult::kSystemError, explicit error code.
        {
            false,
            "INSTALLER_RESULT=3 INSTALLER_ERROR=99",
            UpdateService::ErrorCategory::kInstaller,
            99,
            "Installer error: 0x63",
            {},
            {},
        },

        // InstallerResult::kSuccess.
        {
            false,
            "INSTALLER_RESULT=0",
            UpdateService::ErrorCategory::kNone,
            0,
            {},
            {},
            {},
        },

        // Silent install with a launch command, InstallerResult::kSuccess, will
        // not run `more.com` since silent install.
        {
            false,
            "INSTALLER_RESULT=0 "
            "REGISTER_LAUNCH_COMMAND=more.com",
            UpdateService::ErrorCategory::kNone,
            0,
            {},
            "more.com",
            {},
        },

        // InstallerResult::kMsiError, `ERROR_SUCCESS_REBOOT_REQUIRED`.
        {
            false,
            base::StrCat({"INSTALLER_RESULT=2 INSTALLER_ERROR=",
                          base::NumberToString(ERROR_SUCCESS_REBOOT_REQUIRED)}),
            UpdateService::ErrorCategory::kInstaller,
            ERROR_SUCCESS_REBOOT_REQUIRED,
            "Reboot required: The requested operation is successful. Changes "
            "will not be effective until the system is rebooted. ",
            {},
            {},
        },

        // InstallerResult::kMsiError, `ERROR_INSTALL_ALREADY_RUNNING`.
        {
            false,
            base::StrCat({"INSTALLER_RESULT=2 INSTALLER_ERROR=",
                          base::NumberToString(ERROR_INSTALL_ALREADY_RUNNING)}),
            UpdateService::ErrorCategory::kInstall,
            GOOPDATEINSTALL_E_INSTALL_ALREADY_RUNNING,
            "Installer error: Another installation is already in progress. "
            "Complete that installation before proceeding with this install. ",
            {},
            {},
        },

        // Interactive install via the command line with a launch command,
        // InstallerResult::kSuccess, will run `more.com` since interactive
        // install.
        {
            true,
            "INSTALLER_RESULT=0 "
            "REGISTER_LAUNCH_COMMAND=more.com",
            UpdateService::ErrorCategory::kNone,
            0,
            {},
            "more.com",
            {},
        },

        // Silent install with a launch command, with `always_launch_cmd` set to
        // `true`, InstallerResult::kSuccess, will run `more.com` even for
        // silent install.
        {
            false,
            "INSTALLER_RESULT=0 "
            "REGISTER_LAUNCH_COMMAND=more.com",
            UpdateService::ErrorCategory::kNone,
            0,
            {},
            "more.com",
            {},
            true,
        },

        // InstallerResult::kMsiError, `ERROR_SUCCESS_REBOOT_REQUIRED`.
        {
            true,
            base::StrCat({"INSTALLER_RESULT=2 INSTALLER_ERROR=",
                          base::NumberToString(ERROR_SUCCESS_REBOOT_REQUIRED)}),
            UpdateService::ErrorCategory::kInstaller,
            ERROR_SUCCESS_REBOOT_REQUIRED,
            base::WideToUTF8(GetLocalizedStringF(IDS_TEXT_RESTART_COMPUTER_BASE,
                                                 L"")),
            {},
            {},
        },

        // Interactive install via the command line,
        // `update_client::ProtocolError::UNKNOWN_APPLICATION` error.
        {
            true,
            "INSTALLER_RESULT=0",
            UpdateService::ErrorCategory::kInstall,
            static_cast<int>(update_client::ProtocolError::UNKNOWN_APPLICATION),
            base::WideToUTF8(GetLocalizedString(IDS_UNKNOWN_APPLICATION_BASE)),
            {},
            base::StrCat({"{\"appid\":\"", IntegrationTestMsi::kMsiAppId,
                          "\",\"status\":\"error-unknownApplication\"}"}),
        },

        // Interactive install via the command line,
        // `update_client::ProtocolError::OS_NOT_SUPPORTED` error.
        {
            true,
            "INSTALLER_RESULT=0",
            UpdateService::ErrorCategory::kInstall,
            static_cast<int>(update_client::ProtocolError::OS_NOT_SUPPORTED),
            base::WideToUTF8(GetLocalizedString(IDS_OS_NOT_SUPPORTED_BASE)),
            {},
            base::StrCat({"{\"appid\":\"", IntegrationTestMsi::kMsiAppId,
                          "\",\"status\":\"error-osnotsupported\"}"}),
        },

        // Interactive install via the command line,
        // `update_client::ProtocolError::HW_NOT_SUPPORTED` error.
        {
            true,
            "INSTALLER_RESULT=0",
            UpdateService::ErrorCategory::kInstall,
            static_cast<int>(update_client::ProtocolError::HW_NOT_SUPPORTED),
            base::WideToUTF8(GetLocalizedString(IDS_HW_NOT_SUPPORTED_BASE)),
            {},
            base::StrCat({"{\"appid\":\"", IntegrationTestMsi::kMsiAppId,
                          "\",\"status\":\"error-hwnotsupported\"}"}),
        },

        // Interactive install via the command line,
        // `update_client::ProtocolError::NO_HASH` error.
        {
            true,
            "INSTALLER_RESULT=0",
            UpdateService::ErrorCategory::kInstall,
            static_cast<int>(update_client::ProtocolError::NO_HASH),
            base::WideToUTF8(GetLocalizedString(IDS_NO_HASH_BASE)),
            {},
            base::StrCat({"{\"appid\":\"", IntegrationTestMsi::kMsiAppId,
                          "\",\"status\":\"error-hash\"}"}),
        },

        // Interactive install via the command line,
        // `update_client::ProtocolError::UNSUPPORTED_PROTOCOL` error.
        {
            true,
            "INSTALLER_RESULT=0",
            UpdateService::ErrorCategory::kInstall,
            static_cast<int>(
                update_client::ProtocolError::UNSUPPORTED_PROTOCOL),
            base::WideToUTF8(GetLocalizedString(IDS_UNSUPPORTED_PROTOCOL_BASE)),
            {},
            base::StrCat({"{\"appid\":\"", IntegrationTestMsi::kMsiAppId,
                          "\",\"status\":\"error-unsupportedprotocol\"}"}),
        },

        // Interactive install via the command line,
        // `update_client::ProtocolError::INTERNAL` error.
        {
            true,
            "INSTALLER_RESULT=0",
            UpdateService::ErrorCategory::kInstall,
            static_cast<int>(update_client::ProtocolError::INTERNAL),
            base::WideToUTF8(GetLocalizedString(IDS_INTERNAL_BASE)),
            {},
            base::StrCat({"{\"appid\":\"", IntegrationTestMsi::kMsiAppId,
                          "\",\"status\":\"error-internal\"}"}),
        },
    }));

TEST_P(IntegrationInstallerResultsTest, TestCases) {
  const base::FilePath crx_relative_path = GetInstallerPath(kMsiCrx);
  const bool should_install_successfully =
      !GetParam().error_code ||
      GetParam().error_code == ERROR_SUCCESS_REBOOT_REQUIRED;
  const bool always_launch_cmd = GetParam().always_launch_cmd.value_or(false);

  if (!GetParam().interactive_install && !always_launch_cmd) {
    ASSERT_NO_FATAL_FAILURE(Install());
    ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  }

  ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/{},
      {
          AppUpdateExpectation(
              GetParam().command_line_args, kMsiAppId,
              base::Version({0, 0, 0, 0}), kMsiUpdatedVersion,
              /*is_install=*/true, should_install_successfully, false, "", "",
              crx_relative_path,
              /*always_serve_crx=*/GetParam().custom_app_response.empty(),
              GetParam().error_category, GetParam().error_code,
              /*EVENT_INSTALL_COMPLETE=*/2, GetParam().custom_app_response),
      });
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));

  if (GetParam().interactive_install || always_launch_cmd) {
    ASSERT_NO_FATAL_FAILURE(InstallUpdaterAndApp(
        kMsiAppId, !GetParam().interactive_install, "usagestats=1",
        GetParam().installer_text, always_launch_cmd));
    ASSERT_TRUE(WaitForUpdaterExit());
  } else {
    int64_t crx_file_size = 0;
    {
      base::FilePath exe_path;
      ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &exe_path));
      const base::FilePath crx_path = exe_path.Append(crx_relative_path);
      EXPECT_TRUE(base::GetFileSize(crx_path, &crx_file_size));
    }
    ASSERT_NO_FATAL_FAILURE(InstallAppViaService(
        kMsiAppId,
        base::Value::Dict()
            .Set("expected_update_state",
                 base::Value::Dict()
                     .Set("app_id", kMsiAppId)
                     .Set("state",
                          static_cast<int>(
                              should_install_successfully
                                  ? UpdateService::UpdateState::State::kUpdated
                                  : UpdateService::UpdateState::State::
                                        kUpdateError))
                     .Set("next_version", kMsiUpdatedVersion.GetString())
                     .Set("downloaded_bytes", static_cast<int>(crx_file_size))
                     .Set("total_bytes", static_cast<int>(crx_file_size))
                     .Set("install_progress", -1)
                     .Set("error_category",
                          should_install_successfully
                              ? 0
                              : static_cast<int>(GetParam().error_category))
                     .Set("error_code", GetParam().error_code)
                     .Set("extra_code1", 0)
                     .Set("installer_text", GetParam().installer_text)
                     .Set("installer_cmd_line", GetParam().installer_cmd_line))
            .Set("expected_result", 0)));
  }

  if (should_install_successfully) {
    ExpectAppInstalled(kMsiAppId, kMsiUpdatedVersion);
    if (!GetParam().installer_cmd_line.empty()) {
      const std::wstring post_install_launch_command_line =
          base::ASCIIToWide(GetParam().installer_cmd_line);
      EXPECT_EQ(test::IsProcessRunning(post_install_launch_command_line),
                GetParam().interactive_install || always_launch_cmd);
      EXPECT_TRUE(test::KillProcesses(post_install_launch_command_line, 0));
    }
    ASSERT_NO_FATAL_FAILURE(Uninstall());
  } else {
    ASSERT_NO_FATAL_FAILURE(ExpectNotRegistered(kMsiAppId));

    // Wait for the updater to uninstall itself automatically since the app
    // failed to install, and there are now no apps to manage.
    ASSERT_TRUE(WaitForUpdaterExit());
  }
}

TEST_P(IntegrationInstallerResultsTest, OnDemandTestCases) {
  if (GetParam().interactive_install) {
    GTEST_SKIP();
  }

  const base::FilePath crx_relative_path = GetInstallerPath(kMsiCrx);
  const bool should_install_successfully =
      !GetParam().error_code ||
      GetParam().error_code == ERROR_SUCCESS_REBOOT_REQUIRED;

  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallApp(kMsiAppId, base::Version({0, 0, 0, 0})));

  ASSERT_NO_FATAL_FAILURE(ExpectUpdateCheckSequence(
      test_server_.get(), kMsiAppId, UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), kMsiUpdatedVersion));

  ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/{},
      {
          AppUpdateExpectation(
              GetParam().command_line_args, kMsiAppId,
              base::Version({0, 0, 0, 0}), kMsiUpdatedVersion,
              /*is_install=*/false, should_install_successfully, false, "", "",
              crx_relative_path,
              /*always_serve_crx=*/GetParam().custom_app_response.empty(),
              GetParam().error_category, GetParam().error_code,
              /*EVENT_UPDATE_COMPLETE=*/3, GetParam().custom_app_response),
      });
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));

  ASSERT_NO_FATAL_FAILURE(ExpectLegacyUpdate3WebSucceeds(
      kMsiAppId, AppBundleWebCreateMode::kCreateInstalledApp,
      should_install_successfully ? STATE_INSTALL_COMPLETE : STATE_ERROR,
      GetParam().error_code));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

class IntegrationInstallerResultsTestNewInstalls : public IntegrationTestMsi {};

TEST_F(IntegrationInstallerResultsTestNewInstalls, OnDemandCancel) {
  // Delay download a bit to allow cancellation.
  test_server_->set_download_delay(base::Seconds(5));

  const base::FilePath crx_relative_path = GetInstallerPath(kMsiCrx);

  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallApp(kMsiAppId, base::Version({0, 0, 0, 0})));

  ASSERT_NO_FATAL_FAILURE(ExpectUpdateCheckSequence(
      test_server_.get(), kMsiAppId, UpdateService::Priority::kForeground,
      base::Version({0, 0, 0, 0}), kMsiUpdatedVersion));

  ExpectAppsUpdateSequence(
      UpdaterScope::kSystem, test_server_.get(),
      /*request_attributes=*/{},
      {
          AppUpdateExpectation(
              "INSTALLER_RESULT=0", kMsiAppId, base::Version({0, 0, 0, 0}),
              kMsiUpdatedVersion,
              /*is_install=*/false, /*should_update=*/false,
              /*allow_rollback=*/false,
              /*target_version_prefix=*/{}, /*target_channel=*/{},
              crx_relative_path,
              /*always_serve_crx=*/true, UpdateService::ErrorCategory::kService,
              static_cast<int>(update_client::ServiceError::CANCELLED),
              /*EVENT_INSTALL_COMPLETE=*/2, {}),
      });
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));

  ASSERT_NO_FATAL_FAILURE(ExpectLegacyUpdate3WebSucceeds(
      kMsiAppId, AppBundleWebCreateMode::kCreateApp, STATE_ERROR,
      static_cast<int>(update_client::ServiceError::CANCELLED),
      /*cancel_when_downloading=*/true));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestDeviceManagement, NamedProxy) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp1, /*install_v1=*/false));

  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp1.appid, kApp1.v2));

  // Fetch proxy settings policy.
  DMPushEnrollmentToken(kEnrollmentToken);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  OmahaSettingsClientProto omaha_settings;
  omaha_settings.set_proxy_mode("fixed_servers");
  omaha_settings.set_proxy_server(
      base::StrCat({test_server_->proxy_url_no_path(), ";DIRECT"}));
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);
  ASSERT_NO_FATAL_FAILURE(
      ExpectNoUpdateSequence(test_server_.get(), kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  // Redirect network traffic to remote hosts to engage the proxy.
  const GURL update_check_url = GURL("http://update.server.not_exist/update");
  const GURL dm_server_url = GURL("http://dm.server.not_exist/dmapi");
  EnterTestMode(update_check_url, test_server_->crash_upload_url(),
                dm_server_url, {}, base::Minutes(5));
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings, false, false,
                                           dm_server_url);
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(
      ExpectUninstallPing(test_server_.get(), update_check_url));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}

TEST_F(IntegrationTestDeviceManagement, PacScript) {
  ASSERT_NO_FATAL_FAILURE(Install());
  ASSERT_NO_FATAL_FAILURE(InstallTestApp(kApp1, /*install_v1=*/false));

  ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
  ASSERT_NO_FATAL_FAILURE(ExpectAppInstalled(kApp1.appid, kApp1.v2));

  // Fetch proxy settings policy.
  DMPushEnrollmentToken(kEnrollmentToken);
  ExpectDeviceManagementRegistrationRequest(test_server_.get(),
                                            kEnrollmentToken, kDMToken);
  OmahaSettingsClientProto omaha_settings;
  omaha_settings.set_proxy_mode("pac_script");
  omaha_settings.set_proxy_pac_url(test_server_->proxy_pac_url().spec());
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings);
  ExpectProxyPacScriptRequest(test_server_.get());
  ASSERT_NO_FATAL_FAILURE(
      ExpectNoUpdateSequence(test_server_.get(), kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());

  // Redirect network traffic to remote hosts to engage the proxy.
  // Note the test server won't receive additional PAC script download requests
  // because Windows caches it.
  const GURL update_check_url = GURL("http://update.server.not_exist/update");
  const GURL dm_server_url = GURL("http://dm.server.not_exist2/dmapi");
  EnterTestMode(update_check_url, test_server_->crash_upload_url(),
                dm_server_url, {}, base::Minutes(5));
  ExpectDeviceManagementPolicyFetchRequest(test_server_.get(), kDMToken,
                                           omaha_settings, false, false,
                                           dm_server_url);
  ASSERT_NO_FATAL_FAILURE(RunWake(0));
  ASSERT_TRUE(WaitForUpdaterExit());
  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(test_server_.get()));
  ASSERT_NO_FATAL_FAILURE(UninstallApp(kApp1.appid));
  ASSERT_NO_FATAL_FAILURE(Uninstall());
}
#endif  // BUILDFLAG(IS_WIN)

}  // namespace updater::test