blob: 4cb7a31de94219a49681ccb807778744c29796fe [file] [log] [blame]
//===-- 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/FileUtilities.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.
}
TEST_F(WrapperGenTest, DeclStrlenAliasUsingAliaseeFile) {
llvm::Optional<llvm::StringRef> Redirects[] = {
llvm::None, llvm::StringRef(STDOutFile.get().TmpName),
llvm::StringRef(STDErrFile.get().TmpName)};
const char *AliaseeFileContent = "abc\nxyz__llvm_libcSTRLEN_ALIAS\nijk\n";
llvm::SmallVector<char> AliaseeFilePath;
auto AliaseeFileCreateError = llvm::sys::fs::createUniqueFile(
"libc-wrappergen-test-aliasee-file-%%-%%-%%-%%.txt", AliaseeFilePath);
ASSERT_FALSE(AliaseeFileCreateError);
auto AliaseeFileWriteError = llvm::writeFileAtomically(
"libc-wrappergen-temp-test-aliasee-file-%%-%%-%%-%%.txt",
llvm::StringRef(AliaseeFilePath.data()),
llvm::StringRef(AliaseeFileContent));
ASSERT_FALSE(AliaseeFileWriteError);
llvm::StringRef ArgV[] = {ProgPath,
llvm::StringRef(IncludeArg),
llvm::StringRef(APIArg),
"--aliasee-file",
llvm::StringRef(AliaseeFilePath.data()),
"--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(\"xyz__llvm_libcSTRLEN_ALIAS\")));\n");
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// 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, "");
}
TEST_F(WrapperGenTest, RunWithAliaseeFileMissingLLVMLibcName) {
llvm::Optional<llvm::StringRef> Redirects[] = {
llvm::None, llvm::StringRef(STDOutFile.get().TmpName),
llvm::StringRef(STDErrFile.get().TmpName)};
llvm::SmallVector<char> AliaseeFilePath;
auto AliaseeFileCreateError = llvm::sys::fs::createUniqueFile(
"libc-wrappergen-test-aliasee-file-%%-%%-%%-%%.txt", AliaseeFilePath);
ASSERT_FALSE(AliaseeFileCreateError);
llvm::StringRef ArgV[] = {ProgPath,
llvm::StringRef(IncludeArg),
llvm::StringRef(APIArg),
"--aliasee-file",
llvm::StringRef(AliaseeFilePath.data()),
"--name",
"strlen"};
int ExitCode =
llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects);
EXPECT_NE(ExitCode, 0);
auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName);
std::string STDErrOutput = STDErrOrError.get()->getBuffer().str();
ASSERT_EQ(STDErrOutput, ("error: Did not find an LLVM libc mangled name in " +
AliaseeFilePath + "\n")
.str());
}