[libc] add tests to WrapperGen
authorMichael Jones <michaelrj@google.com>
Thu, 19 Nov 2020 21:11:42 +0000 (21:11 +0000)
committerMichael Jones <michaelrj@google.com>
Fri, 4 Dec 2020 18:14:17 +0000 (18:14 +0000)
This adds an initial test that can serve as a basis for other tests on
wrappergen.

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D92137

libc/test/utils/CMakeLists.txt
libc/test/utils/tools/CMakeLists.txt [new file with mode: 0644]
libc/test/utils/tools/WrapperGen/CMakeLists.txt [new file with mode: 0644]
libc/test/utils/tools/WrapperGen/testapi.td [new file with mode: 0644]
libc/test/utils/tools/WrapperGen/wrappergen_test.cpp [new file with mode: 0644]

index ee9bff4..c796961 100644 (file)
@@ -1,2 +1,3 @@
 add_subdirectory(FPUtil)
 add_subdirectory(CPP)
+add_subdirectory(tools)
diff --git a/libc/test/utils/tools/CMakeLists.txt b/libc/test/utils/tools/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9a06be5
--- /dev/null
@@ -0,0 +1,36 @@
+add_custom_target(libc-tool-util-tests)
+
+function(add_libc_tool_unittest target_name)
+
+  cmake_parse_arguments(
+    "LIBC_TOOL_UNITTEST"
+    "" # No optional arguments
+    "" # Single value arguments
+    "SRCS;DEPENDS;ARGS" # Multi-value arguments
+    ${ARGN}
+  )
+
+  add_executable(${target_name}
+    EXCLUDE_FROM_ALL
+    ${LIBC_TOOL_UNITTEST_SRCS}
+  )
+  target_link_libraries(${target_name}
+    PRIVATE
+    gtest_main
+    gtest
+    ${LIBC_TOOL_UNITTEST_DEPENDS}
+  )
+
+  add_custom_command(
+    TARGET ${target_name}
+    POST_BUILD
+    COMMAND $<TARGET_FILE:${target_name}> 
+            ${LIBC_TOOL_UNITTEST_ARGS}
+  )
+  add_dependencies(libc-tool-util-tests ${target_name})
+
+  target_compile_options(${target_name} PUBLIC -fno-rtti)
+  target_link_libraries(${target_name} PRIVATE LLVMSupport)
+endfunction()
+
+add_subdirectory(WrapperGen)
diff --git a/libc/test/utils/tools/WrapperGen/CMakeLists.txt b/libc/test/utils/tools/WrapperGen/CMakeLists.txt
new file mode 100644 (file)
index 0000000..cfb3dab
--- /dev/null
@@ -0,0 +1,10 @@
+add_libc_tool_unittest( wrappergen_test
+  SRCS 
+    wrappergen_test.cpp   
+  ARGS
+    --path=${LIBC_SOURCE_DIR}
+    --tool=${CMAKE_BINARY_DIR}/bin/libc-wrappergen
+    --api=${LIBC_SOURCE_DIR}/test/utils/tools/WrapperGen/testapi.td
+)
+
+add_dependencies(wrappergen_test libc-wrappergen)
diff --git a/libc/test/utils/tools/WrapperGen/testapi.td b/libc/test/utils/tools/WrapperGen/testapi.td
new file mode 100644 (file)
index 0000000..6349ec0
--- /dev/null
@@ -0,0 +1,2 @@
+include "spec/spec.td"
+include "spec/stdc.td"
diff --git a/libc/test/utils/tools/WrapperGen/wrappergen_test.cpp b/libc/test/utils/tools/WrapperGen/wrappergen_test.cpp
new file mode 100644 (file)
index 0000000..a6c29b7
--- /dev/null
@@ -0,0 +1,247 @@
+//===-- Unittests for WrapperGen ------------------------------------------===//
+//
+// 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 "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <unistd.h>
+
+llvm::cl::opt<std::string>
+    LibcPath("path", llvm::cl::desc("Path to the top level libc directory."),
+             llvm::cl::value_desc("<path to libc>"), llvm::cl::Required);
+llvm::cl::opt<std::string>
+    ToolPath("tool", llvm::cl::desc("Path to the tool executable."),
+             llvm::cl::value_desc("<path to tool>"), llvm::cl::Required);
+llvm::cl::opt<std::string>
+    APIPath("api",
+            llvm::cl::desc("Path to the api tablegen file used by the tests."),
+            llvm::cl::value_desc("<path to testapi.td>"), llvm::cl::Required);
+
+class WrapperGenTest : public ::testing::Test {
+public:
+  std::string IncludeArg;
+  std::string APIArg;
+  llvm::StringRef ProgPath;
+  llvm::Expected<llvm::sys::fs::TempFile> STDOutFile =
+      llvm::sys::fs::TempFile::create("wrappergen-stdout-%%-%%-%%-%%.txt");
+  llvm::Expected<llvm::sys::fs::TempFile> STDErrFile =
+      llvm::sys::fs::TempFile::create("wrappergen-stderr-%%-%%-%%-%%.txt");
+
+protected:
+  void SetUp() override {
+    IncludeArg = "-I=";
+    IncludeArg.append(LibcPath);
+    APIArg = APIPath;
+    ProgPath = llvm::StringRef(ToolPath);
+
+    if (!STDOutFile) {
+      llvm::errs() << "Error: " << llvm::toString(STDOutFile.takeError())
+                   << "\n";
+      llvm::report_fatal_error(
+          "Temporary file failed to initialize for libc-wrappergen tests.");
+    }
+    if (!STDErrFile) {
+      llvm::errs() << "Error: " << llvm::toString(STDErrFile.takeError())
+                   << "\n";
+      llvm::report_fatal_error(
+          "Temporary file failed to initialize for libc-wrappergen tests.");
+    }
+  }
+  void TearDown() override {
+    llvm::consumeError(STDOutFile.get().discard());
+    llvm::consumeError(STDErrFile.get().discard());
+  }
+};
+
+TEST_F(WrapperGenTest, RunWrapperGenAndGetNoErrors) {
+  llvm::Optional<llvm::StringRef> Redirects[] = {
+      llvm::None, llvm::StringRef(STDOutFile.get().TmpName),
+      llvm::StringRef(STDErrFile.get().TmpName)};
+
+  llvm::StringRef ArgV[] = {ProgPath, llvm::StringRef(IncludeArg),
+                            llvm::StringRef(APIArg), "--name", "strlen"};
+
+  int ExitCode =
+      llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects);
+
+  EXPECT_EQ(ExitCode, 0);
+
+  auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName);
+  std::string STDErrOutput = STDErrOrError.get()->getBuffer().str();
+  ASSERT_EQ(STDErrOutput, "");
+}
+
+TEST_F(WrapperGenTest, RunWrapperGenOnStrlen) {
+  llvm::Optional<llvm::StringRef> Redirects[] = {
+      llvm::None, llvm::StringRef(STDOutFile.get().TmpName),
+      llvm::StringRef(STDErrFile.get().TmpName)};
+
+  llvm::StringRef ArgV[] = {ProgPath, llvm::StringRef(IncludeArg),
+                            llvm::StringRef(APIArg), "--name", "strlen"};
+
+  int ExitCode =
+      llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects);
+
+  EXPECT_EQ(ExitCode, 0);
+
+  auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName);
+  std::string STDErrOutput = STDErrOrError.get()->getBuffer().str();
+
+  ASSERT_EQ(STDErrOutput, "");
+
+  auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName);
+  std::string STDOutOutput = STDOutOrError.get()->getBuffer().str();
+
+  ASSERT_EQ(STDOutOutput, "#include \"src/string/strlen.h\"\n"
+                          "extern \"C\" size_t strlen(const char * __arg0) {\n"
+                          "  return __llvm_libc::strlen(__arg0);\n"
+                          "}\n");
+  // TODO:(michaelrj) Figure out how to make this output comparison
+  // less brittle. Currently it's just comparing the output of the program
+  // to an exact string, this means that even a small formatting change
+  // would break this test.
+}
+
+TEST_F(WrapperGenTest, RunWrapperGenOnStrlenWithAliasee) {
+  llvm::Optional<llvm::StringRef> Redirects[] = {
+      llvm::None, llvm::StringRef(STDOutFile.get().TmpName),
+      llvm::StringRef(STDErrFile.get().TmpName)};
+
+  llvm::StringRef ArgV[] = {ProgPath,
+                            llvm::StringRef(IncludeArg),
+                            llvm::StringRef(APIArg),
+                            "--aliasee",
+                            "STRLEN_ALIAS",
+                            "--name",
+                            "strlen"};
+
+  int ExitCode =
+      llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects);
+
+  EXPECT_EQ(ExitCode, 0);
+
+  auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName);
+  std::string STDErrOutput = STDErrOrError.get()->getBuffer().str();
+
+  ASSERT_EQ(STDErrOutput, "");
+
+  auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName);
+  std::string STDOutOutput = STDOutOrError.get()->getBuffer().str();
+
+  ASSERT_EQ(STDOutOutput, "extern \"C\" size_t strlen(const char * __arg0) "
+                          "__attribute__((alias(\"STRLEN_ALIAS\")));\n");
+  // TODO:(michaelrj) Figure out how to make this output comparison
+  // less brittle. Currently it's just comparing the output of the program
+  // to an exact string, this means that even a small formatting change
+  // would break this test.
+}
+
+/////////////////////////////////////////////////////////////////////
+// BAD INPUT TESTS
+// all of the tests after this point are testing inputs that should
+// return errors
+/////////////////////////////////////////////////////////////////////
+
+TEST_F(WrapperGenTest,
+       RunWrapperGenOnStrlenWithAliaseeAndAliaseeFileWhichIsError) {
+  llvm::Optional<llvm::StringRef> Redirects[] = {
+      llvm::None, llvm::StringRef(STDOutFile.get().TmpName),
+      llvm::StringRef(STDErrFile.get().TmpName)};
+
+  llvm::StringRef ArgV[] = {ProgPath,
+                            llvm::StringRef(IncludeArg),
+                            llvm::StringRef(APIArg),
+                            "--aliasee",
+                            "STRLEN_ALIAS",
+                            "--aliasee-file",
+                            "STRLEN_ALIAS_FILE",
+                            "--name",
+                            "strlen"};
+
+  int ExitCode =
+      llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects);
+
+  EXPECT_EQ(ExitCode, 1);
+
+  auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName);
+  std::string STDErrOutput = STDErrOrError.get()->getBuffer().str();
+
+  ASSERT_EQ(STDErrOutput, "error: The options 'aliasee' and 'aliasee-file' "
+                          "cannot be specified simultaniously.\n");
+
+  auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName);
+  std::string STDOutOutput = STDOutOrError.get()->getBuffer().str();
+
+  ASSERT_EQ(STDOutOutput, "");
+}
+
+TEST_F(WrapperGenTest, RunWrapperGenOnBadFuncName) {
+  llvm::Optional<llvm::StringRef> Redirects[] = {
+      llvm::None, llvm::StringRef(STDOutFile.get().TmpName),
+      llvm::StringRef(STDErrFile.get().TmpName)};
+
+  llvm::StringRef BadFuncName = "FAKE_TEST_FUNC";
+
+  llvm::StringRef ArgV[] = {ProgPath, llvm::StringRef(IncludeArg),
+                            llvm::StringRef(APIArg), "--name", BadFuncName};
+
+  int ExitCode =
+      llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects);
+
+  EXPECT_EQ(ExitCode, 1);
+
+  auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName);
+  std::string STDErrOutput = STDErrOrError.get()->getBuffer().str();
+
+  ASSERT_EQ(STDErrOutput, ("error: Function '" + BadFuncName +
+                           "' not found in any standard spec.\n")
+                              .str());
+
+  auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName);
+  std::string STDOutOutput = STDOutOrError.get()->getBuffer().str();
+
+  ASSERT_EQ(STDOutOutput, "");
+}
+
+TEST_F(WrapperGenTest, RunWrapperGenOnStrlenWithBadAliaseeFile) {
+  llvm::Optional<llvm::StringRef> Redirects[] = {
+      llvm::None, llvm::StringRef(STDOutFile.get().TmpName),
+      llvm::StringRef(STDErrFile.get().TmpName)};
+
+  llvm::StringRef BadAliaseeFileName = "FILE_THAT_DOESNT_EXIST.txt";
+
+  llvm::StringRef ArgV[] = {
+      ProgPath,         llvm::StringRef(IncludeArg), llvm::StringRef(APIArg),
+      "--aliasee-file", BadAliaseeFileName,          "--name",
+      "strlen"};
+
+  int ExitCode =
+      llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects);
+
+  EXPECT_EQ(ExitCode, 1);
+
+  auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName);
+  std::string STDErrOutput = STDErrOrError.get()->getBuffer().str();
+
+  ASSERT_EQ(STDErrOutput, ("error: Unable to read the aliasee file " +
+                           BadAliaseeFileName + "\n")
+                              .str());
+
+  auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName);
+  std::string STDOutOutput = STDOutOrError.get()->getBuffer().str();
+
+  ASSERT_EQ(STDOutOutput, "");
+}