Index: libcxx/test/support/check_assertion.h =================================================================== --- libcxx/test/support/check_assertion.h +++ libcxx/test/support/check_assertion.h @@ -17,9 +17,8 @@ #include #include -#include #include -#include +#include #include "test_macros.h" #include "test_allocator.h" @@ -108,9 +107,16 @@ return GMatch; } +static jmp_buf& GetJmpBuf() { + static jmp_buf env; + return env; +} + struct DeathTest { enum ResultKind { - RK_DidNotDie, RK_MatchFound, RK_MatchFailure, RK_SetupFailure, RK_Unknown + // This enum starts from 1 and not 0 as we want to return the ResultKind + // through setjmp/longjmp, which doesn't permit 0. + RK_DidNotDie = 1, RK_MatchFound, RK_MatchFailure, RK_Unknown }; static const char* ResultKindToString(ResultKind RK) { @@ -118,7 +124,6 @@ switch (RK) { CASE(RK_MatchFailure); CASE(RK_DidNotDie); - CASE(RK_SetupFailure); CASE(RK_MatchFound); CASE(RK_Unknown); } @@ -133,114 +138,30 @@ template ResultKind Run(Func&& f) { - int pipe_res = pipe(stdout_pipe_fd_); - assert(pipe_res != -1 && "failed to create pipe"); - pipe_res = pipe(stderr_pipe_fd_); - assert(pipe_res != -1 && "failed to create pipe"); - pid_t child_pid = fork(); - assert(child_pid != -1 && - "failed to fork a process to perform a death test"); - child_pid_ = child_pid; - if (child_pid_ == 0) { - RunForChild(std::forward(f)); - assert(false && "unreachable"); - } - return RunForParent(); - } - - int getChildExitCode() const { return exit_code_; } - std::string const& getChildStdOut() const { return stdout_from_child_; } - std::string const& getChildStdErr() const { return stderr_from_child_; } -private: - template - TEST_NORETURN void RunForChild(Func&& f) { - close(GetStdOutReadFD()); // don't need to read from the pipe in the child. - close(GetStdErrReadFD()); - auto DupFD = [](int DestFD, int TargetFD) { - int dup_result = dup2(DestFD, TargetFD); - if (dup_result == -1) - std::exit(RK_SetupFailure); - }; - DupFD(GetStdOutWriteFD(), STDOUT_FILENO); - DupFD(GetStdErrWriteFD(), STDERR_FILENO); - GlobalMatcher() = matcher_; - f(); - std::exit(RK_DidNotDie); - } - - static std::string ReadChildIOUntilEnd(int FD) { - std::string error_msg; - char buffer[256]; - int num_read; - do { - while ((num_read = read(FD, buffer, 255)) > 0) { - buffer[num_read] = '\0'; - error_msg += buffer; - } - } while (num_read == -1 && errno == EINTR); - return error_msg; - } - - void CaptureIOFromChild() { - close(GetStdOutWriteFD()); // no need to write from the parent process - close(GetStdErrWriteFD()); - stdout_from_child_ = ReadChildIOUntilEnd(GetStdOutReadFD()); - stderr_from_child_ = ReadChildIOUntilEnd(GetStdErrReadFD()); - close(GetStdOutReadFD()); - close(GetStdErrReadFD()); - } - - ResultKind RunForParent() { - CaptureIOFromChild(); - - int status_value; - pid_t result = waitpid(child_pid_, &status_value, 0); - assert(result != -1 && "there is no child process to wait for"); - - if (WIFEXITED(status_value)) { - exit_code_ = WEXITSTATUS(status_value); - if (!IsValidResultKind(exit_code_)) - return RK_Unknown; - return static_cast(exit_code_); + int n = setjmp(GetJmpBuf()); + if (n == 0) { + f(); + return RK_DidNotDie; } + if (IsValidResultKind(n)) + return (ResultKind)n; return RK_Unknown; } DeathTest(DeathTest const&) = delete; DeathTest& operator=(DeathTest const&) = delete; - int GetStdOutReadFD() const { - return stdout_pipe_fd_[0]; - } - - int GetStdOutWriteFD() const { - return stdout_pipe_fd_[1]; - } - - int GetStdErrReadFD() const { - return stderr_pipe_fd_[0]; - } - - int GetStdErrWriteFD() const { - return stderr_pipe_fd_[1]; - } private: AssertionInfoMatcher matcher_; - pid_t child_pid_ = -1; - int exit_code_ = -1; - int stdout_pipe_fd_[2]; - int stderr_pipe_fd_[2]; - std::string stdout_from_child_; - std::string stderr_from_child_; }; void std::__libcpp_assertion_handler(char const* file, int line, char const* /*expression*/, char const* message) { assert(!GlobalMatcher().empty()); if (GlobalMatcher().Matches(file, line, message)) { - std::exit(DeathTest::RK_MatchFound); + longjmp(GetJmpBuf(), DeathTest::RK_MatchFound); } - std::exit(DeathTest::RK_MatchFailure); + longjmp(GetJmpBuf(), DeathTest::RK_MatchFailure); } template @@ -249,22 +170,11 @@ DeathTest::ResultKind RK = DT.Run(func); auto OnFailure = [&](const char* msg) { std::fprintf(stderr, "EXPECT_DEATH( %s ) failed! (%s)\n\n", stmt, msg); - if (RK != DeathTest::RK_Unknown) { - std::fprintf(stderr, "child exit code: %d\n", DT.getChildExitCode()); - } - if (!DT.getChildStdErr().empty()) { - std::fprintf(stderr, "---------- standard err ----------\n%s\n", DT.getChildStdErr().c_str()); - } - if (!DT.getChildStdOut().empty()) { - std::fprintf(stderr, "---------- standard out ----------\n%s\n", DT.getChildStdOut().c_str()); - } return false; }; switch (RK) { case DeathTest::RK_MatchFound: return true; - case DeathTest::RK_SetupFailure: - return OnFailure("child failed to setup test environment"); case DeathTest::RK_Unknown: return OnFailure("reason unknown"); case DeathTest::RK_DidNotDie: Index: libcxx/test/support/test.support/test_check_assertion.pass.cpp =================================================================== --- libcxx/test/support/test.support/test_check_assertion.pass.cpp +++ libcxx/test/support/test.support/test_check_assertion.pass.cpp @@ -22,12 +22,6 @@ DeathTest::ResultKind RK = DT.Run(func); auto OnFailure = [&](std::string msg) { std::fprintf(stderr, "EXPECT_DEATH( %s ) failed! (%s)\n\n", stmt, msg.c_str()); - if (!DT.getChildStdErr().empty()) { - std::fprintf(stderr, "---------- standard err ----------\n%s\n", DT.getChildStdErr().c_str()); - } - if (!DT.getChildStdOut().empty()) { - std::fprintf(stderr, "---------- standard out ----------\n%s\n", DT.getChildStdOut().c_str()); - } return false; }; if (RK != ExpectResult)