Index: llvm/unittests/Support/SignalsTest.cpp =================================================================== --- llvm/unittests/Support/SignalsTest.cpp +++ llvm/unittests/Support/SignalsTest.cpp @@ -19,7 +19,69 @@ using namespace llvm; #if !defined(_WIN32) -TEST(SignalTest, IgnoreMultipleSIGPIPEs) { +struct SignalDeathTest : ::testing::Test { + void SetUp() { + // Note: SetUp runs after llvm registers its default signal handlers. Also, + // the static initializers from Signals.inc only run once, so the order of + // test execution matters a lot. Swapping two tests around can break them, + // as they may rely on llvm's handlers being in a specific state. + + // The unit test process may inherit a mask for SIGPIPE from its parent. + // Unblock SIGPIPE. + sigset_t UnblockSet; + sigemptyset(&UnblockSet); + sigaddset(&UnblockSet, SIGPIPE); + sigprocmask(SIG_UNBLOCK, &UnblockSet, nullptr); + } +}; + +void NoOpSigHandler(void *) {} + +// This test must run first, as it assumes llvm's default signal handler state. +TEST_F(SignalDeathTest, EnableExitOnSIGPIPE) { + // Register default signal handlers. + sys::AddSignalHandler(NoOpSigHandler, nullptr); + + // Create unidirectional read/write pipes. + int fds[2]; + int err = pipe(fds); + if (err != 0) + return; // If we can't make pipes, this isn't testing anything. + + // Close the read pipe. + close(fds[0]); + + // Attempt to write to the write pipe. Expect exit(IOERR). + const void *buf = (const void *)&fds; + + // LLVM's default handler should catch SIGPIPE and exit with IOERR. + EXPECT_EXIT(write(fds[1], buf, 1), ::testing::ExitedWithCode(EX_IOERR), ""); +} + +TEST_F(SignalDeathTest, DisableExitOnSIGPIPE) { + // Reset SIGPIPE's default behavior. + signal(SIGPIPE, SIG_DFL); + + // Disable exit-on-SIGPIPE. + sys::SetPipeSignalFunction(nullptr); + + // Create unidirectional read/write pipes. + int fds[2]; + int err = pipe(fds); + if (err != 0) + return; // If we can't make pipes, this isn't testing anything. + + // Close the read pipe. + close(fds[0]); + + // Attempt to write to the write pipe. + const void *buf = (const void *)&fds; + + // No handler should be installed: we expect kill-by-SIGPIPE. + EXPECT_EXIT(write(fds[1], buf, 1), ::testing::KilledBySignal(SIGPIPE), ""); +} + +TEST_F(SignalDeathTest, IgnoreMultipleSIGPIPEs) { // Ignore SIGPIPE. signal(SIGPIPE, SIG_IGN); @@ -35,20 +97,16 @@ // Close the read pipe. close(fds[0]); - // Attempt to write to the write pipe. Currently we're asserting that the - // write fails, which isn't great. - // - // What we really want is a death test that checks that this block exits - // with a special exit "success" code, as opposed to unexpectedly exiting due - // to a kill-by-SIGNAL or due to the default SIGPIPE handler. - // - // Unfortunately llvm's unit tests aren't set up to support death tests well. - // For one, death tests are flaky in a multithreaded context. And sigactions - // inherited from llvm-lit interfere with what's being tested. - const void *buf = (const void *)&fds; - err = write(fds[1], buf, 1); - ASSERT_EQ(err, -1); - err = write(fds[1], buf, 1); - ASSERT_EQ(err, -1); + // Attempt to write to the write pipe. Assert that the writes both fail. + EXPECT_EXIT( + { + const void *buf = (const void *)&fds; + err = write(fds[1], buf, 1); + ASSERT_EQ(err, -1); + err = write(fds[1], buf, 1); + ASSERT_EQ(err, -1); + exit(123); + }, + ::testing::ExitedWithCode(123), ""); } #endif // !defined(_WIN32)