1 // Copyright 2014 The Chromium Authors
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/exit_code_watcher_win.h"
11 #include "base/command_line.h"
12 #include "base/process/process.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/test/multiprocess_test.h"
15 #include "base/test/test_reg_util_win.h"
16 #include "base/test/test_timeouts.h"
17 #include "base/threading/platform_thread.h"
18 #include "base/time/time.h"
19 #include "base/win/scoped_handle.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "testing/multiprocess_func_list.h"
25 MULTIPROCESS_TEST_MAIN(Sleeper) {
26 // Sleep as long as possible - the test harness will kill this process to give
28 base::PlatformThread::Sleep(base::TimeDelta::Max());
32 class ScopedSleeperProcess {
34 ScopedSleeperProcess() : is_killed_(false) {}
36 ~ScopedSleeperProcess() {
37 if (process_.IsValid()) {
38 process_.Terminate(-1, false);
39 EXPECT_TRUE(process_.WaitForExit(nullptr));
44 ASSERT_FALSE(process_.IsValid());
46 base::CommandLine cmd_line(base::GetMultiProcessTestChildBaseCommandLine());
47 base::LaunchOptions options;
48 options.start_hidden = true;
49 process_ = base::SpawnMultiProcessTestChild("Sleeper", cmd_line, options);
50 ASSERT_TRUE(process_.IsValid());
53 void Kill(int exit_code, bool wait) {
54 ASSERT_TRUE(process_.IsValid());
55 ASSERT_FALSE(is_killed_);
56 process_.Terminate(exit_code, false);
57 int seen_exit_code = 0;
58 EXPECT_TRUE(process_.WaitForExit(&seen_exit_code));
59 EXPECT_EQ(exit_code, seen_exit_code);
63 const base::Process& process() const { return process_; }
66 base::Process process_;
70 class ExitCodeWatcherTest : public testing::Test {
72 typedef testing::Test Super;
74 static const int kExitCode = 0xCAFEBABE;
76 ExitCodeWatcherTest() : cmd_line_(base::CommandLine::NO_PROGRAM) {}
78 void SetUp() override { Super::SetUp(); }
80 base::Process OpenSelfWithAccess(uint32_t access) {
81 return base::Process::OpenWithAccess(base::GetCurrentProcId(), access);
85 base::CommandLine cmd_line_;
90 TEST_F(ExitCodeWatcherTest, ExitCodeWatcherNoAccessHandleFailsInit) {
91 ExitCodeWatcher watcher;
93 // Open a SYNCHRONIZE-only handle to this process.
94 base::Process self = OpenSelfWithAccess(SYNCHRONIZE);
95 ASSERT_TRUE(self.IsValid());
97 // A process handle with insufficient access should fail.
98 EXPECT_FALSE(watcher.Initialize(std::move(self)));
101 TEST_F(ExitCodeWatcherTest, ExitCodeWatcherSucceedsInit) {
102 ExitCodeWatcher watcher;
104 // Open a handle to this process with sufficient access for the watcher.
106 OpenSelfWithAccess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION);
107 ASSERT_TRUE(self.IsValid());
109 // A process handle with sufficient access should succeed init.
110 EXPECT_TRUE(watcher.Initialize(std::move(self)));
113 TEST_F(ExitCodeWatcherTest, ExitCodeWatcherOnExitedProcess) {
114 ScopedSleeperProcess sleeper;
115 ASSERT_NO_FATAL_FAILURE(sleeper.Launch());
117 ExitCodeWatcher watcher;
119 EXPECT_TRUE(watcher.Initialize(sleeper.process().Duplicate()));
121 EXPECT_TRUE(watcher.StartWatching());
123 // Kill the sleeper, and make sure it's exited before we continue.
124 ASSERT_NO_FATAL_FAILURE(sleeper.Kill(kExitCode, true));
126 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
128 // Verify we got the expected exit code
129 EXPECT_TRUE(watcher.ExitCodeForTesting() == kExitCode);
132 TEST_F(ExitCodeWatcherTest, ExitCodeWatcherStopWatching) {
133 ScopedSleeperProcess sleeper;
134 ASSERT_NO_FATAL_FAILURE(sleeper.Launch());
136 ExitCodeWatcher watcher;
138 EXPECT_TRUE(watcher.Initialize(sleeper.process().Duplicate()));
140 EXPECT_TRUE(watcher.StartWatching());
142 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
143 watcher.StopWatching();
145 // Verify we got the expected exit code
146 EXPECT_TRUE(watcher.ExitCodeForTesting() == STILL_ACTIVE);
148 // Cleanup the sleeper, and make sure it's exited before we continue.
149 ASSERT_NO_FATAL_FAILURE(sleeper.Kill(kExitCode, true));
150 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());