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

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

Issue 743463002: Browser watcher, part deux. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkgr
Patch Set: Address Erik's remaining comments. 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_client_win.h"
6
7 #include <string>
8
9 #include "base/base_switches.h"
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/process/process_handle.h"
13 #include "base/process/kill.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/test/multiprocess_test.h"
17 #include "base/test/test_reg_util_win.h"
18 #include "base/win/scoped_handle.h"
19 #include "base/win/windows_version.h"
20 #include "components/browser_watcher/exit_code_watcher_win.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "testing/multiprocess_func_list.h"
23
24 namespace browser_watcher {
25
26 namespace {
27
28 // Command line switches used to communiate to the child test.
29 const char kParentPid[] = "parent-pid";
30 const char kLeakHandle[] = "leak-handle";
31 const char kNoLeakHandle[] = "no-leak-handle";
32
33 bool IsValidParentProcessHandle(base::CommandLine& cmd_line,
34 const char* switch_name) {
35 std::string str_handle =
36 cmd_line.GetSwitchValueASCII(switch_name);
37
38 unsigned int_handle = 0;
39 if (!base::StringToUint(str_handle, &int_handle))
40 return false;
41
42 int parent_pid = 0;
43 if (!base::StringToInt(cmd_line.GetSwitchValueASCII(kParentPid),
44 &parent_pid)) {
45 return false;
46 }
47
48 base::ProcessHandle handle =
49 reinterpret_cast<base::ProcessHandle>(int_handle);
50 // Verify that we can get the associated process id.
51 base::ProcessId parent_id = base::GetProcId(handle);
52 if (parent_id == 0) {
53 // Unable to get the parent pid - perhaps insufficient permissions.
54 return false;
55 }
56
57 // Make sure the handle grants SYNCHRONIZE by waiting on it.
58 DWORD err = ::WaitForSingleObject(handle, 0);
59 if (err != WAIT_OBJECT_0 && err != WAIT_TIMEOUT) {
60 // Unable to wait on the handle - perhaps insufficient permissions.
61 return false;
62 }
63
64 return true;
65 }
66
67 MULTIPROCESS_TEST_MAIN(VerifyParentHandle) {
68 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
69
70 // Make sure we got a valid parent process handle from the watcher client.
71 if (!IsValidParentProcessHandle(*cmd_line,
72 ExitCodeWatcher::kParenthHandleSwitch)) {
73 LOG(ERROR) << "Invalid or missing parent-handle.";
74 return 1;
75 }
76
77 // If in the legacy mode, we expect this second handle will leak into the
78 // child process. This mainly serves to verify that the legacy mode is
79 // getting tested.
80 if (cmd_line->HasSwitch(kLeakHandle) &&
81 !IsValidParentProcessHandle(*cmd_line, kLeakHandle)) {
82 LOG(ERROR) << "Parent process handle unexpectedly didn't leak.";
83 return 1;
84 }
85
86 // If not in the legacy mode, this second handle should not leak into the
87 // child process.
88 if (cmd_line->HasSwitch(kNoLeakHandle) &&
89 IsValidParentProcessHandle(*cmd_line, kLeakHandle)) {
90 LOG(ERROR) << "Parent process handle unexpectedly leaked.";
91 return 1;
92 }
93
94 return 0;
95 }
96
97 class BrowserWatcherClientTest : public base::MultiProcessTest {
98 public:
99 virtual void SetUp() {
100 // Open an inheritable handle on our own process to test handle leakage.
101 self_.Set(::OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
102 TRUE, // Ineritable handle.
103 base::GetCurrentProcId()));
104
105 ASSERT_TRUE(self_.IsValid());
106 }
107
108 enum HandlePolicy {
109 LEAK_HANDLE,
110 NO_LEAK_HANDLE
111 };
112
113 // Get a base command line to launch back into this test fixture.
114 base::CommandLine GetBaseCommandLine(HandlePolicy handle_policy) {
115 base::CommandLine ret = base::GetMultiProcessTestChildBaseCommandLine();
116
117 ret.AppendSwitchASCII(switches::kTestChildProcess, "VerifyParentHandle");
118 ret.AppendSwitchASCII(kParentPid,
119 base::StringPrintf("%d", base::GetCurrentProcId()));
120
121 switch (handle_policy) {
122 case LEAK_HANDLE:
123 ret.AppendSwitchASCII(kLeakHandle,
124 base::StringPrintf("%d", self_.Get()));
125 break;
126
127 case NO_LEAK_HANDLE:
128 ret.AppendSwitchASCII(kNoLeakHandle,
129 base::StringPrintf("%d", self_.Get()));
130 break;
131
132 default:
133 ADD_FAILURE() << "Impossible handle_policy";
134 }
135
136 return ret;
137 }
138
139 void AssertSuccessfulExitCode(base::ProcessHandle handle) {
140 ASSERT_NE(base::kNullProcessHandle, handle);
141
142 // Duplicate the process handle to work around the fact that
143 // WaitForExitCode closes it(!!!).
144 base::ProcessHandle dupe = NULL;
145 ASSERT_TRUE(::DuplicateHandle(base::GetCurrentProcessHandle(),
146 handle,
147 base::GetCurrentProcessHandle(),
148 &dupe,
149 SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
150 FALSE,
151 0));
152 ASSERT_NE(base::kNullProcessHandle, dupe);
153 int exit_code = 0;
154 if (!base::WaitForExitCode(dupe, &exit_code)) {
155 base::CloseProcessHandle(dupe);
156 FAIL() << "WaitForExitCode failed.";
157 }
158 ASSERT_EQ(0, exit_code);
159 }
160
161 // Inheritable process handle used for testing.
162 base::win::ScopedHandle self_;
163 };
164
165 } // namespace
166
167 // TODO(siggi): More testing - test WatcherClient base implementation.
168
169 TEST_F(BrowserWatcherClientTest, LaunchWatcherSucceeds) {
170 // We can only use the non-legacy launch method on Windows Vista or better.
171 if (base::win::GetVersion() < base::win::VERSION_VISTA)
172 return;
173
174 WatcherClient client(GetBaseCommandLine(NO_LEAK_HANDLE));
175 ASSERT_FALSE(client.use_legacy_launch());
176
177 client.LaunchWatcher();
178
179 ASSERT_NO_FATAL_FAILURE(AssertSuccessfulExitCode(client.process()));
180 }
181
182 TEST_F(BrowserWatcherClientTest, LaunchWatcherLegacyModeSucceeds) {
183 // Test the XP-compatible legacy launch mode. This is expected to leak
184 // a handle to the child process.
185 WatcherClient client(GetBaseCommandLine(LEAK_HANDLE));
186
187 // Use the legacy launch mode.
188 client.set_use_legacy_launch(true);
189
190 client.LaunchWatcher();
191
192 ASSERT_NO_FATAL_FAILURE(AssertSuccessfulExitCode(client.process()));
193 }
194
195 TEST_F(BrowserWatcherClientTest, LegacyModeDetectedOnXP) {
196 // This test only works on Windows XP.
197 if (base::win::GetVersion() > base::win::VERSION_XP)
198 return;
199
200 // Test that the client detects the need to use legacy launch mode, and that
201 // it works on Windows XP.
202 WatcherClient client(GetBaseCommandLine(LEAK_HANDLE));
203 ASSERT_TRUE(client.use_legacy_launch());
204
205 client.LaunchWatcher();
206
207 ASSERT_NO_FATAL_FAILURE(AssertSuccessfulExitCode(client.process()));
208 }
209
210 } // namespace browser_watcher
OLDNEW
« no previous file with comments | « components/browser_watcher/watcher_client_win.cc ('k') | components/browser_watcher/watcher_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698