• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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