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