1 // Copyright (c) 2014 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 "components/browser_watcher/exit_code_watcher_win.h"
7 #include "base/command_line.h"
8 #include "base/process/process.h"
9 #include "base/strings/string16.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/test/multiprocess_test.h"
14 #include "base/test/test_reg_util_win.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/time/time.h"
17 #include "base/win/scoped_handle.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "testing/multiprocess_func_list.h"
21 namespace browser_watcher
{
25 const base::char16 kRegistryPath
[] = L
"Software\\ExitCodeWatcherTest";
27 MULTIPROCESS_TEST_MAIN(Sleeper
) {
28 // Sleep forever - the test harness will kill this process to give it an
30 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(INFINITE
));
34 class ScopedSleeperProcess
{
36 ScopedSleeperProcess() : is_killed_(false) {
39 ~ScopedSleeperProcess() {
40 if (process_
.IsValid()) {
41 process_
.Terminate(-1, false);
42 EXPECT_TRUE(process_
.WaitForExit(nullptr));
47 ASSERT_FALSE(process_
.IsValid());
49 base::CommandLine
cmd_line(base::GetMultiProcessTestChildBaseCommandLine());
50 base::LaunchOptions options
;
51 options
.start_hidden
= true;
52 process_
= base::SpawnMultiProcessTestChild("Sleeper", cmd_line
, options
);
53 ASSERT_TRUE(process_
.IsValid());
56 void Kill(int exit_code
, bool wait
) {
57 ASSERT_TRUE(process_
.IsValid());
58 ASSERT_FALSE(is_killed_
);
59 process_
.Terminate(exit_code
, false);
60 int seen_exit_code
= 0;
61 EXPECT_TRUE(process_
.WaitForExit(&seen_exit_code
));
62 EXPECT_EQ(exit_code
, seen_exit_code
);
66 const base::Process
& process() const { return process_
; }
69 base::Process process_
;
73 class ExitCodeWatcherTest
: public testing::Test
{
75 typedef testing::Test Super
;
77 static const int kExitCode
= 0xCAFEBABE;
79 ExitCodeWatcherTest() : cmd_line_(base::CommandLine::NO_PROGRAM
) {}
81 void SetUp() override
{
84 override_manager_
.OverrideRegistry(HKEY_CURRENT_USER
);
87 base::Process
OpenSelfWithAccess(uint32 access
) {
88 return base::Process::OpenWithAccess(base::GetCurrentProcId(), access
);
91 void VerifyWroteExitCode(base::ProcessId proc_id
, int exit_code
) {
92 base::win::RegistryValueIterator
it(
93 HKEY_CURRENT_USER
, kRegistryPath
);
95 ASSERT_EQ(1, it
.ValueCount());
96 base::win::RegKey
key(HKEY_CURRENT_USER
,
100 // The value name should encode the process id at the start.
101 EXPECT_TRUE(StartsWith(it
.Name(),
102 base::StringPrintf(L
"%d-", proc_id
),
105 ASSERT_EQ(ERROR_SUCCESS
, key
.ReadValueDW(it
.Name(), &value
));
106 ASSERT_EQ(exit_code
, value
);
110 base::CommandLine cmd_line_
;
111 registry_util::RegistryOverrideManager override_manager_
;
116 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherInvalidHandleFailsInit
) {
117 ExitCodeWatcher
watcher(kRegistryPath
);
119 // A waitable event has a non process-handle.
120 base::Process
event(::CreateEvent(NULL
, false, false, NULL
));
122 // A non-process handle should fail.
123 EXPECT_FALSE(watcher
.Initialize(event
.Pass()));
126 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherNoAccessHandleFailsInit
) {
127 ExitCodeWatcher
watcher(kRegistryPath
);
129 // Open a SYNCHRONIZE-only handle to this process.
130 base::Process self
= OpenSelfWithAccess(SYNCHRONIZE
);
131 ASSERT_TRUE(self
.IsValid());
133 // A process handle with insufficient access should fail.
134 EXPECT_FALSE(watcher
.Initialize(self
.Pass()));
137 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherSucceedsInit
) {
138 ExitCodeWatcher
watcher(kRegistryPath
);
140 // Open a handle to this process with sufficient access for the watcher.
142 OpenSelfWithAccess(SYNCHRONIZE
| PROCESS_QUERY_INFORMATION
);
143 ASSERT_TRUE(self
.IsValid());
145 // A process handle with sufficient access should succeed init.
146 EXPECT_TRUE(watcher
.Initialize(self
.Pass()));
149 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherOnExitedProcess
) {
150 ScopedSleeperProcess sleeper
;
151 ASSERT_NO_FATAL_FAILURE(sleeper
.Launch());
153 ExitCodeWatcher
watcher(kRegistryPath
);
155 EXPECT_TRUE(watcher
.Initialize(sleeper
.process().Duplicate()));
157 // Verify that the watcher wrote a sentinel for the process.
158 VerifyWroteExitCode(sleeper
.process().Pid(), STILL_ACTIVE
);
160 // Kill the sleeper, and make sure it's exited before we continue.
161 ASSERT_NO_FATAL_FAILURE(sleeper
.Kill(kExitCode
, true));
163 watcher
.WaitForExit();
164 EXPECT_EQ(kExitCode
, watcher
.exit_code());
166 VerifyWroteExitCode(sleeper
.process().Pid(), kExitCode
);
169 } // namespace browser_watcher