1// Copyright (c) 2012 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.
4
5#include <windows.h>
6
7#include <string>
8
9#include "base/command_line.h"
10#include "base/process/kill.h"
11#include "base/test/multiprocess_test.h"
12#include "base/win/scoped_process_information.h"
13#include "testing/multiprocess_func_list.h"
14
15namespace {
16
17const DWORD kProcessId = 4321;
18const DWORD kThreadId = 1234;
19const HANDLE kProcessHandle = reinterpret_cast<HANDLE>(7651);
20const HANDLE kThreadHandle = reinterpret_cast<HANDLE>(1567);
21
22void MockCreateProcess(base::win::ScopedProcessInformation* process_info) {
23  PROCESS_INFORMATION process_information = {};
24  process_information.dwProcessId = kProcessId;
25  process_information.dwThreadId = kThreadId;
26  process_information.hProcess = kProcessHandle;
27  process_information.hThread = kThreadHandle;
28  process_info->Set(process_information);
29}
30
31}  // namespace
32
33class ScopedProcessInformationTest : public base::MultiProcessTest {
34 protected:
35  void DoCreateProcess(const std::string& main_id,
36                       PROCESS_INFORMATION* process_handle);
37};
38
39MULTIPROCESS_TEST_MAIN(ReturnSeven) {
40  return 7;
41}
42
43MULTIPROCESS_TEST_MAIN(ReturnNine) {
44  return 9;
45}
46
47void ScopedProcessInformationTest::DoCreateProcess(
48    const std::string& main_id, PROCESS_INFORMATION* process_handle) {
49  std::wstring cmd_line =
50      this->MakeCmdLine(main_id, false).GetCommandLineString();
51  STARTUPINFO startup_info = {};
52  startup_info.cb = sizeof(startup_info);
53
54  EXPECT_TRUE(::CreateProcess(NULL,
55                              const_cast<wchar_t*>(cmd_line.c_str()),
56                              NULL, NULL, false, 0, NULL, NULL,
57                              &startup_info, process_handle));
58}
59
60TEST_F(ScopedProcessInformationTest, InitiallyInvalid) {
61  base::win::ScopedProcessInformation process_info;
62  ASSERT_FALSE(process_info.IsValid());
63}
64
65TEST_F(ScopedProcessInformationTest, Receive) {
66  base::win::ScopedProcessInformation process_info;
67  MockCreateProcess(&process_info);
68
69  EXPECT_TRUE(process_info.IsValid());
70  EXPECT_EQ(kProcessId, process_info.process_id());
71  EXPECT_EQ(kThreadId, process_info.thread_id());
72  EXPECT_EQ(kProcessHandle, process_info.process_handle());
73  EXPECT_EQ(kThreadHandle, process_info.thread_handle());
74  PROCESS_INFORMATION to_discard = process_info.Take();
75}
76
77TEST_F(ScopedProcessInformationTest, TakeProcess) {
78  base::win::ScopedProcessInformation process_info;
79  MockCreateProcess(&process_info);
80
81  HANDLE process = process_info.TakeProcessHandle();
82  EXPECT_EQ(kProcessHandle, process);
83  EXPECT_EQ(NULL, process_info.process_handle());
84  EXPECT_EQ(0, process_info.process_id());
85  EXPECT_TRUE(process_info.IsValid());
86  PROCESS_INFORMATION to_discard = process_info.Take();
87}
88
89TEST_F(ScopedProcessInformationTest, TakeThread) {
90  base::win::ScopedProcessInformation process_info;
91  MockCreateProcess(&process_info);
92
93  HANDLE thread = process_info.TakeThreadHandle();
94  EXPECT_EQ(kThreadHandle, thread);
95  EXPECT_EQ(NULL, process_info.thread_handle());
96  EXPECT_EQ(0, process_info.thread_id());
97  EXPECT_TRUE(process_info.IsValid());
98  PROCESS_INFORMATION to_discard = process_info.Take();
99}
100
101TEST_F(ScopedProcessInformationTest, TakeBoth) {
102  base::win::ScopedProcessInformation process_info;
103  MockCreateProcess(&process_info);
104
105  HANDLE process = process_info.TakeProcessHandle();
106  HANDLE thread = process_info.TakeThreadHandle();
107  EXPECT_FALSE(process_info.IsValid());
108  PROCESS_INFORMATION to_discard = process_info.Take();
109}
110
111TEST_F(ScopedProcessInformationTest, TakeWholeStruct) {
112  base::win::ScopedProcessInformation process_info;
113  MockCreateProcess(&process_info);
114
115  PROCESS_INFORMATION to_discard = process_info.Take();
116  EXPECT_EQ(kProcessId, to_discard.dwProcessId);
117  EXPECT_EQ(kThreadId, to_discard.dwThreadId);
118  EXPECT_EQ(kProcessHandle, to_discard.hProcess);
119  EXPECT_EQ(kThreadHandle, to_discard.hThread);
120  EXPECT_FALSE(process_info.IsValid());
121}
122
123TEST_F(ScopedProcessInformationTest, Duplicate) {
124  PROCESS_INFORMATION temp_process_information;
125  DoCreateProcess("ReturnSeven", &temp_process_information);
126  base::win::ScopedProcessInformation process_info;
127  process_info.Set(temp_process_information);
128
129  base::win::ScopedProcessInformation duplicate;
130  duplicate.DuplicateFrom(process_info);
131
132  ASSERT_TRUE(process_info.IsValid());
133  ASSERT_NE(0u, process_info.process_id());
134  ASSERT_EQ(duplicate.process_id(), process_info.process_id());
135  ASSERT_NE(0u, process_info.thread_id());
136  ASSERT_EQ(duplicate.thread_id(), process_info.thread_id());
137
138  // Validate that we have separate handles that are good.
139  int exit_code = 0;
140  ASSERT_TRUE(base::WaitForExitCode(process_info.TakeProcessHandle(),
141                                    &exit_code));
142  ASSERT_EQ(7, exit_code);
143
144  exit_code = 0;
145  ASSERT_TRUE(base::WaitForExitCode(duplicate.TakeProcessHandle(),
146                                    &exit_code));
147  ASSERT_EQ(7, exit_code);
148
149  ASSERT_TRUE(::CloseHandle(process_info.TakeThreadHandle()));
150  ASSERT_TRUE(::CloseHandle(duplicate.TakeThreadHandle()));
151}
152
153TEST_F(ScopedProcessInformationTest, Set) {
154  base::win::ScopedProcessInformation base_process_info;
155  MockCreateProcess(&base_process_info);
156
157  PROCESS_INFORMATION base_struct = base_process_info.Take();
158
159  base::win::ScopedProcessInformation process_info;
160  process_info.Set(base_struct);
161
162  EXPECT_EQ(kProcessId, process_info.process_id());
163  EXPECT_EQ(kThreadId, process_info.thread_id());
164  EXPECT_EQ(kProcessHandle, process_info.process_handle());
165  EXPECT_EQ(kThreadHandle, process_info.thread_handle());
166  base_struct = process_info.Take();
167}
168