// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/updater/util/win_util.h"
#include <objbase.h>
#include <windows.h>
#include <regstr.h>
#include <shellapi.h>
#include <shlobj.h>
#include <optional>
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/system/sys_info.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/gmock_expected_support.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/uuid.h"
#include "base/win/atl.h"
#include "base/win/registry.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_handle.h"
#include "base/win/scoped_localalloc.h"
#include "base/win/win_util.h"
#include "chrome/updater/test/integration_tests_impl.h"
#include "chrome/updater/test/test_scope.h"
#include "chrome/updater/test/unit_test_util.h"
#include "chrome/updater/test/unit_test_util_win.h"
#include "chrome/updater/updater_branding.h"
#include "chrome/updater/updater_version.h"
#include "chrome/updater/win/scoped_impersonation.h"
#include "chrome/updater/win/test/test_executables.h"
#include "chrome/updater/win/test/test_strings.h"
#include "chrome/updater/win/win_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace updater::test {
namespace {
constexpr char kTestAppID[] = "{D07D2B56-F583-4631-9E8E-9942F63765BE}";
} // namespace
TEST(WinUtil, GetServiceDisplayName) {
for (const bool is_internal_service : {true, false}) {
EXPECT_EQ(base::StrCat({base::ASCIIToWide(PRODUCT_FULLNAME_STRING), L" ",
is_internal_service ? kWindowsInternalServiceName
: kWindowsServiceName,
L" ", kUpdaterVersionUtf16}),
GetServiceDisplayName(is_internal_service));
}
}
TEST(WinUtil, GetServiceName) {
for (const bool is_internal_service : {true, false}) {
EXPECT_EQ(base::StrCat({base::ASCIIToWide(PRODUCT_FULLNAME_STRING),
is_internal_service ? kWindowsInternalServiceName
: kWindowsServiceName,
kUpdaterVersionUtf16}),
GetServiceName(is_internal_service));
}
}
TEST(WinUtil, BuildMsiCommandLine) {
EXPECT_STREQ(L"", BuildMsiCommandLine(std::wstring(L"arg1 arg2 arg3"), {},
base::FilePath(L"NotMsi.exe"))
.c_str());
EXPECT_STREQ(
L"msiexec arg1 arg2 arg3 REBOOT=ReallySuppress /qn /i \"c:\\my "
L"path\\YesMsi.msi\" /log \"c:\\my path\\YesMsi.msi.log\"",
BuildMsiCommandLine(std::wstring(L"arg1 arg2 arg3"), {},
base::FilePath(L"c:\\my path\\YesMsi.msi"))
.c_str());
EXPECT_STREQ(
L"msiexec arg1 arg2 arg3 INSTALLERDATA=\"c:\\my path\\installer data "
L"file.dat\" REBOOT=ReallySuppress /qn /i \"c:\\my "
L"path\\YesMsi.msi\" /log \"c:\\my path\\YesMsi.msi.log\"",
BuildMsiCommandLine(
std::wstring(L"arg1 arg2 arg3"),
base::FilePath(L"c:\\my path\\installer data file.dat"),
base::FilePath(L"c:\\my path\\YesMsi.msi"))
.c_str());
}
TEST(WinUtil, BuildExeCommandLine) {
EXPECT_STREQ(L"", BuildExeCommandLine(std::wstring(L"arg1 arg2 arg3"), {},
base::FilePath(L"NotExe.msi"))
.c_str());
EXPECT_STREQ(L"\"c:\\my path\\YesExe.exe\" arg1 arg2 arg3",
BuildExeCommandLine(std::wstring(L"arg1 arg2 arg3"), {},
base::FilePath(L"c:\\my path\\YesExe.exe"))
.c_str());
EXPECT_STREQ(
L"\"c:\\my path\\YesExe.exe\" arg1 arg2 arg3 --installerdata=\"c:\\my "
L"path\\installer data file.dat\"",
BuildExeCommandLine(
std::wstring(L"arg1 arg2 arg3"),
base::FilePath(L"c:\\my path\\installer data file.dat"),
base::FilePath(L"c:\\my path\\YesExe.exe"))
.c_str());
}
TEST(WinUtil, ShellExecuteAndWait) {
EXPECT_THAT(ShellExecuteAndWait(base::FilePath(L"NonExistent.Exe"), {}, {}),
base::test::ErrorIs(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)));
EXPECT_THAT(
ShellExecuteAndWait(GetTestProcessCommandLine(GetUpdaterScopeForTesting(),
test::GetTestName())
.GetProgram(),
{}, {}),
base::test::ValueIs(DWORD{0}));
}
TEST(WinUtil, RunElevated) {
if (!::IsUserAnAdmin()) {
return;
}
const base::CommandLine test_process_cmd_line = GetTestProcessCommandLine(
GetUpdaterScopeForTesting(), test::GetTestName());
EXPECT_THAT(RunElevated(test_process_cmd_line.GetProgram(),
test_process_cmd_line.GetArgumentsString()),
base::test::ValueIs(DWORD{0}));
}
TEST(WinUtil, RunDeElevated_Exe) {
if (!::IsUserAnAdmin() || !IsUACOn()) {
GTEST_SKIP();
}
// Create a shared event to be waited for in this process and signaled in the
// test process to confirm that the test process is running at medium
// integrity.
// The event is created with a security descriptor that allows the medium
// integrity process to signal it.
test::EventHolder event_holder(CreateEveryoneWaitableEventForTest());
ASSERT_NE(event_holder.event.handle(), nullptr);
base::CommandLine test_process_cmd_line = GetTestProcessCommandLine(
GetUpdaterScopeForTesting(), test::GetTestName());
test_process_cmd_line.AppendSwitchNative(kTestEventToSignalIfMediumIntegrity,
event_holder.name);
EXPECT_THAT(RunDeElevated(test_process_cmd_line),
base::test::ValueIs(DWORD{0}));
EXPECT_TRUE(event_holder.event.IsSignaled());
EXPECT_TRUE(test::WaitFor(
[] { return test::FindProcesses(kTestProcessExecutableName).empty(); }));
}
TEST(WinUtil, RunDeElevatedNoWait_Exe) {
if (!::IsUserAnAdmin() || !IsUACOn()) {
return;
}
// Create a shared event to be waited for in this process and signaled in the
// test process to confirm that the test process is running at medium
// integrity.
// The event is created with a security descriptor that allows the medium
// integrity process to signal it.
test::EventHolder event_holder(CreateEveryoneWaitableEventForTest());
ASSERT_NE(event_holder.event.handle(), nullptr);
base::CommandLine test_process_cmd_line = GetTestProcessCommandLine(
GetUpdaterScopeForTesting(), test::GetTestName());
test_process_cmd_line.AppendSwitchNative(kTestEventToSignalIfMediumIntegrity,
event_holder.name);
EXPECT_HRESULT_SUCCEEDED(
RunDeElevatedNoWait(test_process_cmd_line.GetProgram().value(),
test_process_cmd_line.GetArgumentsString()));
EXPECT_TRUE(event_holder.event.TimedWait(TestTimeouts::action_max_timeout()));
EXPECT_TRUE(test::WaitFor(
[] { return test::FindProcesses(kTestProcessExecutableName).empty(); }));
}
TEST(WinUtil, RunDeElevatedCmdLine_Exe) {
// Create a shared event to be waited for in this process and signaled in the
// test process to confirm that the test process is running at medium
// integrity.
test::EventHolder event_holder(IsElevatedWithUACOn()
? CreateEveryoneWaitableEventForTest()
: test::CreateWaitableEventForTest());
ASSERT_NE(event_holder.event.handle(), nullptr);
base::CommandLine test_process_cmd_line = GetTestProcessCommandLine(
GetUpdaterScopeForTesting(), test::GetTestName());
test_process_cmd_line.AppendSwitchNative(
IsElevatedWithUACOn() ? kTestEventToSignalIfMediumIntegrity
: kTestEventToSignal,
event_holder.name);
EXPECT_HRESULT_SUCCEEDED(
RunDeElevatedCmdLine(test_process_cmd_line.GetCommandLineString()));
EXPECT_TRUE(event_holder.event.TimedWait(TestTimeouts::action_max_timeout()));
EXPECT_TRUE(test::WaitFor(
[] { return test::FindProcesses(kTestProcessExecutableName).empty(); }));
}
TEST(WinUtil, GetOSVersion) {
std::optional<OSVERSIONINFOEX> rtl_os_version = GetOSVersion();
ASSERT_NE(rtl_os_version, std::nullopt);
// Compare to the version from `::GetVersionEx`.
OSVERSIONINFOEX os = {};
os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
EXPECT_TRUE(::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&os)));
#pragma clang diagnostic pop
EXPECT_EQ(rtl_os_version->dwOSVersionInfoSize, os.dwOSVersionInfoSize);
EXPECT_EQ(rtl_os_version->dwMajorVersion, os.dwMajorVersion);
EXPECT_EQ(rtl_os_version->dwMinorVersion, os.dwMinorVersion);
EXPECT_EQ(rtl_os_version->dwBuildNumber, os.dwBuildNumber);
EXPECT_EQ(rtl_os_version->dwPlatformId, os.dwPlatformId);
EXPECT_STREQ(rtl_os_version->szCSDVersion, os.szCSDVersion);
EXPECT_EQ(rtl_os_version->wServicePackMajor, os.wServicePackMajor);
EXPECT_EQ(rtl_os_version->wServicePackMinor, os.wServicePackMinor);
EXPECT_EQ(rtl_os_version->wSuiteMask, os.wSuiteMask);
EXPECT_EQ(rtl_os_version->wProductType, os.wProductType);
}
TEST(WinUtil, CompareOSVersions_SameAsCurrent) {
std::optional<OSVERSIONINFOEX> this_os = GetOSVersion();
ASSERT_NE(this_os, std::nullopt);
EXPECT_TRUE(CompareOSVersions(this_os.value(), VER_EQUAL));
EXPECT_TRUE(CompareOSVersions(this_os.value(), VER_GREATER_EQUAL));
EXPECT_FALSE(CompareOSVersions(this_os.value(), VER_GREATER));
EXPECT_FALSE(CompareOSVersions(this_os.value(), VER_LESS));
EXPECT_TRUE(CompareOSVersions(this_os.value(), VER_LESS_EQUAL));
}
TEST(WinUtil, CompareOSVersions_NewBuildNumber) {
std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
ASSERT_NE(prior_os, std::nullopt);
ASSERT_GT(prior_os->dwBuildNumber, 0UL);
--prior_os->dwBuildNumber;
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_EQUAL));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_GREATER_EQUAL));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_GREATER));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_LESS));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_LESS_EQUAL));
}
TEST(WinUtil, CompareOSVersions_NewMajor) {
std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
ASSERT_NE(prior_os, std::nullopt);
ASSERT_GT(prior_os->dwMajorVersion, 0UL);
--prior_os->dwMajorVersion;
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_EQUAL));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_GREATER_EQUAL));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_GREATER));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_LESS));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_LESS_EQUAL));
}
TEST(WinUtil, CompareOSVersions_NewMinor) {
std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
ASSERT_NE(prior_os, std::nullopt);
// This test only runs if the current OS has a minor version.
if (prior_os->dwMinorVersion >= 1) {
--prior_os->dwMinorVersion;
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_EQUAL));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_GREATER_EQUAL));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_GREATER));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_LESS));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_LESS_EQUAL));
}
}
TEST(WinUtil, CompareOSVersions_NewMajorWithLowerMinor) {
std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
ASSERT_NE(prior_os, std::nullopt);
ASSERT_GT(prior_os->dwMajorVersion, 0UL);
--prior_os->dwMajorVersion;
++prior_os->dwMinorVersion;
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_EQUAL));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_GREATER_EQUAL));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_GREATER));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_LESS));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_LESS_EQUAL));
}
TEST(WinUtil, CompareOSVersions_OldMajor) {
std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
ASSERT_NE(prior_os, std::nullopt);
++prior_os->dwMajorVersion;
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_EQUAL));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_GREATER_EQUAL));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_GREATER));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_LESS));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_LESS_EQUAL));
}
TEST(WinUtil, CompareOSVersions_OldMajorWithHigherMinor) {
std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
ASSERT_NE(prior_os, std::nullopt);
// This test only runs if the current OS has a minor version.
if (prior_os->dwMinorVersion >= 1) {
++prior_os->dwMajorVersion;
--prior_os->dwMinorVersion;
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_EQUAL));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_GREATER_EQUAL));
EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_GREATER));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_LESS));
EXPECT_TRUE(CompareOSVersions(prior_os.value(), VER_LESS_EQUAL));
}
}
TEST(WinUtil, IsCOMCallerAdmin) {
EXPECT_THAT(IsCOMCallerAdmin(), base::test::ValueIs(::IsUserAnAdmin()));
}
TEST(WinUtil, EnableSecureDllLoading) {
EXPECT_TRUE(EnableSecureDllLoading());
}
TEST(WinUtil, EnableProcessHeapMetadataProtection) {
EXPECT_TRUE(EnableProcessHeapMetadataProtection());
}
TEST(WinUtil, CreateSecureTempDir) {
std::optional<base::ScopedTempDir> temp_dir = CreateSecureTempDir();
EXPECT_TRUE(temp_dir);
EXPECT_TRUE(temp_dir->IsValid());
}
TEST(WinUtil, SignalShutdownEvent) {
{
const base::ScopedClosureRunner reset_shutdown_event(
SignalShutdownEvent(GetUpdaterScopeForTesting()));
// Expect that the legacy GoogleUpdate shutdown event is signaled.
EXPECT_TRUE(IsShutdownEventSignaled(GetUpdaterScopeForTesting()))
<< "Unexpected shutdown event not signaled";
}
// Expect that the legacy GoogleUpdate shutdown event is invalid now.
EXPECT_FALSE(IsShutdownEventSignaled(GetUpdaterScopeForTesting()))
<< "Unexpected shutdown event signaled";
}
TEST(WinUtil, StopProcessesUnderPath) {
base::FilePath exe_dir;
ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &exe_dir));
exe_dir = exe_dir.AppendASCII(test::GetTestName());
base::CommandLine command_line = GetTestProcessCommandLine(
GetUpdaterScopeForTesting(), test::GetTestName());
command_line.AppendSwitchASCII(
updater::kTestSleepSecondsSwitch,
base::NumberToString(TestTimeouts::action_timeout().InSeconds() / 4));
std::vector<base::Process> processes;
for (const base::FilePath& dir :
{exe_dir, exe_dir.Append(L"1"), exe_dir.Append(L"2")}) {
ASSERT_TRUE(base::CreateDirectory(dir));
for (const std::wstring exe_name : {L"random1.exe", L"random2.exe"}) {
const base::FilePath exe(dir.Append(exe_name));
ASSERT_TRUE(base::CopyFile(command_line.GetProgram(), exe));
base::Process process = base::LaunchProcess(
base::StrCat(
{base::CommandLine::QuoteForCommandLineToArgvW(exe.value()), L" ",
command_line.GetArgumentsString()}),
{});
ASSERT_TRUE(process.IsValid());
processes.push_back(std::move(process));
}
}
StopProcessesUnderPath(exe_dir, TestTimeouts::action_timeout());
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
for (const base::Process& process : processes) {
EXPECT_FALSE(process.IsRunning()) << process.Pid();
}
EXPECT_TRUE(base::DeletePathRecursively(exe_dir));
}
TEST(WinUtil, IsGuid) {
EXPECT_FALSE(IsGuid(L"c:\\test\\dir"));
EXPECT_FALSE(IsGuid(L"a"));
EXPECT_FALSE(IsGuid(L"CA3045BFA6B14fb8A0EFA615CEFE452C"));
// Missing {}.
EXPECT_FALSE(IsGuid(L"CA3045BF-A6B1-4fb8-A0EF-A615CEFE452C"));
// Invalid char X.
EXPECT_FALSE(IsGuid(L"{XA3045BF-A6B1-4fb8-A0EF-A615CEFE452C}"));
// Invalid binary char 0x200.
EXPECT_FALSE(IsGuid(L"{\0x200a3045bf-a6b1-4fb8-a0ef-a615cefe452c}"));
// Missing -.
EXPECT_FALSE(IsGuid(L"{CA3045BFA6B14fb8A0EFA615CEFE452C}"));
// Double quotes.
EXPECT_FALSE(IsGuid(L"\"{ca3045bf-a6b1-4fb8-a0ef-a615cefe452c}\""));
EXPECT_TRUE(IsGuid(L"{CA3045BF-A6B1-4fb8-A0EF-A615CEFE452C}"));
EXPECT_TRUE(IsGuid(L"{ca3045bf-a6b1-4fb8-a0ef-a615cefe452c}"));
}
TEST(WinUtil, ForEachRegistryRunValueWithPrefix) {
constexpr int kRunEntries = 6;
const std::wstring kRunEntryPrefix(base::ASCIIToWide(test::GetTestName()));
base::win::RegKey key;
ASSERT_EQ(key.Open(HKEY_CURRENT_USER, REGSTR_PATH_RUN, KEY_READ | KEY_WRITE),
ERROR_SUCCESS);
for (int count = 0; count < kRunEntries; ++count) {
std::wstring entry_name(kRunEntryPrefix);
entry_name.push_back(L'0' + count);
ASSERT_EQ(key.WriteValue(entry_name.c_str(), entry_name.c_str()),
ERROR_SUCCESS);
}
int count_entries = 0;
ForEachRegistryRunValueWithPrefix(
kRunEntryPrefix,
[&key, &count_entries, kRunEntryPrefix](const std::wstring& run_name) {
EXPECT_TRUE(base::StartsWith(run_name, kRunEntryPrefix));
++count_entries;
EXPECT_EQ(key.DeleteValue(run_name.c_str()), ERROR_SUCCESS);
});
EXPECT_EQ(count_entries, kRunEntries);
}
TEST(WinUtil, DeleteRegValue) {
constexpr int kRegValues = 6;
const std::wstring kRegValuePrefix(base::ASCIIToWide(test::GetTestName()));
base::win::RegKey key;
ASSERT_EQ(key.Open(HKEY_CURRENT_USER, REGSTR_PATH_RUN, KEY_READ | KEY_WRITE),
ERROR_SUCCESS);
for (int count = 0; count < kRegValues; ++count) {
std::wstring entry_name(kRegValuePrefix);
entry_name.push_back(L'0' + count);
ASSERT_EQ(key.WriteValue(entry_name.c_str(), entry_name.c_str()),
ERROR_SUCCESS);
EXPECT_TRUE(key.HasValue(entry_name.c_str()));
EXPECT_TRUE(DeleteRegValue(HKEY_CURRENT_USER, REGSTR_PATH_RUN, entry_name));
EXPECT_FALSE(key.HasValue(entry_name.c_str()));
EXPECT_TRUE(DeleteRegValue(HKEY_CURRENT_USER, REGSTR_PATH_RUN, entry_name));
}
}
TEST(WinUtil, ForEachServiceWithPrefix) {
if (!::IsUserAnAdmin()) {
return;
}
constexpr int kNumServices = 6;
const std::wstring kServiceNamePrefix(base::ASCIIToWide(test::GetTestName()));
for (int count = 0; count < kNumServices; ++count) {
std::wstring service_name(kServiceNamePrefix);
service_name.push_back(L'0' + count);
EXPECT_TRUE(
CreateService(service_name, service_name, L"C:\\temp\\temp.exe"));
}
int count_entries = 0;
ForEachServiceWithPrefix(
kServiceNamePrefix, kServiceNamePrefix,
[&count_entries, kServiceNamePrefix](const std::wstring& service_name) {
EXPECT_TRUE(base::StartsWith(service_name, kServiceNamePrefix));
++count_entries;
EXPECT_TRUE(DeleteService(service_name));
});
EXPECT_EQ(count_entries, kNumServices);
}
TEST(WinUtil, DeleteService) {
if (!::IsUserAnAdmin()) {
return;
}
constexpr int kNumServices = 6;
const std::wstring kServiceNamePrefix(base::ASCIIToWide(test::GetTestName()));
for (int count = 0; count < kNumServices; ++count) {
std::wstring service_name(kServiceNamePrefix);
service_name.push_back(L'0' + count);
ASSERT_TRUE(
CreateService(service_name, service_name, L"C:\\temp\\temp.exe"));
EXPECT_TRUE(DeleteService(service_name));
}
}
TEST(WinUtil, LogClsidEntries) {
CLSID clsid = {};
EXPECT_HRESULT_SUCCEEDED(
::CLSIDFromProgID(L"InternetExplorer.Application", &clsid));
LogClsidEntries(clsid);
}
TEST(WinUtil, GetAppAPValue) {
std::string ap(GetAppAPValue(GetUpdaterScopeForTesting(), kTestAppID));
EXPECT_EQ(ap, "");
base::win::RegKey client_state_key(CreateAppClientStateKey(
GetUpdaterScopeForTesting(), base::ASCIIToWide(kTestAppID)));
EXPECT_EQ(client_state_key.WriteValue(kRegValueAP, L"TestAP"), ERROR_SUCCESS);
ap = GetAppAPValue(GetUpdaterScopeForTesting(), kTestAppID);
EXPECT_EQ(ap, "TestAP");
DeleteAppClientStateKey(GetUpdaterScopeForTesting(),
base::ASCIIToWide(kTestAppID));
}
struct WinUtilGetRegKeyContentsTestCase {
const std::wstring reg_key;
const std::wstring expected_substring;
};
class WinUtilGetRegKeyContentsTest
: public ::testing::TestWithParam<WinUtilGetRegKeyContentsTestCase> {};
INSTANTIATE_TEST_SUITE_P(
WinUtilGetRegKeyContentsTestCases,
WinUtilGetRegKeyContentsTest,
::testing::ValuesIn(std::vector<WinUtilGetRegKeyContentsTestCase>{
{L"HKLM\\SOFTWARE\\Classes\\CLSID\\{00020424-0000-0000-C000-"
L"000000000046}",
L"{00020424-0000-0000-C000-000000000046}"},
{L"HKLM\\SOFTWARE\\WOW6432Node\\Classes\\CLSID\\{00020424-0000-0000-"
L"C000-000000000046}",
L"{00020424-0000-0000-C000-000000000046}"},
{L"HKCR\\CLSID\\{00020424-0000-0000-C000-000000000046}",
L"{00020424-0000-0000-C000-000000000046}"},
{L"HKCR\\WOW6432Node\\CLSID\\{00020424-0000-0000-C000-000000000046}",
L"{00020424-0000-0000-C000-000000000046}"},
}));
TEST_P(WinUtilGetRegKeyContentsTest, TestCases) {
std::optional<std::wstring> contents = GetRegKeyContents(GetParam().reg_key);
ASSERT_TRUE(contents);
ASSERT_NE(contents->find(GetParam().expected_substring), std::wstring::npos);
}
TEST(WinUtil, GetTextForSystemError) {
EXPECT_EQ(GetTextForSystemError(2),
L"The system cannot find the file specified. ");
EXPECT_EQ(GetTextForSystemError(0x80070002),
L"The system cannot find the file specified. ");
EXPECT_EQ(GetTextForSystemError(12007),
L"The server name or address could not be resolved ");
EXPECT_EQ(GetTextForSystemError(0x80072ee7),
L"The server name or address could not be resolved ");
EXPECT_EQ(GetTextForSystemError(-2147012889),
L"The server name or address could not be resolved ");
EXPECT_EQ(
GetTextForSystemError(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x200)),
L"0x80040200");
}
TEST(WinUtil, GetLoggedOnUserToken) {
if (!::IsUserAnAdmin() || !IsUACOn()) {
return;
}
ASSERT_TRUE(::IsUserAnAdmin());
HResultOr<ScopedKernelHANDLE> token = GetLoggedOnUserToken();
ASSERT_TRUE(token.has_value());
ScopedImpersonation impersonate;
ASSERT_TRUE(SUCCEEDED(impersonate.Impersonate(token.value().get())));
ASSERT_FALSE(::IsUserAnAdmin());
}
TEST(WinUtil, IsAuditMode) {
if (!::IsUserAnAdmin()) {
GTEST_SKIP();
}
ASSERT_FALSE(IsAuditMode());
ASSERT_EQ(base::win::RegKey(HKEY_LOCAL_MACHINE, kSetupStateKey, KEY_SET_VALUE)
.WriteValue(L"ImageState", L"IMAGE_STATE_UNDEPLOYABLE"),
ERROR_SUCCESS);
ASSERT_TRUE(IsAuditMode());
ASSERT_EQ(base::win::RegKey(HKEY_LOCAL_MACHINE, kSetupStateKey, KEY_SET_VALUE)
.DeleteValue(L"ImageState"),
ERROR_SUCCESS);
}
TEST(WinUtil, OemInstallState) {
if (!::IsUserAnAdmin()) {
GTEST_SKIP();
}
ASSERT_EQ(base::win::RegKey(HKEY_LOCAL_MACHINE, kSetupStateKey, KEY_SET_VALUE)
.WriteValue(L"ImageState", L"IMAGE_STATE_UNDEPLOYABLE"),
ERROR_SUCCESS);
ASSERT_TRUE(SetOemInstallState());
ASSERT_TRUE(IsOemInstalling());
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 71 hours and 58 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);
ASSERT_TRUE(IsOemInstalling());
// 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);
ASSERT_FALSE(IsOemInstalling());
ASSERT_TRUE(ResetOemInstallState());
ASSERT_EQ(base::win::RegKey(HKEY_LOCAL_MACHINE, kSetupStateKey, KEY_SET_VALUE)
.DeleteValue(L"ImageState"),
ERROR_SUCCESS);
}
TEST(WinUtil, StringFromGuid) {
GUID guid = {0};
EXPECT_HRESULT_SUCCEEDED(::CoCreateGuid(&guid));
EXPECT_EQ(base::win::WStringFromGUID(guid), StringFromGuid(guid));
}
TEST(WinUtil, GetUniqueTempFilePath) {
EXPECT_FALSE(GetUniqueTempFilePath({}));
std::optional<base::FilePath> p = GetUniqueTempFilePath(base::FilePath(
L"C:\\Program Files (x86)\\Google\\GoogleUpdater\\updater.log"));
ASSERT_TRUE(p);
std::wstring p_base = p->BaseName().value();
EXPECT_TRUE(base::StartsWith(p_base, L"updater"));
EXPECT_TRUE(base::EndsWith(p_base, L".log"));
base::ReplaceSubstringsAfterOffset(&p_base, 0, L"updater", {});
base::ReplaceSubstringsAfterOffset(&p_base, 0, L".log", {});
EXPECT_TRUE(base::Uuid::ParseLowercase(base::WideToUTF8(p_base)).is_valid());
}
TEST(WinUtil, SetEulaAccepted) {
// This will set `eulaaccepted=0` in the registry.
EXPECT_TRUE(
SetEulaAccepted(GetUpdaterScopeForTesting(), /*eula_accepted=*/false));
DWORD eula_accepted = 0;
const HKEY root = UpdaterScopeToHKeyRoot(GetUpdaterScopeForTesting());
EXPECT_EQ(base::win::RegKey(root, UPDATER_KEY, Wow6432(KEY_READ))
.ReadValueDW(L"eulaaccepted", &eula_accepted),
ERROR_SUCCESS);
EXPECT_EQ(eula_accepted, 0ul);
// This will delete the `eulaaccepted` value in the registry.
EXPECT_TRUE(
SetEulaAccepted(GetUpdaterScopeForTesting(), /*eula_accepted=*/true));
EXPECT_FALSE(base::win::RegKey(root, UPDATER_KEY, Wow6432(KEY_READ))
.HasValue(L"eulaaccepted"));
}
} // namespace updater::test