1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/app/exit_code_watcher_win.h"
#include <stdint.h>
#include <utility>
#include "base/command_line.h"
#include "base/process/process.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/multiprocess_test.h"
#include "base/test/test_reg_util_win.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "base/win/scoped_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace {
MULTIPROCESS_TEST_MAIN(Sleeper) {
// Sleep as long as possible - the test harness will kill this process to give
// it an exit code.
base::PlatformThread::Sleep(base::TimeDelta::Max());
return 1;
}
class ScopedSleeperProcess {
public:
ScopedSleeperProcess() : is_killed_(false) {}
~ScopedSleeperProcess() {
if (process_.IsValid()) {
process_.Terminate(-1, false);
EXPECT_TRUE(process_.WaitForExit(nullptr));
}
}
void Launch() {
ASSERT_FALSE(process_.IsValid());
base::CommandLine cmd_line(base::GetMultiProcessTestChildBaseCommandLine());
base::LaunchOptions options;
options.start_hidden = true;
process_ = base::SpawnMultiProcessTestChild("Sleeper", cmd_line, options);
ASSERT_TRUE(process_.IsValid());
}
void Kill(int exit_code, bool wait) {
ASSERT_TRUE(process_.IsValid());
ASSERT_FALSE(is_killed_);
process_.Terminate(exit_code, false);
int seen_exit_code = 0;
EXPECT_TRUE(process_.WaitForExit(&seen_exit_code));
EXPECT_EQ(exit_code, seen_exit_code);
is_killed_ = true;
}
const base::Process& process() const { return process_; }
private:
base::Process process_;
bool is_killed_;
};
class ExitCodeWatcherTest : public testing::Test {
public:
typedef testing::Test Super;
static const int kExitCode = 0xCAFEBABE;
ExitCodeWatcherTest() : cmd_line_(base::CommandLine::NO_PROGRAM) {}
void SetUp() override { Super::SetUp(); }
base::Process OpenSelfWithAccess(uint32_t access) {
return base::Process::OpenWithAccess(base::GetCurrentProcId(), access);
}
protected:
base::CommandLine cmd_line_;
};
} // namespace
TEST_F(ExitCodeWatcherTest, ExitCodeWatcherNoAccessHandleFailsInit) {
ExitCodeWatcher watcher;
// Open a SYNCHRONIZE-only handle to this process.
base::Process self = OpenSelfWithAccess(SYNCHRONIZE);
ASSERT_TRUE(self.IsValid());
// A process handle with insufficient access should fail.
EXPECT_FALSE(watcher.Initialize(std::move(self)));
}
TEST_F(ExitCodeWatcherTest, ExitCodeWatcherSucceedsInit) {
ExitCodeWatcher watcher;
// Open a handle to this process with sufficient access for the watcher.
base::Process self =
OpenSelfWithAccess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION);
ASSERT_TRUE(self.IsValid());
// A process handle with sufficient access should succeed init.
EXPECT_TRUE(watcher.Initialize(std::move(self)));
}
TEST_F(ExitCodeWatcherTest, ExitCodeWatcherOnExitedProcess) {
ScopedSleeperProcess sleeper;
ASSERT_NO_FATAL_FAILURE(sleeper.Launch());
ExitCodeWatcher watcher;
EXPECT_TRUE(watcher.Initialize(sleeper.process().Duplicate()));
EXPECT_TRUE(watcher.StartWatching());
// Kill the sleeper, and make sure it's exited before we continue.
ASSERT_NO_FATAL_FAILURE(sleeper.Kill(kExitCode, true));
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
// Verify we got the expected exit code
EXPECT_TRUE(watcher.ExitCodeForTesting() == kExitCode);
}
TEST_F(ExitCodeWatcherTest, ExitCodeWatcherStopWatching) {
ScopedSleeperProcess sleeper;
ASSERT_NO_FATAL_FAILURE(sleeper.Launch());
ExitCodeWatcher watcher;
EXPECT_TRUE(watcher.Initialize(sleeper.process().Duplicate()));
EXPECT_TRUE(watcher.StartWatching());
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
watcher.StopWatching();
// Verify we got the expected exit code
EXPECT_TRUE(watcher.ExitCodeForTesting() == STILL_ACTIVE);
// Cleanup the sleeper, and make sure it's exited before we continue.
ASSERT_NO_FATAL_FAILURE(sleeper.Kill(kExitCode, true));
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
}
|