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:
SetUp()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 }
TearDown()63 void TearDown() override {
64 llvm::consumeError(STDOutFile.get().discard());
65 llvm::consumeError(STDErrFile.get().discard());
66 }
67 };
68
TEST_F(WrapperGenTest,RunWrapperGenAndGetNoErrors)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
TEST_F(WrapperGenTest,RunWrapperGenOnStrlen)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
TEST_F(WrapperGenTest,RunWrapperGenOnStrlenWithAliasee)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
TEST_F(WrapperGenTest,RunWrapperGenOnStrlenWithAliaseeAndAliaseeFileWhichIsError)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
TEST_F(WrapperGenTest,RunWrapperGenOnBadFuncName)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
TEST_F(WrapperGenTest,RunWrapperGenOnStrlenWithBadAliaseeFile)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