Only send _NET_ACTIVE_WINDOW hint if the chromium window is not already active.
[chromium-blink-merge.git] / components / browser_watcher / window_hang_monitor_win_unittest.cc
blobf884e09025cd1e04669a5140198ae462190d5a4f
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"
7 #include <vector>
9 #include "base/base_switches.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/callback.h"
13 #include "base/callback_helpers.h"
14 #include "base/command_line.h"
15 #include "base/location.h"
16 #include "base/macros.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/process/launch.h"
20 #include "base/process/process.h"
21 #include "base/process/process_handle.h"
22 #include "base/run_loop.h"
23 #include "base/strings/string16.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/synchronization/waitable_event.h"
28 #include "base/test/multiprocess_test.h"
29 #include "base/threading/thread.h"
30 #include "base/win/message_window.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 #include "testing/multiprocess_func_list.h"
34 namespace browser_watcher {
36 namespace {
38 // Simulates a process that never opens a window.
39 MULTIPROCESS_TEST_MAIN(NoWindowChild) {
40 ::Sleep(INFINITE);
41 return 0;
44 // Manages a WindowHangMonitor that lives on a background thread.
45 class HangMonitorThread {
46 public:
47 // Instantiates the background thread.
48 HangMonitorThread()
49 : event_(WindowHangMonitor::WINDOW_NOT_FOUND),
50 event_received_(false, false),
51 thread_("HangMonitorThread") {}
53 ~HangMonitorThread() {
54 if (hang_monitor_.get())
55 DestroyWatcher();
58 // Starts the background thread and the monitor to observe the window named
59 // |window_name| in |process|. Blocks until the monitor has been initialized.
60 bool Start(base::Process process, const base::string16& window_name) {
61 if (!thread_.StartWithOptions(
62 base::Thread::Options(base::MessageLoop::TYPE_UI, 0))) {
63 return false;
66 base::WaitableEvent complete(false, false);
67 if (!thread_.task_runner()->PostTask(
68 FROM_HERE, base::Bind(&HangMonitorThread::StartupOnThread,
69 base::Unretained(this), window_name,
70 base::Passed(process.Pass()),
71 base::Unretained(&complete)))) {
72 return false;
75 complete.Wait();
77 return true;
80 // Returns true if a window event is detected within |timeout|.
81 bool TimedWaitForEvent(base::TimeDelta timeout) {
82 return event_received_.TimedWait(timeout);
85 // Blocks indefinitely for a window event and returns it.
86 WindowHangMonitor::WindowEvent WaitForEvent() {
87 event_received_.Wait();
88 return event_;
91 // Destroys the monitor and stops the background thread. Blocks until the
92 // operation completes.
93 void DestroyWatcher() {
94 thread_.task_runner()->PostTask(
95 FROM_HERE, base::Bind(&HangMonitorThread::ShutdownOnThread,
96 base::Unretained(this)));
97 // This will block until the above-posted task completes.
98 thread_.Stop();
101 private:
102 // Invoked when the monitor signals an event. Unblocks a call to
103 // TimedWaitForEvent or WaitForEvent.
104 void EventCallback(WindowHangMonitor::WindowEvent event) {
105 if (event_received_.IsSignaled())
106 ADD_FAILURE() << "Multiple calls to EventCallback.";
107 event_ = event;
108 event_received_.Signal();
111 // Initializes the WindowHangMonitor to observe the window named |window_name|
112 // in |process|. Signals |complete| when done.
113 void StartupOnThread(const base::string16& window_name,
114 base::Process process,
115 base::WaitableEvent* complete) {
116 hang_monitor_.reset(new WindowHangMonitor(
117 base::TimeDelta::FromMilliseconds(100),
118 base::TimeDelta::FromMilliseconds(100),
119 base::Bind(&HangMonitorThread::EventCallback, base::Unretained(this))));
120 hang_monitor_->Initialize(process.Pass(), window_name);
121 complete->Signal();
124 // Destroys the WindowHangMonitor.
125 void ShutdownOnThread() { hang_monitor_.reset(); }
127 // The detected event. Invalid if |event_received_| has not been signaled.
128 WindowHangMonitor::WindowEvent event_;
129 // Indicates that |event_| has been assigned in response to a callback from
130 // the WindowHangMonitor.
131 base::WaitableEvent event_received_;
132 // The WindowHangMonitor under test.
133 scoped_ptr<WindowHangMonitor> hang_monitor_;
134 // The background thread.
135 base::Thread thread_;
137 DISALLOW_COPY_AND_ASSIGN(HangMonitorThread);
140 class WindowHangMonitorTest : public testing::Test {
141 public:
142 WindowHangMonitorTest()
143 : ping_event_(false, false),
144 pings_(0),
145 window_thread_("WindowHangMonitorTest window_thread") {}
147 void SetUp() override {
148 // Pick a window name unique to this process.
149 window_name_ = base::StringPrintf(L"WindowHanMonitorTest-%d",
150 base::GetCurrentProcId());
151 ASSERT_TRUE(window_thread_.StartWithOptions(
152 base::Thread::Options(base::MessageLoop::TYPE_UI, 0)));
155 void TearDown() override {
156 DeleteMessageWindow();
157 window_thread_.Stop();
160 void CreateMessageWindow() {
161 bool succeeded = false;
162 base::WaitableEvent created(true, false);
163 ASSERT_TRUE(window_thread_.task_runner()->PostTask(
164 FROM_HERE,
165 base::Bind(&WindowHangMonitorTest::CreateMessageWindowInWorkerThread,
166 base::Unretained(this), window_name_, &succeeded,
167 &created)));
168 created.Wait();
169 ASSERT_TRUE(succeeded);
172 void DeleteMessageWindow() {
173 base::WaitableEvent deleted(true, false);
174 window_thread_.task_runner()->PostTask(
175 FROM_HERE,
176 base::Bind(&WindowHangMonitorTest::DeleteMessageWindowInWorkerThread,
177 base::Unretained(this), &deleted));
178 deleted.Wait();
181 void WaitForPing() {
182 while (true) {
184 base::AutoLock auto_lock(ping_lock_);
185 if (pings_) {
186 ping_event_.Reset();
187 --pings_;
188 return;
191 ping_event_.Wait();
195 HangMonitorThread& monitor_thread() { return monitor_thread_; }
197 const base::win::MessageWindow* message_window() const {
198 return message_window_.get();
201 const base::string16& window_name() const { return window_name_; }
203 base::Thread* window_thread() { return &window_thread_; }
205 private:
206 bool MessageCallback(UINT message,
207 WPARAM wparam,
208 LPARAM lparam,
209 LRESULT* result) {
210 EXPECT_EQ(window_thread_.message_loop(), base::MessageLoop::current());
211 if (message == WM_NULL) {
212 base::AutoLock auto_lock(ping_lock_);
213 ++pings_;
214 ping_event_.Signal();
217 return false; // Pass through to DefWindowProc.
220 void CreateMessageWindowInWorkerThread(const base::string16& name,
221 bool* success,
222 base::WaitableEvent* created) {
223 message_window_.reset(new base::win::MessageWindow);
224 *success = message_window_->CreateNamed(
225 base::Bind(&WindowHangMonitorTest::MessageCallback,
226 base::Unretained(this)),
227 name);
228 created->Signal();
231 void DeleteMessageWindowInWorkerThread(base::WaitableEvent* deleted) {
232 message_window_.reset();
233 if (deleted)
234 deleted->Signal();
237 HangMonitorThread monitor_thread_;
238 scoped_ptr<base::win::MessageWindow> message_window_;
239 base::string16 window_name_;
240 base::Lock ping_lock_;
241 base::WaitableEvent ping_event_;
242 size_t pings_;
243 base::Thread window_thread_;
245 DISALLOW_COPY_AND_ASSIGN(WindowHangMonitorTest);
248 } // namespace
250 TEST_F(WindowHangMonitorTest, NoWindow) {
251 base::CommandLine child_command_line =
252 base::GetMultiProcessTestChildBaseCommandLine();
253 child_command_line.AppendSwitchASCII(switches::kTestChildProcess,
254 "NoWindowChild");
255 base::Process process =
256 base::LaunchProcess(child_command_line, base::LaunchOptions());
257 ASSERT_TRUE(process.IsValid());
259 base::ScopedClosureRunner terminate_process_runner(
260 base::Bind(base::IgnoreResult(&base::Process::Terminate),
261 base::Unretained(&process), 1, true));
263 monitor_thread().Start(process.Duplicate(), window_name());
265 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
266 base::TimeDelta::FromMilliseconds(150)));
268 terminate_process_runner.Reset();
270 ASSERT_EQ(WindowHangMonitor::WINDOW_NOT_FOUND,
271 monitor_thread().WaitForEvent());
273 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
274 base::TimeDelta::FromMilliseconds(150)));
277 TEST_F(WindowHangMonitorTest, WindowBeforeWatcher) {
278 CreateMessageWindow();
280 monitor_thread().Start(base::Process::Current(), window_name());
282 WaitForPing();
284 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
285 base::TimeDelta::FromMilliseconds(150)));
288 TEST_F(WindowHangMonitorTest, WindowBeforeDestroy) {
289 CreateMessageWindow();
291 monitor_thread().Start(base::Process::Current(), window_name());
293 WaitForPing();
295 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
296 base::TimeDelta::FromMilliseconds(150)));
298 monitor_thread().DestroyWatcher();
300 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta()));
303 TEST_F(WindowHangMonitorTest, NoWindowBeforeDestroy) {
304 monitor_thread().Start(base::Process::Current(), window_name());
306 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
307 base::TimeDelta::FromMilliseconds(150)));
308 monitor_thread().DestroyWatcher();
310 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta()));
313 TEST_F(WindowHangMonitorTest, WatcherBeforeWindow) {
314 monitor_thread().Start(base::Process::Current(), window_name());
316 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
317 base::TimeDelta::FromMilliseconds(150)));
319 CreateMessageWindow();
321 WaitForPing();
323 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
324 base::TimeDelta::FromMilliseconds(150)));
327 TEST_F(WindowHangMonitorTest, DetectsWindowDisappearance) {
328 CreateMessageWindow();
330 monitor_thread().Start(base::Process::Current(), window_name());
332 WaitForPing();
334 DeleteMessageWindow();
336 ASSERT_EQ(WindowHangMonitor::WINDOW_VANISHED,
337 monitor_thread().WaitForEvent());
339 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
340 base::TimeDelta::FromMilliseconds(150)));
343 TEST_F(WindowHangMonitorTest, DetectsWindowNameChange) {
344 // This test changes the title of the message window as a proxy for what
345 // happens if the window handle is reused for a different purpose. The latter
346 // is impossible to test in a deterministic fashion.
347 CreateMessageWindow();
349 monitor_thread().Start(base::Process::Current(), window_name());
351 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
352 base::TimeDelta::FromMilliseconds(150)));
354 ASSERT_TRUE(::SetWindowText(message_window()->hwnd(), L"Gonsky"));
356 ASSERT_EQ(WindowHangMonitor::WINDOW_VANISHED,
357 monitor_thread().WaitForEvent());
360 TEST_F(WindowHangMonitorTest, DetectsWindowHang) {
361 CreateMessageWindow();
363 monitor_thread().Start(base::Process::Current(), window_name());
365 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
366 base::TimeDelta::FromMilliseconds(150)));
368 // Block the worker thread.
369 base::WaitableEvent hang(true, false);
371 window_thread()->task_runner()->PostTask(
372 FROM_HERE,
373 base::Bind(&base::WaitableEvent::Wait, base::Unretained(&hang)));
375 EXPECT_EQ(WindowHangMonitor::WINDOW_HUNG,
376 monitor_thread().WaitForEvent());
378 // Unblock the worker thread.
379 hang.Signal();
381 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
382 base::TimeDelta::FromMilliseconds(150)));
385 } // namespace browser_watcher