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