Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(286)

Side by Side Diff: components/browser_watcher/watcher_win_unittest.cc

Issue 729363004: Browser watcher first installment. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkgr
Patch Set: Address Erik's remaining comment. Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/browser_watcher/watcher_win.cc ('k') | components/components.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "components/browser_watcher/watcher_win.h"
6
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"
19
20 namespace browser_watcher {
21
22 namespace {
23
24 const wchar_t kRegistryPath[] = L"Software\\BrowserWatcherTest";
25
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;
31 }
32
33 class ScopedSleeperProcess {
34 public:
35 ScopedSleeperProcess() :
36 process_(base::kNullProcessHandle),
37 process_id_(base::kNullProcessId),
38 is_killed_(false) {
39 }
40
41 ~ScopedSleeperProcess() {
42 if (process_ != base::kNullProcessHandle) {
43 base::KillProcess(process_, -1, true);
44 base::CloseProcessHandle(process_);
45 }
46 }
47
48 void Launch() {
49 ASSERT_EQ(base::kNullProcessHandle, process_);
50
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_);
57 }
58
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;
64 }
65
66 void GetNewHandle(base::ProcessHandle* output) {
67 ASSERT_NE(process_, base::kNullProcessHandle);
68
69 ASSERT_TRUE(DuplicateHandle(::GetCurrentProcess(),
70 process_,
71 ::GetCurrentProcess(),
72 output,
73 0,
74 FALSE,
75 DUPLICATE_SAME_ACCESS));
76 }
77
78 base::ProcessHandle process() const { return process_; }
79 base::ProcessId process_id() const { return process_id_; }
80
81 private:
82 base::ProcessHandle process_;
83 base::ProcessId process_id_;
84 bool is_killed_;
85 };
86
87 class BrowserWatcherTest : public testing::Test {
88 public:
89 typedef testing::Test Super;
90
91 static const int kExitCode = 0xCAFEBABE;
92
93 BrowserWatcherTest() :
94 cmd_line_(base::CommandLine::NO_PROGRAM),
95 process_(base::kNullProcessHandle) {
96 }
97
98 virtual void SetUp() override {
99 Super::SetUp();
100
101 override_manager_.OverrideRegistry(HKEY_CURRENT_USER);
102 }
103
104 virtual void TearDown() override {
105 if (process_ != base::kNullProcessHandle) {
106 base::CloseProcessHandle(process_);
107 process_ = base::kNullProcessHandle;
108 }
109
110 Super::TearDown();
111 }
112
113 void OpenSelfWithAccess(uint32 access) {
114 ASSERT_EQ(base::kNullProcessHandle, process_);
115 ASSERT_TRUE(base::OpenProcessHandleWithAccess(
116 base::GetCurrentProcId(), access, &process_));
117 }
118
119 void VerifyWroteExitCode(base::ProcessId proc_id, int exit_code) {
120 base::win::RegistryValueIterator it(
121 HKEY_CURRENT_USER, kRegistryPath);
122
123 ASSERT_EQ(1, it.ValueCount());
124 base::win::RegKey key(HKEY_CURRENT_USER,
125 kRegistryPath,
126 KEY_QUERY_VALUE);
127
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);
135 }
136
137 protected:
138 base::CommandLine cmd_line_;
139 base::ProcessHandle process_;
140 registry_util::RegistryOverrideManager override_manager_;
141 };
142
143 } // namespace
144
145 TEST_F(BrowserWatcherTest, ExitCodeWatcherInvalidCmdLineFailsInit) {
146 ExitCodeWatcher watcher(kRegistryPath);
147
148 // An empty command line should fail.
149 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
150
151 // A non-numeric parent-handle argument should fail.
152 cmd_line_.AppendSwitchASCII(ExitCodeWatcher::kParenthHandleSwitch, "asdf");
153 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
154 }
155
156 TEST_F(BrowserWatcherTest, ExitCodeWatcherInvalidHandleFailsInit) {
157 ExitCodeWatcher watcher(kRegistryPath);
158
159 // A waitable event has a non process-handle.
160 base::WaitableEvent event(false, false);
161
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_));
166 }
167
168 TEST_F(BrowserWatcherTest, ExitCodeWatcherNoAccessHandleFailsInit) {
169 ExitCodeWatcher watcher(kRegistryPath);
170
171 // Open a SYNCHRONIZE-only handle to this process.
172 ASSERT_NO_FATAL_FAILURE(OpenSelfWithAccess(SYNCHRONIZE));
173
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_));
178 }
179
180 TEST_F(BrowserWatcherTest, ExitCodeWatcherSucceedsInit) {
181 ExitCodeWatcher watcher(kRegistryPath);
182
183 // Open a handle to this process with sufficient access for the watcher.
184 ASSERT_NO_FATAL_FAILURE(
185 OpenSelfWithAccess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION));
186
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_));
191
192 ASSERT_EQ(process_, watcher.process());
193
194 // The watcher takes ownership of the handle, make sure it's not
195 // double-closed.
196 process_ = base::kNullProcessHandle;
197 }
198
199 TEST_F(BrowserWatcherTest, ExitCodeWatcherOnExitedProcess) {
200 ScopedSleeperProcess sleeper;
201 ASSERT_NO_FATAL_FAILURE(sleeper.Launch());
202
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);
208
209 ExitCodeWatcher watcher(kRegistryPath);
210
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());
215
216 // Verify that the watcher wrote a sentinel for the process.
217 VerifyWroteExitCode(sleeper.process_id(), STILL_ACTIVE);
218
219 // Kill the sleeper, and make sure it's exited before we continue.
220 ASSERT_NO_FATAL_FAILURE(sleeper.Kill(kExitCode, true));
221
222 watcher.WaitForExit();
223
224 VerifyWroteExitCode(sleeper.process_id(), kExitCode);
225 }
226
227 } // namespace browser_watcher
OLDNEW
« no previous file with comments | « components/browser_watcher/watcher_win.cc ('k') | components/components.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698