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);
43 EXPECT_TRUE(process_
.WaitForExit(&exit_code
));
48 ASSERT_FALSE(process_
.IsValid());
50 base::CommandLine
cmd_line(base::GetMultiProcessTestChildBaseCommandLine());
51 base::LaunchOptions options
;
52 options
.start_hidden
= true;
53 process_
= base::SpawnMultiProcessTestChild("Sleeper", cmd_line
, options
);
54 ASSERT_TRUE(process_
.IsValid());
57 void Kill(int exit_code
, bool wait
) {
58 ASSERT_TRUE(process_
.IsValid());
59 ASSERT_FALSE(is_killed_
);
60 process_
.Terminate(exit_code
, false);
61 int seen_exit_code
= 0;
62 EXPECT_TRUE(process_
.WaitForExit(&seen_exit_code
));
63 EXPECT_EQ(exit_code
, seen_exit_code
);
67 const base::Process
& process() const { return process_
; }
70 base::Process process_
;
74 class ExitCodeWatcherTest
: public testing::Test
{
76 typedef testing::Test Super
;
78 static const int kExitCode
= 0xCAFEBABE;
80 ExitCodeWatcherTest() : cmd_line_(base::CommandLine::NO_PROGRAM
) {}
82 void SetUp() override
{
85 override_manager_
.OverrideRegistry(HKEY_CURRENT_USER
);
88 base::Process
OpenSelfWithAccess(uint32 access
) {
89 return base::Process::OpenWithAccess(base::GetCurrentProcId(), access
);
92 void VerifyWroteExitCode(base::ProcessId proc_id
, int exit_code
) {
93 base::win::RegistryValueIterator
it(
94 HKEY_CURRENT_USER
, kRegistryPath
);
96 ASSERT_EQ(1, it
.ValueCount());
97 base::win::RegKey
key(HKEY_CURRENT_USER
,
101 // The value name should encode the process id at the start.
102 EXPECT_TRUE(StartsWith(it
.Name(),
103 base::StringPrintf(L
"%d-", proc_id
),
106 ASSERT_EQ(ERROR_SUCCESS
, key
.ReadValueDW(it
.Name(), &value
));
107 ASSERT_EQ(exit_code
, value
);
111 base::CommandLine cmd_line_
;
112 registry_util::RegistryOverrideManager override_manager_
;
117 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherInvalidHandleFailsInit
) {
118 ExitCodeWatcher
watcher(kRegistryPath
);
120 // A waitable event has a non process-handle.
121 base::Process
event(::CreateEvent(NULL
, false, false, NULL
));
123 // A non-process handle should fail.
124 EXPECT_FALSE(watcher
.Initialize(event
.Pass()));
127 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherNoAccessHandleFailsInit
) {
128 ExitCodeWatcher
watcher(kRegistryPath
);
130 // Open a SYNCHRONIZE-only handle to this process.
131 base::Process self
= OpenSelfWithAccess(SYNCHRONIZE
);
132 ASSERT_TRUE(self
.IsValid());
134 // A process handle with insufficient access should fail.
135 EXPECT_FALSE(watcher
.Initialize(self
.Pass()));
138 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherSucceedsInit
) {
139 ExitCodeWatcher
watcher(kRegistryPath
);
141 // Open a handle to this process with sufficient access for the watcher.
143 OpenSelfWithAccess(SYNCHRONIZE
| PROCESS_QUERY_INFORMATION
);
144 ASSERT_TRUE(self
.IsValid());
146 // A process handle with sufficient access should succeed init.
147 EXPECT_TRUE(watcher
.Initialize(self
.Pass()));
150 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherOnExitedProcess
) {
151 ScopedSleeperProcess sleeper
;
152 ASSERT_NO_FATAL_FAILURE(sleeper
.Launch());
154 ExitCodeWatcher
watcher(kRegistryPath
);
156 EXPECT_TRUE(watcher
.Initialize(sleeper
.process().Duplicate()));
158 // Verify that the watcher wrote a sentinel for the process.
159 VerifyWroteExitCode(sleeper
.process().Pid(), STILL_ACTIVE
);
161 // Kill the sleeper, and make sure it's exited before we continue.
162 ASSERT_NO_FATAL_FAILURE(sleeper
.Kill(kExitCode
, true));
164 watcher
.WaitForExit();
165 EXPECT_EQ(kExitCode
, watcher
.exit_code());
167 VerifyWroteExitCode(sleeper
.process().Pid(), kExitCode
);
170 } // namespace browser_watcher