[libc-commits] [libc] 1962bc1 - [libc] [UnitTest] Add Matchers
Alex Brachet via libc-commits
libc-commits at lists.llvm.org
Thu Mar 5 15:37:09 PST 2020
Author: Alex Brachet
Date: 2020-03-05T18:36:11-05:00
New Revision: 1962bc1dfb728c6ebf44be9174d8a2a269ce0299
URL: https://github.com/llvm/llvm-project/commit/1962bc1dfb728c6ebf44be9174d8a2a269ce0299
DIFF: https://github.com/llvm/llvm-project/commit/1962bc1dfb728c6ebf44be9174d8a2a269ce0299.diff
LOG: [libc] [UnitTest] Add Matchers
Summary: This patch adds gtest-like matchers and `EXPECT|ASSERT_THAT` macros. It also adds matchers `Succeeds` and `Fails` and has examples using these in test/src/signal/sigaddset_test.cpp.
Reviewers: sivachandra, gchatelet, PaulkaToast
Reviewed By: sivachandra, PaulkaToast
Subscribers: mgorny, MaskRay, tschuett, libc-commits
Differential Revision: https://reviews.llvm.org/D75487
Added:
libc/utils/UnitTest/ErrnoSetterMatcher.h
libc/utils/testutils/StreamWrapper.cpp
libc/utils/testutils/StreamWrapper.h
Modified:
libc/test/src/signal/sigaddset_test.cpp
libc/utils/UnitTest/CMakeLists.txt
libc/utils/UnitTest/Test.cpp
libc/utils/UnitTest/Test.h
libc/utils/testutils/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/test/src/signal/sigaddset_test.cpp b/libc/test/src/signal/sigaddset_test.cpp
index 58df1feb1a2d..5a5fe179f6a4 100644
--- a/libc/test/src/signal/sigaddset_test.cpp
+++ b/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/ErrnoSetterMatcher.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);
+ using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+ using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+ 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);
-
- llvmlibc_errno = 0;
- EXPECT_EQ(__llvm_libc::sigaddset(&sigset, 0), -1);
- EXPECT_EQ(llvmlibc_errno, EINVAL);
-
- llvmlibc_errno = 0;
- EXPECT_EQ(__llvm_libc::sigaddset(&sigset, bitsInSigsetT), 0);
- EXPECT_EQ(llvmlibc_errno, 0);
+ EXPECT_THAT(__llvm_libc::sigaddset(&sigset, bitsInSigsetT + 1),
+ Fails(EINVAL));
+ EXPECT_THAT(__llvm_libc::sigaddset(&sigset, 0), Fails(EINVAL));
+ EXPECT_THAT(__llvm_libc::sigaddset(&sigset, bitsInSigsetT), Succeeds());
}
diff --git a/libc/utils/UnitTest/CMakeLists.txt b/libc/utils/UnitTest/CMakeLists.txt
index 5c15e3f7fa4b..85630075582d 100644
--- a/libc/utils/UnitTest/CMakeLists.txt
+++ b/libc/utils/UnitTest/CMakeLists.txt
@@ -2,6 +2,7 @@ add_llvm_library(
LibcUnitTest
Test.cpp
Test.h
+ ErrnoSetterMatcher.h
LINK_COMPONENTS Support
)
target_include_directories(LibcUnitTest PUBLIC ${LIBC_SOURCE_DIR})
diff --git a/libc/utils/UnitTest/ErrnoSetterMatcher.h b/libc/utils/UnitTest/ErrnoSetterMatcher.h
new file mode 100644
index 000000000000..2c09ab64c5a4
--- /dev/null
+++ b/libc/utils/UnitTest/ErrnoSetterMatcher.h
@@ -0,0 +1,76 @@
+//===--------------------- ErrnoSetterMatcher.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_ERRNOSETTERMATCHER_H
+#define LLVM_LIBC_UTILS_UNITTEST_ERRNOSETTERMATCHER_H
+
+#include "Test.h"
+
+// Using LLVM libc headers in UnitTest is not ideal however we also want the
+// test/ directory to have the same layout as libc/ so there is no clean place
+// to put this file except for in utils/UnitTest/.
+#include "src/errno/llvmlibc_errno.h"
+
+namespace __llvm_libc {
+namespace testing {
+
+namespace internal {
+
+extern "C" const char *strerror(int);
+
+template <typename T> class ErrnoSetterMatcher : public Matcher<T> {
+ T ExpectedReturn;
+ T ActualReturn;
+ int ExpectedErrno;
+ int ActualErrno;
+
+public:
+ ErrnoSetterMatcher(T ExpectedReturn, int ExpectedErrno)
+ : ExpectedReturn(ExpectedReturn), ExpectedErrno(ExpectedErrno) {}
+
+ void explainError(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;
+ }
+};
+
+} // namespace internal
+
+namespace ErrnoSetterMatcher {
+
+template <typename RetT = int>
+static internal::ErrnoSetterMatcher<RetT> Succeeds(RetT ExpectedReturn = 0,
+ int ExpectedErrno = 0) {
+ return {ExpectedReturn, ExpectedErrno};
+}
+
+template <typename RetT = int>
+static internal::ErrnoSetterMatcher<RetT> Fails(int ExpectedErrno,
+ RetT ExpectedReturn = -1) {
+ return {ExpectedReturn, ExpectedErrno};
+}
+
+} // namespace ErrnoSetterMatcher
+
+} // namespace testing
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_UTILS_UNITTEST_ERRNOSETTERMATCHER_H
diff --git a/libc/utils/UnitTest/Test.cpp b/libc/utils/UnitTest/Test.cpp
index b2c4f2df0ed8..608a4df89979 100644
--- a/libc/utils/UnitTest/Test.cpp
+++ b/libc/utils/UnitTest/Test.cpp
@@ -232,6 +232,21 @@ bool Test::testStrNe(RunContext &Ctx, const char *LHS, const char *RHS,
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\n"
+ << "Failed to match " << LHSStr << " against " << RHSStr
+ << ".\n";
+ testutils::StreamWrapper OutsWrapper = 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) {
diff --git a/libc/utils/UnitTest/Test.h b/libc/utils/UnitTest/Test.h
index 509c532448f5..7982cd66a8cc 100644
--- a/libc/utils/UnitTest/Test.h
+++ b/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 @@ bool test(RunContext &Ctx, TestCondition Cond, ValType LHS, ValType RHS,
} // namespace internal
+struct MatcherBase {
+ virtual ~MatcherBase() {}
+ virtual void explainError(testutils::StreamWrapper &OS) {
+ OS << "unknown error\n";
+ }
+};
+
+template <typename T> 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 @@ class Test {
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 @@ class Test {
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
diff --git a/libc/utils/testutils/CMakeLists.txt b/libc/utils/testutils/CMakeLists.txt
index aab8d34dc2db..9ee03e18c66b 100644
--- a/libc/utils/testutils/CMakeLists.txt
+++ b/libc/utils/testutils/CMakeLists.txt
@@ -4,7 +4,10 @@ endif()
add_llvm_library(
libc_test_utils
+ StreamWrapper.cpp
+ StreamWrapper.h
${EFFile}
ExecuteFunction.h
- LINK_COMPONENTS Support
+ LINK_COMPONENTS
+ Support
)
diff --git a/libc/utils/testutils/StreamWrapper.cpp b/libc/utils/testutils/StreamWrapper.cpp
new file mode 100644
index 000000000000..7909b6c8790d
--- /dev/null
+++ b/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 <cassert>
+#include <memory>
+
+namespace __llvm_libc {
+namespace testutils {
+
+StreamWrapper outs() { return {std::addressof(llvm::outs())}; }
+
+template <typename T> StreamWrapper &StreamWrapper::operator<<(T t) {
+ assert(OS);
+ llvm::raw_ostream &Stream = *reinterpret_cast<llvm::raw_ostream *>(OS);
+ Stream << t;
+ return *this;
+}
+
+template StreamWrapper &StreamWrapper::operator<<<const char *>(const char *t);
+template StreamWrapper &StreamWrapper::operator<<<char *>(char *t);
+template StreamWrapper &StreamWrapper::operator<<<char>(char t);
+template StreamWrapper &StreamWrapper::operator<<<short>(short t);
+template StreamWrapper &StreamWrapper::operator<<<int>(int t);
+template StreamWrapper &StreamWrapper::operator<<<long>(long t);
+template StreamWrapper &StreamWrapper::operator<<<long long>(long long t);
+template StreamWrapper &
+ StreamWrapper::operator<<<unsigned char>(unsigned char t);
+template StreamWrapper &
+ StreamWrapper::operator<<<unsigned short>(unsigned short t);
+template StreamWrapper &StreamWrapper::operator<<<unsigned int>(unsigned int t);
+template StreamWrapper &
+ StreamWrapper::operator<<<unsigned long>(unsigned long t);
+template StreamWrapper &
+ StreamWrapper::operator<<<unsigned long long>(unsigned long long t);
+template StreamWrapper &StreamWrapper::operator<<<bool>(bool t);
+
+} // namespace testutils
+} // namespace __llvm_libc
diff --git a/libc/utils/testutils/StreamWrapper.h b/libc/utils/testutils/StreamWrapper.h
new file mode 100644
index 000000000000..a2fecba617a8
--- /dev/null
+++ b/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 <typename T> StreamWrapper &operator<<(T t);
+};
+
+StreamWrapper outs();
+
+} // namespace testutils
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_UTILS_TESTUTILS_STREAMWRAPPER_H
More information about the libc-commits
mailing list