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