Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1162)

Side by Side Diff: components/browser_watcher/window_hang_monitor_win_unittest.cc

Issue 1543803005: Added an integration test for kasko hang reports (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Using components for integration test. Rewrote unittests Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/browser_watcher/window_hang_monitor_win.h" 5 #include "components/browser_watcher/window_hang_monitor_win.h"
6 6
7 #include <stddef.h> 7 #include "base/base_paths.h"
8
9 #include <vector>
10
11 #include "base/base_switches.h" 8 #include "base/base_switches.h"
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/callback_helpers.h"
16 #include "base/command_line.h" 9 #include "base/command_line.h"
17 #include "base/location.h" 10 #include "base/path_service.h"
18 #include "base/macros.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/message_loop/message_loop.h"
21 #include "base/process/launch.h" 11 #include "base/process/launch.h"
22 #include "base/process/process.h" 12 #include "base/process/process.h"
23 #include "base/process/process_handle.h"
24 #include "base/run_loop.h"
25 #include "base/strings/string16.h"
26 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/synchronization/waitable_event.h" 14 #include "base/synchronization/waitable_event.h"
30 #include "base/test/multiprocess_test.h" 15 #include "base/test/multiprocess_test.h"
31 #include "base/threading/thread.h" 16 #include "base/threading/thread.h"
32 #include "base/win/message_window.h" 17 #include "base/win/message_window.h"
18 #include "base/win/win_util.h"
33 #include "testing/gtest/include/gtest/gtest.h" 19 #include "testing/gtest/include/gtest/gtest.h"
34 #include "testing/multiprocess_func_list.h" 20 #include "testing/multiprocess_func_list.h"
35 21
36 namespace browser_watcher { 22 namespace browser_watcher {
37 23
38 namespace { 24 namespace {
39 25
40 // Simulates a process that never opens a window. 26 const char kChildReadPipeSwitch[] = "child_read_pipe";
41 MULTIPROCESS_TEST_MAIN(NoWindowChild) { 27 const char kChildWritePipeSwitch[] = "child_write_pipe";
42 ::Sleep(INFINITE); 28
29 // Signals used for IPC between the monitor process and the monitor.
30 enum IPCSignal {
31 IPC_SIGNAL_INVALID,
32 IPC_SIGNAL_READY,
33 IPC_SIGNAL_TERMINATE_PROCESS,
34 IPC_SIGNAL_CREATE_MESSAGE_WINDOW,
35 IPC_SIGNAL_DELETE_MESSAGE_WINDOW,
36 IPC_SIGNAL_HANG_MESSAGE_WINDOW,
37 };
38
39 // Sends |ipc_signal| through the |write_pipe|.
40 bool SendPipeSignal(HANDLE write_pipe, IPCSignal ipc_signal) {
41 DWORD bytes_written = 0;
42 if (!WriteFile(write_pipe, &ipc_signal, sizeof(ipc_signal), &bytes_written,
43 nullptr))
44 return false;
45
46 return bytes_written == sizeof(ipc_signal);
47 }
48
49 // Blocks on |read_pipe| until a signal is received into |ipc_signal|.
50 bool WaitForPipeSignal(HANDLE read_pipe, IPCSignal* ipc_signal) {
51 CHECK(ipc_signal);
52 DWORD bytes_read = 0;
53 if (!ReadFile(read_pipe, ipc_signal, sizeof(*ipc_signal), &bytes_read,
54 nullptr))
55 return false;
56
57 return bytes_read == sizeof(*ipc_signal);
58 }
59
60 // Blocks on |read_pipe| until a signal is received and returns true if it
61 // matches |expected_ipc_signal|.
62 bool WaitForSpecificPipeSignal(HANDLE read_pipe,
63 IPCSignal expected_ipc_signal) {
64 IPCSignal received_signal = IPC_SIGNAL_INVALID;
65 return WaitForPipeSignal(read_pipe, &received_signal) &&
66 received_signal == expected_ipc_signal;
67 }
68
69 // Appends |handle| as a command line switch.
70 void AppendSwitchHandle(base::CommandLine* command_line,
71 std::string switch_name,
72 HANDLE handle) {
73 command_line->AppendSwitchASCII(
74 switch_name, base::UintToString(base::win::HandleToUint32(handle)));
75 }
76
77 // Retrieves the |handle| associated to |switch_name| from the command line.
78 HANDLE GetSwitchValueHandle(base::CommandLine* command_line,
79 std::string switch_name) {
80 std::string switch_string = command_line->GetSwitchValueASCII(switch_name);
81 unsigned int switch_uint = 0;
82 if (switch_string.empty() ||
83 !base::StringToUint(switch_string, &switch_uint)) {
84 DLOG(ERROR) << "Missing or invalid " << switch_name << " argument.";
85 return nullptr;
86 }
87 return reinterpret_cast<HANDLE>(switch_uint);
88 }
89
90 // An instance of this class lives in the monitored process and receives signals
91 // and executes their associated function.
92 class MonitoredProcessClient {
Sigurður Ásgeirsson 2016/01/20 18:21:13 wow - what a rigamarole to get some IPC going :/.
Patrick Monette 2016/01/21 00:05:10 Haha yes.
93 public:
94 MonitoredProcessClient()
95 : message_window_thread_("Message window thread"),
96 hang_event_(true, false) {
97 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
98
99 read_pipe_ = GetSwitchValueHandle(command_line, kChildReadPipeSwitch);
100 write_pipe_ = GetSwitchValueHandle(command_line, kChildWritePipeSwitch);
101 }
102
103 ~MonitoredProcessClient() {
104 if (message_window_thread_.IsRunning()) {
105 DeleteMessageWindow();
106 }
107 }
108
109 void RunEventLoop() {
110 bool running = true;
111 IPCSignal ipc_signal = IPC_SIGNAL_INVALID;
112 while (running) {
113 CHECK(WaitForPipeSignal(read_pipe_, &ipc_signal));
114 switch (ipc_signal) {
115 // The parent process should never send those.
116 case IPC_SIGNAL_INVALID:
117 case IPC_SIGNAL_READY:
118 CHECK(false);
119 break;
120 case IPC_SIGNAL_TERMINATE_PROCESS:
121 running = false;
122 break;
123 case IPC_SIGNAL_CREATE_MESSAGE_WINDOW:
124 CreateMessageWindow();
125 break;
126 case IPC_SIGNAL_DELETE_MESSAGE_WINDOW:
127 DeleteMessageWindow();
128 break;
129 case IPC_SIGNAL_HANG_MESSAGE_WINDOW:
130 HangMessageWindow();
131 break;
132 }
133 SendSignalToParent(IPC_SIGNAL_READY);
134 }
135 }
136
137 // Creates a thread then creates the message window on it.
138 void CreateMessageWindow() {
139 ASSERT_TRUE(message_window_thread_.StartWithOptions(
140 base::Thread::Options(base::MessageLoop::TYPE_UI, 0)));
141
142 bool succeeded = false;
143 base::WaitableEvent created(true, false);
144 ASSERT_TRUE(message_window_thread_.task_runner()->PostTask(
145 FROM_HERE,
146 base::Bind(&MonitoredProcessClient::CreateMessageWindowInWorkerThread,
147 base::Unretained(this), &succeeded, &created)));
148 created.Wait();
149 ASSERT_TRUE(succeeded);
150 }
151
152 // Creates a thread then creates the message window on it.
153 void HangMessageWindow() {
154 message_window_thread_.task_runner()->PostTask(
155 FROM_HERE,
156 base::Bind(&base::WaitableEvent::Wait, base::Unretained(&hang_event_)));
157 }
158
159 bool SendSignalToParent(IPCSignal ipc_signal) {
160 return SendPipeSignal(write_pipe_, ipc_signal);
161 }
162
163 private:
164 bool EmptyMessageCallback(UINT message,
165 WPARAM wparam,
166 LPARAM lparam,
167 LRESULT* result) {
168 EXPECT_EQ(message_window_thread_.message_loop(),
169 base::MessageLoop::current());
170 return false; // Pass through to DefWindowProc.
171 }
172
173 void CreateMessageWindowInWorkerThread(bool* success,
174 base::WaitableEvent* created) {
175 CHECK(created);
176
177 // As an alternative to checking if the name of the message window is the
178 // user data directory, the hang watcher verifies that the window name is an
179 // existing directory. DIR_CURRENT is used to meet this constraint.
180 base::FilePath existing_dir;
181 CHECK(PathService::Get(base::DIR_CURRENT, &existing_dir));
182
183 message_window_.reset(new base::win::MessageWindow);
184 *success = message_window_->CreateNamed(
185 base::Bind(&MonitoredProcessClient::EmptyMessageCallback,
186 base::Unretained(this)),
187 existing_dir.value().c_str());
188 created->Signal();
189 }
190
191 void DeleteMessageWindow() {
192 base::WaitableEvent deleted(true, false);
193 message_window_thread_.task_runner()->PostTask(
194 FROM_HERE,
195 base::Bind(&MonitoredProcessClient::DeleteMessageWindowInWorkerThread,
196 base::Unretained(this), &deleted));
197 deleted.Wait();
198
199 message_window_thread_.Stop();
200 }
201
202 void DeleteMessageWindowInWorkerThread(base::WaitableEvent* deleted) {
203 CHECK(deleted);
204 message_window_.reset();
205 deleted->Signal();
206 }
207
208 // The thread that holds the message window.
209 base::Thread message_window_thread_;
210 scoped_ptr<base::win::MessageWindow> message_window_;
211
212 // Event used to hang the message window.
213 base::WaitableEvent hang_event_;
214
215 // Anonymous pipe handles for IPC with the parent process.
216 HANDLE read_pipe_;
217 HANDLE write_pipe_;
218
219 DISALLOW_COPY_AND_ASSIGN(MonitoredProcessClient);
220 };
221
222 // The monitored process main function.
223 MULTIPROCESS_TEST_MAIN(MonitoredProcess) {
224 MonitoredProcessClient monitored_process_client;
225 CHECK(monitored_process_client.SendSignalToParent(IPC_SIGNAL_READY));
226
227 monitored_process_client.RunEventLoop();
228
43 return 0; 229 return 0;
44 } 230 }
45 231
46 // Manages a WindowHangMonitor that lives on a background thread. 232 // Manages a WindowHangMonitor that lives on a background thread.
47 class HangMonitorThread { 233 class HangMonitorThread {
48 public: 234 public:
49 // Instantiates the background thread. 235 // Instantiates the background thread.
50 HangMonitorThread() 236 HangMonitorThread()
51 : event_(WindowHangMonitor::WINDOW_NOT_FOUND), 237 : event_(WindowHangMonitor::WINDOW_NOT_FOUND),
52 event_received_(false, false), 238 event_received_(false, false),
53 thread_("HangMonitorThread") {} 239 thread_("Hang monitor thread") {}
54 240
55 ~HangMonitorThread() { 241 ~HangMonitorThread() {
56 if (hang_monitor_.get()) 242 if (hang_monitor_.get())
57 DestroyWatcher(); 243 DestroyWatcher();
58 } 244 }
59 245
60 // Starts the background thread and the monitor to observe the window named 246 // Starts the background thread and the monitor to observe Chrome message
61 // |window_name| in |process|. Blocks until the monitor has been initialized. 247 // window for |process|. Blocks until the monitor has been initialized.
62 bool Start(base::Process process, const base::string16& window_name) { 248 bool Start(base::Process process) {
63 if (!thread_.StartWithOptions( 249 if (!thread_.StartWithOptions(
64 base::Thread::Options(base::MessageLoop::TYPE_UI, 0))) { 250 base::Thread::Options(base::MessageLoop::TYPE_UI, 0))) {
65 return false; 251 return false;
66 } 252 }
67 253
68 base::WaitableEvent complete(false, false); 254 base::WaitableEvent complete(false, false);
69 if (!thread_.task_runner()->PostTask( 255 if (!thread_.task_runner()->PostTask(
70 FROM_HERE, base::Bind(&HangMonitorThread::StartupOnThread, 256 FROM_HERE,
71 base::Unretained(this), window_name, 257 base::Bind(&HangMonitorThread::StartupOnThread,
72 base::Passed(process.Pass()), 258 base::Unretained(this), base::Passed(std::move(process)),
73 base::Unretained(&complete)))) { 259 base::Unretained(&complete)))) {
74 return false; 260 return false;
75 } 261 }
76 262
77 complete.Wait(); 263 complete.Wait();
78 264
79 return true; 265 return true;
80 } 266 }
81 267
82 // Returns true if a window event is detected within |timeout|. 268 // Returns true if a window event is detected within |timeout|.
83 bool TimedWaitForEvent(base::TimeDelta timeout) { 269 bool TimedWaitForEvent(base::TimeDelta timeout) {
84 return event_received_.TimedWait(timeout); 270 return event_received_.TimedWait(timeout);
85 } 271 }
86 272
87 // Blocks indefinitely for a window event and returns it. 273 // Blocks indefinitely for a window event and returns it.
88 WindowHangMonitor::WindowEvent WaitForEvent() { 274 WindowHangMonitor::WindowEvent WaitForEvent() {
89 event_received_.Wait(); 275 event_received_.Wait();
90 return event_; 276 return event_;
91 } 277 }
92 278
279 private:
93 // Destroys the monitor and stops the background thread. Blocks until the 280 // Destroys the monitor and stops the background thread. Blocks until the
94 // operation completes. 281 // operation completes.
95 void DestroyWatcher() { 282 void DestroyWatcher() {
96 thread_.task_runner()->PostTask( 283 thread_.task_runner()->PostTask(
97 FROM_HERE, base::Bind(&HangMonitorThread::ShutdownOnThread, 284 FROM_HERE, base::Bind(&HangMonitorThread::ShutdownOnThread,
98 base::Unretained(this))); 285 base::Unretained(this)));
99 // This will block until the above-posted task completes. 286 // This will block until the above-posted task completes.
100 thread_.Stop(); 287 thread_.Stop();
101 } 288 }
102 289
103 private:
104 // Invoked when the monitor signals an event. Unblocks a call to 290 // Invoked when the monitor signals an event. Unblocks a call to
105 // TimedWaitForEvent or WaitForEvent. 291 // TimedWaitForEvent or WaitForEvent.
106 void EventCallback(WindowHangMonitor::WindowEvent event) { 292 void EventCallback(WindowHangMonitor::WindowEvent event) {
107 if (event_received_.IsSignaled()) 293 if (event_received_.IsSignaled())
108 ADD_FAILURE() << "Multiple calls to EventCallback."; 294 ADD_FAILURE() << "Multiple calls to EventCallback.";
109 event_ = event; 295 event_ = event;
110 event_received_.Signal(); 296 event_received_.Signal();
111 } 297 }
112 298
113 // Initializes the WindowHangMonitor to observe the window named |window_name| 299 // Initializes the WindowHangMonitor to observe the Chrome message window for
114 // in |process|. Signals |complete| when done. 300 // |process|. Signals |complete| when done.
115 void StartupOnThread(const base::string16& window_name, 301 void StartupOnThread(base::Process process, base::WaitableEvent* complete) {
116 base::Process process,
117 base::WaitableEvent* complete) {
118 hang_monitor_.reset(new WindowHangMonitor( 302 hang_monitor_.reset(new WindowHangMonitor(
119 base::TimeDelta::FromMilliseconds(100), 303 base::TimeDelta::FromMilliseconds(100),
120 base::TimeDelta::FromMilliseconds(100), 304 base::TimeDelta::FromMilliseconds(100),
121 base::Bind(&HangMonitorThread::EventCallback, base::Unretained(this)))); 305 base::Bind(&HangMonitorThread::EventCallback, base::Unretained(this))));
122 hang_monitor_->Initialize(process.Pass(), window_name); 306 hang_monitor_->Initialize(std::move(process));
123 complete->Signal(); 307 complete->Signal();
124 } 308 }
125 309
126 // Destroys the WindowHangMonitor. 310 // Destroys the WindowHangMonitor.
127 void ShutdownOnThread() { hang_monitor_.reset(); } 311 void ShutdownOnThread() { hang_monitor_.reset(); }
128 312
129 // The detected event. Invalid if |event_received_| has not been signaled. 313 // The detected event. Invalid if |event_received_| has not been signaled.
130 WindowHangMonitor::WindowEvent event_; 314 WindowHangMonitor::WindowEvent event_;
131 // Indicates that |event_| has been assigned in response to a callback from 315 // Indicates that |event_| has been assigned in response to a callback from
132 // the WindowHangMonitor. 316 // the WindowHangMonitor.
133 base::WaitableEvent event_received_; 317 base::WaitableEvent event_received_;
134 // The WindowHangMonitor under test. 318 // The WindowHangMonitor under test.
135 scoped_ptr<WindowHangMonitor> hang_monitor_; 319 scoped_ptr<WindowHangMonitor> hang_monitor_;
136 // The background thread. 320 // The background thread.
137 base::Thread thread_; 321 base::Thread thread_;
138 322
139 DISALLOW_COPY_AND_ASSIGN(HangMonitorThread); 323 DISALLOW_COPY_AND_ASSIGN(HangMonitorThread);
140 }; 324 };
141 325
142 class WindowHangMonitorTest : public testing::Test { 326 class WindowHangMonitorTest : public testing::Test {
143 public: 327 public:
144 WindowHangMonitorTest() 328 WindowHangMonitorTest() {}
145 : ping_event_(false, false),
146 pings_(0),
147 window_thread_("WindowHangMonitorTest window_thread") {}
148 329
149 void SetUp() override { 330 ~WindowHangMonitorTest() {
150 // Pick a window name unique to this process. 331 // Close process if running.
151 window_name_ = base::StringPrintf(L"WindowHanMonitorTest-%d", 332 monitored_process_.Terminate(1, false);
152 base::GetCurrentProcId());
153 ASSERT_TRUE(window_thread_.StartWithOptions(
154 base::Thread::Options(base::MessageLoop::TYPE_UI, 0)));
155 } 333 }
156 334
157 void TearDown() override { 335 // Starts a child process that will be monitored. Handles to anonymous pipes
158 DeleteMessageWindow(); 336 // are passed to the command line to provide a way to communicate with the
159 window_thread_.Stop(); 337 // child process. This function blocks until IPC_SIGNAL_READY is received.
338 bool StartMonitoredProcess() {
339 HANDLE child_read_pipe = nullptr;
340 HANDLE child_write_pipe = nullptr;
341 if (!CreatePipes(&child_read_pipe, &child_write_pipe))
342 return false;
343
344 base::CommandLine command_line =
345 base::GetMultiProcessTestChildBaseCommandLine();
346 command_line.AppendSwitchASCII(switches::kTestChildProcess,
347 "MonitoredProcess");
348
349 AppendSwitchHandle(&command_line, kChildReadPipeSwitch, child_read_pipe);
350 AppendSwitchHandle(&command_line, kChildWritePipeSwitch, child_write_pipe);
351
352 base::LaunchOptions options = {};
353 options.inherit_handles = true;
354 monitored_process_ = base::LaunchProcess(command_line, options);
355 if (!monitored_process_.IsValid())
356 return false;
357
358 return WaitForSignal(IPC_SIGNAL_READY);
160 } 359 }
161 360
162 void CreateMessageWindow() { 361 void StartHangMonitor() {
163 bool succeeded = false; 362 monitor_thread_.Start(monitored_process_.Duplicate());
164 base::WaitableEvent created(true, false);
165 ASSERT_TRUE(window_thread_.task_runner()->PostTask(
166 FROM_HERE,
167 base::Bind(&WindowHangMonitorTest::CreateMessageWindowInWorkerThread,
168 base::Unretained(this), window_name_, &succeeded,
169 &created)));
170 created.Wait();
171 ASSERT_TRUE(succeeded);
172 } 363 }
173 364
174 void DeleteMessageWindow() { 365 // Sends the |ipc_signal| to the child process and wait for a IPC_SIGNAL_READY
175 base::WaitableEvent deleted(true, false); 366 // response.
176 window_thread_.task_runner()->PostTask( 367 bool SendSignal(IPCSignal ipc_signal) {
177 FROM_HERE, 368 if (!SendPipeSignal(write_pipe_, ipc_signal))
178 base::Bind(&WindowHangMonitorTest::DeleteMessageWindowInWorkerThread, 369 return false;
179 base::Unretained(this), &deleted)); 370
180 deleted.Wait(); 371 return WaitForSignal(IPC_SIGNAL_READY);
181 } 372 }
182 373
183 void WaitForPing() { 374 // Blocks until |ipc_signal| is received from the child process.
184 while (true) { 375 bool WaitForSignal(IPCSignal ipc_signal) {
185 { 376 return WaitForSpecificPipeSignal(read_pipe_, ipc_signal);
186 base::AutoLock auto_lock(ping_lock_);
187 if (pings_) {
188 ping_event_.Reset();
189 --pings_;
190 return;
191 }
192 }
193 ping_event_.Wait();
194 }
195 } 377 }
196 378
197 HangMonitorThread& monitor_thread() { return monitor_thread_; } 379 HangMonitorThread& monitor_thread() { return monitor_thread_; }
198 380
199 const base::win::MessageWindow* message_window() const { 381 private:
200 return message_window_.get(); 382 // Creates pipes for IPC with the child process.
383 bool CreatePipes(HANDLE* child_read_pipe, HANDLE* child_write_pipe) {
384 CHECK(child_read_pipe);
385 CHECK(child_write_pipe);
386 SECURITY_ATTRIBUTES security_attributes = {
387 sizeof(SECURITY_ATTRIBUTES), nullptr, true /* inherit handles */};
388
389 if (!CreatePipe(&read_pipe_, child_write_pipe, &security_attributes, 0)) {
390 return false;
391 }
392 if (!CreatePipe(child_read_pipe, &write_pipe_, &security_attributes, 0)) {
393 return false;
394 }
395 return true;
201 } 396 }
202 397
203 const base::string16& window_name() const { return window_name_; } 398 // The thread that monitors the child process.
399 HangMonitorThread monitor_thread_;
400 // The process that is monitored.
401 base::Process monitored_process_;
204 402
205 base::Thread* window_thread() { return &window_thread_; } 403 // Anonymous pipe handles for IPC with the monitored process.
206 404 HANDLE read_pipe_;
Sigurður Ásgeirsson 2016/01/20 18:21:13 do these perhaps want to be base::win::ScopedHandl
Patrick Monette 2016/01/21 00:05:10 Done.
207 private: 405 HANDLE write_pipe_;
208 bool MessageCallback(UINT message,
209 WPARAM wparam,
210 LPARAM lparam,
211 LRESULT* result) {
212 EXPECT_EQ(window_thread_.message_loop(), base::MessageLoop::current());
213 if (message == WM_NULL) {
214 base::AutoLock auto_lock(ping_lock_);
215 ++pings_;
216 ping_event_.Signal();
217 }
218
219 return false; // Pass through to DefWindowProc.
220 }
221
222 void CreateMessageWindowInWorkerThread(const base::string16& name,
223 bool* success,
224 base::WaitableEvent* created) {
225 message_window_.reset(new base::win::MessageWindow);
226 *success = message_window_->CreateNamed(
227 base::Bind(&WindowHangMonitorTest::MessageCallback,
228 base::Unretained(this)),
229 name);
230 created->Signal();
231 }
232
233 void DeleteMessageWindowInWorkerThread(base::WaitableEvent* deleted) {
234 message_window_.reset();
235 if (deleted)
236 deleted->Signal();
237 }
238
239 HangMonitorThread monitor_thread_;
240 scoped_ptr<base::win::MessageWindow> message_window_;
241 base::string16 window_name_;
242 base::Lock ping_lock_;
243 base::WaitableEvent ping_event_;
244 size_t pings_;
245 base::Thread window_thread_;
246 406
247 DISALLOW_COPY_AND_ASSIGN(WindowHangMonitorTest); 407 DISALLOW_COPY_AND_ASSIGN(WindowHangMonitorTest);
248 }; 408 };
249 409
250 } // namespace 410 } // namespace
251 411
252 TEST_F(WindowHangMonitorTest, NoWindow) { 412 TEST_F(WindowHangMonitorTest, WindowNotFound) {
253 base::CommandLine child_command_line = 413 ASSERT_TRUE(StartMonitoredProcess());
254 base::GetMultiProcessTestChildBaseCommandLine();
255 child_command_line.AppendSwitchASCII(switches::kTestChildProcess,
256 "NoWindowChild");
257 base::Process process =
258 base::LaunchProcess(child_command_line, base::LaunchOptions());
259 ASSERT_TRUE(process.IsValid());
260 414
261 base::ScopedClosureRunner terminate_process_runner( 415 StartHangMonitor();
262 base::Bind(base::IgnoreResult(&base::Process::Terminate),
263 base::Unretained(&process), 1, true));
264 416
265 monitor_thread().Start(process.Duplicate(), window_name()); 417 ASSERT_TRUE(SendSignal(IPC_SIGNAL_TERMINATE_PROCESS));
266 418
267 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( 419 EXPECT_EQ(WindowHangMonitor::WINDOW_NOT_FOUND,
268 base::TimeDelta::FromMilliseconds(150)));
269
270 terminate_process_runner.Reset();
271
272 ASSERT_EQ(WindowHangMonitor::WINDOW_NOT_FOUND,
273 monitor_thread().WaitForEvent());
274
275 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
276 base::TimeDelta::FromMilliseconds(150)));
277 }
278
279 TEST_F(WindowHangMonitorTest, WindowBeforeWatcher) {
280 CreateMessageWindow();
281
282 monitor_thread().Start(base::Process::Current(), window_name());
283
284 WaitForPing();
285
286 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
287 base::TimeDelta::FromMilliseconds(150)));
288 }
289
290 TEST_F(WindowHangMonitorTest, WindowBeforeDestroy) {
291 CreateMessageWindow();
292
293 monitor_thread().Start(base::Process::Current(), window_name());
294
295 WaitForPing();
296
297 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
298 base::TimeDelta::FromMilliseconds(150)));
299
300 monitor_thread().DestroyWatcher();
301
302 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta()));
303 }
304
305 TEST_F(WindowHangMonitorTest, NoWindowBeforeDestroy) {
306 monitor_thread().Start(base::Process::Current(), window_name());
307
308 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
309 base::TimeDelta::FromMilliseconds(150)));
310 monitor_thread().DestroyWatcher();
311
312 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta()));
313 }
314
315 TEST_F(WindowHangMonitorTest, WatcherBeforeWindow) {
316 monitor_thread().Start(base::Process::Current(), window_name());
317
318 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
319 base::TimeDelta::FromMilliseconds(150)));
320
321 CreateMessageWindow();
322
323 WaitForPing();
324
325 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
326 base::TimeDelta::FromMilliseconds(150)));
327 }
328
329 TEST_F(WindowHangMonitorTest, DetectsWindowDisappearance) {
330 CreateMessageWindow();
331
332 monitor_thread().Start(base::Process::Current(), window_name());
333
334 WaitForPing();
335
336 DeleteMessageWindow();
337
338 ASSERT_EQ(WindowHangMonitor::WINDOW_VANISHED,
339 monitor_thread().WaitForEvent());
340
341 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
342 base::TimeDelta::FromMilliseconds(150)));
343 }
344
345 TEST_F(WindowHangMonitorTest, DetectsWindowNameChange) {
346 // This test changes the title of the message window as a proxy for what
347 // happens if the window handle is reused for a different purpose. The latter
348 // is impossible to test in a deterministic fashion.
349 CreateMessageWindow();
350
351 monitor_thread().Start(base::Process::Current(), window_name());
352
353 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
354 base::TimeDelta::FromMilliseconds(150)));
355
356 ASSERT_TRUE(::SetWindowText(message_window()->hwnd(), L"Gonsky"));
357
358 ASSERT_EQ(WindowHangMonitor::WINDOW_VANISHED,
359 monitor_thread().WaitForEvent()); 420 monitor_thread().WaitForEvent());
360 } 421 }
361 422
362 TEST_F(WindowHangMonitorTest, DetectsWindowHang) { 423 TEST_F(WindowHangMonitorTest, WindowVanished) {
363 CreateMessageWindow(); 424 ASSERT_TRUE(StartMonitoredProcess());
364 425
365 monitor_thread().Start(base::Process::Current(), window_name()); 426 ASSERT_TRUE(SendSignal(IPC_SIGNAL_CREATE_MESSAGE_WINDOW));
427
428 StartHangMonitor();
366 429
367 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( 430 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
368 base::TimeDelta::FromMilliseconds(150))); 431 base::TimeDelta::FromMilliseconds(250)));
369 432
370 // Block the worker thread. 433 ASSERT_TRUE(SendSignal(IPC_SIGNAL_DELETE_MESSAGE_WINDOW));
371 base::WaitableEvent hang(true, false);
372 434
373 window_thread()->task_runner()->PostTask( 435 EXPECT_EQ(WindowHangMonitor::WINDOW_VANISHED,
374 FROM_HERE, 436 monitor_thread().WaitForEvent());
375 base::Bind(&base::WaitableEvent::Wait, base::Unretained(&hang))); 437
438 ASSERT_TRUE(SendSignal(IPC_SIGNAL_TERMINATE_PROCESS));
439 }
440
441 TEST_F(WindowHangMonitorTest, WindowHang) {
442 ASSERT_TRUE(StartMonitoredProcess());
443
444 ASSERT_TRUE(SendSignal(IPC_SIGNAL_CREATE_MESSAGE_WINDOW));
445
446 StartHangMonitor();
447
448 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
449 base::TimeDelta::FromMilliseconds(250)));
450
451 ASSERT_TRUE(SendSignal(IPC_SIGNAL_HANG_MESSAGE_WINDOW));
376 452
377 EXPECT_EQ(WindowHangMonitor::WINDOW_HUNG, 453 EXPECT_EQ(WindowHangMonitor::WINDOW_HUNG,
378 monitor_thread().WaitForEvent()); 454 monitor_thread().WaitForEvent());
379 455
380 // Unblock the worker thread. 456 ASSERT_TRUE(SendSignal(IPC_SIGNAL_TERMINATE_PROCESS));
381 hang.Signal();
382
383 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(
384 base::TimeDelta::FromMilliseconds(150)));
385 } 457 }
386 458
387 } // namespace browser_watcher 459 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698