Index: libc/test/src/signal/sigaddset_test.cpp =================================================================== --- libc/test/src/signal/sigaddset_test.cpp +++ libc/test/src/signal/sigaddset_test.cpp @@ -11,32 +11,24 @@ #include "src/errno/llvmlibc_errno.h" #include "src/signal/sigaddset.h" +#include "utils/UnitTest/Matchers.h" #include "utils/UnitTest/Test.h" // This tests invalid inputs and ensures errno is properly set. TEST(SignalTest, SigaddsetInvalid) { - llvmlibc_errno = 0; - EXPECT_EQ(__llvm_libc::sigaddset(nullptr, SIGSEGV), -1); - EXPECT_EQ(llvmlibc_errno, EINVAL); + EXPECT_THAT(__llvm_libc::sigaddset(nullptr, SIGSEGV), Fails(EINVAL)); sigset_t sigset; - llvmlibc_errno = 0; - EXPECT_EQ(__llvm_libc::sigaddset(&sigset, -1), -1); - EXPECT_EQ(llvmlibc_errno, EINVAL); + EXPECT_THAT(__llvm_libc::sigaddset(&sigset, -1), Fails(EINVAL)); // This doesn't use NSIG because __llvm_libc::sigaddset error checking is // against sizeof(sigset_t) not NSIG. constexpr int bitsInSigsetT = 8 * sizeof(sigset_t); - llvmlibc_errno = 0; - EXPECT_EQ(__llvm_libc::sigaddset(&sigset, bitsInSigsetT + 1), -1); - EXPECT_EQ(llvmlibc_errno, EINVAL); + EXPECT_THAT(__llvm_libc::sigaddset(&sigset, bitsInSigsetT + 1), + Fails(EINVAL)); - llvmlibc_errno = 0; - EXPECT_EQ(__llvm_libc::sigaddset(&sigset, 0), -1); - EXPECT_EQ(llvmlibc_errno, EINVAL); + EXPECT_THAT(__llvm_libc::sigaddset(&sigset, 0), Fails(EINVAL)); - llvmlibc_errno = 0; - EXPECT_EQ(__llvm_libc::sigaddset(&sigset, bitsInSigsetT), 0); - EXPECT_EQ(llvmlibc_errno, 0); + EXPECT_THAT(__llvm_libc::sigaddset(&sigset, bitsInSigsetT), Succeeds()); } Index: libc/utils/UnitTest/CMakeLists.txt =================================================================== --- libc/utils/UnitTest/CMakeLists.txt +++ libc/utils/UnitTest/CMakeLists.txt @@ -2,6 +2,7 @@ LibcUnitTest Test.cpp Test.h + Matchers.h LINK_COMPONENTS Support ) target_include_directories(LibcUnitTest PUBLIC ${LIBC_SOURCE_DIR}) Index: libc/utils/UnitTest/Matchers.h =================================================================== --- /dev/null +++ libc/utils/UnitTest/Matchers.h @@ -0,0 +1,57 @@ +//===-------------------------- Matchers.h ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_UTILS_UNITTEST_MATCHERS_H +#define LLVM_LIBC_UTILS_UNITTEST_MATCHERS_H + +#include "Test.h" +#include "src/errno/llvmlibc_errno.h" + +extern "C" const char *strerror(int); + +template +class ErrnoSetterMatcher : public __llvm_libc::testing::Matcher { + T ExpectedReturn; + T ActualReturn; + int ExpectedErrno; + int ActualErrno; + +public: + ErrnoSetterMatcher(T ExpectedReturn, int ExpectedErrno) + : ExpectedReturn(ExpectedReturn), ExpectedErrno(ExpectedErrno) {} + + void explainError(__llvm_libc::testutils::StreamWrapper &OS) override { + if (ActualReturn != ExpectedReturn) + OS << "Expected return to be " << ExpectedReturn << " but got " + << ActualReturn << ".\nExpecte errno to be " << strerror(ExpectedErrno) + << " but got " << strerror(ActualErrno) << ".\n"; + else + OS << "Correct value " << ExpectedReturn + << " was returned\nBut errno was unexpectely set to " + << strerror(ActualErrno) << ".\n"; + } + + bool match(T Got) { + ActualReturn = Got; + ActualErrno = llvmlibc_errno; + llvmlibc_errno = 0; + return Got == ExpectedReturn && ActualErrno == ExpectedErrno; + } +}; + +template +ErrnoSetterMatcher Succeeds(T ExpectedReturn = 0, int ExpectedErrno = 0) { + return ErrnoSetterMatcher(ExpectedReturn, ExpectedErrno); +} + +template +ErrnoSetterMatcher Fails(int ExpectedErrno, T ExpectedReturn = -1) { + return ErrnoSetterMatcher(ExpectedReturn, ExpectedErrno); +} + +#endif // LLVM_LIBC_UTILS_UNITTEST_MATCHERS_H Index: libc/utils/UnitTest/Test.h =================================================================== --- libc/utils/UnitTest/Test.h +++ libc/utils/UnitTest/Test.h @@ -14,6 +14,7 @@ #include "utils/CPP/TypeTraits.h" #include "utils/testutils/ExecuteFunction.h" +#include "utils/testutils/StreamWrapper.h" namespace __llvm_libc { namespace testing { @@ -44,6 +45,15 @@ } // namespace internal +struct MatcherBase { + virtual ~MatcherBase() {} + virtual void explainError(testutils::StreamWrapper &OS) { + OS << "unknown error\n"; + } +}; + +template struct Matcher : public MatcherBase { bool match(T &t); }; + // NOTE: One should not create instances and call methods on them directly. One // should use the macros TEST or TEST_F to write test cases. class Test { @@ -93,6 +103,10 @@ const char *LHSStr, const char *RHSStr, const char *File, unsigned long Line); + static bool testMatch(RunContext &Ctx, bool MatchResult, MatcherBase &Matcher, + const char *LHSStr, const char *RHSStr, + const char *File, unsigned long Line); + static bool testProcessExits(RunContext &Ctx, testutils::FunctionCaller *Func, int ExitCode, const char *LHSStr, const char *RHSStr, const char *File, @@ -230,4 +244,18 @@ if (!EXPECT_DEATH(FUNC, EXIT)) \ return +#define __CAT1(a, b) a##b +#define __CAT(a, b) __CAT1(a, b) +#define UNIQUE_VAR(prefix) __CAT(prefix, __LINE__) + +#define EXPECT_THAT(MATCH, MATCHER) \ + auto UNIQUE_VAR(__matcher) = (MATCHER); \ + __llvm_libc::testing::Test::testMatch( \ + Ctx, UNIQUE_VAR(__matcher).match((MATCH)), UNIQUE_VAR(__matcher), \ + #MATCH, #MATCHER, __FILE__, __LINE__) + +#define ASSERT_THAT(MATCH, MATCHER) \ + if (!EXPECT_THAT(MATCH, MATCHER)) \ + return + #endif // LLVM_LIBC_UTILS_UNITTEST_H Index: libc/utils/UnitTest/Test.cpp =================================================================== --- libc/utils/UnitTest/Test.cpp +++ libc/utils/UnitTest/Test.cpp @@ -232,6 +232,20 @@ llvm::StringRef(RHS), LHSStr, RHSStr, File, Line); } +bool Test::testMatch(RunContext &Ctx, bool MatchResult, MatcherBase &Matcher, + const char *LHSStr, const char *RHSStr, const char *File, + unsigned long Line) { + if (MatchResult) + return true; + + Ctx.markFail(); + llvm::outs() << File << ":" << Line << ": FAILURE\nFailed to match " << LHSStr + << " against " << RHSStr << ".\n"; + testutils::StreamWrapper OutsWrapper = __llvm_libc::testutils::outs(); + Matcher.explainError(OutsWrapper); + return false; +} + bool Test::testProcessKilled(RunContext &Ctx, testutils::FunctionCaller *Func, int Signal, const char *LHSStr, const char *RHSStr, const char *File, unsigned long Line) { Index: libc/utils/testutils/CMakeLists.txt =================================================================== --- libc/utils/testutils/CMakeLists.txt +++ libc/utils/testutils/CMakeLists.txt @@ -4,7 +4,10 @@ add_llvm_library( libc_test_utils + StreamWrapper.cpp + StreamWrapper.h ${EFFile} ExecuteFunction.h - LINK_COMPONENTS Support + LINK_COMPONENTS + Support ) Index: libc/utils/testutils/StreamWrapper.h =================================================================== --- /dev/null +++ libc/utils/testutils/StreamWrapper.h @@ -0,0 +1,32 @@ +//===------------------------ StreamWrapper.h -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_UTILS_TESTUTILS_STREAMWRAPPER_H +#define LLVM_LIBC_UTILS_TESTUTILS_STREAMWRAPPER_H + +namespace __llvm_libc { +namespace testutils { + +// StreamWrapper is necessary because llvm/Support/raw_ostream.h includes +// standard headers so we must provide streams through indirection to not +// expose the system libc headers. +class StreamWrapper { + void *OS; + +public: + StreamWrapper(void *OS) : OS(OS) {} + + template StreamWrapper &operator<<(T t); +}; + +StreamWrapper outs(); + +} // namespace testutils +} // namespace __llvm_libc + +#endif // LLVM_LIBC_UTILS_TESTUTILS_STREAMWRAPPER_H Index: libc/utils/testutils/StreamWrapper.cpp =================================================================== --- /dev/null +++ libc/utils/testutils/StreamWrapper.cpp @@ -0,0 +1,45 @@ +//===--------------------------- StreamWrapper.cpp ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "StreamWrapper.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace __llvm_libc { +namespace testutils { + +StreamWrapper outs() { return {std::addressof(llvm::outs())}; } + +template StreamWrapper &StreamWrapper::operator<<(T t) { + assert(OS); + llvm::raw_ostream &Stream = *reinterpret_cast(OS); + Stream << t; + return *this; +} + +template StreamWrapper &StreamWrapper::operator<<(const char *t); +template StreamWrapper &StreamWrapper::operator<<(char *t); +template StreamWrapper &StreamWrapper::operator<<(char t); +template StreamWrapper &StreamWrapper::operator<<(short t); +template StreamWrapper &StreamWrapper::operator<<(int t); +template StreamWrapper &StreamWrapper::operator<<(long t); +template StreamWrapper &StreamWrapper::operator<<(long long t); +template StreamWrapper & + StreamWrapper::operator<<(unsigned char t); +template StreamWrapper & + StreamWrapper::operator<<(unsigned short t); +template StreamWrapper &StreamWrapper::operator<<(unsigned int t); +template StreamWrapper & + StreamWrapper::operator<<(unsigned long t); +template StreamWrapper & + StreamWrapper::operator<<(unsigned long long t); +template StreamWrapper &StreamWrapper::operator<<(bool t); + +} // namespace testutils +} // namespace __llvm_libc