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

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: 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
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), is_killed_(false) {
erikwright (departed) 2014/11/17 18:12:30 process_id_ should be initialized
Sigurður Ásgeirsson 2014/11/17 21:27:12 Done.
37 }
38
39 ~ScopedSleeperProcess() {
40 if (process_ != base::kNullProcessHandle) {
41 base::KillProcess(process_, -1, true);
42 base::CloseProcessHandle(process_);
43 }
44 }
45
46 void Launch() {
47 ASSERT_EQ(base::kNullProcessHandle, process_);
erikwright (departed) 2014/11/17 18:12:31 I'm personally opposed to using "ASSERT" in functi
Sigurður Ásgeirsson 2014/11/17 21:27:12 Hmmm - there's an implicit return value in such te
48
49 base::CommandLine cmd_line(base::GetMultiProcessTestChildBaseCommandLine());
50 base::LaunchOptions options;
51 options.start_hidden = true;
52 process_ = base::SpawnMultiProcessTestChild("Sleeper", cmd_line, options);
53 process_id_ = base::GetProcId(process_);
54 ASSERT_NE(base::kNullProcessHandle, process_);
55 }
56
57 void Kill(int exit_code, bool wait) {
58 ASSERT_NE(process_, base::kNullProcessHandle);
59 ASSERT_FALSE(is_killed_);
60 ASSERT_TRUE(base::KillProcess(process_, exit_code, wait));
61 is_killed_ = true;
62 }
63
64 base::ProcessHandle process() const { return process_; }
65 base::ProcessId process_id() const { return process_id_; }
66 void set_process(base::ProcessHandle process) { process_ = process; }
67
68 private:
69 base::ProcessHandle process_;
70 base::ProcessId process_id_;
71 bool is_killed_;
72 };
73
74 class BrowserWatcherTest : public testing::Test {
75 public:
76 typedef testing::Test Super;
77
78 static const int kExitCode = 0xCAFEBABE;
79
80 BrowserWatcherTest() :
81 cmd_line_(base::CommandLine::NO_PROGRAM),
82 process_(base::kNullProcessHandle) {
83 }
84
85 virtual void SetUp() override {
86 Super::SetUp();
87
88 override_manager_.OverrideRegistry(HKEY_CURRENT_USER);
89 }
90
91 virtual void TearDown() override {
92 if (process_ != base::kNullProcessHandle) {
93 base::CloseProcessHandle(process_);
94 process_ = base::kNullProcessHandle;
95 }
96
97 Super::TearDown();
98 }
99
100 void OpenSelfWithAccess(uint32 access) {
101 ASSERT_EQ(base::kNullProcessHandle, process_);
102 ASSERT_TRUE(base::OpenProcessHandleWithAccess(
103 base::GetCurrentProcId(), access, &process_));
104 }
105
106 void VerifyWroteExitCode(base::ProcessId proc_id, int exit_code) {
107 base::win::RegistryValueIterator it(
108 HKEY_CURRENT_USER, kRegistryPath);
109
110 ASSERT_EQ(1, it.ValueCount());
111 base::win::RegKey key(HKEY_CURRENT_USER,
112 kRegistryPath,
113 KEY_QUERY_VALUE);
114
115 // The value name should encode the process id at the start.
116 EXPECT_TRUE(StartsWith(it.Name(),
117 base::StringPrintf(L"%d-", proc_id),
118 false));
119 DWORD value = 0;
120 ASSERT_EQ(ERROR_SUCCESS, key.ReadValueDW(it.Name(), &value));
121 ASSERT_EQ(exit_code, value);
122 }
123
124 protected:
125 base::CommandLine cmd_line_;
126 base::ProcessHandle process_;
127 registry_util::RegistryOverrideManager override_manager_;
128 };
129
130 } // namespace
131
132 TEST_F(BrowserWatcherTest, ExitCodeWatcherInvalidCmdLineFailsInit) {
133 ExitCodeWatcher watcher(kRegistryPath);
134
135 // An empty command line should fail.
136 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
137
138 // A non-numeric parent-handle argument should fail.
139 cmd_line_.AppendSwitchASCII("parent-handle", "asdf");
140 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
141 }
142
143 TEST_F(BrowserWatcherTest, ExitCodeWatcherInvalidHandleFailsInit) {
144 ExitCodeWatcher watcher(kRegistryPath);
145
146 // A waitable event has a non process-handle.
147 base::WaitableEvent event(false, false);
148
149 // A non-process handle should fail.
150 cmd_line_.AppendSwitchASCII("parent-handle",
151 base::StringPrintf("%d", event.handle()));
152 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
153 }
154
155 TEST_F(BrowserWatcherTest, ExitCodeWatcherNoAccessHandleFailsInit) {
156 ExitCodeWatcher watcher(kRegistryPath);
157
158 // Open a SYNCHRONIZE-only handle to this process.
159 ASSERT_NO_FATAL_FAILURE(OpenSelfWithAccess(SYNCHRONIZE));
160
161 // A process handle with insufficient access should fail.
162 cmd_line_.AppendSwitchASCII("parent-handle",
163 base::StringPrintf("%d", process_));
164 EXPECT_FALSE(watcher.ParseArguments(cmd_line_));
165 }
166
167 TEST_F(BrowserWatcherTest, ExitCodeWatcherSucceedsInit) {
168 ExitCodeWatcher watcher(kRegistryPath);
169
170 // Open a handle to this process with sufficient access for the watcher.
171 ASSERT_NO_FATAL_FAILURE(
172 OpenSelfWithAccess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION));
173
174 // A process handle with sufficient access should succeed init.
175 cmd_line_.AppendSwitchASCII("parent-handle",
176 base::StringPrintf("%d", process_));
177 EXPECT_TRUE(watcher.ParseArguments(cmd_line_));
178
179 ASSERT_EQ(process_, watcher.process());
180
181 // The watcher takes ownership of the handle, make sure it's not
182 // double-closed.
183 process_ = base::kNullProcessHandle;
184 }
185
186 TEST_F(BrowserWatcherTest, ExitCodeWatcherOnExitedProcess) {
187 ScopedSleeperProcess sleeper;
188 ASSERT_NO_FATAL_FAILURE(sleeper.Launch());
189
190 ExitCodeWatcher watcher(kRegistryPath);
191
192 cmd_line_.AppendSwitchASCII("parent-handle",
193 base::StringPrintf("%d", sleeper.process()));
194 EXPECT_TRUE(watcher.ParseArguments(cmd_line_));
195 ASSERT_EQ(sleeper.process(), watcher.process());
196
197 // Verify that the watcher wrote a sentinel for the process.
198 VerifyWroteExitCode(sleeper.process_id(), STILL_ACTIVE);
199
200 // Kill the sleeper, and make sure it's exited before we continue.
201 ASSERT_NO_FATAL_FAILURE(sleeper.Kill(kExitCode, true));
202
203 // The watcher took ownership of the handle, make sure it's not
204 // double-closed.
205 sleeper.set_process(base::kNullProcessHandle);
erikwright (departed) 2014/11/17 18:12:30 Maybe replace with a nullary "Release()" (or Relea
Sigurður Ásgeirsson 2014/11/17 21:27:12 Went a slightly different way...
206
207 watcher.WaitForExit();
208
209 VerifyWroteExitCode(sleeper.process_id(), kExitCode);
210 }
211
212 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698