Index: util/test/multiprocess_test.cc |
diff --git a/util/test/multiprocess_test.cc b/util/test/multiprocess_test.cc |
index aab0181b1f1e40260b38585e00cee268131b4100..b1a9685645186262b3082bfd74061e96d455d795 100644 |
--- a/util/test/multiprocess_test.cc |
+++ b/util/test/multiprocess_test.cc |
@@ -34,6 +34,8 @@ class TestMultiprocess final : public Multiprocess { |
~TestMultiprocess() {} |
private: |
+ // Multiprocess: |
+ |
virtual void MultiprocessParent() override { |
int read_fd = ReadPipeFD(); |
char c; |
@@ -99,6 +101,8 @@ class TestMultiprocessUnclean final : public Multiprocess { |
return type_; |
} |
+ // Multiprocess: |
+ |
virtual void MultiprocessParent() override { |
} |
@@ -115,24 +119,176 @@ class TestMultiprocessUnclean final : public Multiprocess { |
DISALLOW_COPY_AND_ASSIGN(TestMultiprocessUnclean); |
}; |
-TEST(Multiprocess, MultiprocessSuccessfulExit) { |
+TEST(Multiprocess, SuccessfulExit) { |
TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitSuccess); |
multiprocess.Run(); |
} |
-TEST(Multiprocess, MultiprocessUnsuccessfulExit) { |
+TEST(Multiprocess, UnsuccessfulExit) { |
TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitFailure); |
multiprocess.Run(); |
} |
-TEST(Multiprocess, MultiprocessExit2) { |
+TEST(Multiprocess, Exit2) { |
TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExit2); |
multiprocess.Run(); |
} |
-TEST(Multiprocess, MultiprocessAbortSignal) { |
+TEST(Multiprocess, AbortSignal) { |
TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kAbort); |
multiprocess.Run(); |
} |
+class TestMultiprocessClosePipe final : public Multiprocess { |
+ public: |
+ enum WhoCloses { |
+ kParentCloses = 0, |
+ kChildCloses, |
+ }; |
+ enum WhatCloses { |
+ kReadCloses = 0, |
+ kWriteCloses, |
+ kReadAndWriteClose, |
+ }; |
+ |
+ TestMultiprocessClosePipe(WhoCloses who_closes, WhatCloses what_closes) |
+ : Multiprocess(), |
+ who_closes_(who_closes), |
+ what_closes_(what_closes) { |
+ } |
+ |
+ ~TestMultiprocessClosePipe() {} |
+ |
+ private: |
+ void VerifyInitial() { |
+ ASSERT_NE(-1, ReadPipeFD()); |
+ ASSERT_NE(-1, WritePipeFD()); |
+ } |
+ |
+ // Verifies that the partner process did what it was supposed to do. This must |
+ // only be called when who_closes_ names the partner process, not this |
+ // process. |
+ // |
+ // If the partner was supposed to close its write pipe, the read pipe will be |
+ // checked to ensure that it shows end-of-file. |
+ // |
+ // If the partner was supposed to close its read pipe, the write pipe will be |
+ // checked to ensure that a checked write causes death. This can only be done |
+ // if the partner also provides some type of signal when it has closed its |
+ // read pipe, which is done in the form of it closing its write pipe, causing |
+ // the read pipe in this process to show end-of-file. |
+ void VerifyPartner() { |
+ if (what_closes_ == kWriteCloses) { |
+ CheckedReadFDAtEOF(ReadPipeFD()); |
+ } else if (what_closes_ == kReadAndWriteClose) { |
+ CheckedReadFDAtEOF(ReadPipeFD()); |
+ char c = '\0'; |
+ |
+ // This will raise SIGPIPE. If fatal (the normal case), that will cause |
+ // process termination. If SIGPIPE is being handled somewhere, the write |
+ // will still fail and set errno to EPIPE, and CheckedWriteFD() will abort |
+ // execution. Regardless of how SIGPIPE is handled, the process will be |
+ // terminated. Because the actual termination mechanism is not known, no |
+ // regex can be specified. |
+ EXPECT_DEATH(CheckedWriteFD(WritePipeFD(), &c, 1), ""); |
+ } |
+ } |
+ |
+ void Close() { |
+ switch (what_closes_) { |
+ case kReadCloses: |
+ CloseReadPipe(); |
+ EXPECT_NE(-1, WritePipeFD()); |
+ EXPECT_DEATH(ReadPipeFD(), "fd"); |
+ break; |
+ case kWriteCloses: |
+ CloseWritePipe(); |
+ EXPECT_NE(-1, ReadPipeFD()); |
+ EXPECT_DEATH(WritePipeFD(), "fd"); |
+ break; |
+ case kReadAndWriteClose: |
+ CloseReadPipe(); |
+ CloseWritePipe(); |
+ EXPECT_DEATH(ReadPipeFD(), "fd"); |
+ EXPECT_DEATH(WritePipeFD(), "fd"); |
+ break; |
+ } |
+ } |
+ |
+ // Multiprocess: |
+ |
+ virtual void MultiprocessParent() override { |
+ VerifyInitial(); |
+ if (testing::Test::HasFatalFailure()) { |
+ return; |
+ } |
+ |
+ if (who_closes_ == kParentCloses) { |
+ Close(); |
+ } else { |
+ VerifyPartner(); |
+ } |
+ } |
+ |
+ virtual void MultiprocessChild() override { |
+ VerifyInitial(); |
+ if (testing::Test::HasFatalFailure()) { |
+ return; |
+ } |
+ |
+ if (who_closes_ == kChildCloses) { |
+ Close(); |
+ } else { |
+ VerifyPartner(); |
+ } |
+ } |
+ |
+ WhoCloses who_closes_; |
+ WhatCloses what_closes_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TestMultiprocessClosePipe); |
+}; |
+ |
+TEST(MultiprocessDeathTest, ParentClosesReadPipe) { |
+ TestMultiprocessClosePipe multiprocess( |
+ TestMultiprocessClosePipe::kParentCloses, |
+ TestMultiprocessClosePipe::kReadCloses); |
+ multiprocess.Run(); |
+} |
+ |
+TEST(MultiprocessDeathTest, ParentClosesWritePipe) { |
+ TestMultiprocessClosePipe multiprocess( |
+ TestMultiprocessClosePipe::kParentCloses, |
+ TestMultiprocessClosePipe::kWriteCloses); |
+ multiprocess.Run(); |
+} |
+ |
+TEST(MultiprocessDeathTest, ParentClosesReadAndWritePipe) { |
+ TestMultiprocessClosePipe multiprocess( |
+ TestMultiprocessClosePipe::kParentCloses, |
+ TestMultiprocessClosePipe::kReadAndWriteClose); |
+ multiprocess.Run(); |
+} |
+ |
+TEST(MultiprocessDeathTest, ChildClosesReadPipe) { |
+ TestMultiprocessClosePipe multiprocess( |
+ TestMultiprocessClosePipe::kChildCloses, |
+ TestMultiprocessClosePipe::kReadCloses); |
+ multiprocess.Run(); |
+} |
+ |
+TEST(MultiprocessDeathTest, ChildClosesWritePipe) { |
+ TestMultiprocessClosePipe multiprocess( |
+ TestMultiprocessClosePipe::kChildCloses, |
+ TestMultiprocessClosePipe::kWriteCloses); |
+ multiprocess.Run(); |
+} |
+ |
+TEST(MultiprocessDeathTest, ChildClosesReadAndWritePipe) { |
+ TestMultiprocessClosePipe multiprocess( |
+ TestMultiprocessClosePipe::kChildCloses, |
+ TestMultiprocessClosePipe::kReadAndWriteClose); |
+ multiprocess.Run(); |
+} |
+ |
} // namespace |