OLD | NEW |
| (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 <stdlib.h> | |
18 #include <sys/signal.h> | |
19 #include <unistd.h> | |
20 | |
21 #include "base/basictypes.h" | |
22 #include "gtest/gtest.h" | |
23 #include "util/file/file_io.h" | |
24 | |
25 namespace crashpad { | |
26 namespace test { | |
27 namespace { | |
28 | |
29 class TestMultiprocess final : public Multiprocess { | |
30 public: | |
31 TestMultiprocess() : Multiprocess() {} | |
32 | |
33 ~TestMultiprocess() {} | |
34 | |
35 private: | |
36 // Multiprocess: | |
37 | |
38 void MultiprocessParent() override { | |
39 int read_fd = ReadPipeFD(); | |
40 char c; | |
41 CheckedReadFile(read_fd, &c, 1); | |
42 EXPECT_EQ('M', c); | |
43 | |
44 pid_t pid; | |
45 CheckedReadFile(read_fd, &pid, sizeof(pid)); | |
46 EXPECT_EQ(pid, ChildPID()); | |
47 | |
48 c = 'm'; | |
49 CheckedWriteFile(WritePipeFD(), &c, 1); | |
50 | |
51 // The child will close its end of the pipe and exit. Make sure that the | |
52 // parent sees EOF. | |
53 CheckedReadFileAtEOF(read_fd); | |
54 } | |
55 | |
56 void MultiprocessChild() override { | |
57 int write_fd = WritePipeFD(); | |
58 | |
59 char c = 'M'; | |
60 CheckedWriteFile(write_fd, &c, 1); | |
61 | |
62 pid_t pid = getpid(); | |
63 CheckedWriteFile(write_fd, &pid, sizeof(pid)); | |
64 | |
65 CheckedReadFile(ReadPipeFD(), &c, 1); | |
66 EXPECT_EQ('m', c); | |
67 } | |
68 | |
69 DISALLOW_COPY_AND_ASSIGN(TestMultiprocess); | |
70 }; | |
71 | |
72 TEST(Multiprocess, Multiprocess) { | |
73 TestMultiprocess multiprocess; | |
74 multiprocess.Run(); | |
75 } | |
76 | |
77 class TestMultiprocessUnclean final : public Multiprocess { | |
78 public: | |
79 enum TerminationType { | |
80 kExitSuccess = 0, | |
81 kExitFailure, | |
82 kExit2, | |
83 kAbort, | |
84 }; | |
85 | |
86 explicit TestMultiprocessUnclean(TerminationType type) | |
87 : Multiprocess(), | |
88 type_(type) { | |
89 if (type_ == kAbort) { | |
90 SetExpectedChildTermination(kTerminationSignal, SIGABRT); | |
91 } else { | |
92 SetExpectedChildTermination(kTerminationNormal, ExitCode()); | |
93 } | |
94 } | |
95 | |
96 ~TestMultiprocessUnclean() {} | |
97 | |
98 private: | |
99 int ExitCode() const { | |
100 return type_; | |
101 } | |
102 | |
103 // Multiprocess: | |
104 | |
105 void MultiprocessParent() override { | |
106 } | |
107 | |
108 void MultiprocessChild() override { | |
109 if (type_ == kAbort) { | |
110 abort(); | |
111 } else { | |
112 _exit(ExitCode()); | |
113 } | |
114 } | |
115 | |
116 TerminationType type_; | |
117 | |
118 DISALLOW_COPY_AND_ASSIGN(TestMultiprocessUnclean); | |
119 }; | |
120 | |
121 TEST(Multiprocess, SuccessfulExit) { | |
122 TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitSuccess); | |
123 multiprocess.Run(); | |
124 } | |
125 | |
126 TEST(Multiprocess, UnsuccessfulExit) { | |
127 TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitFailure); | |
128 multiprocess.Run(); | |
129 } | |
130 | |
131 TEST(Multiprocess, Exit2) { | |
132 TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExit2); | |
133 multiprocess.Run(); | |
134 } | |
135 | |
136 TEST(Multiprocess, AbortSignal) { | |
137 TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kAbort); | |
138 multiprocess.Run(); | |
139 } | |
140 | |
141 class TestMultiprocessClosePipe final : public Multiprocess { | |
142 public: | |
143 enum WhoCloses { | |
144 kParentCloses = 0, | |
145 kChildCloses, | |
146 }; | |
147 enum WhatCloses { | |
148 kReadCloses = 0, | |
149 kWriteCloses, | |
150 kReadAndWriteClose, | |
151 }; | |
152 | |
153 TestMultiprocessClosePipe(WhoCloses who_closes, WhatCloses what_closes) | |
154 : Multiprocess(), | |
155 who_closes_(who_closes), | |
156 what_closes_(what_closes) { | |
157 } | |
158 | |
159 ~TestMultiprocessClosePipe() {} | |
160 | |
161 private: | |
162 void VerifyInitial() { | |
163 ASSERT_NE(-1, ReadPipeFD()); | |
164 ASSERT_NE(-1, WritePipeFD()); | |
165 } | |
166 | |
167 // Verifies that the partner process did what it was supposed to do. This must | |
168 // only be called when who_closes_ names the partner process, not this | |
169 // process. | |
170 // | |
171 // If the partner was supposed to close its write pipe, the read pipe will be | |
172 // checked to ensure that it shows end-of-file. | |
173 // | |
174 // If the partner was supposed to close its read pipe, the write pipe will be | |
175 // checked to ensure that a checked write causes death. This can only be done | |
176 // if the partner also provides some type of signal when it has closed its | |
177 // read pipe, which is done in the form of it closing its write pipe, causing | |
178 // the read pipe in this process to show end-of-file. | |
179 void VerifyPartner() { | |
180 if (what_closes_ == kWriteCloses) { | |
181 CheckedReadFileAtEOF(ReadPipeFD()); | |
182 } else if (what_closes_ == kReadAndWriteClose) { | |
183 CheckedReadFileAtEOF(ReadPipeFD()); | |
184 char c = '\0'; | |
185 | |
186 // This will raise SIGPIPE. If fatal (the normal case), that will cause | |
187 // process termination. If SIGPIPE is being handled somewhere, the write | |
188 // will still fail and set errno to EPIPE, and CheckedWriteFile() will | |
189 // abort execution. Regardless of how SIGPIPE is handled, the process will | |
190 // be terminated. Because the actual termination mechanism is not known, | |
191 // no regex can be specified. | |
192 EXPECT_DEATH(CheckedWriteFile(WritePipeFD(), &c, 1), ""); | |
193 } | |
194 } | |
195 | |
196 void Close() { | |
197 switch (what_closes_) { | |
198 case kReadCloses: | |
199 CloseReadPipe(); | |
200 EXPECT_NE(-1, WritePipeFD()); | |
201 EXPECT_DEATH(ReadPipeFD(), "fd"); | |
202 break; | |
203 case kWriteCloses: | |
204 CloseWritePipe(); | |
205 EXPECT_NE(-1, ReadPipeFD()); | |
206 EXPECT_DEATH(WritePipeFD(), "fd"); | |
207 break; | |
208 case kReadAndWriteClose: | |
209 CloseReadPipe(); | |
210 CloseWritePipe(); | |
211 EXPECT_DEATH(ReadPipeFD(), "fd"); | |
212 EXPECT_DEATH(WritePipeFD(), "fd"); | |
213 break; | |
214 } | |
215 } | |
216 | |
217 // Multiprocess: | |
218 | |
219 void MultiprocessParent() override { | |
220 ASSERT_NO_FATAL_FAILURE(VerifyInitial()); | |
221 | |
222 if (who_closes_ == kParentCloses) { | |
223 Close(); | |
224 } else { | |
225 VerifyPartner(); | |
226 } | |
227 } | |
228 | |
229 void MultiprocessChild() override { | |
230 ASSERT_NO_FATAL_FAILURE(VerifyInitial()); | |
231 | |
232 if (who_closes_ == kChildCloses) { | |
233 Close(); | |
234 } else { | |
235 VerifyPartner(); | |
236 } | |
237 } | |
238 | |
239 WhoCloses who_closes_; | |
240 WhatCloses what_closes_; | |
241 | |
242 DISALLOW_COPY_AND_ASSIGN(TestMultiprocessClosePipe); | |
243 }; | |
244 | |
245 TEST(MultiprocessDeathTest, ParentClosesReadPipe) { | |
246 TestMultiprocessClosePipe multiprocess( | |
247 TestMultiprocessClosePipe::kParentCloses, | |
248 TestMultiprocessClosePipe::kReadCloses); | |
249 multiprocess.Run(); | |
250 } | |
251 | |
252 TEST(MultiprocessDeathTest, ParentClosesWritePipe) { | |
253 TestMultiprocessClosePipe multiprocess( | |
254 TestMultiprocessClosePipe::kParentCloses, | |
255 TestMultiprocessClosePipe::kWriteCloses); | |
256 multiprocess.Run(); | |
257 } | |
258 | |
259 TEST(MultiprocessDeathTest, ParentClosesReadAndWritePipe) { | |
260 TestMultiprocessClosePipe multiprocess( | |
261 TestMultiprocessClosePipe::kParentCloses, | |
262 TestMultiprocessClosePipe::kReadAndWriteClose); | |
263 multiprocess.Run(); | |
264 } | |
265 | |
266 TEST(MultiprocessDeathTest, ChildClosesReadPipe) { | |
267 TestMultiprocessClosePipe multiprocess( | |
268 TestMultiprocessClosePipe::kChildCloses, | |
269 TestMultiprocessClosePipe::kReadCloses); | |
270 multiprocess.Run(); | |
271 } | |
272 | |
273 TEST(MultiprocessDeathTest, ChildClosesWritePipe) { | |
274 TestMultiprocessClosePipe multiprocess( | |
275 TestMultiprocessClosePipe::kChildCloses, | |
276 TestMultiprocessClosePipe::kWriteCloses); | |
277 multiprocess.Run(); | |
278 } | |
279 | |
280 TEST(MultiprocessDeathTest, ChildClosesReadAndWritePipe) { | |
281 TestMultiprocessClosePipe multiprocess( | |
282 TestMultiprocessClosePipe::kChildCloses, | |
283 TestMultiprocessClosePipe::kReadAndWriteClose); | |
284 multiprocess.Run(); | |
285 } | |
286 | |
287 } // namespace | |
288 } // namespace test | |
289 } // namespace crashpad | |
OLD | NEW |