target_include_directories(
${target_name}
PRIVATE
- ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include
- ${LLVM_MAIN_SRC_DIR}/utils/unittest/googlemock/include
${LIBC_SOURCE_DIR}
${LIBC_BUILD_DIR}
+ ${LIBC_BUILD_DIR}/include
)
if(library_deps)
gtest
)
- target_link_libraries(${target_name} PRIVATE gtest_main gtest)
+ target_link_libraries(${target_name} PRIVATE LibcUnitTest LLVMSupport)
add_custom_command(
TARGET ${target_name}
#ifndef LLVM_LIBC_SRC_STRING_STRCAT_H
#define LLVM_LIBC_SRC_STRING_STRCAT_H
-#include <string.h>
+#include "include/string.h"
namespace __llvm_libc {
#ifndef LLVM_LIBC_SRC_STRING_STRCPY_H
#define LLVM_LIBC_SRC_STRING_STRCPY_H
-#include <string.h>
+#include "include/string.h"
namespace __llvm_libc {
//===----------------------------------------------------------------------===//
#include "src/sys/mman/mmap.h"
+#include "include/sys/syscall.h" // For syscall numbers.
#include "src/__support/common.h"
#include "src/errno/llvmlibc_errno.h"
-
#include "src/unistd/syscall.h" // For internal syscall function.
-#include <sys/syscall.h> // For syscall numbers.
-
namespace __llvm_libc {
// This function is currently linux only. It has to be refactored suitably if
#ifndef LLVM_LIBC_SRC_SYS_MMAN_MMAP_H
#define LLVM_LIBC_SRC_SYS_MMAN_MMAP_H
-#include <sys/mman.h> // For size_t and off_t
+#include "include/sys/mman.h" // For size_t and off_t
namespace __llvm_libc {
//===----------------------------------------------------------------------===//
#include "src/sys/mman/munmap.h"
+#include "include/sys/syscall.h" // For syscall numbers.
#include "src/__support/common.h"
#include "src/errno/llvmlibc_errno.h"
-
#include "src/unistd/syscall.h" // For internal syscall function.
-#include <sys/syscall.h> // For syscall numbers.
-
namespace __llvm_libc {
// This function is currently linux only. It has to be refactored suitably if
#ifndef LLVM_LIBC_SRC_SYS_MMAN_MUNMAP_H
#define LLVM_LIBC_SRC_SYS_MMAN_MUNMAP_H
-#include <sys/mman.h> // For size_t
+#include "include/sys/mman.h" // For size_t.
namespace __llvm_libc {
//===----------------------------------------------------------------------===//
#include "src/unistd/syscall.h"
-
-#include "gtest/gtest.h"
+#include "utils/UnitTest/Test.h"
#include <functional>
//===----------------------------------------------------------------------===//
#include "src/errno/llvmlibc_errno.h"
-
-#include "gtest/gtest.h"
+#include "utils/UnitTest/Test.h"
TEST(ErrnoTest, Basic) {
int test_val = 123;
//
//===----------------------------------------------------------------------===//
-#include <string>
-
#include "src/string/strcat.h"
-#include "gtest/gtest.h"
+#include "utils/UnitTest/Test.h"
TEST(StrCatTest, EmptyDest) {
- std::string abc = "abc";
+ const char *abc = "abc";
char dest[4];
dest[0] = '\0';
- char *result = __llvm_libc::strcat(dest, abc.c_str());
+ char *result = __llvm_libc::strcat(dest, abc);
ASSERT_EQ(dest, result);
- ASSERT_EQ(std::string(dest), abc);
- ASSERT_EQ(std::string(dest).size(), abc.size());
+ ASSERT_STREQ(dest, result);
+ ASSERT_STREQ(dest, abc);
}
TEST(StrCatTest, NonEmptyDest) {
- std::string abc = "abc";
+ const char *abc = "abc";
char dest[7];
dest[0] = 'x';
dest[2] = 'z';
dest[3] = '\0';
- char *result = __llvm_libc::strcat(dest, abc.c_str());
+ char *result = __llvm_libc::strcat(dest, abc);
ASSERT_EQ(dest, result);
- ASSERT_EQ(std::string(dest), std::string("xyz") + abc);
- ASSERT_EQ(std::string(dest).size(), abc.size() + 3);
+ ASSERT_STREQ(dest, result);
+ ASSERT_STREQ(dest, "xyzabc");
}
//
//===----------------------------------------------------------------------===//
-#include <string>
-
#include "src/string/strcpy.h"
-#include "gtest/gtest.h"
+#include "utils/UnitTest/Test.h"
TEST(StrCpyTest, EmptyDest) {
- std::string abc = "abc";
+ const char *abc = "abc";
char dest[4];
- char *result = __llvm_libc::strcpy(dest, abc.c_str());
+ char *result = __llvm_libc::strcpy(dest, abc);
ASSERT_EQ(dest, result);
- ASSERT_EQ(std::string(dest), abc);
- ASSERT_EQ(std::string(dest).size(), abc.size());
+ ASSERT_STREQ(dest, result);
+ ASSERT_STREQ(dest, abc);
}
TEST(StrCpyTest, OffsetDest) {
- std::string abc = "abc";
+ const char *abc = "abc";
char dest[7];
dest[0] = 'x';
dest[1] = 'y';
dest[2] = 'z';
- char *result = __llvm_libc::strcpy(dest + 3, abc.c_str());
+ char *result = __llvm_libc::strcpy(dest + 3, abc);
ASSERT_EQ(dest + 3, result);
- ASSERT_EQ(std::string(dest), std::string("xyz") + abc);
- ASSERT_EQ(std::string(dest).size(), abc.size() + 3);
+ ASSERT_STREQ(dest + 3, result);
+ ASSERT_STREQ(dest, "xyzabc");
}
SRCS
mmap_test.cpp
DEPENDS
+ errno_h
+ sys_mman_h
mmap
munmap
__errno_location
//
//===----------------------------------------------------------------------===//
+#include "include/errno.h"
+#include "include/sys/mman.h"
#include "src/errno/llvmlibc_errno.h"
#include "src/sys/mman/mmap.h"
#include "src/sys/mman/munmap.h"
-
-#include "gtest/gtest.h"
-
-#include "errno.h"
-#include "sys/mman.h"
+#include "utils/UnitTest/Test.h"
TEST(MMapTest, NoError) {
size_t alloc_size = 128;
add_subdirectory(HdrGen)
+add_subdirectory(UnitTest)
--- /dev/null
+add_llvm_library(
+ LibcUnitTest
+ Test.cpp
+ Test.h
+)
--- /dev/null
+//===--------- Implementation of the base class for libc unittests --------===//
+//
+// 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 "Test.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm_libc {
+namespace testing {
+
+// This need not be a class as all it has is a single read-write state variable.
+// But, we make it class as then its implementation can be hidden from the
+// header file.
+class RunContext {
+public:
+ enum RunResult { Result_Pass = 1, Result_Fail = 2 };
+
+ RunResult status() const { return Status; }
+
+ void markFail() { Status = Result_Fail; }
+
+private:
+ RunResult Status = Result_Pass;
+};
+
+namespace internal {
+
+template <typename ValType>
+bool test(RunContext &Ctx, TestCondition Cond, ValType LHS, ValType RHS,
+ const char *LHSStr, const char *RHSStr, const char *File,
+ unsigned long Line) {
+ switch (Cond) {
+ case Cond_EQ:
+ if (LHS == RHS)
+ return true;
+
+ Ctx.markFail();
+ llvm::outs() << File << ":" << Line << ": FAILURE\n"
+ << " Expected: " << LHSStr << '\n'
+ << " Which is: " << LHS << '\n'
+ << "To be equal to: " << RHSStr << '\n'
+ << " Which is: " << RHS << '\n';
+
+ return false;
+ case Cond_NE:
+ if (LHS != RHS)
+ return true;
+
+ Ctx.markFail();
+ llvm::outs() << File << ":" << Line << ": FAILURE\n"
+ << " Expected: " << LHSStr << '\n'
+ << " Which is: " << LHS << '\n'
+ << "To be not equal to: " << RHSStr << '\n'
+ << " Which is: " << RHS << '\n';
+ return false;
+ case Cond_LT:
+ if (LHS < RHS)
+ return true;
+
+ Ctx.markFail();
+ llvm::outs() << File << ":" << Line << ": FAILURE\n"
+ << " Expected: " << LHSStr << '\n'
+ << " Which is: " << LHS << '\n'
+ << "To be less than: " << RHSStr << '\n'
+ << " Which is: " << RHS << '\n';
+ return false;
+ case Cond_LE:
+ if (LHS <= RHS)
+ return true;
+
+ Ctx.markFail();
+ llvm::outs() << File << ":" << Line << ": FAILURE\n"
+ << " Expected: " << LHSStr << '\n'
+ << " Which is: " << LHS << '\n'
+ << "To be less than or equal to: " << RHSStr << '\n'
+ << " Which is: " << RHS << '\n';
+ return false;
+ case Cond_GT:
+ if (LHS > RHS)
+ return true;
+
+ Ctx.markFail();
+ llvm::outs() << File << ":" << Line << ": FAILURE\n"
+ << " Expected: " << LHSStr << '\n'
+ << " Which is: " << LHS << '\n'
+ << "To be greater than: " << RHSStr << '\n'
+ << " Which is: " << RHS << '\n';
+ return false;
+ case Cond_GE:
+ if (LHS >= RHS)
+ return true;
+
+ Ctx.markFail();
+ llvm::outs() << File << ":" << Line << ": FAILURE\n"
+ << " Expected: " << LHSStr << '\n'
+ << " Which is: " << LHS << '\n'
+ << "To be greater than or equal to: " << RHSStr << '\n'
+ << " Which is: " << RHS << '\n';
+ return false;
+ default:
+ Ctx.markFail();
+ llvm::outs() << "Unexpected test condition.\n";
+ return false;
+ }
+}
+
+} // namespace internal
+
+Test *Test::Start = nullptr;
+Test *Test::End = nullptr;
+
+void Test::addTest(Test *T) {
+ if (End == nullptr) {
+ Start = T;
+ End = T;
+ return;
+ }
+
+ End->Next = T;
+ End = T;
+}
+
+int Test::runTests() {
+ int TestCount = 0;
+ int FailCount = 0;
+ for (Test *T = Start; T != nullptr; T = T->Next, ++TestCount) {
+ const char *TestName = T->getName();
+ llvm::outs() << "[ RUN ] " << TestName << '\n';
+ RunContext Ctx;
+ T->SetUp();
+ T->Run(Ctx);
+ T->TearDown();
+ auto Result = Ctx.status();
+ switch (Result) {
+ case RunContext::Result_Fail:
+ llvm::outs() << "[ FAILED ] " << TestName << '\n';
+ ++FailCount;
+ break;
+ case RunContext::Result_Pass:
+ llvm::outs() << "[ OK ] " << TestName << '\n';
+ break;
+ }
+ }
+
+ llvm::outs() << "Ran " << TestCount << " tests. "
+ << " PASS: " << TestCount - FailCount << ' '
+ << " FAIL: " << FailCount << '\n';
+
+ return FailCount > 0 ? 1 : 0;
+}
+
+template bool Test::test<char, 0>(RunContext &Ctx, TestCondition Cond, char LHS,
+ char RHS, const char *LHSStr,
+ const char *RHSStr, const char *File,
+ unsigned long Line);
+
+template bool Test::test<short, 0>(RunContext &Ctx, TestCondition Cond,
+ short LHS, short RHS, const char *LHSStr,
+ const char *RHSStr, const char *File,
+ unsigned long Line);
+
+template bool Test::test<int, 0>(RunContext &Ctx, TestCondition Cond, int LHS,
+ int RHS, const char *LHSStr,
+ const char *RHSStr, const char *File,
+ unsigned long Line);
+
+template bool Test::test<long, 0>(RunContext &Ctx, TestCondition Cond, long LHS,
+ long RHS, const char *LHSStr,
+ const char *RHSStr, const char *File,
+ unsigned long Line);
+
+template bool Test::test<long long, 0>(RunContext &Ctx, TestCondition Cond,
+ long long LHS, long long RHS,
+ const char *LHSStr, const char *RHSStr,
+ const char *File, unsigned long Line);
+
+template bool Test::test<unsigned char, 0>(RunContext &Ctx, TestCondition Cond,
+ unsigned char LHS, unsigned char RHS,
+ const char *LHSStr,
+ const char *RHSStr, const char *File,
+ unsigned long Line);
+
+template bool
+Test::test<unsigned short, 0>(RunContext &Ctx, TestCondition Cond,
+ unsigned short LHS, unsigned short RHS,
+ const char *LHSStr, const char *RHSStr,
+ const char *File, unsigned long Line);
+
+template bool Test::test<unsigned int, 0>(RunContext &Ctx, TestCondition Cond,
+ unsigned int LHS, unsigned int RHS,
+ const char *LHSStr,
+ const char *RHSStr, const char *File,
+ unsigned long Line);
+
+template bool Test::test<unsigned long, 0>(RunContext &Ctx, TestCondition Cond,
+ unsigned long LHS, unsigned long RHS,
+ const char *LHSStr,
+ const char *RHSStr, const char *File,
+ unsigned long Line);
+
+template bool Test::test<unsigned long long, 0>(
+ RunContext &Ctx, TestCondition Cond, unsigned long long LHS,
+ unsigned long long RHS, const char *LHSStr, const char *RHSStr,
+ const char *File, unsigned long Line);
+
+bool Test::testStrEq(RunContext &Ctx, const char *LHS, const char *RHS,
+ const char *LHSStr, const char *RHSStr, const char *File,
+ unsigned long Line) {
+ return internal::test(Ctx, Cond_EQ, llvm::StringRef(LHS),
+ llvm::StringRef(RHS), LHSStr, RHSStr, File, Line);
+}
+
+bool Test::testStrNe(RunContext &Ctx, const char *LHS, const char *RHS,
+ const char *LHSStr, const char *RHSStr, const char *File,
+ unsigned long Line) {
+ return internal::test(Ctx, Cond_NE, llvm::StringRef(LHS),
+ llvm::StringRef(RHS), LHSStr, RHSStr, File, Line);
+}
+
+} // namespace testing
+} // namespace llvm_libc
+
+int main() { return llvm_libc::testing::Test::runTests(); }
--- /dev/null
+//===------------------ Base class for libc unittests -----------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+// This file should stricly not include any other file. Not even standard
+// library headers.
+
+namespace llvm_libc {
+namespace testing {
+
+// We define our own EnableIf and IsIntegerType traits because we do not want to
+// include even the standard header <type_traits>.
+template <bool B, typename T> struct EnableIf;
+template <typename T> struct EnableIf<true, T> { typedef T Type; };
+
+template <bool B, typename T>
+using EnableIfType = typename EnableIf<B, T>::Type;
+
+template <typename Type> struct IsIntegerType {
+ static const bool Value = false;
+};
+
+template <> struct IsIntegerType<char> { static const bool Value = true; };
+template <> struct IsIntegerType<unsigned char> {
+ static const bool Value = true;
+};
+
+template <> struct IsIntegerType<short> { static const bool Value = true; };
+template <> struct IsIntegerType<unsigned short> {
+ static const bool Value = true;
+};
+
+template <> struct IsIntegerType<int> { static const bool Value = true; };
+template <> struct IsIntegerType<unsigned int> {
+ static const bool Value = true;
+};
+
+template <> struct IsIntegerType<long> { static const bool Value = true; };
+template <> struct IsIntegerType<unsigned long> {
+ static const bool Value = true;
+};
+
+template <> struct IsIntegerType<long long> { static const bool Value = true; };
+template <> struct IsIntegerType<unsigned long long> {
+ static const bool Value = true;
+};
+
+template <typename T> struct IsPointerType;
+
+template <typename T> struct IsPointerType<T *> {
+ static const bool Value = true;
+};
+
+class RunContext;
+
+// Only the following conditions are supported. Notice that we do not have
+// a TRUE or FALSE condition. That is because, C library funtions do not
+// return, but use integral return values to indicate true or false
+// conditions. Hence, it is more appropriate to use the other comparison
+// condtions for such cases.
+enum TestCondition {
+ Cond_None,
+ Cond_EQ,
+ Cond_NE,
+ Cond_LT,
+ Cond_LE,
+ Cond_GT,
+ Cond_GE,
+};
+
+namespace internal {
+
+template <typename ValType>
+bool test(RunContext &Ctx, TestCondition Cond, ValType LHS, ValType RHS,
+ const char *LHSStr, const char *RHSStr, const char *File,
+ unsigned long Line);
+
+} // namespace internal
+
+// 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 {
+private:
+ Test *Next = nullptr;
+
+public:
+ virtual ~Test() {}
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+
+ static int runTests();
+
+protected:
+ static void addTest(Test *T);
+
+ // We make use of a template function, with |LHS| and |RHS| as explicit
+ // parameters, for enhanced type checking. Other gtest like test unittest
+ // frameworks have a similar functions which takes a boolean argument
+ // instead of the explicit |LHS| and |RHS| arguments. This boolean argument
+ // is the result of the |Cond| operation on |LHS| and |RHS|. Though not bad,
+ // mismatched |LHS| and |RHS| types can potentially succeed because of type
+ // promotion.
+ template <typename ValType,
+ EnableIfType<IsIntegerType<ValType>::Value, ValType> = 0>
+ static bool test(RunContext &Ctx, TestCondition Cond, ValType LHS,
+ ValType RHS, const char *LHSStr, const char *RHSStr,
+ const char *File, unsigned long Line) {
+ return internal::test(Ctx, Cond, LHS, RHS, LHSStr, RHSStr, File, Line);
+ }
+
+ template <typename ValType,
+ EnableIfType<IsPointerType<ValType>::Value, ValType> = nullptr>
+ static bool test(RunContext &Ctx, TestCondition Cond, ValType LHS,
+ ValType RHS, const char *LHSStr, const char *RHSStr,
+ const char *File, unsigned long Line) {
+ return internal::test(Ctx, Cond, (unsigned long long)LHS,
+ (unsigned long long)RHS, LHSStr, RHSStr, File, Line);
+ }
+
+ static bool testStrEq(RunContext &Ctx, const char *LHS, const char *RHS,
+ const char *LHSStr, const char *RHSStr,
+ const char *File, unsigned long Line);
+
+ static bool testStrNe(RunContext &Ctx, const char *LHS, const char *RHS,
+ 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;
+
+ static Test *Start;
+ static Test *End;
+};
+
+} // namespace testing
+} // namespace llvm_libc
+
+#define TEST(SuiteName, TestName) \
+ class SuiteName##_##TestName : public llvm_libc::testing::Test { \
+ public: \
+ SuiteName##_##TestName() { addTest(this); } \
+ void Run(llvm_libc::testing::RunContext &) override; \
+ const char *getName() const override { return #SuiteName "." #TestName; } \
+ }; \
+ SuiteName##_##TestName SuiteName##_##TestName##_Instance; \
+ void SuiteName##_##TestName::Run(llvm_libc::testing::RunContext &Ctx)
+
+#define TEST_F(SuiteClass, TestName) \
+ class SuiteClass##_##TestName : public SuiteClass { \
+ public: \
+ SuiteClass##_##TestName() { addTest(this); } \
+ void Run(llvm_libc::testing::RunContext &) override; \
+ const char *getName() const override { return #SuiteClass "." #TestName; } \
+ }; \
+ SuiteClass##_##TestName SuiteClass##_##TestName##_Instance; \
+ void SuiteClass##_##TestName::Run(llvm_libc::testing::RunContext &Ctx)
+
+#define EXPECT_EQ(LHS, RHS) \
+ llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_EQ, (LHS), \
+ (RHS), #LHS, #RHS, __FILE__, __LINE__)
+#define ASSERT_EQ(LHS, RHS) \
+ if (!EXPECT_EQ(LHS, RHS)) \
+ return
+
+#define EXPECT_NE(LHS, RHS) \
+ llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_NE, (LHS), \
+ (RHS), #LHS, #RHS, __FILE__, __LINE__)
+#define ASSERT_NE(LHS, RHS) \
+ if (!EXPECT_NE(LHS, RHS)) \
+ return
+
+#define EXPECT_LT(LHS, RHS) \
+ llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_LT, (LHS), \
+ (RHS), #LHS, #RHS, __FILE__, __LINE__)
+#define ASSERT_LT(LHS, RHS) \
+ if (!EXPECT_LT(LHS, RHS)) \
+ return
+
+#define EXPECT_LE(LHS, RHS) \
+ llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_LE, (LHS), \
+ (RHS), #LHS, #RHS, __FILE__, __LINE__)
+#define ASSERT_LE(LHS, RHS) \
+ if (!EXPECT_LE(LHS, RHS)) \
+ return
+
+#define EXPECT_GT(LHS, RHS) \
+ llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_GT, (LHS), \
+ (RHS), #LHS, #RHS, __FILE__, __LINE__)
+#define ASSERT_GT(LHS, RHS) \
+ if (!EXPECT_GT(LHS, RHS)) \
+ return
+
+#define EXPECT_GE(LHS, RHS) \
+ llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_GE, (LHS), \
+ (RHS), #LHS, #RHS, __FILE__, __LINE__)
+#define ASSERT_GE(LHS, RHS) \
+ if (!EXPECT_GE(LHS, RHS)) \
+ return
+
+#define EXPECT_STREQ(LHS, RHS) \
+ llvm_libc::testing::Test::testStrEq(Ctx, (LHS), (RHS), #LHS, #RHS, __FILE__, \
+ __LINE__)
+#define ASSERT_STREQ(LHS, RHS) \
+ if (!EXPECT_STREQ(LHS, RHS)) \
+ return
+
+#define EXPECT_STRNE(LHS, RHS) \
+ llvm_libc::testing::Test::testStrNe(Ctx, (LHS), (RHS), #LHS, #RHS, __FILE__, \
+ __LINE__)
+#define ASSERT_STRNE(LHS, RHS) \
+ if (!EXPECT_STRNE(LHS, RHS)) \
+ return