Index: libc/utils/UnitTest/Test.h =================================================================== --- libc/utils/UnitTest/Test.h +++ libc/utils/UnitTest/Test.h @@ -38,6 +38,21 @@ const char *LHSStr, const char *RHSStr, const char *File, unsigned long Line); +struct PolymorphicFunction { + virtual ~PolymorphicFunction() {} + virtual void call() = 0; +}; + +template PolymorphicFunction *createCallable(Func f) { + struct Callable : public PolymorphicFunction { + Func f; + Callable(Func f) : f(f) {} + void call() override { f(); } + }; + + return new Callable(f); +} + } // namespace internal // NOTE: One should not create instances and call methods on them directly. One @@ -89,6 +104,17 @@ const char *LHSStr, const char *RHSStr, const char *File, unsigned long Line); + static bool testProcessExits(RunContext &Ctx, + internal::PolymorphicFunction *Func, + int ExitCode, const char *LHSStr, + const char *RHSStr, const char *File, + unsigned long Line); + + static bool testProcessKilled(RunContext &Ctx, + internal::PolymorphicFunction *Func, int Signal, + const char *LHSStr, const char *RHSStr, + const char *File, unsigned long Line); + private: virtual void Run(RunContext &Ctx) = 0; virtual const char *getName() const = 0; @@ -187,3 +213,21 @@ #define ASSERT_FALSE(VAL) \ if (!EXPECT_FALSE(VAL)) \ return + +#define EXPECT_EXITS(FUNC, EXIT) \ + __llvm_libc::testing::Test::testProcessExits( \ + Ctx, __llvm_libc::testing::internal::createCallable(FUNC), EXIT, #FUNC, \ + #EXIT, __FILE__, __LINE__) + +#define ASSERT_EXITS(FUNC, EXIT) \ + if (!EXPECT_EXITS(FUNC, EXIT)) \ + return + +#define EXPECT_DEATH(FUNC, SIG) \ + __llvm_libc::testing::Test::testProcessKilled( \ + Ctx, __llvm_libc::testing::internal::createCallable(FUNC), SIG, #FUNC, \ + #SIG, __FILE__, __LINE__) + +#define ASSERT_DEATH(FUNC, EXIT) \ + if (!EXPECT_DEATH(FUNC, EXIT)) \ + return Index: libc/utils/UnitTest/Test.cpp =================================================================== --- libc/utils/UnitTest/Test.cpp +++ libc/utils/UnitTest/Test.cpp @@ -11,6 +11,9 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" +#include +#include + namespace __llvm_libc { namespace testing { @@ -228,6 +231,81 @@ llvm::StringRef(RHS), LHSStr, RHSStr, File, Line); } +// TODO: Death tests will not currently work on Windows. + +static int getWStatus(internal::PolymorphicFunction *func) { + // Don't copy the buffer into the child process and print twice. + llvm::outs().flush(); + pid_t Pid = ::fork(); + if (!Pid) { + func->call(); + delete func; + std::exit(0); + } + + int WStatus; + ::waitpid(Pid, &WStatus, 0); + return WStatus; +} + +bool Test::testProcessKilled(RunContext &Ctx, + internal::PolymorphicFunction *Func, int Signal, + const char *LHSStr, const char *RHSStr, + const char *File, unsigned long Line) { + int WStatus = getWStatus(Func); + + if (WIFEXITED(WStatus)) { + Ctx.markFail(); + llvm::outs() << File << ":" << Line << ": FAILURE\n" + << "Expected " << LHSStr + << " to be killed by a signal\nBut it exited normally!\n"; + return false; + } + + assert(WIFSIGNALED(WStatus) && "Internal test error"); + int KilledBy = WTERMSIG(WStatus); + if (Signal == -1 || KilledBy == Signal) + return true; + + Ctx.markFail(); + llvm::outs() << File << ":" << Line << ": FAILURE\n" + << " Expected: " << RHSStr << '\n' + << "To be killed by signal: " << Signal << '\n' + << " Which is: " << strsignal(Signal) << '\n' + << " But it was killed by: " << KilledBy << '\n' + << " Which is: " << strsignal(KilledBy) << '\n'; +} + +bool Test::testProcessExits(RunContext &Ctx, + internal::PolymorphicFunction *Func, int ExitCode, + const char *LHSStr, const char *RHSStr, + const char *File, unsigned long Line) { + + int ExitStatus = getWStatus(Func); + + if (!WIFEXITED(ExitStatus)) { + Ctx.markFail(); + llvm::outs() << File << ":" << Line << ": FAILURE\n" + << "Expected " << RHSStr << '\n' + << "to exit with exit code " << ExitCode << '\n' + << "But it exited abnormally!\n"; + return false; + } + + int ActualExit = WEXITSTATUS(ExitStatus); + if (ActualExit != ExitCode) { + Ctx.markFail(); + llvm::outs() << File << ":" << Line << ": FAILURE\n" + << "Expected exit code of: " << LHSStr << '\n' + << " Which is: " << ActualExit << '\n' + << " To be equal to: " << RHSStr << '\n' + << " Which is: " << ExitCode << '\n'; + return false; + } + + return true; +} + } // namespace testing } // namespace __llvm_libc