1 //===-- Unittests for WrapperGen ------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/ADT/SmallString.h" 10 #include "llvm/ADT/StringRef.h" 11 #include "llvm/ADT/Twine.h" 12 #include "llvm/Support/CommandLine.h" 13 #include "llvm/Support/Error.h" 14 #include "llvm/Support/FileSystem.h" 15 #include "llvm/Support/MemoryBuffer.h" 16 #include "llvm/Support/Program.h" 17 #include "llvm/Support/raw_ostream.h" 18 #include "gmock/gmock.h" 19 #include "gtest/gtest.h" 20 #include <unistd.h> 21 22 llvm::cl::opt<std::string> 23 LibcPath("path", llvm::cl::desc("Path to the top level libc directory."), 24 llvm::cl::value_desc("<path to libc>"), llvm::cl::Required); 25 llvm::cl::opt<std::string> 26 ToolPath("tool", llvm::cl::desc("Path to the tool executable."), 27 llvm::cl::value_desc("<path to tool>"), llvm::cl::Required); 28 llvm::cl::opt<std::string> 29 APIPath("api", 30 llvm::cl::desc("Path to the api tablegen file used by the tests."), 31 llvm::cl::value_desc("<path to testapi.td>"), llvm::cl::Required); 32 33 class WrapperGenTest : public ::testing::Test { 34 public: 35 std::string IncludeArg; 36 std::string APIArg; 37 llvm::StringRef ProgPath; 38 llvm::Expected<llvm::sys::fs::TempFile> STDOutFile = 39 llvm::sys::fs::TempFile::create("wrappergen-stdout-%%-%%-%%-%%.txt"); 40 llvm::Expected<llvm::sys::fs::TempFile> STDErrFile = 41 llvm::sys::fs::TempFile::create("wrappergen-stderr-%%-%%-%%-%%.txt"); 42 43 protected: 44 void SetUp() override { 45 IncludeArg = "-I="; 46 IncludeArg.append(LibcPath); 47 APIArg = APIPath; 48 ProgPath = llvm::StringRef(ToolPath); 49 50 if (!STDOutFile) { 51 llvm::errs() << "Error: " << llvm::toString(STDOutFile.takeError()) 52 << "\n"; 53 llvm::report_fatal_error( 54 "Temporary file failed to initialize for libc-wrappergen tests."); 55 } 56 if (!STDErrFile) { 57 llvm::errs() << "Error: " << llvm::toString(STDErrFile.takeError()) 58 << "\n"; 59 llvm::report_fatal_error( 60 "Temporary file failed to initialize for libc-wrappergen tests."); 61 } 62 } 63 void TearDown() override { 64 llvm::consumeError(STDOutFile.get().discard()); 65 llvm::consumeError(STDErrFile.get().discard()); 66 } 67 }; 68 69 TEST_F(WrapperGenTest, RunWrapperGenAndGetNoErrors) { 70 llvm::Optional<llvm::StringRef> Redirects[] = { 71 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 72 llvm::StringRef(STDErrFile.get().TmpName)}; 73 74 llvm::StringRef ArgV[] = {ProgPath, llvm::StringRef(IncludeArg), 75 llvm::StringRef(APIArg), "--name", "strlen"}; 76 77 int ExitCode = 78 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 79 80 EXPECT_EQ(ExitCode, 0); 81 82 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 83 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 84 ASSERT_EQ(STDErrOutput, ""); 85 } 86 87 TEST_F(WrapperGenTest, RunWrapperGenOnStrlen) { 88 llvm::Optional<llvm::StringRef> Redirects[] = { 89 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 90 llvm::StringRef(STDErrFile.get().TmpName)}; 91 92 llvm::StringRef ArgV[] = {ProgPath, llvm::StringRef(IncludeArg), 93 llvm::StringRef(APIArg), "--name", "strlen"}; 94 95 int ExitCode = 96 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 97 98 EXPECT_EQ(ExitCode, 0); 99 100 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 101 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 102 103 ASSERT_EQ(STDErrOutput, ""); 104 105 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 106 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 107 108 ASSERT_EQ(STDOutOutput, "#include \"src/string/strlen.h\"\n" 109 "extern \"C\" size_t strlen(const char * __arg0) {\n" 110 " return __llvm_libc::strlen(__arg0);\n" 111 "}\n"); 112 // TODO:(michaelrj) Figure out how to make this output comparison 113 // less brittle. Currently it's just comparing the output of the program 114 // to an exact string, this means that even a small formatting change 115 // would break this test. 116 } 117 118 TEST_F(WrapperGenTest, RunWrapperGenOnStrlenWithAliasee) { 119 llvm::Optional<llvm::StringRef> Redirects[] = { 120 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 121 llvm::StringRef(STDErrFile.get().TmpName)}; 122 123 llvm::StringRef ArgV[] = {ProgPath, 124 llvm::StringRef(IncludeArg), 125 llvm::StringRef(APIArg), 126 "--aliasee", 127 "STRLEN_ALIAS", 128 "--name", 129 "strlen"}; 130 131 int ExitCode = 132 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 133 134 EXPECT_EQ(ExitCode, 0); 135 136 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 137 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 138 139 ASSERT_EQ(STDErrOutput, ""); 140 141 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 142 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 143 144 ASSERT_EQ(STDOutOutput, "extern \"C\" size_t strlen(const char * __arg0) " 145 "__attribute__((alias(\"STRLEN_ALIAS\")));\n"); 146 // TODO:(michaelrj) Figure out how to make this output comparison 147 // less brittle. Currently it's just comparing the output of the program 148 // to an exact string, this means that even a small formatting change 149 // would break this test. 150 } 151 152 ///////////////////////////////////////////////////////////////////// 153 // BAD INPUT TESTS 154 // all of the tests after this point are testing inputs that should 155 // return errors 156 ///////////////////////////////////////////////////////////////////// 157 158 TEST_F(WrapperGenTest, 159 RunWrapperGenOnStrlenWithAliaseeAndAliaseeFileWhichIsError) { 160 llvm::Optional<llvm::StringRef> Redirects[] = { 161 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 162 llvm::StringRef(STDErrFile.get().TmpName)}; 163 164 llvm::StringRef ArgV[] = {ProgPath, 165 llvm::StringRef(IncludeArg), 166 llvm::StringRef(APIArg), 167 "--aliasee", 168 "STRLEN_ALIAS", 169 "--aliasee-file", 170 "STRLEN_ALIAS_FILE", 171 "--name", 172 "strlen"}; 173 174 int ExitCode = 175 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 176 177 EXPECT_EQ(ExitCode, 1); 178 179 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 180 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 181 182 ASSERT_EQ(STDErrOutput, "error: The options 'aliasee' and 'aliasee-file' " 183 "cannot be specified simultaniously.\n"); 184 185 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 186 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 187 188 ASSERT_EQ(STDOutOutput, ""); 189 } 190 191 TEST_F(WrapperGenTest, RunWrapperGenOnBadFuncName) { 192 llvm::Optional<llvm::StringRef> Redirects[] = { 193 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 194 llvm::StringRef(STDErrFile.get().TmpName)}; 195 196 llvm::StringRef BadFuncName = "FAKE_TEST_FUNC"; 197 198 llvm::StringRef ArgV[] = {ProgPath, llvm::StringRef(IncludeArg), 199 llvm::StringRef(APIArg), "--name", BadFuncName}; 200 201 int ExitCode = 202 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 203 204 EXPECT_EQ(ExitCode, 1); 205 206 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 207 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 208 209 ASSERT_EQ(STDErrOutput, ("error: Function '" + BadFuncName + 210 "' not found in any standard spec.\n") 211 .str()); 212 213 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 214 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 215 216 ASSERT_EQ(STDOutOutput, ""); 217 } 218 219 TEST_F(WrapperGenTest, RunWrapperGenOnStrlenWithBadAliaseeFile) { 220 llvm::Optional<llvm::StringRef> Redirects[] = { 221 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 222 llvm::StringRef(STDErrFile.get().TmpName)}; 223 224 llvm::StringRef BadAliaseeFileName = "FILE_THAT_DOESNT_EXIST.txt"; 225 226 llvm::StringRef ArgV[] = { 227 ProgPath, llvm::StringRef(IncludeArg), llvm::StringRef(APIArg), 228 "--aliasee-file", BadAliaseeFileName, "--name", 229 "strlen"}; 230 231 int ExitCode = 232 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 233 234 EXPECT_EQ(ExitCode, 1); 235 236 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 237 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 238 239 ASSERT_EQ(STDErrOutput, ("error: Unable to read the aliasee file " + 240 BadAliaseeFileName + "\n") 241 .str()); 242 243 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 244 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 245 246 ASSERT_EQ(STDOutOutput, ""); 247 } 248