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 TEST_F(WrapperGenTest, RunWrapperGenAndGetNoErrors) { 71 llvm::Optional<llvm::StringRef> Redirects[] = { 72 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 73 llvm::StringRef(STDErrFile.get().TmpName)}; 74 75 llvm::StringRef ArgV[] = {ProgPath, 76 llvm::StringRef(IncludeArg), 77 llvm::StringRef(APIArg), 78 "--gen-wrapper", 79 "--name", 80 "strlen"}; 81 82 int ExitCode = 83 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 84 85 EXPECT_EQ(ExitCode, 0); 86 87 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 88 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 89 ASSERT_EQ(STDErrOutput, ""); 90 } 91 92 TEST_F(WrapperGenTest, RunWrapperGenOnStrlen) { 93 llvm::Optional<llvm::StringRef> Redirects[] = { 94 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 95 llvm::StringRef(STDErrFile.get().TmpName)}; 96 97 llvm::StringRef ArgV[] = {ProgPath, 98 llvm::StringRef(IncludeArg), 99 llvm::StringRef(APIArg), 100 "--gen-wrapper", 101 "--name", 102 "strlen"}; 103 104 int ExitCode = 105 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 106 107 EXPECT_EQ(ExitCode, 0); 108 109 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 110 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 111 112 ASSERT_EQ(STDErrOutput, ""); 113 114 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 115 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 116 117 ASSERT_EQ(STDOutOutput, "#include \"src/string/strlen.h\"\n" 118 "extern \"C\" size_t strlen(const char * __arg0) {\n" 119 " return __llvm_libc::strlen(__arg0);\n" 120 "}\n"); 121 // TODO:(michaelrj) Figure out how to make this output comparison 122 // less brittle. Currently it's just comparing the output of the program 123 // to an exact string, this means that even a small formatting change 124 // would break this test. 125 } 126 127 TEST_F(WrapperGenTest, GenAliasForStrlen) { 128 llvm::Optional<llvm::StringRef> Redirects[] = { 129 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 130 llvm::StringRef(STDErrFile.get().TmpName)}; 131 132 llvm::StringRef ArgV[] = {ProgPath, 133 llvm::StringRef(IncludeArg), 134 llvm::StringRef(APIArg), 135 "--gen-alias", 136 "--mangled-name", 137 "__llvm_libc_strlen_mangled_name", 138 "--name", 139 "strlen"}; 140 141 int ExitCode = 142 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 143 144 EXPECT_EQ(ExitCode, 0); 145 146 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 147 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 148 149 ASSERT_EQ(STDErrOutput, ""); 150 151 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 152 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 153 154 ASSERT_EQ(STDOutOutput, 155 "extern \"C\" size_t strlen(const char * __arg0) " 156 "__attribute__((alias(\"__llvm_libc_strlen_mangled_name\")));\n"); 157 // TODO:(michaelrj) Figure out how to make this output comparison 158 // less brittle. Currently it's just comparing the output of the program 159 // to an exact string, this means that even a small formatting change 160 // would break this test. 161 } 162 163 TEST_F(WrapperGenTest, DeclStrlenAliasUsingMangledNameFile) { 164 llvm::Optional<llvm::StringRef> Redirects[] = { 165 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 166 llvm::StringRef(STDErrFile.get().TmpName)}; 167 168 const char *MangledNameFileContent = 169 "abc\nxyz__llvm_libc_strlen_mangled_name\nijk\n"; 170 llvm::SmallVector<char> MangledNameFilePath; 171 auto MangledNameFileCreateError = llvm::sys::fs::createUniqueFile( 172 "libc-wrappergen-test-aliasee-file-%%-%%-%%-%%.txt", MangledNameFilePath); 173 ASSERT_FALSE(MangledNameFileCreateError); 174 auto MangledNameFileWriteError = llvm::writeFileAtomically( 175 "libc-wrappergen-temp-test-aliasee-file-%%-%%-%%-%%.txt", 176 llvm::StringRef(MangledNameFilePath.data()), 177 llvm::StringRef(MangledNameFileContent)); 178 ASSERT_FALSE(MangledNameFileWriteError); 179 180 llvm::StringRef ArgV[] = {ProgPath, 181 llvm::StringRef(IncludeArg), 182 llvm::StringRef(APIArg), 183 "--gen-alias", 184 "--mangled-name-file", 185 llvm::StringRef(MangledNameFilePath.data()), 186 "--name", 187 "strlen"}; 188 189 int ExitCode = 190 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 191 192 EXPECT_EQ(ExitCode, 0); 193 194 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 195 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 196 197 ASSERT_EQ(STDErrOutput, ""); 198 199 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 200 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 201 202 ASSERT_EQ( 203 STDOutOutput, 204 "extern \"C\" size_t strlen(const char * __arg0) " 205 "__attribute__((alias(\"xyz__llvm_libc_strlen_mangled_name\")));\n"); 206 } 207 208 ///////////////////////////////////////////////////////////////////// 209 ///////////////////////////////////////////////////////////////////// 210 // BAD INPUT TESTS 211 // all of the tests after this point are testing inputs that should 212 // return errors 213 ///////////////////////////////////////////////////////////////////// 214 215 TEST_F(WrapperGenTest, RunWrapperGenOnStrlenWithMangledNameAndMangledNameFile) { 216 llvm::Optional<llvm::StringRef> Redirects[] = { 217 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 218 llvm::StringRef(STDErrFile.get().TmpName)}; 219 220 llvm::StringRef ArgV[] = {ProgPath, 221 llvm::StringRef(IncludeArg), 222 llvm::StringRef(APIArg), 223 "--gen-alias", 224 "--mangled-name", 225 "__llvm_libc_strlen_mangled_name", 226 "--mangled-name-file", 227 "non-existant-mangled-name-file.txt", 228 "--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, 240 "error: The options 'mangled-name' and 'mangled-name-file' " 241 "cannot be specified simultaneously.\n"); 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 249 TEST_F(WrapperGenTest, RunWrapperGenOnBadFuncName) { 250 llvm::Optional<llvm::StringRef> Redirects[] = { 251 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 252 llvm::StringRef(STDErrFile.get().TmpName)}; 253 254 llvm::StringRef BadFuncName = "FAKE_TEST_FUNC"; 255 256 llvm::StringRef ArgV[] = {ProgPath, 257 llvm::StringRef(IncludeArg), 258 llvm::StringRef(APIArg), 259 "--gen-wrapper", 260 "--name", 261 BadFuncName}; 262 263 int ExitCode = 264 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 265 266 EXPECT_EQ(ExitCode, 1); 267 268 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 269 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 270 271 ASSERT_EQ(STDErrOutput, ("error: Function '" + BadFuncName + 272 "' not found in any standard spec.\n") 273 .str()); 274 275 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 276 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 277 278 ASSERT_EQ(STDOutOutput, ""); 279 } 280 281 TEST_F(WrapperGenTest, RunWrapperGenOnStrlenWithBadMangledNameFile) { 282 llvm::Optional<llvm::StringRef> Redirects[] = { 283 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 284 llvm::StringRef(STDErrFile.get().TmpName)}; 285 286 llvm::StringRef BadMangledNameFileName = "FILE_THAT_DOESNT_EXIST.txt"; 287 288 llvm::StringRef ArgV[] = {ProgPath, 289 llvm::StringRef(IncludeArg), 290 llvm::StringRef(APIArg), 291 "--gen-alias", 292 "--mangled-name-file", 293 BadMangledNameFileName, 294 "--name", 295 "strlen"}; 296 297 int ExitCode = 298 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 299 300 EXPECT_EQ(ExitCode, 1); 301 302 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 303 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 304 305 ASSERT_EQ(STDErrOutput, ("error: Unable to read the mangled name file " + 306 BadMangledNameFileName + "\n") 307 .str()); 308 309 auto STDOutOrError = llvm::MemoryBuffer::getFile(STDOutFile.get().TmpName); 310 std::string STDOutOutput = STDOutOrError.get()->getBuffer().str(); 311 312 ASSERT_EQ(STDOutOutput, ""); 313 } 314 315 TEST_F(WrapperGenTest, RunWithMangledNameFileMissingLLVMLibcName) { 316 llvm::Optional<llvm::StringRef> Redirects[] = { 317 llvm::None, llvm::StringRef(STDOutFile.get().TmpName), 318 llvm::StringRef(STDErrFile.get().TmpName)}; 319 320 llvm::SmallVector<char> MangledNameFilePath; 321 auto MangledNameFileCreateError = llvm::sys::fs::createUniqueFile( 322 "libc-wrappergen-test-mangled-name-file-%%-%%-%%-%%.txt", 323 MangledNameFilePath); 324 ASSERT_FALSE(MangledNameFileCreateError); 325 326 llvm::StringRef ArgV[] = {ProgPath, 327 llvm::StringRef(IncludeArg), 328 llvm::StringRef(APIArg), 329 "--gen-alias", 330 "--mangled-name-file", 331 llvm::StringRef(MangledNameFilePath.data()), 332 "--name", 333 "strlen"}; 334 335 int ExitCode = 336 llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); 337 338 EXPECT_NE(ExitCode, 0); 339 340 auto STDErrOrError = llvm::MemoryBuffer::getFile(STDErrFile.get().TmpName); 341 std::string STDErrOutput = STDErrOrError.get()->getBuffer().str(); 342 343 ASSERT_EQ(STDErrOutput, ("error: Did not find an LLVM libc mangled name in " + 344 MangledNameFilePath + "\n") 345 .str()); 346 } 347