Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(169)

Side by Side Diff: util/test/multiprocess_posix.cc

Issue 1051533002: test: Move util/test to its own top-level directory, test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Rebase Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "util/test/multiprocess.h"
16
17 #include <signal.h>
18 #include <stdlib.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21
22 #include <string>
23
24 #include "base/auto_reset.h"
25 #include "base/files/scoped_file.h"
26 #include "base/logging.h"
27 #include "base/memory/scoped_ptr.h"
28 #include "base/posix/eintr_wrapper.h"
29 #include "base/strings/stringprintf.h"
30 #include "gtest/gtest.h"
31 #include "util/misc/scoped_forbid_return.h"
32 #include "util/test/errors.h"
33
34 namespace crashpad {
35 namespace test {
36
37 namespace internal {
38
39 struct MultiprocessInfo {
40 MultiprocessInfo()
41 : pipe_c2p_read(-1),
42 pipe_c2p_write(-1),
43 pipe_p2c_read(-1),
44 pipe_p2c_write(-1),
45 child_pid(0) {}
46
47 base::ScopedFD pipe_c2p_read; // child to parent
48 base::ScopedFD pipe_c2p_write; // child to parent
49 base::ScopedFD pipe_p2c_read; // parent to child
50 base::ScopedFD pipe_p2c_write; // parent to child
51 pid_t child_pid; // valid only in parent
52 };
53
54 } // namespace internal
55
56 Multiprocess::Multiprocess()
57 : info_(nullptr),
58 code_(EXIT_SUCCESS),
59 reason_(kTerminationNormal) {
60 }
61
62 void Multiprocess::Run() {
63 ASSERT_EQ(nullptr, info_);
64 scoped_ptr<internal::MultiprocessInfo> info(new internal::MultiprocessInfo);
65 base::AutoReset<internal::MultiprocessInfo*> reset_info(&info_, info.get());
66
67 ASSERT_NO_FATAL_FAILURE(PreFork());
68
69 pid_t pid = fork();
70 ASSERT_GE(pid, 0) << ErrnoMessage("fork");
71
72 if (pid > 0) {
73 info_->child_pid = pid;
74
75 RunParent();
76
77 // Waiting for the child happens here instead of in RunParent() because even
78 // if RunParent() returns early due to a gtest fatal assertion failure, the
79 // child should still be reaped.
80
81 // This will make the parent hang up on the child as much as would be
82 // visible from the child’s perspective. The child’s side of the pipe will
83 // be broken, the child’s remote port will become a dead name, and an
84 // attempt by the child to look up the service will fail. If this weren’t
85 // done, the child might hang while waiting for a parent that has already
86 // triggered a fatal assertion failure to do something.
87 info.reset();
88 info_ = nullptr;
89
90 int status;
91 pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
92 ASSERT_EQ(pid, wait_pid) << ErrnoMessage("waitpid");
93
94 TerminationReason reason;
95 int code;
96 std::string message;
97 if (WIFEXITED(status)) {
98 reason = kTerminationNormal;
99 code = WEXITSTATUS(status);
100 message = base::StringPrintf("Child exited with code %d, expected", code);
101 } else if (WIFSIGNALED(status)) {
102 reason = kTerminationSignal;
103 code = WTERMSIG(status);
104 message =
105 base::StringPrintf("Child terminated by signal %d (%s)%s, expected",
106 code,
107 strsignal(code),
108 WCOREDUMP(status) ? " (core dumped)" : "");
109 } else {
110 FAIL() << "Unknown termination reason";
111 }
112
113 if (reason_ == kTerminationNormal) {
114 message += base::StringPrintf(" exit with code %d", code_);
115 } else if (reason == kTerminationSignal) {
116 message += base::StringPrintf(" termination by signal %d", code_);
117 }
118
119 if (reason != reason_ || code != code_) {
120 ADD_FAILURE() << message;
121 }
122 } else {
123 RunChild();
124 }
125 }
126
127 void Multiprocess::SetExpectedChildTermination(TerminationReason reason,
128 int code) {
129 reason_ = reason;
130 code_ = code;
131 }
132
133 Multiprocess::~Multiprocess() {
134 }
135
136 void Multiprocess::PreFork() {
137 int pipe_fds_c2p[2];
138 int rv = pipe(pipe_fds_c2p);
139 ASSERT_EQ(0, rv) << ErrnoMessage("pipe");
140
141 info_->pipe_c2p_read.reset(pipe_fds_c2p[0]);
142 info_->pipe_c2p_write.reset(pipe_fds_c2p[1]);
143
144 int pipe_fds_p2c[2];
145 rv = pipe(pipe_fds_p2c);
146 ASSERT_EQ(0, rv) << ErrnoMessage("pipe");
147
148 info_->pipe_p2c_read.reset(pipe_fds_p2c[0]);
149 info_->pipe_p2c_write.reset(pipe_fds_p2c[1]);
150 }
151
152 pid_t Multiprocess::ChildPID() const {
153 EXPECT_NE(0, info_->child_pid);
154 return info_->child_pid;
155 }
156
157 FileHandle Multiprocess::ReadPipeHandle() const {
158 int fd = info_->child_pid ? info_->pipe_c2p_read.get()
159 : info_->pipe_p2c_read.get();
160 CHECK_NE(fd, -1);
161 return fd;
162 }
163
164 FileHandle Multiprocess::WritePipeHandle() const {
165 int fd = info_->child_pid ? info_->pipe_p2c_write.get()
166 : info_->pipe_c2p_write.get();
167 CHECK_NE(fd, -1);
168 return fd;
169 }
170
171 void Multiprocess::CloseReadPipe() {
172 if (info_->child_pid) {
173 info_->pipe_c2p_read.reset();
174 } else {
175 info_->pipe_p2c_read.reset();
176 }
177 }
178
179 void Multiprocess::CloseWritePipe() {
180 if (info_->child_pid) {
181 info_->pipe_p2c_write.reset();
182 } else {
183 info_->pipe_c2p_write.reset();
184 }
185 }
186
187 void Multiprocess::RunParent() {
188 // The parent uses the read end of c2p and the write end of p2c.
189 info_->pipe_c2p_write.reset();
190 info_->pipe_p2c_read.reset();
191
192 MultiprocessParent();
193
194 info_->pipe_c2p_read.reset();
195 info_->pipe_p2c_write.reset();
196 }
197
198 void Multiprocess::RunChild() {
199 ScopedForbidReturn forbid_return;
200
201 // The child uses the write end of c2p and the read end of p2c.
202 info_->pipe_c2p_read.reset();
203 info_->pipe_p2c_write.reset();
204
205 MultiprocessChild();
206
207 info_->pipe_c2p_write.reset();
208 info_->pipe_p2c_read.reset();
209
210 if (testing::Test::HasFailure()) {
211 // Trigger the ScopedForbidReturn destructor.
212 return;
213 }
214
215 // In a forked child, exit() is unsafe. Use _exit() instead.
216 _exit(EXIT_SUCCESS);
217 }
218
219 } // namespace test
220 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698