File: run_all_unittests.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (250 lines) | stat: -rw-r--r-- 9,568 bytes parent folder | download | duplicates (5)
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)));
}