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/watcher_client_win.h"
12 #include "base/base_switches.h"
13 #include "base/bind.h"
14 #include "base/command_line.h"
15 #include "base/logging.h"
16 #include "base/process/kill.h"
17 #include "base/process/process.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/test/multiprocess_test.h"
20 #include "base/test/test_reg_util_win.h"
21 #include "base/win/scoped_handle.h"
22 #include "base/win/windows_version.h"
23 #include "components/browser_watcher/exit_code_watcher_win.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "testing/multiprocess_func_list.h"
27 namespace browser_watcher
{
31 // Command line switches used to communiate to the child test.
32 const char kParentHandle
[] = "parent-handle";
33 const char kLeakHandle
[] = "leak-handle";
34 const char kNoLeakHandle
[] = "no-leak-handle";
36 bool IsValidParentProcessHandle(base::CommandLine
& cmd_line
,
37 const char* switch_name
) {
38 std::string str_handle
=
39 cmd_line
.GetSwitchValueASCII(switch_name
);
41 size_t integer_handle
= 0;
42 if (!base::StringToSizeT(str_handle
, &integer_handle
))
45 base::ProcessHandle handle
=
46 reinterpret_cast<base::ProcessHandle
>(integer_handle
);
47 // Verify that we can get the associated process id.
48 base::ProcessId parent_id
= base::GetProcId(handle
);
50 // Unable to get the parent pid - perhaps insufficient permissions.
54 // Make sure the handle grants SYNCHRONIZE by waiting on it.
55 DWORD err
= ::WaitForSingleObject(handle
, 0);
56 if (err
!= WAIT_OBJECT_0
&& err
!= WAIT_TIMEOUT
) {
57 // Unable to wait on the handle - perhaps insufficient permissions.
64 std::string
HandleToString(HANDLE handle
) {
65 // A HANDLE is a void* pointer, which is the same size as a size_t,
66 // so we can use reinterpret_cast<> on it.
67 size_t integer_handle
= reinterpret_cast<size_t>(handle
);
68 return base::SizeTToString(integer_handle
);
71 MULTIPROCESS_TEST_MAIN(VerifyParentHandle
) {
72 base::CommandLine
* cmd_line
= base::CommandLine::ForCurrentProcess();
74 // Make sure we got a valid parent process handle from the watcher client.
75 if (!IsValidParentProcessHandle(*cmd_line
, kParentHandle
)) {
76 LOG(ERROR
) << "Invalid or missing parent-handle.";
80 // If in the legacy mode, we expect this second handle will leak into the
81 // child process. This mainly serves to verify that the legacy mode is
83 if (cmd_line
->HasSwitch(kLeakHandle
) &&
84 !IsValidParentProcessHandle(*cmd_line
, kLeakHandle
)) {
85 LOG(ERROR
) << "Parent process handle unexpectedly didn't leak.";
89 // If not in the legacy mode, this second handle should not leak into the
91 if (cmd_line
->HasSwitch(kNoLeakHandle
) &&
92 IsValidParentProcessHandle(*cmd_line
, kLeakHandle
)) {
93 LOG(ERROR
) << "Parent process handle unexpectedly leaked.";
100 class WatcherClientTest
: public base::MultiProcessTest
{
102 void SetUp() override
{
103 // Open an inheritable handle on our own process to test handle leakage.
104 self_
.Set(::OpenProcess(SYNCHRONIZE
| PROCESS_QUERY_INFORMATION
,
105 TRUE
, // Ineritable handle.
106 base::GetCurrentProcId()));
108 ASSERT_TRUE(self_
.IsValid());
116 // Get a base command line to launch back into this test fixture.
117 base::CommandLine
GetBaseCommandLine(HandlePolicy handle_policy
,
118 HANDLE parent_handle
) {
119 base::CommandLine ret
= base::GetMultiProcessTestChildBaseCommandLine();
121 ret
.AppendSwitchASCII(switches::kTestChildProcess
, "VerifyParentHandle");
122 ret
.AppendSwitchASCII(kParentHandle
, HandleToString(parent_handle
));
124 switch (handle_policy
) {
126 ret
.AppendSwitchASCII(kLeakHandle
, HandleToString(self_
.Get()));
130 ret
.AppendSwitchASCII(kNoLeakHandle
, HandleToString(self_
.Get()));
134 ADD_FAILURE() << "Impossible handle_policy";
140 WatcherClient::CommandLineGenerator
GetBaseCommandLineGenerator(
141 HandlePolicy handle_policy
) {
142 return base::Bind(&WatcherClientTest::GetBaseCommandLine
,
143 base::Unretained(this), handle_policy
);
146 void AssertSuccessfulExitCode(base::Process process
) {
147 ASSERT_TRUE(process
.IsValid());
149 if (!process
.WaitForExit(&exit_code
))
150 FAIL() << "Process::WaitForExit failed.";
151 ASSERT_EQ(0, exit_code
);
154 // Inheritable process handle used for testing.
155 base::win::ScopedHandle self_
;
160 // TODO(siggi): More testing - test WatcherClient base implementation.
162 TEST_F(WatcherClientTest
, LaunchWatcherSucceeds
) {
163 // We can only use the non-legacy launch method on Windows Vista or better.
164 if (base::win::GetVersion() < base::win::VERSION_VISTA
)
167 WatcherClient
client(GetBaseCommandLineGenerator(NO_LEAK_HANDLE
));
168 ASSERT_FALSE(client
.use_legacy_launch());
170 client
.LaunchWatcher();
172 ASSERT_NO_FATAL_FAILURE(
173 AssertSuccessfulExitCode(client
.process().Duplicate()));
176 TEST_F(WatcherClientTest
, LaunchWatcherLegacyModeSucceeds
) {
177 // Test the XP-compatible legacy launch mode. This is expected to leak
178 // a handle to the child process.
179 WatcherClient
client(GetBaseCommandLineGenerator(LEAK_HANDLE
));
181 // Use the legacy launch mode.
182 client
.set_use_legacy_launch(true);
184 client
.LaunchWatcher();
186 ASSERT_NO_FATAL_FAILURE(
187 AssertSuccessfulExitCode(client
.process().Duplicate()));
190 TEST_F(WatcherClientTest
, LegacyModeDetectedOnXP
) {
191 // This test only works on Windows XP.
192 if (base::win::GetVersion() > base::win::VERSION_XP
)
195 // Test that the client detects the need to use legacy launch mode, and that
196 // it works on Windows XP.
197 WatcherClient
client(GetBaseCommandLineGenerator(LEAK_HANDLE
));
198 ASSERT_TRUE(client
.use_legacy_launch());
200 client
.LaunchWatcher();
202 ASSERT_NO_FATAL_FAILURE(
203 AssertSuccessfulExitCode(client
.process().Duplicate()));
206 } // namespace browser_watcher