1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
|
// 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 <cstdlib>
#include <string_view>
#include "base/base_paths.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/process/process.h"
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h"
#include "base/test/test_switches.h"
#include "build/build_config.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/test/integration_test_commands.h"
#include "chrome/updater/test/test_scope.h"
#include "chrome/updater/test/unit_test_util.h"
#include "chrome/updater/updater_branding.h"
#include "chrome/updater/updater_scope.h"
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
#if BUILDFLAG(IS_WIN)
#include <shlobj.h>
#include <memory>
#include <string>
#include "base/path_service.h"
#include "base/win/registry.h"
#include "base/win/scoped_com_initializer.h"
#include "chrome/installer/util/scoped_token_privilege.h"
#include "chrome/updater/util/win_util.h"
namespace {
// If the priority class is not NORMAL_PRIORITY_CLASS, then the function makes:
// - the priority class of the process NORMAL_PRIORITY_CLASS
// - the process memory priority MEMORY_PRIORITY_NORMAL
// - the current thread priority THREAD_PRIORITY_NORMAL
void FixExecutionPriorities() {
const HANDLE process = ::GetCurrentProcess();
const DWORD priority_class = ::GetPriorityClass(process);
if (priority_class == NORMAL_PRIORITY_CLASS) {
return;
}
::SetPriorityClass(process, NORMAL_PRIORITY_CLASS);
static const auto set_process_information_fn =
reinterpret_cast<decltype(&::SetProcessInformation)>(::GetProcAddress(
::GetModuleHandle(L"Kernel32.dll"), "SetProcessInformation"));
if (!set_process_information_fn) {
return;
}
MEMORY_PRIORITY_INFORMATION memory_priority = {};
memory_priority.MemoryPriority = MEMORY_PRIORITY_NORMAL;
set_process_information_fn(process, ProcessMemoryPriority, &memory_priority,
sizeof(memory_priority));
::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_NORMAL);
}
// Sets the _NT_ALT_SYMBOL_PATH for the system or the user, if it is not set
// already. Resets it on destruction. The environment variable is set in the
// corresponding registry hive. _NT_ALT_SYMBOL_PATH is used to avoid symbol
// path collision because its usage is less common than _NT_SYMBOL_PATH. The
// environment variable points to the directory where this unit test binary is.
// The symbol files for the updater targets are expected to be present in this
// directory.
class ScopedSymbolPath {
public:
explicit ScopedSymbolPath(bool is_system)
: is_system_(is_system),
rootkey_(is_system ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER),
subkey_(is_system ? L"SYSTEM\\CurrentControlSet\\Control\\Session "
L"Manager\\Environment"
: L"Environment") {
base::FilePath out_dir;
base::PathService::Get(base::DIR_EXE, &out_dir);
const std::wstring symbol_path = out_dir.value();
// For an unknown reason, symbolized stacks for code running as user
// requires setting up the environment variable for this unit test process.
if (::GetEnvironmentVariable(kNtSymbolPathEnVar, nullptr, 0) == 0) {
::SetEnvironmentVariable(kNtSymbolPathEnVar, symbol_path.c_str());
}
base::win::RegKey reg_key(rootkey_, subkey_.c_str(), KEY_READ | KEY_WRITE);
if (reg_key.Valid() && !reg_key.HasValue(kNtSymbolPathEnVar)) {
is_owned = reg_key.WriteValue(kNtSymbolPathEnVar, symbol_path.c_str()) ==
ERROR_SUCCESS;
if (!is_owned) {
return;
}
BroadcastEnvironmentChange();
VLOG(0) << "Symbol path for " << (is_system_ ? "system" : "user")
<< " set to: " << symbol_path;
}
}
~ScopedSymbolPath() {
if (!is_owned) {
return;
}
base::win::RegKey reg_key(rootkey_, subkey_.c_str(), KEY_WRITE);
if (reg_key.Valid()) {
reg_key.DeleteValue(kNtSymbolPathEnVar);
BroadcastEnvironmentChange();
}
}
private:
// Notifies the processes that the environment has been changed to reload it.
static void BroadcastEnvironmentChange() {
static constexpr int kTimeOutMilliSeconds = 100;
DWORD_PTR result = 0;
::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
reinterpret_cast<LPARAM>(L"Environment"), SMTO_NORMAL,
kTimeOutMilliSeconds, &result);
}
// The name of the environment variable to create under the registry key.
static constexpr wchar_t kNtSymbolPathEnVar[] = L"_NT_ALT_SYMBOL_PATH";
const bool is_system_ = false;
const HKEY rootkey_ = nullptr;
const std::wstring subkey_;
// True if the registry value is owned by this instance and it must be
// cleaned up on destruction.
bool is_owned = false;
};
} // namespace
#endif // BUILDFLAG(IS_WIN)
namespace {
void MaybeIncreaseTestTimeouts(int argc, char** argv) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
// The minimum and the default value when unspecified is 45000.
if (!command_line->HasSwitch(switches::kTestLauncherTimeout)) {
command_line->AppendSwitchUTF8(switches::kTestLauncherTimeout, "90000");
}
// The minimum and the default value when unspecified is 30000.
if (!command_line->HasSwitch(switches::kUiTestActionMaxTimeout)) {
command_line->AppendSwitchUTF8(switches::kUiTestActionMaxTimeout, "45000");
}
// The minimum and the default value when unspecified is 10000.
if (!command_line->HasSwitch(switches::kUiTestActionTimeout)) {
command_line->AppendSwitchUTF8(switches::kUiTestActionTimeout, "40000");
}
}
// Disable the fallback fetcher for the current process. This is achieved by
// adding `kNetWorkerSwitch` to the process command line to make it look like
// a net worker process.
void SkipFallbackNetworkFetcher() {
#if BUILDFLAG(IS_MAC)
base::CommandLine::ForCurrentProcess()->AppendSwitch(
updater::kNetWorkerSwitch);
#endif // BUILDFLAG(IS_MAC)
}
} // namespace
int main(int argc, char** argv) {
base::CommandLine::Init(argc, argv);
absl::Cleanup reset_command_line = &base::CommandLine::Reset;
// Change the test timeout defaults if the command line arguments to override
// them are not present.
MaybeIncreaseTestTimeouts(argc, argv);
// To make setting up network response expectations easier in test, just
// disable fallback fetcher altogether within this process.
SkipFallbackNetworkFetcher();
#if BUILDFLAG(IS_WIN)
VLOG(0) << "Process priority: " << base::Process::Current().GetOSPriority();
VLOG(0) << updater::GetUACState();
// The test suite runner expects the swarming task to run with normal priority
// but for some reason, on the updater bots with UAC on, the swarming task
// runs with a priority below normal (see crbug.com/1245429).
FixExecutionPriorities();
auto scoped_com_initializer =
std::make_unique<base::win::ScopedCOMInitializer>(
base::win::ScopedCOMInitializer::kMTA);
// Failing to disable COM exception handling is a critical error.
CHECK(SUCCEEDED(updater::DisableCOMExceptionHandling()))
<< "Failed to disable COM exception handling.";
installer::ScopedTokenPrivilege token_se_debug(SE_DEBUG_NAME);
if (::IsUserAnAdmin() && !token_se_debug.is_enabled()) {
LOG(ERROR) << "Running as administrator but can't enable SE_DEBUG_NAME.";
}
// Set up the _NT_ALT_SYMBOL_PATH to get symbolized stack traces in logs.
ScopedSymbolPath scoped_symbol_path_system(/*is_system=*/true);
ScopedSymbolPath scoped_symbol_path_user(/*is_system=*/false);
#endif // BUILDFLAG(IS_WIN)
// Assume all test bots have the {ISOLATED_OUTDIR} environment variable set.
// Otherwise, don't run branded updater tests on a developer's system because
// doing so breaks the updater on the system.
using std::operator""sv;
if constexpr ("ChromiumUpdater"sv.compare(PRODUCT_FULLNAME_STRING)) {
if (!std::getenv("ISOLATED_OUTDIR")) {
LOG(ERROR)
<< "Running branded updater tests breaks the updater for "
"the branded browser. This is unavoidable in the current "
"implementation. If you don't care about broken updaters and "
"want to run the branded updater tests locally, define an "
"environment variable ISOLATED_OUTDIR and set it to a local "
"directory.";
return -1;
}
}
// Use the {ISOLATED_OUTDIR} as a log destination for the test suite.
base::TestSuite test_suite(argc, argv);
updater::test::InitLoggingForUnitTest(base::FilePath([] {
switch (updater::GetUpdaterScopeForTesting()) {
case updater::UpdaterScope::kSystem:
return FILE_PATH_LITERAL("updater_test_system.log");
case updater::UpdaterScope::kUser:
return FILE_PATH_LITERAL("updater_test.log");
}
}()));
chrome::RegisterPathProvider();
return base::LaunchUnitTestsWithOptions(
argc, argv, 1, 10, true, base::BindRepeating([] {
LOG(ERROR) << "A test timeout has occured in "
<< updater::test::GetTestName();
updater::test::CreateIntegrationTestCommands()->PrintLog();
}),
base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
}
|