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 "chrome/app/chrome_watcher_client_win.h"
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"
27 const char kParentHandle
[] = "parent-handle";
28 const char kEventHandle
[] = "event-handle";
29 const char kNamedEventSuffix
[] = "named-event-suffix";
31 const base::char16 kExitEventBaseName
[] = L
"ChromeWatcherClientTestExitEvent_";
32 const base::char16 kInitializeEventBaseName
[] =
33 L
"ChromeWatcherClientTestInitializeEvent_";
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();
44 unsigned int_handle
= 0;
45 if (!base::StringToUint(str_handle
, &int_handle
)) {
46 LOG(ERROR
) << "Switch " << switch_name
<< " has invalid value "
48 return base::win::ScopedHandle();
51 return base::win::ScopedHandle(
52 reinterpret_cast<base::ProcessHandle
>(int_handle
));
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();
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.";
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
;
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
;
85 base::win::ScopedHandle
parent_process(
86 InterpretHandleSwitch(*cmd_line
, kParentHandle
));
87 if (!parent_process
.IsValid())
90 base::win::ScopedHandle
on_initialized_event(
91 InterpretHandleSwitch(*cmd_line
, kEventHandle
));
92 if (!on_initialized_event
.IsValid())
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()};
101 ::WaitForMultipleObjects(arraysize(handles
), handles
, FALSE
, INFINITE
);
106 case WAIT_OBJECT_0
+ 1:
108 ::SetEvent(on_initialized_event
.Get());
111 PLOG(ERROR
) << "Unexpected failure in WaitForMultipleObjects.";
114 NOTREACHED() << "Unexpected result from WaitForMultipleObjects: "
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
{
126 ChromeWatcherClientThread()
127 : SimpleThread("ChromeWatcherClientTest thread"),
128 client_(base::Bind(&ChromeWatcherClientThread::GenerateCommandLine
,
129 base::Unretained(this))),
130 complete_(false, false),
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(base::TimeDelta timeout
, bool* result
) {
137 if (!complete_
.TimedWait(timeout
))
143 // Waits indefinitely for the call to WaitForInitialization to complete.
144 // Returns the return value of WaitForInitialization.
145 bool WaitForResult() {
150 ChromeWatcherClient
& client() { return client_
; }
152 base::string16
NamedEventSuffix() {
153 return base::UintToString16(base::GetCurrentProcId());
156 // base::SimpleThread implementation.
157 void Run() override
{
158 result_
= client_
.LaunchWatcher();
160 result_
= client_
.EnsureInitialized();
165 // Returns a command line to launch back into ChromeWatcherClientTestProcess.
166 base::CommandLine
GenerateCommandLine(HANDLE parent_handle
,
167 HANDLE on_initialized_event
) {
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(
176 base::UintToString(reinterpret_cast<unsigned int>(parent_handle
)));
177 ret
.AppendSwitchASCII(kNamedEventSuffix
,
178 base::UTF16ToASCII(NamedEventSuffix()));
182 // The instance under test.
183 ChromeWatcherClient client_
;
184 // Signaled when WaitForInitialization returns.
185 base::WaitableEvent complete_
;
186 // The return value of WaitForInitialization.
189 DISALLOW_COPY_AND_ASSIGN(ChromeWatcherClientThread
);
194 class ChromeWatcherClientTest
: public testing::Test
{
196 // Sends a signal to the simulated watcher process to exit. Returns true if
198 bool SignalExit() { return ::SetEvent(exit_event_
.Get()) != FALSE
; }
200 // Sends a signal to the simulated watcher process to signal its
201 // "initialization". Returns true if successful.
202 bool SignalInitialize() {
203 return ::SetEvent(initialize_event_
.Get()) != FALSE
;
206 // The helper thread, which also provides access to the ChromeWatcherClient.
207 ChromeWatcherClientThread
& thread() { return thread_
; }
209 // testing::Test implementation.
210 void SetUp() override
{
211 exit_event_
.Set(::CreateEvent(
213 (kExitEventBaseName
+ thread_
.NamedEventSuffix()).c_str()));
214 ASSERT_TRUE(exit_event_
.IsValid());
215 initialize_event_
.Set(::CreateEvent(
217 (kInitializeEventBaseName
+ thread_
.NamedEventSuffix()).c_str()));
218 ASSERT_TRUE(initialize_event_
.IsValid());
221 void TearDown() override
{
222 // Even if we never launched, the following is harmless.
224 thread_
.client().WaitForExit(nullptr);
229 // Used to launch and block on the Chrome watcher process in a background
231 ChromeWatcherClientThread thread_
;
232 // Used to signal the Chrome watcher process to exit.
233 base::win::ScopedHandle exit_event_
;
234 // Used to signal the Chrome watcher process to signal its own
236 base::win::ScopedHandle initialize_event_
;
239 TEST_F(ChromeWatcherClientTest
, SuccessTest
) {
242 // Give a broken implementation a chance to exit unexpectedly.
243 ASSERT_FALSE(thread().WaitForResultWithTimeout(
244 base::TimeDelta::FromMilliseconds(100), &result
));
245 ASSERT_TRUE(SignalInitialize());
246 ASSERT_TRUE(thread().WaitForResult());
247 // The watcher should still be running. Give a broken implementation a chance
248 // to exit unexpectedly, then signal it to exit.
250 ASSERT_FALSE(thread().client().WaitForExitWithTimeout(
251 base::TimeDelta::FromMilliseconds(100), &exit_code
));
253 ASSERT_TRUE(thread().client().WaitForExit(&exit_code
));
254 ASSERT_EQ(0, exit_code
);
257 TEST_F(ChromeWatcherClientTest
, FailureTest
) {
260 // Give a broken implementation a chance to exit unexpectedly.
261 ASSERT_FALSE(thread().WaitForResultWithTimeout(
262 base::TimeDelta::FromMilliseconds(100), &result
));
263 ASSERT_TRUE(SignalExit());
264 ASSERT_FALSE(thread().WaitForResult());
267 thread().client().WaitForExitWithTimeout(base::TimeDelta(), &exit_code
));
268 ASSERT_EQ(0, exit_code
);