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

Side by Side Diff: chrome/browser/process_singleton_win_unittest.cc

Issue 981223004: Add test for ProcessSingleton hung rendezvous case. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Now tests the user interaction cases. Created 5 years, 9 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
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
erikwright (departed) 2015/03/10 19:22:49 Fix the copyright. I believe the (c) should go, a
Sigurður Ásgeirsson 2015/03/10 19:48:15 Done.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/process_singleton.h"
6
7 #include <windows.h>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/files/file_path.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/process/launch.h"
16 #include "base/process/process.h"
17 #include "base/process/process_handle.h"
18 #include "base/strings/string16.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/test/multiprocess_test.h"
21 #include "base/win/scoped_handle.h"
22 #include "base/win/wrapped_window_proc.h"
23 #include "chrome/browser/chrome_process_finder_win.h"
24 #include "chrome/common/chrome_constants.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "content/public/common/result_codes.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "testing/multiprocess_func_list.h"
29
30 namespace {
31
32 const char kReadyEventNameFlag[] = "ready_event_name";
33 const char kContinueEventNameFlag[] = "continue_event_name";
34 const char kCreateWindowFlag[] = "create_window";
35 const int kErrorResultCode = 0x345;
36
37 bool NotificationCallback(
38 const base::CommandLine& command_line,
39 const base::FilePath& current_directory) {
40 return true;
41 }
42
43 HWND CreateVisibleWindow() {
44 WNDCLASSEX wnd_cls = {};
45 base::win::InitializeWindowClass(
46 L"ProcessSingletonTest",
47 base::win::WrappedWindowProc<::DefWindowProc>,
48 0, // style
49 0, // class_extra
50 0, // window_extra
51 NULL, // cursor
52 NULL, // background
53 L"", // menu_name
54 NULL, // large_icon
55 NULL, // small_icon
56 &wnd_cls);
57
58 ATOM clazz = ::RegisterClassEx(&wnd_cls);
59
60 // Create a visible popup window to trigger user interaction in the
61 // process singleton.
62 HWND window = ::CreateWindow(reinterpret_cast<LPCWSTR>(clazz),
63 0, WS_POPUP, 0, 0, 0, 0, 0, 0, NULL, 0);
64 if (window)
65 ::ShowWindow(window, SW_SHOW);
66
67 return window;
68 }
69
70 MULTIPROCESS_TEST_MAIN(ProcessSingletonTestProcessMain) {
71 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
72 base::FilePath user_data_dir =
73 cmd_line->GetSwitchValuePath(switches::kUserDataDir);
74 if (user_data_dir.empty())
75 return kErrorResultCode;
76
77 base::string16 ready_event_name =
78 cmd_line->GetSwitchValueNative(kReadyEventNameFlag);
79
80 base::win::ScopedHandle ready_event(
81 ::OpenEvent(EVENT_MODIFY_STATE, FALSE, ready_event_name.c_str()));
82 if (!ready_event.IsValid())
83 return kErrorResultCode;
84
85 base::string16 continue_event_name =
86 cmd_line->GetSwitchValueNative(kContinueEventNameFlag);
87
88 base::win::ScopedHandle continue_event(
89 ::OpenEvent(SYNCHRONIZE, FALSE, continue_event_name.c_str()));
90 if (!continue_event.IsValid())
91 return kErrorResultCode;
92
93 if (cmd_line->HasSwitch(kCreateWindowFlag)) {
94 HWND window = CreateVisibleWindow();
95 if (!window)
96 return kErrorResultCode;
97 }
98
99 // Instantiate the process singleton.
100 ProcessSingleton process_singleton(user_data_dir,
101 base::Bind(&NotificationCallback));
102
103 if (!process_singleton.Create())
104 return kErrorResultCode;
105
106 // Signal ready and block for the continue event.
107 ::SetEvent(ready_event.Get());
erikwright (departed) 2015/03/10 19:22:49 Check the result of SetEvent and WaitForSingleObje
Sigurður Ásgeirsson 2015/03/10 19:48:15 Done.
108 ::WaitForSingleObject(continue_event.Get(), INFINITE);
109
110 return 0;
111 }
112
113 class ProcessSingletonTest : public base::MultiProcessTest {
114 typedef base::MultiProcessTest Super;
115 public:
116 ProcessSingletonTest() : window_option_(NO_WINDOW), msg_box_called_(false) {
117 }
118
119 void SetUp() override {
120 ASSERT_NO_FATAL_FAILURE(Super::SetUp());
121
122 // Drop the process finder notification timeout to a second for testing.
erikwright (departed) 2015/03/10 19:22:49 a -> one or 1
Sigurður Ásgeirsson 2015/03/10 19:48:15 Done.
123 old_notification_timeout_ = chrome::SetNotificationTimeoutForTesting(1);
124 }
125
126 void TearDown() override {
127 chrome::SetNotificationTimeoutForTesting(old_notification_timeout_);
128
129 if (browser_victim_.IsValid()) {
130 EXPECT_TRUE(::SetEvent(continue_event_.Get()));
131 int exit_code = 0;
132 EXPECT_TRUE(browser_victim_.WaitForExit(&exit_code));
133 }
134
135 Super::TearDown();
136 }
137
138 enum WindowOption {
139 WITH_WINDOW,
140 NO_WINDOW
141 };
142
143 void LaunchHungBrowserProcess(WindowOption window_option) {
144 // Create a unique user data dir to rendezvous on.
145 ASSERT_TRUE(user_data_dir_.CreateUniqueTempDir());
146
147 // Create the named "ready" event, this is unique to our process.
148 ready_event_name_ =
149 base::StringPrintf(L"ready-event-%d", base::GetCurrentProcId());
150 base::win::ScopedHandle ready_event(
151 ::CreateEvent(NULL, TRUE, FALSE, ready_event_name_.c_str()));
152 ASSERT_TRUE(ready_event.IsValid());
153
154 // Create the named "ready" event, this is unique to our process.
erikwright (departed) 2015/03/10 19:22:49 ready->continue
Sigurður Ásgeirsson 2015/03/10 19:48:15 Done.
155 continue_event_name_ =
156 base::StringPrintf(L"continue-event-%d", base::GetCurrentProcId());
157 continue_event_.Set(
158 ::CreateEvent(NULL, TRUE, FALSE, continue_event_name_.c_str()));
159 ASSERT_TRUE(continue_event_.IsValid());
160
161 window_option_ = window_option;
162
163 base::LaunchOptions options;
164 options.start_hidden = true;
165 browser_victim_ =
166 SpawnChildWithOptions("ProcessSingletonTestProcessMain", options);
167
168 // Wait for the ready event (or process exit).
169 HANDLE handles[] = { ready_event.Get(), browser_victim_.Handle() };
170 DWORD result = ::WaitForMultipleObjects(arraysize(handles),
171 handles,
172 FALSE,
173 INFINITE);
174 ASSERT_EQ(result, WAIT_OBJECT_0);
175 }
176
177 base::CommandLine MakeCmdLine(const std::string& procname) override {
178 base::CommandLine cmd_line = Super::MakeCmdLine(procname);
179
180 cmd_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir_.path());
181 cmd_line.AppendSwitchNative(kReadyEventNameFlag, ready_event_name_);
182 cmd_line.AppendSwitchNative(kContinueEventNameFlag, continue_event_name_);
183 if (window_option_ == WITH_WINDOW)
184 cmd_line.AppendSwitch(kCreateWindowFlag);
185
186 return cmd_line;
187 }
188
189 void PrepareTest(WindowOption window_option, bool allow_kill) {
190 ASSERT_NO_FATAL_FAILURE(LaunchHungBrowserProcess(window_option));
191
192 // The ready event has been signalled - the process singleton is held by
193 // the hung sub process.
194 test_singleton_.reset(
195 new ProcessSingleton(user_data_dir(),
196 base::Bind(&NotificationCallback)));
197
198 test_singleton_->OverrideKillMessageBoxCallbackForTesting(
199 base::Bind(&ProcessSingletonTest::MockDisplayKillMessageBox,
200 base::Unretained(this), allow_kill));
201 }
202
203 base::Process& browser_victim() { return browser_victim_; }
204 const base::FilePath& user_data_dir() const { return user_data_dir_.path(); }
205 ProcessSingleton* test_singleton() const { return test_singleton_.get(); }
206 bool msg_box_called() const { return msg_box_called_; }
207
208 private:
209 bool MockDisplayKillMessageBox(bool result) {
210 msg_box_called_ = true;
211 return result;
212 }
213
214 base::string16 ready_event_name_;
215 base::string16 continue_event_name_;
216
217 WindowOption window_option_;
218 base::ScopedTempDir user_data_dir_;
219 base::Process browser_victim_;
220 base::win::ScopedHandle continue_event_;
221
222 scoped_ptr<ProcessSingleton> test_singleton_;
223
224 int old_notification_timeout_;
225 int msg_box_called_;
226
227 DISALLOW_COPY_AND_ASSIGN(ProcessSingletonTest);
228 };
229
230 } // namespace
231
232 TEST_F(ProcessSingletonTest, KillsHungBrowserWithNoWindows) {
233 ASSERT_NO_FATAL_FAILURE(PrepareTest(NO_WINDOW, false));
234
235 // As the hung browser has no visible window, it'll be killed without
236 // user interaction.
237 ProcessSingleton::NotifyResult notify_result =
238 test_singleton()->NotifyOtherProcessOrCreate();
239 ASSERT_EQ(notify_result, ProcessSingleton::PROFILE_IN_USE);
240
241 // The message box should not have been invoked, as the "browser" had no
242 // windows.
243 EXPECT_FALSE(msg_box_called());
244
245 int exit_code = 0;
246 EXPECT_TRUE(browser_victim().WaitForExit(&exit_code));
erikwright (departed) 2015/03/10 19:22:49 Should this be WaitForExitWithTimeout with a delay
Sigurður Ásgeirsson 2015/03/10 19:48:15 This is true - good point!
247 EXPECT_EQ(exit_code, content::RESULT_CODE_HUNG);
248 }
249
250 TEST_F(ProcessSingletonTest, DoesntKillWithoutUserPermission) {
251 ASSERT_NO_FATAL_FAILURE(PrepareTest(WITH_WINDOW, false));
252
253 // As the hung browser has a visible window, this should query the user
254 // before killing the hung process.
255 ProcessSingleton::NotifyResult notify_result =
256 test_singleton()->NotifyOtherProcessOrCreate();
257 ASSERT_EQ(notify_result, ProcessSingleton::PROCESS_NOTIFIED);
258
259 // The message box should not have been invoked, as the "browser" had no
260 // windows.
erikwright (departed) 2015/03/10 19:22:49 comment in error
Sigurður Ásgeirsson 2015/03/10 19:48:15 Done.
261 EXPECT_TRUE(msg_box_called());
262
263 // Make sure the process hasn't been killed.
264 int exit_code = 0;
265 EXPECT_FALSE(
266 browser_victim().WaitForExitWithTimeout(base::TimeDelta(), &exit_code));
267 }
268
269 TEST_F(ProcessSingletonTest, KillWithUserPermission) {
270 ASSERT_NO_FATAL_FAILURE(PrepareTest(WITH_WINDOW, true));
271
272 // As the hung browser has a visible window, this should query the user
273 // before killing the hung process.
274 ProcessSingleton::NotifyResult notify_result =
275 test_singleton()->NotifyOtherProcessOrCreate();
276 ASSERT_EQ(notify_result, ProcessSingleton::PROFILE_IN_USE);
277
278 // The message box should not have been invoked, as the "browser" had no
erikwright (departed) 2015/03/10 19:22:49 comment in error
Sigurður Ásgeirsson 2015/03/10 19:48:15 Done.
279 // windows.
280 EXPECT_TRUE(msg_box_called());
281
282 int exit_code = 0;
283 EXPECT_TRUE(browser_victim().WaitForExit(&exit_code));
erikwright (departed) 2015/03/10 19:22:49 same question about timeout
Sigurður Ásgeirsson 2015/03/10 19:48:15 Done.
284 EXPECT_EQ(exit_code, content::RESULT_CODE_HUNG);
285 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698