| 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());
|
| +}
|
|
|