diff options
author | pmonette <pmonette@chromium.org> | 2016-01-29 14:24:37 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-01-29 22:26:03 +0000 |
commit | 3955a4ff6fb5b66408cf3748c9dcd4bd22885c80 (patch) | |
tree | 04eef123c924537220d796372623094903005382 /components/browser_watcher | |
parent | f1ca4764a94823a92c49a0b98c9f5cc9681894b9 (diff) | |
download | chromium_src-3955a4ff6fb5b66408cf3748c9dcd4bd22885c80.zip chromium_src-3955a4ff6fb5b66408cf3748c9dcd4bd22885c80.tar.gz chromium_src-3955a4ff6fb5b66408cf3748c9dcd4bd22885c80.tar.bz2 |
Added an integration test for kasko hang reports
Review URL: https://codereview.chromium.org/1543803005
Cr-Commit-Position: refs/heads/master@{#372442}
Diffstat (limited to 'components/browser_watcher')
3 files changed, 346 insertions, 242 deletions
diff --git a/components/browser_watcher/window_hang_monitor_win.cc b/components/browser_watcher/window_hang_monitor_win.cc index 964a6f7..da0eff1 100644 --- a/components/browser_watcher/window_hang_monitor_win.cc +++ b/components/browser_watcher/window_hang_monitor_win.cc @@ -4,22 +4,48 @@ #include "components/browser_watcher/window_hang_monitor_win.h" #include "base/callback.h" +#include "base/files/file_util.h" #include "base/location.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" #include "base/win/message_window.h" namespace browser_watcher { namespace { -HWND FindNamedWindowForProcess(const base::string16 name, base::ProcessId pid) { - HWND candidate = base::win::MessageWindow::FindWindow(name); - if (candidate) { +// Returns true if the class name for |window| equals |str|. +bool WindowClassNameEqualsString(HWND window, base::StringPiece16 str) { + wchar_t class_name[MAX_PATH]; + int str_length = ::GetClassName(window, class_name, MAX_PATH); + return str_length && str.compare(class_name) == 0; +} + +// Returns true if the window text is an existing directory. Ensures that +// |window| is the right Chrome message window to ping. This could be improved +// by testing for a valid profile in the directory. +bool WindowNameIsExistingDirectory(HWND window) { + base::string16 window_name; + int str_length = ::GetWindowText( + window, base::WriteInto(&window_name, MAX_PATH), MAX_PATH); + window_name.resize(str_length); + return base::DirectoryExists(base::FilePath(window_name)); +} + +// Returns the Chrome message window handle for the specified |pid| or nullptr +// if not found. +HWND FindChromeMessageWindow(base::ProcessId pid) { + HWND candidate = ::FindWindowEx(HWND_MESSAGE, nullptr, nullptr, nullptr); + while (candidate) { DWORD actual_process_id = 0; ::GetWindowThreadProcessId(candidate, &actual_process_id); - if (actual_process_id == pid) + if (WindowClassNameEqualsString(candidate, L"Chrome_MessageWindow") && + WindowNameIsExistingDirectory(candidate) && actual_process_id == pid) { return candidate; + } + candidate = ::GetNextWindow(candidate, GW_HWNDNEXT); } return nullptr; } @@ -45,9 +71,7 @@ WindowHangMonitor::~WindowHangMonitor() { } } -void WindowHangMonitor::Initialize(base::Process process, - const base::string16& window_name) { - window_name_ = window_name; +void WindowHangMonitor::Initialize(base::Process process) { window_process_ = process.Pass(); timer_.SetTaskRunner(base::MessageLoop::current()->task_runner()); @@ -69,7 +93,7 @@ void WindowHangMonitor::PollForWindow() { return; } - HWND hwnd = FindNamedWindowForProcess(window_name_, window_process_.Pid()); + HWND hwnd = FindChromeMessageWindow(window_process_.Pid()); if (hwnd) { // Sends a ping and schedules a timeout task. Upon receiving a ping response // further pings will be scheduled ad infinitum. Will signal any failure now @@ -121,7 +145,6 @@ void WindowHangMonitor::SendPing(HWND hwnd) { void WindowHangMonitor::OnHangTimeout(HWND hwnd) { DCHECK(window_process_.IsValid()); - if (outstanding_ping_) { // The ping is still outstanding, the window is hung or has vanished. // Orphan the outstanding ping. If the callback arrives late, it will @@ -129,8 +152,7 @@ void WindowHangMonitor::OnHangTimeout(HWND hwnd) { outstanding_ping_->monitor = NULL; outstanding_ping_ = NULL; - if (hwnd != - FindNamedWindowForProcess(window_name_, window_process_.Pid())) { + if (hwnd != FindChromeMessageWindow(window_process_.Pid())) { // The window vanished. callback_.Run(WINDOW_VANISHED); } else { @@ -147,6 +169,7 @@ void WindowHangMonitor::OnHangTimeout(HWND hwnd) { void WindowHangMonitor::OnRetryTimeout() { DCHECK(window_process_.IsValid()); + DCHECK(window_process_.IsValid()); DCHECK(!outstanding_ping_); // We can't simply hold onto the previously located HWND due to potential // aliasing. @@ -155,11 +178,12 @@ void WindowHangMonitor::OnRetryTimeout() { // process. // 2. The window handle might have been re-assigned to a different process // at any point after we found it. - HWND hwnd = FindNamedWindowForProcess(window_name_, window_process_.Pid()); - if (hwnd) + HWND hwnd = FindChromeMessageWindow(window_process_.Pid()); + if (hwnd) { SendPing(hwnd); - else + } else { callback_.Run(WINDOW_VANISHED); + } } } // namespace browser_watcher diff --git a/components/browser_watcher/window_hang_monitor_win.h b/components/browser_watcher/window_hang_monitor_win.h index 4c515d7..5f2b415 100644 --- a/components/browser_watcher/window_hang_monitor_win.h +++ b/components/browser_watcher/window_hang_monitor_win.h @@ -43,9 +43,9 @@ class WindowHangMonitor { const WindowEventCallback& callback); ~WindowHangMonitor(); - // Initializes the watcher to monitor a window named |window_name| and owned + // Initializes the watcher to monitor the Chrome message window owned // by |process|. May be invoked prior to the appearance of the window. - void Initialize(base::Process process, const base::string16& window_name); + void Initialize(base::Process process); private: struct OutstandingPing { diff --git a/components/browser_watcher/window_hang_monitor_win_unittest.cc b/components/browser_watcher/window_hang_monitor_win_unittest.cc index 0a2f414..8ff4a8e 100644 --- a/components/browser_watcher/window_hang_monitor_win_unittest.cc +++ b/components/browser_watcher/window_hang_monitor_win_unittest.cc @@ -4,32 +4,19 @@ #include "components/browser_watcher/window_hang_monitor_win.h" -#include <stddef.h> - -#include <vector> - +#include "base/base_paths.h" #include "base/base_switches.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/callback_helpers.h" #include "base/command_line.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" +#include "base/path_service.h" #include "base/process/launch.h" #include "base/process/process.h" -#include "base/process/process_handle.h" -#include "base/run_loop.h" -#include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/test/multiprocess_test.h" #include "base/threading/thread.h" #include "base/win/message_window.h" +#include "base/win/scoped_handle.h" +#include "base/win/win_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/multiprocess_func_list.h" @@ -37,9 +24,209 @@ namespace browser_watcher { namespace { -// Simulates a process that never opens a window. -MULTIPROCESS_TEST_MAIN(NoWindowChild) { - ::Sleep(INFINITE); +const char kChildReadPipeSwitch[] = "child_read_pipe"; +const char kChildWritePipeSwitch[] = "child_write_pipe"; + +// Signals used for IPC between the monitor process and the monitor. +enum IPCSignal { + IPC_SIGNAL_INVALID, + IPC_SIGNAL_READY, + IPC_SIGNAL_TERMINATE_PROCESS, + IPC_SIGNAL_CREATE_MESSAGE_WINDOW, + IPC_SIGNAL_DELETE_MESSAGE_WINDOW, + IPC_SIGNAL_HANG_MESSAGE_WINDOW, +}; + +// Sends |ipc_signal| through the |write_pipe|. +bool SendPipeSignal(HANDLE write_pipe, IPCSignal ipc_signal) { + DWORD bytes_written = 0; + if (!WriteFile(write_pipe, &ipc_signal, sizeof(ipc_signal), &bytes_written, + nullptr)) + return false; + + return bytes_written == sizeof(ipc_signal); +} + +// Blocks on |read_pipe| until a signal is received into |ipc_signal|. +bool WaitForPipeSignal(HANDLE read_pipe, IPCSignal* ipc_signal) { + CHECK(ipc_signal); + DWORD bytes_read = 0; + if (!ReadFile(read_pipe, ipc_signal, sizeof(*ipc_signal), &bytes_read, + nullptr)) + return false; + + return bytes_read == sizeof(*ipc_signal); +} + +// Blocks on |read_pipe| until a signal is received and returns true if it +// matches |expected_ipc_signal|. +bool WaitForSpecificPipeSignal(HANDLE read_pipe, + IPCSignal expected_ipc_signal) { + IPCSignal received_signal = IPC_SIGNAL_INVALID; + return WaitForPipeSignal(read_pipe, &received_signal) && + received_signal == expected_ipc_signal; +} + +// Appends |handle| as a command line switch. +void AppendSwitchHandle(base::CommandLine* command_line, + std::string switch_name, + HANDLE handle) { + command_line->AppendSwitchASCII( + switch_name, base::UintToString(base::win::HandleToUint32(handle))); +} + +// Retrieves the |handle| associated to |switch_name| from the command line. +HANDLE GetSwitchValueHandle(base::CommandLine* command_line, + std::string switch_name) { + std::string switch_string = command_line->GetSwitchValueASCII(switch_name); + unsigned int switch_uint = 0; + if (switch_string.empty() || + !base::StringToUint(switch_string, &switch_uint)) { + DLOG(ERROR) << "Missing or invalid " << switch_name << " argument."; + return nullptr; + } + return reinterpret_cast<HANDLE>(switch_uint); +} + +// An instance of this class lives in the monitored process and receives signals +// and executes their associated function. +class MonitoredProcessClient { + public: + MonitoredProcessClient() + : message_window_thread_("Message window thread"), + hang_event_(true, false) { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + + read_pipe_.Set(GetSwitchValueHandle(command_line, kChildReadPipeSwitch)); + write_pipe_.Set(GetSwitchValueHandle(command_line, kChildWritePipeSwitch)); + } + + ~MonitoredProcessClient() { + if (message_window_thread_.IsRunning()) { + DeleteMessageWindow(); + } + } + + void RunEventLoop() { + bool running = true; + IPCSignal ipc_signal = IPC_SIGNAL_INVALID; + while (running) { + CHECK(WaitForPipeSignal(read_pipe_.Get(), &ipc_signal)); + switch (ipc_signal) { + // The parent process should never send those. + case IPC_SIGNAL_INVALID: + case IPC_SIGNAL_READY: + CHECK(false); + break; + case IPC_SIGNAL_TERMINATE_PROCESS: + running = false; + break; + case IPC_SIGNAL_CREATE_MESSAGE_WINDOW: + CreateMessageWindow(); + break; + case IPC_SIGNAL_DELETE_MESSAGE_WINDOW: + DeleteMessageWindow(); + break; + case IPC_SIGNAL_HANG_MESSAGE_WINDOW: + HangMessageWindow(); + break; + } + SendSignalToParent(IPC_SIGNAL_READY); + } + } + + // Creates a thread then creates the message window on it. + void CreateMessageWindow() { + ASSERT_TRUE(message_window_thread_.StartWithOptions( + base::Thread::Options(base::MessageLoop::TYPE_UI, 0))); + + bool succeeded = false; + base::WaitableEvent created(true, false); + ASSERT_TRUE(message_window_thread_.task_runner()->PostTask( + FROM_HERE, + base::Bind(&MonitoredProcessClient::CreateMessageWindowInWorkerThread, + base::Unretained(this), &succeeded, &created))); + created.Wait(); + ASSERT_TRUE(succeeded); + } + + // Creates a thread then creates the message window on it. + void HangMessageWindow() { + message_window_thread_.task_runner()->PostTask( + FROM_HERE, + base::Bind(&base::WaitableEvent::Wait, base::Unretained(&hang_event_))); + } + + bool SendSignalToParent(IPCSignal ipc_signal) { + return SendPipeSignal(write_pipe_.Get(), ipc_signal); + } + + private: + bool EmptyMessageCallback(UINT message, + WPARAM wparam, + LPARAM lparam, + LRESULT* result) { + EXPECT_EQ(message_window_thread_.message_loop(), + base::MessageLoop::current()); + return false; // Pass through to DefWindowProc. + } + + void CreateMessageWindowInWorkerThread(bool* success, + base::WaitableEvent* created) { + CHECK(created); + + // As an alternative to checking if the name of the message window is the + // user data directory, the hang watcher verifies that the window name is an + // existing directory. DIR_CURRENT is used to meet this constraint. + base::FilePath existing_dir; + CHECK(PathService::Get(base::DIR_CURRENT, &existing_dir)); + + message_window_.reset(new base::win::MessageWindow); + *success = message_window_->CreateNamed( + base::Bind(&MonitoredProcessClient::EmptyMessageCallback, + base::Unretained(this)), + existing_dir.value().c_str()); + created->Signal(); + } + + void DeleteMessageWindow() { + base::WaitableEvent deleted(true, false); + message_window_thread_.task_runner()->PostTask( + FROM_HERE, + base::Bind(&MonitoredProcessClient::DeleteMessageWindowInWorkerThread, + base::Unretained(this), &deleted)); + deleted.Wait(); + + message_window_thread_.Stop(); + } + + void DeleteMessageWindowInWorkerThread(base::WaitableEvent* deleted) { + CHECK(deleted); + message_window_.reset(); + deleted->Signal(); + } + + // The thread that holds the message window. + base::Thread message_window_thread_; + scoped_ptr<base::win::MessageWindow> message_window_; + + // Event used to hang the message window. + base::WaitableEvent hang_event_; + + // Anonymous pipe handles for IPC with the parent process. + base::win::ScopedHandle read_pipe_; + base::win::ScopedHandle write_pipe_; + + DISALLOW_COPY_AND_ASSIGN(MonitoredProcessClient); +}; + +// The monitored process main function. +MULTIPROCESS_TEST_MAIN(MonitoredProcess) { + MonitoredProcessClient monitored_process_client; + CHECK(monitored_process_client.SendSignalToParent(IPC_SIGNAL_READY)); + + monitored_process_client.RunEventLoop(); + return 0; } @@ -50,16 +237,16 @@ class HangMonitorThread { HangMonitorThread() : event_(WindowHangMonitor::WINDOW_NOT_FOUND), event_received_(false, false), - thread_("HangMonitorThread") {} + thread_("Hang monitor thread") {} ~HangMonitorThread() { if (hang_monitor_.get()) DestroyWatcher(); } - // Starts the background thread and the monitor to observe the window named - // |window_name| in |process|. Blocks until the monitor has been initialized. - bool Start(base::Process process, const base::string16& window_name) { + // Starts the background thread and the monitor to observe Chrome message + // window for |process|. Blocks until the monitor has been initialized. + bool Start(base::Process process) { if (!thread_.StartWithOptions( base::Thread::Options(base::MessageLoop::TYPE_UI, 0))) { return false; @@ -67,10 +254,10 @@ class HangMonitorThread { base::WaitableEvent complete(false, false); if (!thread_.task_runner()->PostTask( - FROM_HERE, base::Bind(&HangMonitorThread::StartupOnThread, - base::Unretained(this), window_name, - base::Passed(process.Pass()), - base::Unretained(&complete)))) { + FROM_HERE, + base::Bind(&HangMonitorThread::StartupOnThread, + base::Unretained(this), base::Passed(std::move(process)), + base::Unretained(&complete)))) { return false; } @@ -90,6 +277,7 @@ class HangMonitorThread { return event_; } + private: // Destroys the monitor and stops the background thread. Blocks until the // operation completes. void DestroyWatcher() { @@ -100,7 +288,6 @@ class HangMonitorThread { thread_.Stop(); } - private: // Invoked when the monitor signals an event. Unblocks a call to // TimedWaitForEvent or WaitForEvent. void EventCallback(WindowHangMonitor::WindowEvent event) { @@ -110,16 +297,14 @@ class HangMonitorThread { event_received_.Signal(); } - // Initializes the WindowHangMonitor to observe the window named |window_name| - // in |process|. Signals |complete| when done. - void StartupOnThread(const base::string16& window_name, - base::Process process, - base::WaitableEvent* complete) { + // Initializes the WindowHangMonitor to observe the Chrome message window for + // |process|. Signals |complete| when done. + void StartupOnThread(base::Process process, base::WaitableEvent* complete) { hang_monitor_.reset(new WindowHangMonitor( base::TimeDelta::FromMilliseconds(100), base::TimeDelta::FromMilliseconds(100), base::Bind(&HangMonitorThread::EventCallback, base::Unretained(this)))); - hang_monitor_->Initialize(process.Pass(), window_name); + hang_monitor_->Initialize(std::move(process)); complete->Signal(); } @@ -141,247 +326,142 @@ class HangMonitorThread { class WindowHangMonitorTest : public testing::Test { public: - WindowHangMonitorTest() - : ping_event_(false, false), - pings_(0), - window_thread_("WindowHangMonitorTest window_thread") {} - - void SetUp() override { - // Pick a window name unique to this process. - window_name_ = base::StringPrintf(L"WindowHanMonitorTest-%d", - base::GetCurrentProcId()); - ASSERT_TRUE(window_thread_.StartWithOptions( - base::Thread::Options(base::MessageLoop::TYPE_UI, 0))); - } + WindowHangMonitorTest() {} - void TearDown() override { - DeleteMessageWindow(); - window_thread_.Stop(); + ~WindowHangMonitorTest() { + // Close process if running. + monitored_process_.Terminate(1, false); } - void CreateMessageWindow() { - bool succeeded = false; - base::WaitableEvent created(true, false); - ASSERT_TRUE(window_thread_.task_runner()->PostTask( - FROM_HERE, - base::Bind(&WindowHangMonitorTest::CreateMessageWindowInWorkerThread, - base::Unretained(this), window_name_, &succeeded, - &created))); - created.Wait(); - ASSERT_TRUE(succeeded); - } + // Starts a child process that will be monitored. Handles to anonymous pipes + // are passed to the command line to provide a way to communicate with the + // child process. This function blocks until IPC_SIGNAL_READY is received. + bool StartMonitoredProcess() { + HANDLE child_read_pipe = nullptr; + HANDLE child_write_pipe = nullptr; + if (!CreatePipes(&child_read_pipe, &child_write_pipe)) + return false; - void DeleteMessageWindow() { - base::WaitableEvent deleted(true, false); - window_thread_.task_runner()->PostTask( - FROM_HERE, - base::Bind(&WindowHangMonitorTest::DeleteMessageWindowInWorkerThread, - base::Unretained(this), &deleted)); - deleted.Wait(); + base::CommandLine command_line = + base::GetMultiProcessTestChildBaseCommandLine(); + command_line.AppendSwitchASCII(switches::kTestChildProcess, + "MonitoredProcess"); + + AppendSwitchHandle(&command_line, kChildReadPipeSwitch, child_read_pipe); + AppendSwitchHandle(&command_line, kChildWritePipeSwitch, child_write_pipe); + + base::LaunchOptions options = {}; + options.inherit_handles = true; + monitored_process_ = base::LaunchProcess(command_line, options); + if (!monitored_process_.IsValid()) + return false; + + return WaitForSignal(IPC_SIGNAL_READY); } - void WaitForPing() { - while (true) { - { - base::AutoLock auto_lock(ping_lock_); - if (pings_) { - ping_event_.Reset(); - --pings_; - return; - } - } - ping_event_.Wait(); - } + void StartHangMonitor() { + monitor_thread_.Start(monitored_process_.Duplicate()); } - HangMonitorThread& monitor_thread() { return monitor_thread_; } + // Sends the |ipc_signal| to the child process and wait for a IPC_SIGNAL_READY + // response. + bool SendSignal(IPCSignal ipc_signal) { + if (!SendPipeSignal(write_pipe_.Get(), ipc_signal)) + return false; - const base::win::MessageWindow* message_window() const { - return message_window_.get(); + return WaitForSignal(IPC_SIGNAL_READY); } - const base::string16& window_name() const { return window_name_; } + // Blocks until |ipc_signal| is received from the child process. + bool WaitForSignal(IPCSignal ipc_signal) { + return WaitForSpecificPipeSignal(read_pipe_.Get(), ipc_signal); + } - base::Thread* window_thread() { return &window_thread_; } + HangMonitorThread& monitor_thread() { return monitor_thread_; } private: - bool MessageCallback(UINT message, - WPARAM wparam, - LPARAM lparam, - LRESULT* result) { - EXPECT_EQ(window_thread_.message_loop(), base::MessageLoop::current()); - if (message == WM_NULL) { - base::AutoLock auto_lock(ping_lock_); - ++pings_; - ping_event_.Signal(); + // Creates pipes for IPC with the child process. + bool CreatePipes(HANDLE* child_read_pipe, HANDLE* child_write_pipe) { + CHECK(child_read_pipe); + CHECK(child_write_pipe); + SECURITY_ATTRIBUTES security_attributes = { + sizeof(SECURITY_ATTRIBUTES), nullptr, true /* inherit handles */}; + + HANDLE parent_read_pipe = nullptr; + if (!CreatePipe(&parent_read_pipe, child_write_pipe, &security_attributes, + 0)) { + return false; } + read_pipe_.Set(parent_read_pipe); - return false; // Pass through to DefWindowProc. - } - - void CreateMessageWindowInWorkerThread(const base::string16& name, - bool* success, - base::WaitableEvent* created) { - message_window_.reset(new base::win::MessageWindow); - *success = message_window_->CreateNamed( - base::Bind(&WindowHangMonitorTest::MessageCallback, - base::Unretained(this)), - name); - created->Signal(); - } - - void DeleteMessageWindowInWorkerThread(base::WaitableEvent* deleted) { - message_window_.reset(); - if (deleted) - deleted->Signal(); + HANDLE parent_write_pipe = nullptr; + if (!CreatePipe(child_read_pipe, &parent_write_pipe, &security_attributes, + 0)) { + return false; + } + write_pipe_.Set(parent_write_pipe); + return true; } + // The thread that monitors the child process. HangMonitorThread monitor_thread_; - scoped_ptr<base::win::MessageWindow> message_window_; - base::string16 window_name_; - base::Lock ping_lock_; - base::WaitableEvent ping_event_; - size_t pings_; - base::Thread window_thread_; + // The process that is monitored. + base::Process monitored_process_; + + // Anonymous pipe handles for IPC with the monitored process. + base::win::ScopedHandle read_pipe_; + base::win::ScopedHandle write_pipe_; DISALLOW_COPY_AND_ASSIGN(WindowHangMonitorTest); }; } // namespace -TEST_F(WindowHangMonitorTest, NoWindow) { - base::CommandLine child_command_line = - base::GetMultiProcessTestChildBaseCommandLine(); - child_command_line.AppendSwitchASCII(switches::kTestChildProcess, - "NoWindowChild"); - base::Process process = - base::LaunchProcess(child_command_line, base::LaunchOptions()); - ASSERT_TRUE(process.IsValid()); +TEST_F(WindowHangMonitorTest, WindowNotFound) { + ASSERT_TRUE(StartMonitoredProcess()); - base::ScopedClosureRunner terminate_process_runner( - base::Bind(base::IgnoreResult(&base::Process::Terminate), - base::Unretained(&process), 1, true)); + StartHangMonitor(); - monitor_thread().Start(process.Duplicate(), window_name()); + ASSERT_TRUE(SendSignal(IPC_SIGNAL_TERMINATE_PROCESS)); - ASSERT_FALSE(monitor_thread().TimedWaitForEvent( - base::TimeDelta::FromMilliseconds(150))); - - terminate_process_runner.Reset(); - - ASSERT_EQ(WindowHangMonitor::WINDOW_NOT_FOUND, + EXPECT_EQ(WindowHangMonitor::WINDOW_NOT_FOUND, monitor_thread().WaitForEvent()); - - ASSERT_FALSE(monitor_thread().TimedWaitForEvent( - base::TimeDelta::FromMilliseconds(150))); -} - -TEST_F(WindowHangMonitorTest, WindowBeforeWatcher) { - CreateMessageWindow(); - - monitor_thread().Start(base::Process::Current(), window_name()); - - WaitForPing(); - - ASSERT_FALSE(monitor_thread().TimedWaitForEvent( - base::TimeDelta::FromMilliseconds(150))); -} - -TEST_F(WindowHangMonitorTest, WindowBeforeDestroy) { - CreateMessageWindow(); - - monitor_thread().Start(base::Process::Current(), window_name()); - - WaitForPing(); - - ASSERT_FALSE(monitor_thread().TimedWaitForEvent( - base::TimeDelta::FromMilliseconds(150))); - - monitor_thread().DestroyWatcher(); - - ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta())); -} - -TEST_F(WindowHangMonitorTest, NoWindowBeforeDestroy) { - monitor_thread().Start(base::Process::Current(), window_name()); - - ASSERT_FALSE(monitor_thread().TimedWaitForEvent( - base::TimeDelta::FromMilliseconds(150))); - monitor_thread().DestroyWatcher(); - - ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta())); } -TEST_F(WindowHangMonitorTest, WatcherBeforeWindow) { - monitor_thread().Start(base::Process::Current(), window_name()); - - ASSERT_FALSE(monitor_thread().TimedWaitForEvent( - base::TimeDelta::FromMilliseconds(150))); +TEST_F(WindowHangMonitorTest, WindowVanished) { + ASSERT_TRUE(StartMonitoredProcess()); - CreateMessageWindow(); + ASSERT_TRUE(SendSignal(IPC_SIGNAL_CREATE_MESSAGE_WINDOW)); - WaitForPing(); + StartHangMonitor(); ASSERT_FALSE(monitor_thread().TimedWaitForEvent( - base::TimeDelta::FromMilliseconds(150))); -} - -TEST_F(WindowHangMonitorTest, DetectsWindowDisappearance) { - CreateMessageWindow(); - - monitor_thread().Start(base::Process::Current(), window_name()); + base::TimeDelta::FromMilliseconds(250))); - WaitForPing(); + ASSERT_TRUE(SendSignal(IPC_SIGNAL_DELETE_MESSAGE_WINDOW)); - DeleteMessageWindow(); - - ASSERT_EQ(WindowHangMonitor::WINDOW_VANISHED, + EXPECT_EQ(WindowHangMonitor::WINDOW_VANISHED, monitor_thread().WaitForEvent()); - ASSERT_FALSE(monitor_thread().TimedWaitForEvent( - base::TimeDelta::FromMilliseconds(150))); + ASSERT_TRUE(SendSignal(IPC_SIGNAL_TERMINATE_PROCESS)); } -TEST_F(WindowHangMonitorTest, DetectsWindowNameChange) { - // This test changes the title of the message window as a proxy for what - // happens if the window handle is reused for a different purpose. The latter - // is impossible to test in a deterministic fashion. - CreateMessageWindow(); +TEST_F(WindowHangMonitorTest, WindowHang) { + ASSERT_TRUE(StartMonitoredProcess()); - monitor_thread().Start(base::Process::Current(), window_name()); + ASSERT_TRUE(SendSignal(IPC_SIGNAL_CREATE_MESSAGE_WINDOW)); - ASSERT_FALSE(monitor_thread().TimedWaitForEvent( - base::TimeDelta::FromMilliseconds(150))); - - ASSERT_TRUE(::SetWindowText(message_window()->hwnd(), L"Gonsky")); - - ASSERT_EQ(WindowHangMonitor::WINDOW_VANISHED, - monitor_thread().WaitForEvent()); -} - -TEST_F(WindowHangMonitorTest, DetectsWindowHang) { - CreateMessageWindow(); - - monitor_thread().Start(base::Process::Current(), window_name()); + StartHangMonitor(); ASSERT_FALSE(monitor_thread().TimedWaitForEvent( - base::TimeDelta::FromMilliseconds(150))); - - // Block the worker thread. - base::WaitableEvent hang(true, false); + base::TimeDelta::FromMilliseconds(250))); - window_thread()->task_runner()->PostTask( - FROM_HERE, - base::Bind(&base::WaitableEvent::Wait, base::Unretained(&hang))); + ASSERT_TRUE(SendSignal(IPC_SIGNAL_HANG_MESSAGE_WINDOW)); EXPECT_EQ(WindowHangMonitor::WINDOW_HUNG, monitor_thread().WaitForEvent()); - // Unblock the worker thread. - hang.Signal(); - - ASSERT_FALSE(monitor_thread().TimedWaitForEvent( - base::TimeDelta::FromMilliseconds(150))); + ASSERT_TRUE(SendSignal(IPC_SIGNAL_TERMINATE_PROCESS)); } } // namespace browser_watcher |