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:
SetUp()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 }
TearDown()64 void TearDown() override {
65 llvm::consumeError(STDOutFile.get().discard());
66 llvm::consumeError(STDErrFile.get().discard());
67 }
68 };
69
70 using LlvmLibcWrapperGenTest = WrapperGenTest;
71
TEST_F(LlvmLibcWrapperGenTest,RunWrapperGenAndGetNoErrors)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
TEST_F(LlvmLibcWrapperGenTest,RunWrapperGenOnStrlen)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
TEST_F(LlvmLibcWrapperGenTest,GenAliasForStrlen)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
TEST_F(LlvmLibcWrapperGenTest,DeclStrlenAliasUsingMangledNameFile)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
TEST_F(LlvmLibcWrapperGenTest,RunWrapperGenOnStrlenWithMangledNameAndMangledNameFile)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
TEST_F(LlvmLibcWrapperGenTest,RunWrapperGenOnBadFuncName)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
TEST_F(LlvmLibcWrapperGenTest,RunWrapperGenOnStrlenWithBadMangledNameFile)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
TEST_F(LlvmLibcWrapperGenTest,RunWithMangledNameFileMissingLLVMLibcName)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