blob: 04f041ab2cb4c3f93084e3b8973a2adf997a3e9a [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <optional>
#include <string>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/task_environment.h"
#include "base/win/registry.h"
#include "base/win/windows_types.h"
#include "chrome/enterprise_companion/enterprise_companion.h"
#include "chrome/enterprise_companion/enterprise_companion_branding.h"
#include "chrome/enterprise_companion/enterprise_companion_version.h"
#include "chrome/enterprise_companion/installer.h"
#include "chrome/enterprise_companion/installer_paths.h"
#include "chrome/enterprise_companion/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace enterprise_companion {
namespace {
// The filename of the companion app binary under test.
constexpr char kTestExe[] = "enterprise_companion_test.exe";
} // namespace
class InstallerTest : public ::testing::Test {
public:
void SetUp() override {
std::optional<base::FilePath> install_dir = GetInstallDirectory();
ASSERT_TRUE(install_dir);
install_dir_ = *install_dir;
Clean();
}
void TearDown() override { Clean(); }
protected:
base::test::TaskEnvironment environment_;
// The install directory for the current process architecture.
base::FilePath install_dir_;
// If 64-on-64, the 32-bit install directory. If 32-on-64 the 64-bit install
// directory. Otherwise nullopt.
std::optional<base::FilePath> alt_install_dir_;
// Run the installer and expect success or failure.
void RunInstaller(bool expect_success) {
base::FilePath installer_pkg_path =
base::PathService::CheckedGet(base::DIR_EXE).AppendASCII(kTestExe);
ASSERT_TRUE(base::PathExists(installer_pkg_path));
base::CommandLine command_line(installer_pkg_path);
command_line.AppendSwitch(kInstallSwitch);
base::Process installer_process = base::LaunchProcess(command_line, {});
int exit_code = WaitForProcess(installer_process);
if (expect_success) {
EXPECT_EQ(exit_code, 0);
} else {
EXPECT_NE(exit_code, 0);
}
}
void ExpectUpdaterRegistration(const std::wstring expected_version,
const std::wstring expected_name) {
base::win::RegKey app_key(HKEY_LOCAL_MACHINE, kAppRegKey,
KEY_QUERY_VALUE | KEY_WOW64_32KEY);
std::wstring pv;
ASSERT_EQ(app_key.ReadValue(kRegValuePV, &pv), ERROR_SUCCESS);
EXPECT_EQ(pv, expected_version);
std::wstring name;
ASSERT_EQ(app_key.ReadValue(kRegValueName, &name), ERROR_SUCCESS);
EXPECT_EQ(name, expected_name);
}
void SetUpdaterRegistration(const std::wstring version,
const std::wstring name) {
base::win::RegKey app_key(HKEY_LOCAL_MACHINE, kAppRegKey,
KEY_WRITE | KEY_WOW64_32KEY);
ASSERT_EQ(app_key.WriteValue(kRegValuePV, version.c_str()), ERROR_SUCCESS);
ASSERT_EQ(app_key.WriteValue(kRegValueName, name.c_str()), ERROR_SUCCESS);
}
private:
void Clean() {
// After the installer process exits, it may take some time for Windows to
// recognize that files in the install directory (e.g. the log file) are not
// open.
ASSERT_TRUE(WaitFor(
[&] { return base::DeletePathRecursively(install_dir_); },
[&] { VLOG(1) << "Waiting to delete " << install_dir_ << "..."; }));
alt_install_dir_ = GetInstallDirectoryForAlternateArch();
if (alt_install_dir_) {
ASSERT_TRUE(base::DeletePathRecursively(*alt_install_dir_));
}
ASSERT_EQ(base::win::RegKey(HKEY_LOCAL_MACHINE, kAppRegKey,
KEY_ALL_ACCESS | KEY_WOW64_32KEY)
.DeleteKey(L""),
ERROR_SUCCESS);
}
};
TEST_F(InstallerTest, FirstInstall) {
RunInstaller(true);
ASSERT_TRUE(base::PathExists(install_dir_.AppendASCII(kExecutableName)));
ASSERT_FALSE(alt_install_dir_ && base::PathExists(*alt_install_dir_));
ExpectUpdaterRegistration(base::ASCIIToWide(kEnterpriseCompanionVersion),
L"" PRODUCT_FULLNAME_STRING);
}
TEST_F(InstallerTest, OverinstallSameArch) {
SetUpdaterRegistration(L"0.0.0.1", L"Prehistoric Enterprise Companion");
ASSERT_TRUE(base::CreateDirectory(install_dir_));
ASSERT_TRUE(base::WriteFile(install_dir_.AppendASCII(kExecutableName), ""));
RunInstaller(true);
ASSERT_TRUE(base::PathExists(install_dir_.AppendASCII(kExecutableName)));
ASSERT_FALSE(alt_install_dir_ && base::PathExists(*alt_install_dir_));
int64_t exe_size = 0;
ASSERT_TRUE(
base::GetFileSize(install_dir_.AppendASCII(kExecutableName), &exe_size));
EXPECT_GT(exe_size, 0);
ExpectUpdaterRegistration(base::ASCIIToWide(kEnterpriseCompanionVersion),
L"" PRODUCT_FULLNAME_STRING);
}
TEST_F(InstallerTest, OverinstallDifferentArch) {
if (!alt_install_dir_) {
LOG(WARNING) << "OverinstallDifferentArch not implemented for x86 hosts.";
return;
}
SetUpdaterRegistration(L"0.0.0.1", L"Prehistoric Enterprise Companion");
ASSERT_TRUE(base::CreateDirectory(*alt_install_dir_));
ASSERT_TRUE(
base::WriteFile(alt_install_dir_->AppendASCII(kExecutableName), ""));
RunInstaller(true);
ASSERT_TRUE(base::PathExists(install_dir_.AppendASCII(kExecutableName)));
ASSERT_FALSE(alt_install_dir_ && base::PathExists(*alt_install_dir_));
EXPECT_FALSE(base::PathExists(*alt_install_dir_));
ExpectUpdaterRegistration(base::ASCIIToWide(kEnterpriseCompanionVersion),
L"" PRODUCT_FULLNAME_STRING);
}
} // namespace enterprise_companion