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

Side by Side Diff: chrome/app/chrome_watcher_client_unittest_win.cc

Issue 886613002: Introduce the ability to wait for the watcher process to initialize. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add some comments. Created 5 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
(Empty)
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.
4
5 #include "chrome/app/chrome_watcher_client_win.h"
6
7 #include <windows.h>
8 #include <string>
9 #include "base/base_switches.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/process/process_handle.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/test/multiprocess_test.h"
19 #include "base/threading/simple_thread.h"
20 #include "base/time/time.h"
21 #include "base/win/scoped_handle.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "testing/multiprocess_func_list.h"
24
25 namespace {
26
27 const char kParentHandle[] = "parent-handle";
28 const char kEventHandle[] = "event-handle";
29 const char kNamedEventSuffix[] = "named-event-suffix";
30
31 const base::char16 kExitEventBaseName[] = L"ChromeWatcherClientTestExitEvent_";
32 const base::char16 kInitializeEventBaseName[] =
33 L"ChromeWatcherClientTestInitializeEvent_";
34
35 base::win::ScopedHandle InterpretHandleSwitch(base::CommandLine& cmd_line,
36 const char* switch_name) {
37 std::string str_handle =
38 cmd_line.GetSwitchValueASCII(switch_name);
39 if (str_handle.empty()) {
40 LOG(ERROR) << "Switch " << switch_name << " unexpectedly absent.";
41 return base::win::ScopedHandle();
42 }
43
44 unsigned int_handle = 0;
45 if (!base::StringToUint(str_handle, &int_handle)) {
46 LOG(ERROR) << "Switch " << switch_name << " has invalid value "
47 << str_handle;
48 return base::win::ScopedHandle();
49 }
50
51 return base::win::ScopedHandle(
52 reinterpret_cast<base::ProcessHandle>(int_handle));
53 }
54
55 // Simulates a Chrome watcher process. Exits when the global exit event is
56 // signaled. Signals the "on initialized" event (passed on the command-line)
57 // when the global initialization event is signaled.
58 MULTIPROCESS_TEST_MAIN(ChromeWatcherClientTestProcess) {
59 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
60
61 base::string16 named_event_suffix =
62 base::ASCIIToUTF16(cmd_line->GetSwitchValueASCII(kNamedEventSuffix));
63 if (named_event_suffix.empty()) {
64 LOG(ERROR) << "Switch " << kNamedEventSuffix << " unexpectedly absent.";
65 return 1;
66 }
67
68 base::win::ScopedHandle exit_event(::CreateEvent(
69 NULL, FALSE, FALSE, (kExitEventBaseName + named_event_suffix).c_str()));
70 if (!exit_event.IsValid()) {
71 LOG(ERROR) << "Failed to create event named "
72 << kExitEventBaseName + named_event_suffix;
73 return 1;
74 }
75
76 base::win::ScopedHandle initialize_event(
77 ::CreateEvent(NULL, FALSE, FALSE,
78 (kInitializeEventBaseName + named_event_suffix).c_str()));
79 if (!initialize_event.IsValid()) {
80 LOG(ERROR) << "Failed to create event named "
81 << kInitializeEventBaseName + named_event_suffix;
82 return 1;
83 }
84
85 base::win::ScopedHandle parent_process(
86 InterpretHandleSwitch(*cmd_line, kParentHandle));
87 if (!parent_process.IsValid())
88 return 1;
89
90 base::win::ScopedHandle on_initialized_event(
91 InterpretHandleSwitch(*cmd_line, kEventHandle));
92 if (!on_initialized_event.IsValid())
93 return 1;
94
95 while (true) {
96 // We loop as a convenient way to continue waiting for the exit_event after
97 // the initialize_event is signaled. We expect to get initialize_event zero
98 // or one times before exit_event, never more.
99 HANDLE handles[] = {exit_event.Get(), initialize_event.Get()};
100 DWORD result =
101 ::WaitForMultipleObjects(arraysize(handles), handles, FALSE, INFINITE);
102 switch (result) {
103 case WAIT_OBJECT_0:
104 // exit_event
105 return 0;
106 case WAIT_OBJECT_0 + 1:
107 // initialize_event
108 ::SetEvent(on_initialized_event.Get());
109 break;
110 case WAIT_FAILED:
111 PLOG(ERROR) << "Unexpected failure in WaitForMultipleObjects.";
112 return 1;
113 default:
114 NOTREACHED() << "Unexpected result from WaitForMultipleObjects: "
115 << result;
116 return 1;
117 }
118 }
119 }
120
121 // Implements a thread to launch the ChromeWatcherClient and block on
122 // EnsureInitialized. Provides various helpers to interact with the
123 // ChromeWatcherClient.
124 class ChromeWatcherClientThread : public base::SimpleThread {
125 public:
126 ChromeWatcherClientThread()
127 : client_(base::Bind(&ChromeWatcherClientThread::GenerateCommandLine,
128 base::Unretained(this))),
129 complete_(false, false),
130 result_(false),
131 SimpleThread("ChromeWatcherClientTest thread") {}
132
133 // Waits up to |timeout| for the call to EnsureInitialized to complete. If it
134 // does, sets |result| to the return value of EnsureInitialized and returns
135 // true. Otherwise returns false.
136 bool WaitForResultWithTimeout(const base::TimeDelta& timeout, bool* result) {
137 if (!complete_.TimedWait(timeout))
138 return false;
139 *result = result_;
140 return true;
141 }
142
143 // Waits indefinitely for the call to WaitForInitialization to complete.
144 // Returns the return value of WaitForInitialization.
145 bool WaitForResult() {
146 complete_.Wait();
147 return result_;
148 }
149
150 ChromeWatcherClient& client() { return client_; }
151
152 base::string16 NamedEventSuffix() {
153 return base::UintToString16(base::GetCurrentProcId());
154 }
155
156 // base::SimpleThread implementation.
157 void Run() override {
158 result_ = client_.LaunchWatcher();
159 if (result_)
160 result_ = client_.EnsureInitialized();
161 complete_.Signal();
162 }
163
164 private:
165 // Returns a command line to launch back into ChromeWatcherClientTestProcess.
166 base::CommandLine GenerateCommandLine(HANDLE on_initialized_event,
167 HANDLE parent_handle) {
168 base::CommandLine ret = base::GetMultiProcessTestChildBaseCommandLine();
169 ret.AppendSwitchASCII(switches::kTestChildProcess,
170 "ChromeWatcherClientTestProcess");
171 ret.AppendSwitchASCII(kEventHandle,
172 base::UintToString(reinterpret_cast<unsigned int>(
173 on_initialized_event)));
174 ret.AppendSwitchASCII(
175 kParentHandle,
176 base::UintToString(reinterpret_cast<unsigned int>(parent_handle)));
177 ret.AppendSwitchASCII(kNamedEventSuffix,
178 base::UTF16ToASCII(NamedEventSuffix()));
179 return ret;
180 }
181
182 // The instance under test.
183 ChromeWatcherClient client_;
184 // Signaled when WaitForInitialization returns.
185 base::WaitableEvent complete_;
186 // The return value of WaitForInitialization.
187 bool result_;
188
189 DISALLOW_COPY_AND_ASSIGN(ChromeWatcherClientThread);
190 };
191
192 } // namespace
193
194 class ChromeWatcherClientTest : public testing::Test {
195 protected:
196 // Sends a signal to the simulated watcher process to exit. Returns true if
197 // successful.
198 bool SignalExit() { return FALSE != ::SetEvent(exit_event_.Get()); }
199
200 // Sends a signal to the simulated watcher process to signal its
201 // "initialization". Returns true if successful.
202 bool SignalInitialize() {
203 return FALSE != ::SetEvent(initialize_event_.Get());
204 }
205
206 // The helper thread, which also provides access to the ChromeWatcherClient.
207 ChromeWatcherClientThread& thread() { return thread_; }
208
209 // testing::Test implementation.
210 void SetUp() override {
211 exit_event_.Set(::CreateEvent(
212 NULL, FALSE, FALSE,
213 (kExitEventBaseName + thread_.NamedEventSuffix()).c_str()));
214 ASSERT_TRUE(exit_event_.IsValid());
215 initialize_event_.Set(::CreateEvent(
216 NULL, FALSE, FALSE,
217 (kInitializeEventBaseName + thread_.NamedEventSuffix()).c_str()));
218 ASSERT_TRUE(initialize_event_.IsValid());
219 }
220
221 void TearDown() override {
222 // Even if we never launched, the following is harmless.
223 SignalExit();
224 int exit_code = 0;
225 thread_.client().WaitForExit(&exit_code);
226 thread_.Join();
227 }
228
229 private:
230 // Used to launch and block on the Chrome watcher process in a background
231 // thread.
232 ChromeWatcherClientThread thread_;
233 // Used to signal the Chrome watcher process to exit.
234 base::win::ScopedHandle exit_event_;
235 // Used to signal the Chrome watcher process to signal its own
236 // initialization..
237 base::win::ScopedHandle initialize_event_;
238 };
239
240 TEST_F(ChromeWatcherClientTest, SuccessTest) {
241 thread().Start();
242 bool result = false;
243 // Give a broken implementation a chance to exit unexpectedly.
244 ASSERT_FALSE(thread().WaitForResultWithTimeout(
245 base::TimeDelta::FromMilliseconds(100), &result));
246 ASSERT_TRUE(SignalInitialize());
247 ASSERT_TRUE(thread().WaitForResult());
248 // The watcher should still be running. Give a broken implementation a chance
249 // to exit unexpectedly, then signal it to exit.
250 int exit_code = 0;
251 ASSERT_FALSE(thread().client().WaitForExitWithTimeout(
252 base::TimeDelta::FromMilliseconds(100), &exit_code));
253 SignalExit();
254 ASSERT_TRUE(thread().client().WaitForExit(&exit_code));
255 ASSERT_EQ(0, exit_code);
256 }
257
258 TEST_F(ChromeWatcherClientTest, FailureTest) {
259 thread().Start();
260 bool result = false;
261 // Give a broken implementation a chance to exit unexpectedly.
262 ASSERT_FALSE(thread().WaitForResultWithTimeout(
263 base::TimeDelta::FromMilliseconds(100), &result));
264 ASSERT_TRUE(SignalExit());
265 ASSERT_FALSE(thread().WaitForResult());
266 int exit_code = 0;
267 ASSERT_TRUE(
268 thread().client().WaitForExitWithTimeout(base::TimeDelta(), &exit_code));
269 ASSERT_EQ(0, exit_code);
270 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698