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(base::StartsWith(
103 base::StringPrintf(L
"%d-", proc_id
),
104 base::CompareCase::SENSITIVE
));
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