Chromium Code Reviews| Index: components/browser_watcher/watcher_client_win_unittest.cc |
| diff --git a/components/browser_watcher/watcher_client_win_unittest.cc b/components/browser_watcher/watcher_client_win_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b2cfc8805e4709911e6b6e87b7615ba083a0bf6b |
| --- /dev/null |
| +++ b/components/browser_watcher/watcher_client_win_unittest.cc |
| @@ -0,0 +1,192 @@ |
| +// Copyright (c) 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "components/browser_watcher/watcher_client_win.h" |
| + |
| +#include "base/base_switches.h" |
| +#include "base/command_line.h" |
| +#include "base/logging.h" |
| +#include "base/process/kill.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/strings/stringprintf.h" |
| +#include "base/test/multiprocess_test.h" |
| +#include "base/test/test_reg_util_win.h" |
| +#include "base/win/scoped_handle.h" |
| +#include "base/win/windows_version.h" |
| +#include "components/browser_watcher/watcher_win.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "testing/multiprocess_func_list.h" |
| + |
| +namespace browser_watcher { |
| + |
| +namespace { |
| + |
| +// Command line switches used to communiate to the child test. |
| +const char kParentPid[] = "parent-pid"; |
| +const char kLeakHandle[] = "leak-handle"; |
| +const char kNoLeakHandle[] = "no-leak-handle"; |
| + |
| +bool IsValidParentProcessHandle(base::CommandLine& cmd_line, |
| + const char* switch_name) { |
| + std::string str_handle = |
|
erikwright (departed)
2014/11/18 20:00:06
include <string>
Sigurður Ásgeirsson
2014/11/18 21:18:57
Done.
|
| + cmd_line.GetSwitchValueASCII(switch_name); |
| + |
| + unsigned int_handle = 0; |
| + if (!base::StringToUint(str_handle, &int_handle)) |
| + return false; |
| + |
| + int parent_pid = 0; |
| + if (!base::StringToInt(cmd_line.GetSwitchValueASCII(kParentPid), |
| + &parent_pid)) { |
| + return false; |
| + } |
| + |
| + base::ProcessHandle handle = |
|
erikwright (departed)
2014/11/18 20:00:06
base/process/process_handle.h
Sigurður Ásgeirsson
2014/11/18 21:18:57
Done.
|
| + reinterpret_cast<base::ProcessHandle>(int_handle); |
| + // Verify that we can get the associated process id. |
| + base::ProcessId parent_id = base::GetProcId(handle); |
| + if (parent_id == 0) { |
| + // Unable to get the parent pid - perhaps insufficient permissions. |
| + return false; |
| + } |
| + |
| + // Make sure the handle grants SYNCHRONIZE by waiting on it. |
| + DWORD err = ::WaitForSingleObject(handle, 0); |
| + if (err != WAIT_OBJECT_0 && err != WAIT_TIMEOUT) { |
| + // Unable to wait on the handle - perhaps insufficient permissions. |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +MULTIPROCESS_TEST_MAIN(VerifyParentHandle) { |
| + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| + |
| + // Make sure we got a valid parent process handle from the watcher client. |
| + if (!IsValidParentProcessHandle(*cmd_line, |
| + ExitCodeWatcher::kParenthHandleSwitch)) { |
| + LOG(ERROR) << "Invalid or missing parent-handle."; |
| + return 1; |
| + } |
| + |
| + // If in the legacy mode, we expect this second handle will leak into the |
| + // child process. This mainly serves to verify that the legacy mode is |
| + // getting tested. |
| + if (cmd_line->HasSwitch(kLeakHandle) && |
| + !IsValidParentProcessHandle(*cmd_line, kLeakHandle)) { |
| + LOG(ERROR) << "Parent process handle unexpectedly didn't leak."; |
| + return 1; |
| + } |
| + |
| + // If not in the legacy mode, this second handle should not leak into the |
| + // child process. |
| + if (cmd_line->HasSwitch(kNoLeakHandle) && |
| + IsValidParentProcessHandle(*cmd_line, kLeakHandle)) { |
| + LOG(ERROR) << "Parent process handle unexpectedly leaked."; |
| + return 1; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +class BrowserWatcherClientTest : public base::MultiProcessTest { |
| + public: |
| + virtual void SetUp() { |
| + // Open an inheritable handle on our own process to test handle leakage. |
| + self_.Set(::OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, |
| + TRUE, // Ineritable handle. |
| + base::GetCurrentProcId())); |
| + |
| + ASSERT_TRUE(self_.IsValid()); |
| + } |
| + |
| + enum HandlePolicy { |
| + LEAK_HANDLE, |
| + NO_LEAK_HANDLE |
| + }; |
| + |
| + // Get a base command line to launch back into this test fixture. |
| + base::CommandLine GetBaseCommandLine(HandlePolicy handle_policy) { |
| + base::CommandLine ret = base::GetMultiProcessTestChildBaseCommandLine(); |
| + |
| + ret.AppendSwitchASCII(switches::kTestChildProcess, "VerifyParentHandle"); |
| + ret.AppendSwitchASCII(kParentPid, |
| + base::StringPrintf("%d", base::GetCurrentProcId())); |
| + |
| + switch (handle_policy) { |
| + case LEAK_HANDLE: |
| + ret.AppendSwitchASCII(kLeakHandle, |
| + base::StringPrintf("%d", self_.Get())); |
| + break; |
| + |
| + case NO_LEAK_HANDLE: |
| + ret.AppendSwitchASCII(kNoLeakHandle, |
| + base::StringPrintf("%d", self_.Get())); |
| + break; |
| + |
| + default: |
| + EXPECT_FALSE("Impossible handle_policy"); |
|
erikwright (departed)
2014/11/18 20:00:05
ADD_FAILURE() << "..."
Sigurður Ásgeirsson
2014/11/18 21:18:57
Done.
|
| + } |
| + |
| + return ret; |
| + } |
| + |
| + void AssertSuccessfulExitCode(base::ProcessHandle handle) { |
| + ASSERT_NE(base::kNullProcessHandle, handle); |
| + |
| + // Duplicate the process handle to work around the fact that |
| + // WaitForExitCode closes it(!!!). |
| + base::ProcessHandle dupe = NULL; |
| + ASSERT_TRUE(::DuplicateHandle(base::GetCurrentProcessHandle(), |
| + handle, |
| + base::GetCurrentProcessHandle(), |
| + &dupe, |
| + SYNCHRONIZE | PROCESS_QUERY_INFORMATION, |
| + FALSE, |
| + 0)); |
| + ASSERT_NE(base::kNullProcessHandle, dupe); |
| + int exit_code = 0; |
| + if (!base::WaitForExitCode(dupe, &exit_code)) { |
| + base::CloseProcessHandle(dupe); |
| + FAIL() << "WaitForExitCode failed."; |
| + } |
| + ASSERT_EQ(0, exit_code); |
| + } |
| + |
| + // Inheritable event handle used for testing. |
|
erikwright (departed)
2014/11/18 20:00:05
"event" ?
Sigurður Ásgeirsson
2014/11/18 21:18:57
Done.
|
| + base::win::ScopedHandle self_; |
| +}; |
| + |
| +} // namespace |
| + |
| +// TODO(siggi): More testing - test WatcherClient base implementation. |
| + |
| +TEST_F(BrowserWatcherClientTest, LaunchWatcherSucceeds) { |
| + // We can only use the non-legacy launch method on Windows Vista or better. |
| + if (base::win::GetVersion() < base::win::VERSION_VISTA) |
| + return; |
| + |
| + WatcherClient client(GetBaseCommandLine(NO_LEAK_HANDLE)); |
| + ASSERT_FALSE(client.use_legacy_launch()); |
| + |
| + client.LaunchWatcher(); |
| + |
| + ASSERT_NO_FATAL_FAILURE(AssertSuccessfulExitCode(client.process())); |
| +} |
| + |
| +TEST_F(BrowserWatcherClientTest, LaunchWatcherLegacyModeSucceeds) { |
|
erikwright (departed)
2014/11/18 20:00:06
This test uses a hack to verify that the XP-specif
Sigurður Ásgeirsson
2014/11/18 21:18:57
Good idea!
|
| + // Test the XP-compatible legacy launch mode. This is expected to leak |
| + // a handle to the child process. |
| + WatcherClient client(GetBaseCommandLine(LEAK_HANDLE)); |
| + |
| + // USe the legacy launch mode. |
|
erikwright (departed)
2014/11/18 20:00:05
USe -> Use
Sigurður Ásgeirsson
2014/11/18 21:18:57
Done.
|
| + client.set_use_legacy_launch(true); |
| + |
| + client.LaunchWatcher(); |
| + |
| + ASSERT_NO_FATAL_FAILURE(AssertSuccessfulExitCode(client.process())); |
| +} |
| + |
| +} // namespace browser_watcher |