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(it
.Name(), base::StringPrintf(L
"%d-", proc_id
),
104 ASSERT_EQ(ERROR_SUCCESS
, key
.ReadValueDW(it
.Name(), &value
));
105 ASSERT_EQ(exit_code
, value
);
109 base::CommandLine cmd_line_
;
110 registry_util::RegistryOverrideManager override_manager_
;
115 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherInvalidHandleFailsInit
) {
116 ExitCodeWatcher
watcher(kRegistryPath
);
118 // A waitable event has a non process-handle.
119 base::Process
event(::CreateEvent(NULL
, false, false, NULL
));
121 // A non-process handle should fail.
122 EXPECT_FALSE(watcher
.Initialize(event
.Pass()));
125 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherNoAccessHandleFailsInit
) {
126 ExitCodeWatcher
watcher(kRegistryPath
);
128 // Open a SYNCHRONIZE-only handle to this process.
129 base::Process self
= OpenSelfWithAccess(SYNCHRONIZE
);
130 ASSERT_TRUE(self
.IsValid());
132 // A process handle with insufficient access should fail.
133 EXPECT_FALSE(watcher
.Initialize(self
.Pass()));
136 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherSucceedsInit
) {
137 ExitCodeWatcher
watcher(kRegistryPath
);
139 // Open a handle to this process with sufficient access for the watcher.
141 OpenSelfWithAccess(SYNCHRONIZE
| PROCESS_QUERY_INFORMATION
);
142 ASSERT_TRUE(self
.IsValid());
144 // A process handle with sufficient access should succeed init.
145 EXPECT_TRUE(watcher
.Initialize(self
.Pass()));
148 TEST_F(ExitCodeWatcherTest
, ExitCodeWatcherOnExitedProcess
) {
149 ScopedSleeperProcess sleeper
;
150 ASSERT_NO_FATAL_FAILURE(sleeper
.Launch());
152 ExitCodeWatcher
watcher(kRegistryPath
);
154 EXPECT_TRUE(watcher
.Initialize(sleeper
.process().Duplicate()));
156 // Verify that the watcher wrote a sentinel for the process.
157 VerifyWroteExitCode(sleeper
.process().Pid(), STILL_ACTIVE
);
159 // Kill the sleeper, and make sure it's exited before we continue.
160 ASSERT_NO_FATAL_FAILURE(sleeper
.Kill(kExitCode
, true));
162 watcher
.WaitForExit();
163 EXPECT_EQ(kExitCode
, watcher
.exit_code());
165 VerifyWroteExitCode(sleeper
.process().Pid(), kExitCode
);
168 } // namespace browser_watcher