1 // Copyright 2015 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/window_hang_monitor_win.h"
10 #include "base/callback.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/process/process_handle.h"
14 #include "base/run_loop.h"
15 #include "base/strings/string16.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/threading/thread.h"
19 #include "base/win/message_window.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 namespace browser_watcher
{
26 class WindowHangMonitorTest
: public testing::Test
{
28 typedef std::vector
<WindowHangMonitor::WindowEvent
> WindowEventVector
;
30 WindowHangMonitorTest()
31 : monitor_(base::Bind(&WindowHangMonitorTest::OnWindowEvent
,
32 base::Unretained(this))),
33 message_loop_(base::MessageLoop::TYPE_UI
),
36 worker_thread_("HangMan") {}
38 // Callback from the hang detector.
39 void OnWindowEvent(WindowHangMonitor::WindowEvent event
) {
40 // Record the event and terminate the message loop.
41 events_
.push_back(event
);
45 void SetUp() override
{
46 // Pick a window name unique to this process.
47 window_name_
= base::StringPrintf(L
"WindowHanMonitorTest-%d",
48 base::GetCurrentProcId());
49 ASSERT_TRUE(worker_thread_
.StartWithOptions(
50 base::Thread::Options(base::MessageLoop::TYPE_UI
, 0)));
52 // Set relatively short hang detection and ping intervals.
53 monitor_
.SetHangTimeoutForTesting(base::TimeDelta::FromMilliseconds(50));
54 monitor_
.SetPingIntervalForTesting(base::TimeDelta::FromMilliseconds(200));
57 void TearDown() override
{
58 DeleteMessageWindow();
59 worker_thread_
.Stop();
62 void CreateMessageWindow() {
63 bool succeeded
= false;
64 base::WaitableEvent
created(true, false);
65 ASSERT_TRUE(worker_thread_
.task_runner()->PostTask(
67 base::Bind(&WindowHangMonitorTest::CreateMessageWindowInWorkerThread
,
68 base::Unretained(this), window_name_
, &succeeded
,
71 ASSERT_TRUE(succeeded
);
74 void DeleteMessageWindow() {
75 base::WaitableEvent
deleted(true, false);
76 worker_thread_
.task_runner()->PostTask(
78 base::Bind(&WindowHangMonitorTest::DeleteMessageWindowInWorkerThread
,
79 base::Unretained(this), &deleted
));
83 bool MessageCallback(UINT message
,
87 EXPECT_EQ(worker_thread_
.message_loop(), base::MessageLoop::current());
88 if (message
== WM_NULL
)
91 return false; // Pass through to DefWindowProc.
94 void RunMessageLoop() {
95 ASSERT_FALSE(run_loop_
);
104 WindowHangMonitor
* monitor() { return &monitor_
; }
105 const WindowEventVector
& events() const { return events_
; }
106 const base::win::MessageWindow
* message_window() const {
107 return message_window_
.get();
109 size_t pings() const { return pings_
; }
110 const base::string16
& window_name() const { return window_name_
; }
111 base::Thread
* worker_thread() { return &worker_thread_
; }
114 void CreateMessageWindowInWorkerThread(const base::string16
& name
,
116 base::WaitableEvent
* created
) {
117 message_window_
.reset(new base::win::MessageWindow
);
118 *success
= message_window_
->CreateNamed(
119 base::Bind(&WindowHangMonitorTest::MessageCallback
,
120 base::Unretained(this)),
125 void DeleteMessageWindowInWorkerThread(base::WaitableEvent
* deleted
) {
126 message_window_
.reset();
131 WindowHangMonitor monitor_
;
132 WindowEventVector events_
;
134 // Message and run loops for the main thread.
135 base::MessageLoop message_loop_
;
136 base::RunLoop
* run_loop_
;
137 scoped_ptr
<base::win::MessageWindow
> message_window_
;
138 base::string16 window_name_
;
140 base::Thread worker_thread_
;
145 TEST_F(WindowHangMonitorTest
, InitFailsWhenNoWindow
) {
146 ASSERT_FALSE(monitor()->Initialize(window_name()));
147 EXPECT_TRUE(monitor()->IsIdleForTesting());
148 EXPECT_EQ(0, pings());
149 EXPECT_EQ(0, events().size());
152 TEST_F(WindowHangMonitorTest
, InitSucceedsWhenWindow
) {
153 CreateMessageWindow();
155 ASSERT_TRUE(monitor()->Initialize(window_name()));
156 EXPECT_FALSE(monitor()->IsIdleForTesting());
158 // Delete the window to synchronize against any pending message pings.
159 DeleteMessageWindow();
161 EXPECT_EQ(1, pings());
162 EXPECT_EQ(0, events().size());
165 TEST_F(WindowHangMonitorTest
, DetectsWindowDisappearance
) {
166 CreateMessageWindow();
168 EXPECT_TRUE(monitor()->Initialize(window_name()));
169 EXPECT_EQ(0, events().size());
171 DeleteMessageWindow();
175 EXPECT_TRUE(monitor()->IsIdleForTesting());
176 ASSERT_EQ(1, events().size());
177 EXPECT_EQ(WindowHangMonitor::WINDOW_VANISHED
, events()[0]);
180 TEST_F(WindowHangMonitorTest
, DetectsWindowNameChange
) {
181 // This test changes the title of the message window as a proxy for what
182 // happens if the window handle is reused for a different purpose. The latter
183 // is impossible to test in a deterministic fashion.
184 CreateMessageWindow();
186 ASSERT_TRUE(monitor()->Initialize(window_name()));
187 EXPECT_EQ(0, events().size());
189 ASSERT_TRUE(::SetWindowText(message_window()->hwnd(), L
"Gonsky"));
193 EXPECT_TRUE(monitor()->IsIdleForTesting());
194 ASSERT_EQ(1, events().size());
195 EXPECT_EQ(WindowHangMonitor::WINDOW_VANISHED
, events()[0]);
198 TEST_F(WindowHangMonitorTest
, DetectsWindowHang
) {
199 CreateMessageWindow();
201 ASSERT_TRUE(monitor()->Initialize(window_name()));
202 EXPECT_EQ(0, events().size());
204 // Block the worker thread.
205 base::WaitableEvent
hang(true, false);
207 worker_thread()->message_loop_proxy()->PostTask(
209 base::Bind(&base::WaitableEvent::Wait
, base::Unretained(&hang
)));
213 // Unblock the worker thread.
216 EXPECT_TRUE(monitor()->IsIdleForTesting());
217 ASSERT_EQ(1, events().size());
218 EXPECT_EQ(WindowHangMonitor::WINDOW_HUNG
, events()[0]);
221 } // namespace browser_watcher