1 | //===- SpecialCaseListTest.cpp - Unit tests for SpecialCaseList -----------===// |
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/Support/SpecialCaseList.h" |
10 | #include "llvm/Support/FileSystem.h" |
11 | #include "llvm/Support/MemoryBuffer.h" |
12 | #include "llvm/Support/VirtualFileSystem.h" |
13 | #include "gmock/gmock.h" |
14 | #include "gtest/gtest.h" |
15 | |
16 | using testing::HasSubstr; |
17 | using testing::StartsWith; |
18 | using namespace llvm; |
19 | |
20 | namespace { |
21 | |
22 | class SpecialCaseListTest : public ::testing::Test { |
23 | protected: |
24 | std::unique_ptr<SpecialCaseList> makeSpecialCaseList(StringRef List, |
25 | std::string &Error, |
26 | bool UseGlobs = true) { |
27 | auto S = List.str(); |
28 | if (!UseGlobs) |
29 | S = (Twine("#!special-case-list-v1\n" ) + S).str(); |
30 | std::unique_ptr<MemoryBuffer> MB = MemoryBuffer::getMemBuffer(InputData: S); |
31 | return SpecialCaseList::create(MB: MB.get(), Error); |
32 | } |
33 | |
34 | std::unique_ptr<SpecialCaseList> makeSpecialCaseList(StringRef List, |
35 | bool UseGlobs = true) { |
36 | std::string Error; |
37 | auto SCL = makeSpecialCaseList(List, Error, UseGlobs); |
38 | assert(SCL); |
39 | assert(Error == "" ); |
40 | return SCL; |
41 | } |
42 | |
43 | std::string makeSpecialCaseListFile(StringRef Contents, |
44 | bool UseGlobs = true) { |
45 | int FD; |
46 | SmallString<64> Path; |
47 | sys::fs::createTemporaryFile(Prefix: "SpecialCaseListTest" , Suffix: "temp" , ResultFD&: FD, ResultPath&: Path); |
48 | raw_fd_ostream OF(FD, true, true); |
49 | if (!UseGlobs) |
50 | OF << "#!special-case-list-v1\n" ; |
51 | OF << Contents; |
52 | OF.close(); |
53 | return std::string(Path.str()); |
54 | } |
55 | }; |
56 | |
57 | TEST_F(SpecialCaseListTest, Basic) { |
58 | std::unique_ptr<SpecialCaseList> SCL = |
59 | makeSpecialCaseList(List: "# This is a comment.\n" |
60 | "\n" |
61 | "src:hello\n" |
62 | "src:bye\n" |
63 | "src:hi=category\n" |
64 | "src:z*=category\n" ); |
65 | EXPECT_TRUE(SCL->inSection("" , "src" , "hello" )); |
66 | EXPECT_TRUE(SCL->inSection("" , "src" , "bye" )); |
67 | EXPECT_TRUE(SCL->inSection("" , "src" , "hi" , "category" )); |
68 | EXPECT_TRUE(SCL->inSection("" , "src" , "zzzz" , "category" )); |
69 | EXPECT_FALSE(SCL->inSection("" , "src" , "hi" )); |
70 | EXPECT_FALSE(SCL->inSection("" , "fun" , "hello" )); |
71 | EXPECT_FALSE(SCL->inSection("" , "src" , "hello" , "category" )); |
72 | |
73 | EXPECT_EQ(3u, SCL->inSectionBlame("" , "src" , "hello" )); |
74 | EXPECT_EQ(4u, SCL->inSectionBlame("" , "src" , "bye" )); |
75 | EXPECT_EQ(5u, SCL->inSectionBlame("" , "src" , "hi" , "category" )); |
76 | EXPECT_EQ(6u, SCL->inSectionBlame("" , "src" , "zzzz" , "category" )); |
77 | EXPECT_EQ(0u, SCL->inSectionBlame("" , "src" , "hi" )); |
78 | EXPECT_EQ(0u, SCL->inSectionBlame("" , "fun" , "hello" )); |
79 | EXPECT_EQ(0u, SCL->inSectionBlame("" , "src" , "hello" , "category" )); |
80 | } |
81 | |
82 | TEST_F(SpecialCaseListTest, CorrectErrorLineNumberWithBlankLine) { |
83 | std::string Error; |
84 | EXPECT_EQ(nullptr, makeSpecialCaseList("# This is a comment.\n" |
85 | "\n" |
86 | "[not valid\n" , |
87 | Error)); |
88 | EXPECT_THAT(Error, StartsWith("malformed section header on line 3:" )); |
89 | |
90 | EXPECT_EQ(nullptr, makeSpecialCaseList("\n\n\n" |
91 | "[not valid\n" , |
92 | Error)); |
93 | EXPECT_THAT(Error, StartsWith("malformed section header on line 4:" )); |
94 | } |
95 | |
96 | TEST_F(SpecialCaseListTest, SectionGlobErrorHandling) { |
97 | std::string Error; |
98 | EXPECT_EQ(makeSpecialCaseList("[address" , Error), nullptr); |
99 | EXPECT_THAT(Error, StartsWith("malformed section header " )); |
100 | |
101 | EXPECT_EQ(makeSpecialCaseList("[[]" , Error), nullptr); |
102 | EXPECT_EQ( |
103 | Error, |
104 | "malformed section at line 1: '[': invalid glob pattern, unmatched '['" ); |
105 | |
106 | EXPECT_EQ(makeSpecialCaseList("src:=" , Error), nullptr); |
107 | EXPECT_THAT(Error, HasSubstr("Supplied glob was blank" )); |
108 | } |
109 | |
110 | TEST_F(SpecialCaseListTest, Section) { |
111 | std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList(List: "src:global\n" |
112 | "[{sect1,sect2}]\n" |
113 | "src:test1\n" |
114 | "[sect3*]\n" |
115 | "src:test2\n" ); |
116 | EXPECT_TRUE(SCL->inSection("arbitrary" , "src" , "global" )); |
117 | EXPECT_TRUE(SCL->inSection("" , "src" , "global" )); |
118 | EXPECT_TRUE(SCL->inSection("sect1" , "src" , "test1" )); |
119 | EXPECT_FALSE(SCL->inSection("sect1-arbitrary" , "src" , "test1" )); |
120 | EXPECT_FALSE(SCL->inSection("sect" , "src" , "test1" )); |
121 | EXPECT_FALSE(SCL->inSection("sect1" , "src" , "test2" )); |
122 | EXPECT_TRUE(SCL->inSection("sect2" , "src" , "test1" )); |
123 | EXPECT_TRUE(SCL->inSection("sect3" , "src" , "test2" )); |
124 | EXPECT_TRUE(SCL->inSection("sect3-arbitrary" , "src" , "test2" )); |
125 | EXPECT_FALSE(SCL->inSection("" , "src" , "test1" )); |
126 | EXPECT_FALSE(SCL->inSection("" , "src" , "test2" )); |
127 | } |
128 | |
129 | TEST_F(SpecialCaseListTest, GlobalInit) { |
130 | std::unique_ptr<SpecialCaseList> SCL = |
131 | makeSpecialCaseList(List: "global:foo=init\n" ); |
132 | EXPECT_FALSE(SCL->inSection("" , "global" , "foo" )); |
133 | EXPECT_FALSE(SCL->inSection("" , "global" , "bar" )); |
134 | EXPECT_TRUE(SCL->inSection("" , "global" , "foo" , "init" )); |
135 | EXPECT_FALSE(SCL->inSection("" , "global" , "bar" , "init" )); |
136 | |
137 | SCL = makeSpecialCaseList(List: "type:t2=init\n" ); |
138 | EXPECT_FALSE(SCL->inSection("" , "type" , "t1" )); |
139 | EXPECT_FALSE(SCL->inSection("" , "type" , "t2" )); |
140 | EXPECT_FALSE(SCL->inSection("" , "type" , "t1" , "init" )); |
141 | EXPECT_TRUE(SCL->inSection("" , "type" , "t2" , "init" )); |
142 | |
143 | SCL = makeSpecialCaseList(List: "src:hello=init\n" ); |
144 | EXPECT_FALSE(SCL->inSection("" , "src" , "hello" )); |
145 | EXPECT_FALSE(SCL->inSection("" , "src" , "bye" )); |
146 | EXPECT_TRUE(SCL->inSection("" , "src" , "hello" , "init" )); |
147 | EXPECT_FALSE(SCL->inSection("" , "src" , "bye" , "init" )); |
148 | } |
149 | |
150 | TEST_F(SpecialCaseListTest, Substring) { |
151 | std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList(List: "src:hello\n" |
152 | "fun:foo\n" |
153 | "global:bar\n" ); |
154 | EXPECT_FALSE(SCL->inSection("" , "src" , "othello" )); |
155 | EXPECT_FALSE(SCL->inSection("" , "fun" , "tomfoolery" )); |
156 | EXPECT_FALSE(SCL->inSection("" , "global" , "bartender" )); |
157 | |
158 | SCL = makeSpecialCaseList(List: "fun:*foo*\n" ); |
159 | EXPECT_TRUE(SCL->inSection("" , "fun" , "tomfoolery" )); |
160 | EXPECT_TRUE(SCL->inSection("" , "fun" , "foobar" )); |
161 | } |
162 | |
163 | TEST_F(SpecialCaseListTest, InvalidSpecialCaseList) { |
164 | std::string Error; |
165 | EXPECT_EQ(nullptr, makeSpecialCaseList("badline" , Error)); |
166 | EXPECT_EQ("malformed line 1: 'badline'" , Error); |
167 | EXPECT_EQ(nullptr, makeSpecialCaseList("src:bad[a-" , Error)); |
168 | EXPECT_EQ( |
169 | "malformed glob in line 1: 'bad[a-': invalid glob pattern, unmatched '['" , |
170 | Error); |
171 | std::vector<std::string> Files(1, "unexisting" ); |
172 | EXPECT_EQ(nullptr, |
173 | SpecialCaseList::create(Files, *vfs::getRealFileSystem(), Error)); |
174 | EXPECT_THAT(Error, StartsWith("can't open file 'unexisting':" )); |
175 | } |
176 | |
177 | TEST_F(SpecialCaseListTest, EmptySpecialCaseList) { |
178 | std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList(List: "" ); |
179 | EXPECT_FALSE(SCL->inSection("" , "foo" , "bar" )); |
180 | } |
181 | |
182 | TEST_F(SpecialCaseListTest, MultipleExclusions) { |
183 | std::vector<std::string> Files; |
184 | Files.push_back(x: makeSpecialCaseListFile(Contents: "src:bar\n" |
185 | "src:*foo*\n" |
186 | "src:ban=init\n" )); |
187 | Files.push_back(x: makeSpecialCaseListFile(Contents: "src:baz\n" |
188 | "src:*fog*\n" )); |
189 | auto SCL = SpecialCaseList::createOrDie(Paths: Files, FS&: *vfs::getRealFileSystem()); |
190 | EXPECT_TRUE(SCL->inSection("" , "src" , "bar" )); |
191 | EXPECT_TRUE(SCL->inSection("" , "src" , "baz" )); |
192 | EXPECT_FALSE(SCL->inSection("" , "src" , "ban" )); |
193 | EXPECT_TRUE(SCL->inSection("" , "src" , "ban" , "init" )); |
194 | EXPECT_TRUE(SCL->inSection("" , "src" , "tomfoolery" )); |
195 | EXPECT_TRUE(SCL->inSection("" , "src" , "tomfoglery" )); |
196 | for (auto &Path : Files) |
197 | sys::fs::remove(path: Path); |
198 | } |
199 | |
200 | TEST_F(SpecialCaseListTest, NoTrigramsInRules) { |
201 | std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList(List: "fun:b?r\n" |
202 | "fun:za*az\n" ); |
203 | EXPECT_TRUE(SCL->inSection("" , "fun" , "bar" )); |
204 | EXPECT_FALSE(SCL->inSection("" , "fun" , "baz" )); |
205 | EXPECT_TRUE(SCL->inSection("" , "fun" , "zakaz" )); |
206 | EXPECT_FALSE(SCL->inSection("" , "fun" , "zaraza" )); |
207 | } |
208 | |
209 | TEST_F(SpecialCaseListTest, NoTrigramsInARule) { |
210 | std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList(List: "fun:*bar*\n" |
211 | "fun:za*az\n" ); |
212 | EXPECT_TRUE(SCL->inSection("" , "fun" , "abara" )); |
213 | EXPECT_FALSE(SCL->inSection("" , "fun" , "bor" )); |
214 | EXPECT_TRUE(SCL->inSection("" , "fun" , "zakaz" )); |
215 | EXPECT_FALSE(SCL->inSection("" , "fun" , "zaraza" )); |
216 | } |
217 | |
218 | TEST_F(SpecialCaseListTest, RepetitiveRule) { |
219 | std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList(List: "fun:*bar*bar*bar*bar*\n" |
220 | "fun:bar*\n" ); |
221 | EXPECT_TRUE(SCL->inSection("" , "fun" , "bara" )); |
222 | EXPECT_FALSE(SCL->inSection("" , "fun" , "abara" )); |
223 | EXPECT_TRUE(SCL->inSection("" , "fun" , "barbarbarbar" )); |
224 | EXPECT_TRUE(SCL->inSection("" , "fun" , "abarbarbarbar" )); |
225 | EXPECT_FALSE(SCL->inSection("" , "fun" , "abarbarbar" )); |
226 | } |
227 | |
228 | TEST_F(SpecialCaseListTest, SpecialSymbolRule) { |
229 | std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList(List: "src:*c\\+\\+abi*\n" ); |
230 | EXPECT_TRUE(SCL->inSection("" , "src" , "c++abi" )); |
231 | EXPECT_FALSE(SCL->inSection("" , "src" , "c\\+\\+abi" )); |
232 | } |
233 | |
234 | TEST_F(SpecialCaseListTest, PopularTrigram) { |
235 | std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList(List: "fun:*aaaaaa*\n" |
236 | "fun:*aaaaa*\n" |
237 | "fun:*aaaa*\n" |
238 | "fun:*aaa*\n" ); |
239 | EXPECT_TRUE(SCL->inSection("" , "fun" , "aaa" )); |
240 | EXPECT_TRUE(SCL->inSection("" , "fun" , "aaaa" )); |
241 | EXPECT_TRUE(SCL->inSection("" , "fun" , "aaaabbbaaa" )); |
242 | } |
243 | |
244 | TEST_F(SpecialCaseListTest, EscapedSymbols) { |
245 | std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList(List: "src:*c\\+\\+abi*\n" |
246 | "src:*hello\\\\world*\n" ); |
247 | EXPECT_TRUE(SCL->inSection("" , "src" , "dir/c++abi" )); |
248 | EXPECT_FALSE(SCL->inSection("" , "src" , "dir/c\\+\\+abi" )); |
249 | EXPECT_FALSE(SCL->inSection("" , "src" , "c\\+\\+abi" )); |
250 | EXPECT_TRUE(SCL->inSection("" , "src" , "C:\\hello\\world" )); |
251 | EXPECT_TRUE(SCL->inSection("" , "src" , "hello\\world" )); |
252 | EXPECT_FALSE(SCL->inSection("" , "src" , "hello\\\\world" )); |
253 | } |
254 | |
255 | TEST_F(SpecialCaseListTest, Version1) { |
256 | std::unique_ptr<SpecialCaseList> SCL = |
257 | makeSpecialCaseList(List: "[sect1|sect2]\n" |
258 | // Does not match foo! |
259 | "fun:foo.*\n" |
260 | "fun:abc|def\n" |
261 | "fun:b.r\n" , |
262 | /*UseGlobs=*/false); |
263 | |
264 | EXPECT_TRUE(SCL->inSection("sect1" , "fun" , "fooz" )); |
265 | EXPECT_TRUE(SCL->inSection("sect2" , "fun" , "fooz" )); |
266 | EXPECT_FALSE(SCL->inSection("sect3" , "fun" , "fooz" )); |
267 | |
268 | // `foo.*` does not match `foo` because the pattern is translated to `foo..*` |
269 | EXPECT_FALSE(SCL->inSection("sect1" , "fun" , "foo" )); |
270 | |
271 | EXPECT_TRUE(SCL->inSection("sect1" , "fun" , "abc" )); |
272 | EXPECT_TRUE(SCL->inSection("sect2" , "fun" , "abc" )); |
273 | EXPECT_FALSE(SCL->inSection("sect3" , "fun" , "abc" )); |
274 | |
275 | EXPECT_TRUE(SCL->inSection("sect1" , "fun" , "def" )); |
276 | EXPECT_TRUE(SCL->inSection("sect2" , "fun" , "def" )); |
277 | EXPECT_FALSE(SCL->inSection("sect3" , "fun" , "def" )); |
278 | |
279 | EXPECT_TRUE(SCL->inSection("sect1" , "fun" , "bar" )); |
280 | EXPECT_TRUE(SCL->inSection("sect2" , "fun" , "bar" )); |
281 | EXPECT_FALSE(SCL->inSection("sect3" , "fun" , "bar" )); |
282 | } |
283 | |
284 | TEST_F(SpecialCaseListTest, Version2) { |
285 | std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList(List: "[{sect1,sect2}]\n" |
286 | "fun:foo*\n" |
287 | "fun:{abc,def}\n" |
288 | "fun:b?r\n" ); |
289 | EXPECT_TRUE(SCL->inSection("sect1" , "fun" , "fooz" )); |
290 | EXPECT_TRUE(SCL->inSection("sect2" , "fun" , "fooz" )); |
291 | EXPECT_FALSE(SCL->inSection("sect3" , "fun" , "fooz" )); |
292 | |
293 | EXPECT_TRUE(SCL->inSection("sect1" , "fun" , "foo" )); |
294 | EXPECT_TRUE(SCL->inSection("sect2" , "fun" , "foo" )); |
295 | EXPECT_FALSE(SCL->inSection("sect3" , "fun" , "foo" )); |
296 | |
297 | EXPECT_TRUE(SCL->inSection("sect1" , "fun" , "abc" )); |
298 | EXPECT_TRUE(SCL->inSection("sect2" , "fun" , "abc" )); |
299 | EXPECT_FALSE(SCL->inSection("sect3" , "fun" , "abc" )); |
300 | |
301 | EXPECT_TRUE(SCL->inSection("sect1" , "fun" , "def" )); |
302 | EXPECT_TRUE(SCL->inSection("sect2" , "fun" , "def" )); |
303 | EXPECT_FALSE(SCL->inSection("sect3" , "fun" , "def" )); |
304 | |
305 | EXPECT_TRUE(SCL->inSection("sect1" , "fun" , "bar" )); |
306 | EXPECT_TRUE(SCL->inSection("sect2" , "fun" , "bar" )); |
307 | EXPECT_FALSE(SCL->inSection("sect3" , "fun" , "bar" )); |
308 | } |
309 | } |
310 | |