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

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

Powered by Google App Engine
This is Rietveld 408576698