[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