Index: chrome/app/chrome_watcher_command_line_win_unittest.cc |
diff --git a/chrome/app/chrome_watcher_command_line_win_unittest.cc b/chrome/app/chrome_watcher_command_line_win_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9bdb7c63c15f78b12cbe67187790efd7842ea106 |
--- /dev/null |
+++ b/chrome/app/chrome_watcher_command_line_win_unittest.cc |
@@ -0,0 +1,170 @@ |
+// 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 "chrome/app/chrome_watcher_command_line_win.h" |
+ |
+#include <windows.h> |
+ |
+#include "base/command_line.h" |
+#include "base/files/file_path.h" |
+#include "base/process/process.h" |
+#include "base/process/process_handle.h" |
+#include "base/win/scoped_handle.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace { |
+ |
+base::FilePath ExampleExe() { |
+ static const wchar_t kExampleExe[] = FILE_PATH_LITERAL("example.exe"); |
+ base::FilePath example_exe(kExampleExe); |
+ return example_exe; |
+} |
+ |
+} // namespace |
+ |
+class TestChromeWatcherCommandLineGenerator |
+ : public ChromeWatcherCommandLineGenerator { |
+ public: |
+ TestChromeWatcherCommandLineGenerator() |
+ : ChromeWatcherCommandLineGenerator(ExampleExe()) { |
+ } |
+ |
+ // In the normal case the generator and interpreter are run in separate |
+ // processes. However, since they are both being run in the same process in |
+ // these tests the handle tracker will explode as they both claim ownership of |
+ // the same handle. This function allows the generator handles to be released |
+ // before they are subsequently claimed by an interpreter. |
+ void ReleaseHandlesWithoutClosing() { |
+ on_initialized_event_handle_.Take(); |
+ parent_process_handle_.Take(); |
+ } |
+}; |
+ |
+// A fixture for tests that involve parsed command-lines. Contains utility |
+// functions for standing up a well-configured valid generator. |
+class ChromeWatcherCommandLineTest : public testing::Test { |
+ public: |
+ ChromeWatcherCommandLineTest() |
+ : cmd_line_(base::CommandLine::NO_PROGRAM) { |
+ } |
+ |
+ void GenerateAndInterpretCommandLine() { |
+ process_ = base::Process::OpenWithAccess( |
+ base::GetCurrentProcId(), PROCESS_QUERY_INFORMATION | SYNCHRONIZE); |
+ ASSERT_TRUE(process_.Handle()); |
+ |
+ event_.Set(::CreateEvent(nullptr, FALSE, FALSE, nullptr)); |
+ ASSERT_TRUE(event_.IsValid()); |
+ |
+ // The above handles are duplicated by the generator. |
+ generator_.SetOnInitializedEventHandle(event_.Get()); |
+ generator_.SetParentProcessHandle(process_.Handle()); |
+ |
+ // Expect there to be two inherited handles created by the generator. |
+ std::vector<HANDLE> handles; |
+ generator_.GetInheritedHandles(&handles); |
+ EXPECT_EQ(2U, handles.size()); |
+ |
+ cmd_line_ = generator_.GenerateCommandLine(); |
+ |
+ // In the normal case the generator and interpreter are run in separate |
+ // processes. However, since they are both being run in the same process in |
+ // this test that will lead to both the generator and the interpreter |
+ // claiming ownership of the same handles, as far as the handle tracker is |
+ // concerned. To prevent this the handles are first released from tracking |
+ // by the generator. |
+ generator_.ReleaseHandlesWithoutClosing(); |
+ |
+ interpreted_ = ChromeWatcherCommandLine::InterpretCommandLine(cmd_line_); |
+ EXPECT_TRUE(interpreted_); |
+ } |
+ |
+ base::Process process_; |
+ base::win::ScopedHandle event_; |
+ TestChromeWatcherCommandLineGenerator generator_; |
+ base::CommandLine cmd_line_; |
+ scoped_ptr<ChromeWatcherCommandLine> interpreted_; |
+}; |
+ |
+// The corresponding death test fixture. |
+using ChromeWatcherCommandLineDeathTest = ChromeWatcherCommandLineTest; |
+ |
+TEST(ChromeWatcherCommandLineGeneratorTest, UseOfBadHandlesFails) { |
+ // Handles are always machine word aligned so there is no way these are valid. |
+ HANDLE bad_handle_1 = reinterpret_cast<HANDLE>(0x01FC00B1); |
+ HANDLE bad_handle_2 = reinterpret_cast<HANDLE>(0x01FC00B3); |
+ |
+ // The above handles are duplicated by the generator. |
+ TestChromeWatcherCommandLineGenerator generator; |
+ EXPECT_FALSE(generator.SetOnInitializedEventHandle(bad_handle_1)); |
+ EXPECT_FALSE(generator.SetParentProcessHandle(bad_handle_2)); |
+ |
+ // Expect there to be no inherited handles created by the generator. |
+ std::vector<HANDLE> handles; |
+ generator.GetInheritedHandles(&handles); |
+ EXPECT_TRUE(handles.empty()); |
+} |
+ |
+TEST(ChromeWatcherCommandLineGeneratorTest, BadCommandLineFailsInterpretation) { |
+ // Create an invalid command-line that is missing several fields. |
+ base::CommandLine cmd_line(ExampleExe()); |
+ |
+ // Parse the command line. |
+ auto interpreted = ChromeWatcherCommandLine::InterpretCommandLine(cmd_line); |
+ EXPECT_FALSE(interpreted); |
+} |
+ |
+TEST_F(ChromeWatcherCommandLineDeathTest, HandlesLeftUntakenCausesDeath) { |
+ EXPECT_NO_FATAL_FAILURE(GenerateAndInterpretCommandLine()); |
+ |
+ // Leave the handles in the interpreter and expect it to explode upon |
+ // destruction. |
+ EXPECT_DEATH(interpreted_.reset(), "Handles left untaken."); |
+ |
+ // The above call to the destructor only runs in the context of the death test |
+ // child process. To prevent the parent process from exploding in a similar |
+ // fashion, release the handles so the destructor is happy. |
+ interpreted_->TakeOnInitializedEventHandle().Close(); |
+ interpreted_->TakeParentProcessHandle().Close(); |
+} |
+ |
+TEST_F(ChromeWatcherCommandLineTest, SuccessfulParse) { |
+ EXPECT_NO_FATAL_FAILURE(GenerateAndInterpretCommandLine()); |
+ |
+ EXPECT_EQ(::GetCurrentThreadId(), interpreted_->main_thread_id()); |
+ |
+ // Explicitly take the handles from the interpreter so it doesn't explode. |
+ base::win::ScopedHandle on_init = |
+ interpreted_->TakeOnInitializedEventHandle(); |
+ base::win::ScopedHandle proc = interpreted_->TakeParentProcessHandle(); |
+ EXPECT_TRUE(on_init.IsValid()); |
+ EXPECT_TRUE(proc.IsValid()); |
+} |
+ |
+// TODO(chrisha): Remove this test upon switching to using the new command-line |
+// classes. |
+TEST(OldChromeWatcherCommandLineTest, BasicTest) { |
+ // Ownership of these handles is passed to the ScopedHandles below via |
+ // InterpretChromeWatcherCommandLine(). |
+ base::ProcessHandle current = |
+ ::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, |
+ TRUE, // Inheritable. |
+ ::GetCurrentProcessId()); |
+ ASSERT_NE(nullptr, current); |
+ |
+ HANDLE event = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); |
+ ASSERT_NE(nullptr, event); |
+ DWORD current_thread_id = ::GetCurrentThreadId(); |
+ base::CommandLine cmd_line = GenerateChromeWatcherCommandLine( |
+ ExampleExe(), current, current_thread_id, event); |
+ |
+ base::win::ScopedHandle current_result; |
+ DWORD current_thread_id_result = 0; |
+ base::win::ScopedHandle event_result; |
+ ASSERT_TRUE(InterpretChromeWatcherCommandLine( |
+ cmd_line, ¤t_result, ¤t_thread_id_result, &event_result)); |
+ ASSERT_EQ(current, current_result.Get()); |
+ ASSERT_EQ(current_thread_id, current_thread_id_result); |
+ ASSERT_EQ(event, event_result.Get()); |
+} |