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/FileUtilities.h" 16 #include "llvm/Support/MemoryBuffer.h" 17 #include "llvm/Support/Program.h" 18 #include "llvm/Support/raw_ostream.h" 19 #include "gmock/gmock.h" 20 #include "gtest/gtest.h" 21 #include <unistd.h> 22 23 llvm::cl::opt<std::string> 24 LibcPath("path", llvm::cl::desc("Path to the top level libc directory."), 25 llvm::cl::value_desc("<path to libc>"), llvm::cl::Required); 26 llvm::cl::opt<std::string> 27 ToolPath("tool", llvm::cl::desc("Path to the tool executable."), 28 llvm::cl::value_desc("<path to tool>"), llvm::cl::Required); 29 llvm::cl::opt<std::string> 30 APIPath("api", 31 llvm::cl::desc("Path to the api tablegen file used by the tests."), 32 llvm::cl::value_desc("<path to testapi.td>"), llvm::cl::Required); 33 34 class WrapperGenTest : public ::testing::Test { 35 public: 36 std::string IncludeArg; 37 std::string APIArg; 38 llvm::StringRef ProgPath; 39 llvm::Expected<llvm::sys::fs::TempFile> STDOutFile = 40 llvm::sys::fs::TempFile::create("wrappergen-stdout-%%-%%-%%-%%.txt"); 41 llvm::Expected<llvm::sys::fs::TempFile> STDErrFile = 42 llvm::sys::fs::TempFile::create("wrappergen-stderr-%%-%%-%%-%%.txt"); 43 44 protected: 45 void SetUp() override { 46 IncludeArg = "-I="; 47 IncludeArg.append(LibcPath); 48 APIArg = APIPath; 49 ProgPath = llvm::StringRef(ToolPath); 50 51 if (!STDOutFile) { 52 llvm::errs() << "Error: " << llvm::toString(STDOutFile.takeError()) 53 << "\n"; 54 llvm::report_fatal_error( 55 "Temporary file failed to initialize for libc-wrappergen tests."); 56 } 57 if (!STDErrFile) { 58 llvm::errs() << "Error: " << llvm::toString(STDErrFile.takeError()) 59 << "\n"; 60 llvm::report_fatal_error( 61 "Temporary file failed to initialize for libc-wrappergen tests."); 62 } 63 } 64 void TearDown() override { 65 llvm::consumeError(STDOutFile.get().discard()); 66 llvm::consumeError(STDErrFile.get().discard()); 67 } 68 }; 69 70 using LlvmLibcWrapperGenTest = WrapperGenTest; 71 72 TEST_F(LlvmLibcWrapperGenTest, RunWrapperGenAndGetNoErrors) { 73 llvm::Optional<llvm::StringRef> Redirects[] = { 74 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 75 llvm::StringRef(STDErrFile.get().TmpName)}; 76 77 llvm::StringRef ArgV[] = {ProgPath, 78 llvm::StringRef(IncludeArg), 79 llvm::StringRef(APIArg), 80 "--gen-wrapper", 81 "--name", 82 "strlen"}; 83 84 int ExitCode = 85 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 86 87 EXPECT_EQ(ExitCode, 0); 88 89 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 90 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 91 ASSERT_EQ(STDErrOutput, ""); 92 } 93 94 TEST_F(LlvmLibcWrapperGenTest, RunWrapperGenOnStrlen) { 95 llvm::Optional<llvm::StringRef> Redirects[] = { 96 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 97 llvm::StringRef(STDErrFile.get().TmpName)}; 98 99 llvm::StringRef ArgV[] = {ProgPath, 100 llvm::StringRef(IncludeArg), 101 llvm::StringRef(APIArg), 102 "--gen-wrapper", 103 "--name", 104 "strlen"}; 105 106 int ExitCode = 107 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 108 109 EXPECT_EQ(ExitCode, 0); 110 111 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 112 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 113 114 ASSERT_EQ(STDErrOutput, ""); 115 116 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 117 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 118 119 ASSERT_EQ(STDOutOutput, "#include \"src/string/strlen.h\"\n" 120 "extern \"C\" size_t strlen(const char * __arg0) {\n" 121 " return __llvm_libc::strlen(__arg0);\n" 122 "}\n"); 123 // TODO:(michaelrj) Figure out how to make this output comparison 124 // less brittle. Currently it's just comparing the output of the program 125 // to an exact string, this means that even a small formatting change 126 // would break this test. 127 } 128 129 TEST_F(LlvmLibcWrapperGenTest, GenAliasForStrlen) { 130 llvm::Optional<llvm::StringRef> Redirects[] = { 131 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 132 llvm::StringRef(STDErrFile.get().TmpName)}; 133 134 llvm::StringRef ArgV[] = {ProgPath, 135 llvm::StringRef(IncludeArg), 136 llvm::StringRef(APIArg), 137 "--gen-alias", 138 "--mangled-name", 139 "__llvm_libc_strlen_mangled_name", 140 "--name", 141 "strlen"}; 142 143 int ExitCode = 144 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 145 146 EXPECT_EQ(ExitCode, 0); 147 148 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 149 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 150 151 ASSERT_EQ(STDErrOutput, ""); 152 153 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 154 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 155 156 ASSERT_EQ(STDOutOutput, 157 "extern \"C\" size_t strlen(const char * __arg0) " 158 "__attribute__((alias(\"__llvm_libc_strlen_mangled_name\")));\n"); 159 // TODO:(michaelrj) Figure out how to make this output comparison 160 // less brittle. Currently it's just comparing the output of the program 161 // to an exact string, this means that even a small formatting change 162 // would break this test. 163 } 164 165 TEST_F(LlvmLibcWrapperGenTest, DeclStrlenAliasUsingMangledNameFile) { 166 llvm::Optional<llvm::StringRef> Redirects[] = { 167 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 168 llvm::StringRef(STDErrFile.get().TmpName)}; 169 170 const char *MangledNameFileContent = 171 "abc\nxyz__llvm_libc_strlen_mangled_name\nijk\n"; 172 llvm::SmallVector<char> MangledNameFilePath; 173 auto MangledNameFileCreateError = llvm::sys::fs::createUniqueFile( 174 "libc-wrappergen-test-aliasee-file-%%-%%-%%-%%.txt", MangledNameFilePath); 175 ASSERT_FALSE(MangledNameFileCreateError); 176 auto MangledNameFileWriteError = llvm::writeFileAtomically( 177 "libc-wrappergen-temp-test-aliasee-file-%%-%%-%%-%%.txt", 178 llvm::StringRef(MangledNameFilePath.data()), 179 llvm::StringRef(MangledNameFileContent)); 180 ASSERT_FALSE(MangledNameFileWriteError); 181 182 llvm::StringRef ArgV[] = {ProgPath, 183 llvm::StringRef(IncludeArg), 184 llvm::StringRef(APIArg), 185 "--gen-alias", 186 "--mangled-name-file", 187 llvm::StringRef(MangledNameFilePath.data()), 188 "--name", 189 "strlen"}; 190 191 int ExitCode = 192 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 193 194 EXPECT_EQ(ExitCode, 0); 195 196 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 197 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 198 199 ASSERT_EQ(STDErrOutput, ""); 200 201 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 202 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 203 204 ASSERT_EQ( 205 STDOutOutput, 206 "extern \"C\" size_t strlen(const char * __arg0) " 207 "__attribute__((alias(\"xyz__llvm_libc_strlen_mangled_name\")));\n"); 208 } 209 210 ///////////////////////////////////////////////////////////////////// 211 ///////////////////////////////////////////////////////////////////// 212 // BAD INPUT TESTS 213 // all of the tests after this point are testing inputs that should 214 // return errors 215 ///////////////////////////////////////////////////////////////////// 216 217 TEST_F(LlvmLibcWrapperGenTest, 218 RunWrapperGenOnStrlenWithMangledNameAndMangledNameFile) { 219 llvm::Optional<llvm::StringRef> Redirects[] = { 220 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 221 llvm::StringRef(STDErrFile.get().TmpName)}; 222 223 llvm::StringRef ArgV[] = {ProgPath, 224 llvm::StringRef(IncludeArg), 225 llvm::StringRef(APIArg), 226 "--gen-alias", 227 "--mangled-name", 228 "__llvm_libc_strlen_mangled_name", 229 "--mangled-name-file", 230 "non-existant-mangled-name-file.txt", 231 "--name", 232 "strlen"}; 233 234 int ExitCode = 235 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 236 237 EXPECT_EQ(ExitCode, 1); 238 239 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 240 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 241 242 ASSERT_EQ(STDErrOutput, 243 "error: The options 'mangled-name' and 'mangled-name-file' " 244 "cannot be specified simultaneously.\n"); 245 246 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 247 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 248 249 ASSERT_EQ(STDOutOutput, ""); 250 } 251 252 TEST_F(LlvmLibcWrapperGenTest, RunWrapperGenOnBadFuncName) { 253 llvm::Optional<llvm::StringRef> Redirects[] = { 254 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 255 llvm::StringRef(STDErrFile.get().TmpName)}; 256 257 llvm::StringRef BadFuncName = "FAKE_TEST_FUNC"; 258 259 llvm::StringRef ArgV[] = {ProgPath, 260 llvm::StringRef(IncludeArg), 261 llvm::StringRef(APIArg), 262 "--gen-wrapper", 263 "--name", 264 BadFuncName}; 265 266 int ExitCode = 267 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 268 269 EXPECT_EQ(ExitCode, 1); 270 271 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 272 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 273 274 ASSERT_EQ(STDErrOutput, ("error: Function '" + BadFuncName + 275 "' not found in any standard spec.\n") 276 .str()); 277 278 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 279 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 280 281 ASSERT_EQ(STDOutOutput, ""); 282 } 283 284 TEST_F(LlvmLibcWrapperGenTest, RunWrapperGenOnStrlenWithBadMangledNameFile) { 285 llvm::Optional<llvm::StringRef> Redirects[] = { 286 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 287 llvm::StringRef(STDErrFile.get().TmpName)}; 288 289 llvm::StringRef BadMangledNameFileName = "FILE_THAT_DOESNT_EXIST.txt"; 290 291 llvm::StringRef ArgV[] = {ProgPath, 292 llvm::StringRef(IncludeArg), 293 llvm::StringRef(APIArg), 294 "--gen-alias", 295 "--mangled-name-file", 296 BadMangledNameFileName, 297 "--name", 298 "strlen"}; 299 300 int ExitCode = 301 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 302 303 EXPECT_EQ(ExitCode, 1); 304 305 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 306 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 307 308 ASSERT_EQ(STDErrOutput, ("error: Unable to read the mangled name file " + 309 BadMangledNameFileName + "\n") 310 .str()); 311 312 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 313 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 314 315 ASSERT_EQ(STDOutOutput, ""); 316 } 317 318 TEST_F(LlvmLibcWrapperGenTest, RunWithMangledNameFileMissingLLVMLibcName) { 319 llvm::Optional<llvm::StringRef> Redirects[] = { 320 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 321 llvm::StringRef(STDErrFile.get().TmpName)}; 322 323 llvm::SmallVector<char> MangledNameFilePath; 324 auto MangledNameFileCreateError = llvm::sys::fs::createUniqueFile( 325 "libc-wrappergen-test-mangled-name-file-%%-%%-%%-%%.txt", 326 MangledNameFilePath); 327 ASSERT_FALSE(MangledNameFileCreateError); 328 329 llvm::StringRef ArgV[] = {ProgPath, 330 llvm::StringRef(IncludeArg), 331 llvm::StringRef(APIArg), 332 "--gen-alias", 333 "--mangled-name-file", 334 llvm::StringRef(MangledNameFilePath.data()), 335 "--name", 336 "strlen"}; 337 338 int ExitCode = 339 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 340 341 EXPECT_NE(ExitCode, 0); 342 343 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 344 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 345 346 ASSERT_EQ(STDErrOutput, ("error: Did not find an LLVM libc mangled name in " + 347 MangledNameFilePath + "\n") 348 .str()); 349 } 350