1 //===- unittests/Driver/MultilibTest.cpp --- Multilib tests ---------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Unit tests for Multilib and MultilibSet
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/Driver/Multilib.h"
15 #include "clang/Basic/LLVM.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/ADT/StringSwitch.h"
18 #include "gtest/gtest.h"
19 
20 using namespace clang::driver;
21 using namespace clang;
22 
TEST(MultilibTest,MultilibValidity)23 TEST(MultilibTest, MultilibValidity) {
24 
25   ASSERT_TRUE(Multilib().isValid()) << "Empty multilib is not valid";
26 
27   ASSERT_TRUE(Multilib().flag("+foo").isValid())
28       << "Single indicative flag is not valid";
29 
30   ASSERT_TRUE(Multilib().flag("-foo").isValid())
31       << "Single contraindicative flag is not valid";
32 
33   ASSERT_FALSE(Multilib().flag("+foo").flag("-foo").isValid())
34       << "Conflicting flags should invalidate the Multilib";
35 
36   ASSERT_TRUE(Multilib().flag("+foo").flag("+foo").isValid())
37       << "Multilib should be valid even if it has the same flag twice";
38 
39   ASSERT_TRUE(Multilib().flag("+foo").flag("-foobar").isValid())
40       << "Seemingly conflicting prefixes shouldn't actually conflict";
41 }
42 
TEST(MultilibTest,OpEqReflexivity1)43 TEST(MultilibTest, OpEqReflexivity1) {
44   Multilib M;
45   ASSERT_TRUE(M == M) << "Multilib::operator==() is not reflexive";
46 }
47 
TEST(MultilibTest,OpEqReflexivity2)48 TEST(MultilibTest, OpEqReflexivity2) {
49   ASSERT_TRUE(Multilib() == Multilib())
50       << "Separately constructed default multilibs are not equal";
51 }
52 
TEST(MultilibTest,OpEqReflexivity3)53 TEST(MultilibTest, OpEqReflexivity3) {
54   Multilib M1, M2;
55   M1.flag("+foo");
56   M2.flag("+foo");
57   ASSERT_TRUE(M1 == M2) << "Multilibs with the same flag should be the same";
58 }
59 
TEST(MultilibTest,OpEqInequivalence1)60 TEST(MultilibTest, OpEqInequivalence1) {
61   Multilib M1, M2;
62   M1.flag("+foo");
63   M2.flag("-foo");
64   ASSERT_FALSE(M1 == M2) << "Multilibs with conflicting flags are not the same";
65   ASSERT_FALSE(M2 == M1)
66       << "Multilibs with conflicting flags are not the same (commuted)";
67 }
68 
TEST(MultilibTest,OpEqInequivalence2)69 TEST(MultilibTest, OpEqInequivalence2) {
70   Multilib M1, M2;
71   M2.flag("+foo");
72   ASSERT_FALSE(M1 == M2) << "Flags make Multilibs different";
73 }
74 
TEST(MultilibTest,OpEqEquivalence1)75 TEST(MultilibTest, OpEqEquivalence1) {
76   Multilib M1, M2;
77   M1.flag("+foo");
78   M2.flag("+foo").flag("+foo");
79   ASSERT_TRUE(M1 == M2) << "Flag duplication shouldn't affect equivalence";
80   ASSERT_TRUE(M2 == M1)
81       << "Flag duplication shouldn't affect equivalence (commuted)";
82 }
83 
TEST(MultilibTest,OpEqEquivalence2)84 TEST(MultilibTest, OpEqEquivalence2) {
85   Multilib M1("64");
86   Multilib M2;
87   M2.gccSuffix("/64");
88   ASSERT_TRUE(M1 == M2)
89       << "Constructor argument must match Multilib::gccSuffix()";
90   ASSERT_TRUE(M2 == M1)
91       << "Constructor argument must match Multilib::gccSuffix() (commuted)";
92 }
93 
TEST(MultilibTest,OpEqEquivalence3)94 TEST(MultilibTest, OpEqEquivalence3) {
95   Multilib M1("", "32");
96   Multilib M2;
97   M2.osSuffix("/32");
98   ASSERT_TRUE(M1 == M2)
99       << "Constructor argument must match Multilib::osSuffix()";
100   ASSERT_TRUE(M2 == M1)
101       << "Constructor argument must match Multilib::osSuffix() (commuted)";
102 }
103 
TEST(MultilibTest,OpEqEquivalence4)104 TEST(MultilibTest, OpEqEquivalence4) {
105   Multilib M1("", "", "16");
106   Multilib M2;
107   M2.includeSuffix("/16");
108   ASSERT_TRUE(M1 == M2)
109       << "Constructor argument must match Multilib::includeSuffix()";
110   ASSERT_TRUE(M2 == M1)
111       << "Constructor argument must match Multilib::includeSuffix() (commuted)";
112 }
113 
TEST(MultilibTest,OpEqInequivalence3)114 TEST(MultilibTest, OpEqInequivalence3) {
115   Multilib M1("foo");
116   Multilib M2("bar");
117   ASSERT_FALSE(M1 == M2) << "Differing gccSuffixes should be different";
118   ASSERT_FALSE(M2 == M1)
119       << "Differing gccSuffixes should be different (commuted)";
120 }
121 
TEST(MultilibTest,OpEqInequivalence4)122 TEST(MultilibTest, OpEqInequivalence4) {
123   Multilib M1("", "foo");
124   Multilib M2("", "bar");
125   ASSERT_FALSE(M1 == M2) << "Differing osSuffixes should be different";
126   ASSERT_FALSE(M2 == M1)
127       << "Differing osSuffixes should be different (commuted)";
128 }
129 
TEST(MultilibTest,OpEqInequivalence5)130 TEST(MultilibTest, OpEqInequivalence5) {
131   Multilib M1("", "", "foo");
132   Multilib M2("", "", "bar");
133   ASSERT_FALSE(M1 == M2) << "Differing includeSuffixes should be different";
134   ASSERT_FALSE(M2 == M1)
135       << "Differing includeSuffixes should be different (commuted)";
136 }
137 
TEST(MultilibTest,Construction1)138 TEST(MultilibTest, Construction1) {
139   Multilib M("gcc64", "os64", "inc64");
140   ASSERT_TRUE(M.gccSuffix() == "/gcc64");
141   ASSERT_TRUE(M.osSuffix() == "/os64");
142   ASSERT_TRUE(M.includeSuffix() == "/inc64");
143 }
144 
TEST(MultilibTest,Construction2)145 TEST(MultilibTest, Construction2) {
146   Multilib M1;
147   Multilib M2("");
148   Multilib M3("", "");
149   Multilib M4("", "", "");
150   ASSERT_TRUE(M1 == M2)
151       << "Default arguments to Multilib constructor broken (first argument)";
152   ASSERT_TRUE(M1 == M3)
153       << "Default arguments to Multilib constructor broken (second argument)";
154   ASSERT_TRUE(M1 == M4)
155       << "Default arguments to Multilib constructor broken (third argument)";
156 }
157 
TEST(MultilibTest,Construction3)158 TEST(MultilibTest, Construction3) {
159   Multilib M = Multilib().flag("+f1").flag("+f2").flag("-f3");
160   for (Multilib::flags_list::const_iterator I = M.flags().begin(),
161                                             E = M.flags().end();
162        I != E; ++I) {
163     ASSERT_TRUE(llvm::StringSwitch<bool>(*I)
164                     .Cases("+f1", "+f2", "-f3", true)
165                     .Default(false));
166   }
167 }
168 
hasFlag(const Multilib & M,StringRef Flag)169 static bool hasFlag(const Multilib &M, StringRef Flag) {
170   for (Multilib::flags_list::const_iterator I = M.flags().begin(),
171                                             E = M.flags().end();
172        I != E; ++I) {
173     if (*I == Flag)
174       return true;
175     else if (StringRef(*I).substr(1) == Flag.substr(1))
176       return false;
177   }
178   return false;
179 }
180 
TEST(MultilibTest,SetConstruction1)181 TEST(MultilibTest, SetConstruction1) {
182   // Single maybe
183   MultilibSet MS;
184   ASSERT_TRUE(MS.size() == 0);
185   MS.Maybe(Multilib("64").flag("+m64"));
186   ASSERT_TRUE(MS.size() == 2);
187   for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
188     if (I->gccSuffix() == "/64")
189       ASSERT_TRUE(I->flags()[0] == "+m64");
190     else if (I->gccSuffix() == "")
191       ASSERT_TRUE(I->flags()[0] == "-m64");
192     else
193       FAIL() << "Unrecognized gccSufix: " << I->gccSuffix();
194   }
195 }
196 
TEST(MultilibTest,SetConstruction2)197 TEST(MultilibTest, SetConstruction2) {
198   // Double maybe
199   MultilibSet MS;
200   MS.Maybe(Multilib("sof").flag("+sof"));
201   MS.Maybe(Multilib("el").flag("+EL"));
202   ASSERT_TRUE(MS.size() == 4);
203   for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
204     ASSERT_TRUE(I->isValid()) << "Multilb " << *I << " should be valid";
205     ASSERT_TRUE(llvm::StringSwitch<bool>(I->gccSuffix())
206                     .Cases("", "/sof", "/el", "/sof/el", true)
207                     .Default(false))
208         << "Multilib " << *I << " wasn't expected";
209     ASSERT_TRUE(llvm::StringSwitch<bool>(I->gccSuffix())
210                     .Case("", hasFlag(*I, "-sof"))
211                     .Case("/sof", hasFlag(*I, "+sof"))
212                     .Case("/el", hasFlag(*I, "-sof"))
213                     .Case("/sof/el", hasFlag(*I, "+sof"))
214                     .Default(false))
215         << "Multilib " << *I << " didn't have the appropriate {+,-}sof flag";
216     ASSERT_TRUE(llvm::StringSwitch<bool>(I->gccSuffix())
217                     .Case("", hasFlag(*I, "-EL"))
218                     .Case("/sof", hasFlag(*I, "-EL"))
219                     .Case("/el", hasFlag(*I, "+EL"))
220                     .Case("/sof/el", hasFlag(*I, "+EL"))
221                     .Default(false))
222         << "Multilib " << *I << " didn't have the appropriate {+,-}EL flag";
223   }
224 }
225 
TEST(MultilibTest,SetPushback)226 TEST(MultilibTest, SetPushback) {
227   MultilibSet MS;
228   MS.push_back(Multilib("one"));
229   MS.push_back(Multilib("two"));
230   ASSERT_TRUE(MS.size() == 2);
231   for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
232     ASSERT_TRUE(llvm::StringSwitch<bool>(I->gccSuffix())
233                     .Cases("/one", "/two", true)
234                     .Default(false));
235   }
236   MS.clear();
237   ASSERT_TRUE(MS.size() == 0);
238 }
239 
TEST(MultilibTest,SetRegexFilter)240 TEST(MultilibTest, SetRegexFilter) {
241   MultilibSet MS;
242   MS.Maybe(Multilib("one"));
243   MS.Maybe(Multilib("two"));
244   MS.Maybe(Multilib("three"));
245   ASSERT_EQ(MS.size(), (unsigned)2 * 2 * 2)
246       << "Size before filter was incorrect. Contents:\n" << MS;
247   MS.FilterOut("/one/two/three");
248   ASSERT_EQ(MS.size(), (unsigned)2 * 2 * 2 - 1)
249       << "Size after filter was incorrect. Contents:\n" << MS;
250   for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
251     ASSERT_TRUE(I->gccSuffix() != "/one/two/three")
252         << "The filter should have removed " << *I;
253   }
254 }
255 
TEST(MultilibTest,SetFilterObject)256 TEST(MultilibTest, SetFilterObject) {
257   // Filter object
258   struct StartsWithP : public MultilibSet::FilterCallback {
259     bool operator()(const Multilib &M) const override {
260       return StringRef(M.gccSuffix()).startswith("/p");
261     }
262   };
263   MultilibSet MS;
264   MS.Maybe(Multilib("orange"));
265   MS.Maybe(Multilib("pear"));
266   MS.Maybe(Multilib("plum"));
267   ASSERT_EQ((int)MS.size(), 1 /* Default */ +
268                             1 /* pear */ +
269                             1 /* plum */ +
270                             1 /* pear/plum */ +
271                             1 /* orange */ +
272                             1 /* orange/pear */ +
273                             1 /* orange/plum */ +
274                             1 /* orange/pear/plum */ )
275       << "Size before filter was incorrect. Contents:\n" << MS;
276   MS.FilterOut(StartsWithP());
277   ASSERT_EQ((int)MS.size(), 1 /* Default */ +
278                             1 /* orange */ +
279                             1 /* orange/pear */ +
280                             1 /* orange/plum */ +
281                             1 /* orange/pear/plum */ )
282       << "Size after filter was incorrect. Contents:\n" << MS;
283   for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
284     ASSERT_FALSE(StringRef(I->gccSuffix()).startswith("/p"))
285         << "The filter should have removed " << *I;
286   }
287 }
288 
TEST(MultilibTest,SetSelection1)289 TEST(MultilibTest, SetSelection1) {
290   MultilibSet MS1 = MultilibSet()
291     .Maybe(Multilib("64").flag("+m64"));
292 
293   Multilib::flags_list FlagM64;
294   FlagM64.push_back("+m64");
295   Multilib SelectionM64;
296   ASSERT_TRUE(MS1.select(FlagM64, SelectionM64))
297       << "Flag set was {\"+m64\"}, but selection not found";
298   ASSERT_TRUE(SelectionM64.gccSuffix() == "/64")
299       << "Selection picked " << SelectionM64 << " which was not expected";
300 
301   Multilib::flags_list FlagNoM64;
302   FlagNoM64.push_back("-m64");
303   Multilib SelectionNoM64;
304   ASSERT_TRUE(MS1.select(FlagNoM64, SelectionNoM64))
305       << "Flag set was {\"-m64\"}, but selection not found";
306   ASSERT_TRUE(SelectionNoM64.gccSuffix() == "")
307       << "Selection picked " << SelectionNoM64 << " which was not expected";
308 }
309 
TEST(MultilibTest,SetSelection2)310 TEST(MultilibTest, SetSelection2) {
311   MultilibSet MS2 = MultilibSet()
312     .Maybe(Multilib("el").flag("+EL"))
313     .Maybe(Multilib("sf").flag("+SF"));
314 
315   for (unsigned I = 0; I < 4; ++I) {
316     bool IsEL = I & 0x1;
317     bool IsSF = I & 0x2;
318     Multilib::flags_list Flags;
319     if (IsEL)
320       Flags.push_back("+EL");
321     else
322       Flags.push_back("-EL");
323 
324     if (IsSF)
325       Flags.push_back("+SF");
326     else
327       Flags.push_back("-SF");
328 
329     Multilib Selection;
330     ASSERT_TRUE(MS2.select(Flags, Selection)) << "Selection failed for "
331                                               << (IsEL ? "+EL" : "-EL") << " "
332                                               << (IsSF ? "+SF" : "-SF");
333 
334     std::string Suffix;
335     if (IsEL)
336       Suffix += "/el";
337     if (IsSF)
338       Suffix += "/sf";
339 
340     ASSERT_EQ(Selection.gccSuffix(), Suffix) << "Selection picked " << Selection
341                                              << " which was not expected ";
342   }
343 }
344 
TEST(MultilibTest,SetCombineWith)345 TEST(MultilibTest, SetCombineWith) {
346   MultilibSet Coffee;
347   Coffee.push_back(Multilib("coffee"));
348   MultilibSet Milk;
349   Milk.push_back(Multilib("milk"));
350   MultilibSet Latte;
351   ASSERT_EQ(Latte.size(), (unsigned)0);
352   Latte.combineWith(Coffee);
353   ASSERT_EQ(Latte.size(), (unsigned)1);
354   Latte.combineWith(Milk);
355   ASSERT_EQ(Latte.size(), (unsigned)2);
356 }
357