GN + Android: extract android_standalone_library rule.
[chromium-blink-merge.git] / components / browser_watcher / exit_code_watcher_win_unittest.cc
blobcd85f865167d76e5a9f9b8de9589e575e657d06c
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/string16.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/test/multiprocess_test.h"
14 #include "base/test/test_reg_util_win.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/time/time.h"
17 #include "base/win/scoped_handle.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "testing/multiprocess_func_list.h"
21 namespace browser_watcher {
23 namespace {
25 const base::char16 kRegistryPath[] = L"Software\\BrowserWatcherTest";
27 MULTIPROCESS_TEST_MAIN(Sleeper) {
28 // Sleep forever - the test harness will kill this process to give it an
29 // exit code.
30 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(INFINITE));
31 return 1;
34 class ScopedSleeperProcess {
35 public:
36 ScopedSleeperProcess() :
37 process_(base::kNullProcessHandle),
38 process_id_(base::kNullProcessId),
39 is_killed_(false) {
42 ~ScopedSleeperProcess() {
43 if (process_ != base::kNullProcessHandle) {
44 base::KillProcess(process_, -1, true);
45 base::CloseProcessHandle(process_);
49 void Launch() {
50 ASSERT_EQ(base::kNullProcessHandle, process_);
52 base::CommandLine cmd_line(base::GetMultiProcessTestChildBaseCommandLine());
53 base::LaunchOptions options;
54 options.start_hidden = true;
55 process_ = base::SpawnMultiProcessTestChild("Sleeper", cmd_line, options);
56 process_id_ = base::GetProcId(process_);
57 ASSERT_NE(base::kNullProcessHandle, process_);
60 void Kill(int exit_code, bool wait) {
61 ASSERT_NE(process_, base::kNullProcessHandle);
62 ASSERT_FALSE(is_killed_);
63 ASSERT_TRUE(base::KillProcess(process_, exit_code, wait));
64 is_killed_ = true;
67 void GetNewHandle(base::ProcessHandle* output) {
68 ASSERT_NE(process_, base::kNullProcessHandle);
70 ASSERT_TRUE(DuplicateHandle(::GetCurrentProcess(),
71 process_,
72 ::GetCurrentProcess(),
73 output,
75 FALSE,
76 DUPLICATE_SAME_ACCESS));
79 base::ProcessHandle process() const { return process_; }
80 base::ProcessId process_id() const { return process_id_; }
82 private:
83 base::ProcessHandle process_;
84 base::ProcessId process_id_;
85 bool is_killed_;
88 class BrowserWatcherTest : public testing::Test {
89 public:
90 typedef testing::Test Super;
92 static const int kExitCode = 0xCAFEBABE;
94 BrowserWatcherTest() :
95 cmd_line_(base::CommandLine::NO_PROGRAM),
96 process_(base::kNullProcessHandle) {
99 virtual void SetUp() override {
100 Super::SetUp();
102 override_manager_.OverrideRegistry(HKEY_CURRENT_USER);
105 virtual void TearDown() override {
106 if (process_ != base::kNullProcessHandle) {
107 base::CloseProcessHandle(process_);
108 process_ = base::kNullProcessHandle;
111 Super::TearDown();
114 void OpenSelfWithAccess(uint32 access) {
115 ASSERT_EQ(base::kNullProcessHandle, process_);
116 ASSERT_TRUE(base::OpenProcessHandleWithAccess(
117 base::GetCurrentProcId(), access, &process_));
120 void VerifyWroteExitCode(base::ProcessId proc_id, int exit_code) {
121 base::win::RegistryValueIterator it(
122 HKEY_CURRENT_USER, kRegistryPath);
124 ASSERT_EQ(1, it.ValueCount());
125 base::win::RegKey key(HKEY_CURRENT_USER,
126 kRegistryPath,
127 KEY_QUERY_VALUE);
129 // The value name should encode the process id at the start.
130 EXPECT_TRUE(StartsWith(it.Name(),
131 base::StringPrintf(L"%d-", proc_id),
132 false));
133 DWORD value = 0;
134 ASSERT_EQ(ERROR_SUCCESS, key.ReadValueDW(it.Name(), &value));
135 ASSERT_EQ(exit_code, value);
138 protected:
139 base::CommandLine cmd_line_;
140 base::ProcessHandle process_;
141 registry_util::RegistryOverrideManager override_manager_;
144 } // namespace
146 TEST_F(BrowserWatcherTest, ExitCodeWatcherInvalidCmdLineFailsInit) {
147 ExitCodeWatcher watcher(kRegistryPath);
149 // An empty command line should fail.
150 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
152 // A non-numeric parent-handle argument should fail.
153 cmd_line_.AppendSwitchASCII(ExitCodeWatcher::kParenthHandleSwitch, "asdf");
154 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
157 TEST_F(BrowserWatcherTest, ExitCodeWatcherInvalidHandleFailsInit) {
158 ExitCodeWatcher watcher(kRegistryPath);
160 // A waitable event has a non process-handle.
161 base::WaitableEvent event(false, false);
163 // A non-process handle should fail.
164 cmd_line_.AppendSwitchASCII(ExitCodeWatcher::kParenthHandleSwitch,
165 base::StringPrintf("%d", event.handle()));
166 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
169 TEST_F(BrowserWatcherTest, ExitCodeWatcherNoAccessHandleFailsInit) {
170 ExitCodeWatcher watcher(kRegistryPath);
172 // Open a SYNCHRONIZE-only handle to this process.
173 ASSERT_NO_FATAL_FAILURE(OpenSelfWithAccess(SYNCHRONIZE));
175 // A process handle with insufficient access should fail.
176 cmd_line_.AppendSwitchASCII(ExitCodeWatcher::kParenthHandleSwitch,
177 base::StringPrintf("%d", process_));
178 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
181 TEST_F(BrowserWatcherTest, ExitCodeWatcherSucceedsInit) {
182 ExitCodeWatcher watcher(kRegistryPath);
184 // Open a handle to this process with sufficient access for the watcher.
185 ASSERT_NO_FATAL_FAILURE(
186 OpenSelfWithAccess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION));
188 // A process handle with sufficient access should succeed init.
189 cmd_line_.AppendSwitchASCII(ExitCodeWatcher::kParenthHandleSwitch,
190 base::StringPrintf("%d", process_));
191 EXPECT_TRUE(watcher.ParseArguments(cmd_line_));
193 ASSERT_EQ(process_, watcher.process());
195 // The watcher takes ownership of the handle, make sure it's not
196 // double-closed.
197 process_ = base::kNullProcessHandle;
200 TEST_F(BrowserWatcherTest, ExitCodeWatcherOnExitedProcess) {
201 ScopedSleeperProcess sleeper;
202 ASSERT_NO_FATAL_FAILURE(sleeper.Launch());
204 // Create a new handle to the sleeper process. This handle will leak in
205 // the case this test fails. A ScopedHandle cannot be used here, as the
206 // ownership would momentarily be held by two of them, which is disallowed.
207 base::ProcessHandle sleeper_handle;
208 sleeper.GetNewHandle(&sleeper_handle);
210 ExitCodeWatcher watcher(kRegistryPath);
212 cmd_line_.AppendSwitchASCII(ExitCodeWatcher::kParenthHandleSwitch,
213 base::StringPrintf("%d", sleeper_handle));
214 EXPECT_TRUE(watcher.ParseArguments(cmd_line_));
215 ASSERT_EQ(sleeper_handle, watcher.process());
217 // Verify that the watcher wrote a sentinel for the process.
218 VerifyWroteExitCode(sleeper.process_id(), STILL_ACTIVE);
220 // Kill the sleeper, and make sure it's exited before we continue.
221 ASSERT_NO_FATAL_FAILURE(sleeper.Kill(kExitCode, true));
223 watcher.WaitForExit();
225 VerifyWroteExitCode(sleeper.process_id(), kExitCode);
228 } // namespace browser_watcher