Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / components / browser_watcher / exit_code_watcher_win_unittest.cc
blob407635d582bbe4aae47e5e0fe8c27e97a5498371
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/browser_watcher/exit_code_watcher_win.h"
7 #include "base/command_line.h"
8 #include "base/process/kill.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/test/multiprocess_test.h"
13 #include "base/test/test_reg_util_win.h"
14 #include "base/threading/platform_thread.h"
15 #include "base/time/time.h"
16 #include "base/win/scoped_handle.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "testing/multiprocess_func_list.h"
20 namespace browser_watcher {
22 namespace {
24 const wchar_t kRegistryPath[] = L"Software\\BrowserWatcherTest";
26 MULTIPROCESS_TEST_MAIN(Sleeper) {
27 // Sleep forever - the test harness will kill this process to give it an
28 // exit code.
29 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(INFINITE));
30 return 1;
33 class ScopedSleeperProcess {
34 public:
35 ScopedSleeperProcess() :
36 process_(base::kNullProcessHandle),
37 process_id_(base::kNullProcessId),
38 is_killed_(false) {
41 ~ScopedSleeperProcess() {
42 if (process_ != base::kNullProcessHandle) {
43 base::KillProcess(process_, -1, true);
44 base::CloseProcessHandle(process_);
48 void Launch() {
49 ASSERT_EQ(base::kNullProcessHandle, process_);
51 base::CommandLine cmd_line(base::GetMultiProcessTestChildBaseCommandLine());
52 base::LaunchOptions options;
53 options.start_hidden = true;
54 process_ = base::SpawnMultiProcessTestChild("Sleeper", cmd_line, options);
55 process_id_ = base::GetProcId(process_);
56 ASSERT_NE(base::kNullProcessHandle, process_);
59 void Kill(int exit_code, bool wait) {
60 ASSERT_NE(process_, base::kNullProcessHandle);
61 ASSERT_FALSE(is_killed_);
62 ASSERT_TRUE(base::KillProcess(process_, exit_code, wait));
63 is_killed_ = true;
66 void GetNewHandle(base::ProcessHandle* output) {
67 ASSERT_NE(process_, base::kNullProcessHandle);
69 ASSERT_TRUE(DuplicateHandle(::GetCurrentProcess(),
70 process_,
71 ::GetCurrentProcess(),
72 output,
74 FALSE,
75 DUPLICATE_SAME_ACCESS));
78 base::ProcessHandle process() const { return process_; }
79 base::ProcessId process_id() const { return process_id_; }
81 private:
82 base::ProcessHandle process_;
83 base::ProcessId process_id_;
84 bool is_killed_;
87 class BrowserWatcherTest : public testing::Test {
88 public:
89 typedef testing::Test Super;
91 static const int kExitCode = 0xCAFEBABE;
93 BrowserWatcherTest() :
94 cmd_line_(base::CommandLine::NO_PROGRAM),
95 process_(base::kNullProcessHandle) {
98 virtual void SetUp() override {
99 Super::SetUp();
101 override_manager_.OverrideRegistry(HKEY_CURRENT_USER);
104 virtual void TearDown() override {
105 if (process_ != base::kNullProcessHandle) {
106 base::CloseProcessHandle(process_);
107 process_ = base::kNullProcessHandle;
110 Super::TearDown();
113 void OpenSelfWithAccess(uint32 access) {
114 ASSERT_EQ(base::kNullProcessHandle, process_);
115 ASSERT_TRUE(base::OpenProcessHandleWithAccess(
116 base::GetCurrentProcId(), access, &process_));
119 void VerifyWroteExitCode(base::ProcessId proc_id, int exit_code) {
120 base::win::RegistryValueIterator it(
121 HKEY_CURRENT_USER, kRegistryPath);
123 ASSERT_EQ(1, it.ValueCount());
124 base::win::RegKey key(HKEY_CURRENT_USER,
125 kRegistryPath,
126 KEY_QUERY_VALUE);
128 // The value name should encode the process id at the start.
129 EXPECT_TRUE(StartsWith(it.Name(),
130 base::StringPrintf(L"%d-", proc_id),
131 false));
132 DWORD value = 0;
133 ASSERT_EQ(ERROR_SUCCESS, key.ReadValueDW(it.Name(), &value));
134 ASSERT_EQ(exit_code, value);
137 protected:
138 base::CommandLine cmd_line_;
139 base::ProcessHandle process_;
140 registry_util::RegistryOverrideManager override_manager_;
143 } // namespace
145 TEST_F(BrowserWatcherTest, ExitCodeWatcherInvalidCmdLineFailsInit) {
146 ExitCodeWatcher watcher(kRegistryPath);
148 // An empty command line should fail.
149 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
151 // A non-numeric parent-handle argument should fail.
152 cmd_line_.AppendSwitchASCII(ExitCodeWatcher::kParenthHandleSwitch, "asdf");
153 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
156 TEST_F(BrowserWatcherTest, ExitCodeWatcherInvalidHandleFailsInit) {
157 ExitCodeWatcher watcher(kRegistryPath);
159 // A waitable event has a non process-handle.
160 base::WaitableEvent event(false, false);
162 // A non-process handle should fail.
163 cmd_line_.AppendSwitchASCII(ExitCodeWatcher::kParenthHandleSwitch,
164 base::StringPrintf("%d", event.handle()));
165 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
168 TEST_F(BrowserWatcherTest, ExitCodeWatcherNoAccessHandleFailsInit) {
169 ExitCodeWatcher watcher(kRegistryPath);
171 // Open a SYNCHRONIZE-only handle to this process.
172 ASSERT_NO_FATAL_FAILURE(OpenSelfWithAccess(SYNCHRONIZE));
174 // A process handle with insufficient access should fail.
175 cmd_line_.AppendSwitchASCII(ExitCodeWatcher::kParenthHandleSwitch,
176 base::StringPrintf("%d", process_));
177 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
180 TEST_F(BrowserWatcherTest, ExitCodeWatcherSucceedsInit) {
181 ExitCodeWatcher watcher(kRegistryPath);
183 // Open a handle to this process with sufficient access for the watcher.
184 ASSERT_NO_FATAL_FAILURE(
185 OpenSelfWithAccess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION));
187 // A process handle with sufficient access should succeed init.
188 cmd_line_.AppendSwitchASCII(ExitCodeWatcher::kParenthHandleSwitch,
189 base::StringPrintf("%d", process_));
190 EXPECT_TRUE(watcher.ParseArguments(cmd_line_));
192 ASSERT_EQ(process_, watcher.process());
194 // The watcher takes ownership of the handle, make sure it's not
195 // double-closed.
196 process_ = base::kNullProcessHandle;
199 TEST_F(BrowserWatcherTest, ExitCodeWatcherOnExitedProcess) {
200 ScopedSleeperProcess sleeper;
201 ASSERT_NO_FATAL_FAILURE(sleeper.Launch());
203 // Create a new handle to the sleeper process. This handle will leak in
204 // the case this test fails. A ScopedHandle cannot be used here, as the
205 // ownership would momentarily be held by two of them, which is disallowed.
206 base::ProcessHandle sleeper_handle;
207 sleeper.GetNewHandle(&sleeper_handle);
209 ExitCodeWatcher watcher(kRegistryPath);
211 cmd_line_.AppendSwitchASCII(ExitCodeWatcher::kParenthHandleSwitch,
212 base::StringPrintf("%d", sleeper_handle));
213 EXPECT_TRUE(watcher.ParseArguments(cmd_line_));
214 ASSERT_EQ(sleeper_handle, watcher.process());
216 // Verify that the watcher wrote a sentinel for the process.
217 VerifyWroteExitCode(sleeper.process_id(), STILL_ACTIVE);
219 // Kill the sleeper, and make sure it's exited before we continue.
220 ASSERT_NO_FATAL_FAILURE(sleeper.Kill(kExitCode, true));
222 watcher.WaitForExit();
224 VerifyWroteExitCode(sleeper.process_id(), kExitCode);
227 } // namespace browser_watcher