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
20using namespace clang::driver;
21using namespace clang;
22
23TEST(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
43TEST(MultilibTest, OpEqReflexivity1) {
44  Multilib M;
45  ASSERT_TRUE(M == M) << "Multilib::operator==() is not reflexive";
46}
47
48TEST(MultilibTest, OpEqReflexivity2) {
49  ASSERT_TRUE(Multilib() == Multilib())
50      << "Separately constructed default multilibs are not equal";
51}
52
53TEST(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
60TEST(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
69TEST(MultilibTest, OpEqInequivalence2) {
70  Multilib M1, M2;
71  M2.flag("+foo");
72  ASSERT_FALSE(M1 == M2) << "Flags make Multilibs different";
73}
74
75TEST(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
84TEST(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
94TEST(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
104TEST(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
114TEST(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
122TEST(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
130TEST(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
138TEST(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
145TEST(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
158TEST(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
169static 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
181TEST(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
197TEST(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
226TEST(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
240TEST(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
256TEST(MultilibTest, SetFilterObject) {
257  MultilibSet MS;
258  MS.Maybe(Multilib("orange"));
259  MS.Maybe(Multilib("pear"));
260  MS.Maybe(Multilib("plum"));
261  ASSERT_EQ((int)MS.size(), 1 /* Default */ +
262                            1 /* pear */ +
263                            1 /* plum */ +
264                            1 /* pear/plum */ +
265                            1 /* orange */ +
266                            1 /* orange/pear */ +
267                            1 /* orange/plum */ +
268                            1 /* orange/pear/plum */ )
269      << "Size before filter was incorrect. Contents:\n" << MS;
270  MS.FilterOut([](const Multilib &M) {
271    return StringRef(M.gccSuffix()).startswith("/p");
272  });
273  ASSERT_EQ((int)MS.size(), 1 /* Default */ +
274                            1 /* orange */ +
275                            1 /* orange/pear */ +
276                            1 /* orange/plum */ +
277                            1 /* orange/pear/plum */ )
278      << "Size after filter was incorrect. Contents:\n" << MS;
279  for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
280    ASSERT_FALSE(StringRef(I->gccSuffix()).startswith("/p"))
281        << "The filter should have removed " << *I;
282  }
283}
284
285TEST(MultilibTest, SetSelection1) {
286  MultilibSet MS1 = MultilibSet()
287    .Maybe(Multilib("64").flag("+m64"));
288
289  Multilib::flags_list FlagM64;
290  FlagM64.push_back("+m64");
291  Multilib SelectionM64;
292  ASSERT_TRUE(MS1.select(FlagM64, SelectionM64))
293      << "Flag set was {\"+m64\"}, but selection not found";
294  ASSERT_TRUE(SelectionM64.gccSuffix() == "/64")
295      << "Selection picked " << SelectionM64 << " which was not expected";
296
297  Multilib::flags_list FlagNoM64;
298  FlagNoM64.push_back("-m64");
299  Multilib SelectionNoM64;
300  ASSERT_TRUE(MS1.select(FlagNoM64, SelectionNoM64))
301      << "Flag set was {\"-m64\"}, but selection not found";
302  ASSERT_TRUE(SelectionNoM64.gccSuffix() == "")
303      << "Selection picked " << SelectionNoM64 << " which was not expected";
304}
305
306TEST(MultilibTest, SetSelection2) {
307  MultilibSet MS2 = MultilibSet()
308    .Maybe(Multilib("el").flag("+EL"))
309    .Maybe(Multilib("sf").flag("+SF"));
310
311  for (unsigned I = 0; I < 4; ++I) {
312    bool IsEL = I & 0x1;
313    bool IsSF = I & 0x2;
314    Multilib::flags_list Flags;
315    if (IsEL)
316      Flags.push_back("+EL");
317    else
318      Flags.push_back("-EL");
319
320    if (IsSF)
321      Flags.push_back("+SF");
322    else
323      Flags.push_back("-SF");
324
325    Multilib Selection;
326    ASSERT_TRUE(MS2.select(Flags, Selection)) << "Selection failed for "
327                                              << (IsEL ? "+EL" : "-EL") << " "
328                                              << (IsSF ? "+SF" : "-SF");
329
330    std::string Suffix;
331    if (IsEL)
332      Suffix += "/el";
333    if (IsSF)
334      Suffix += "/sf";
335
336    ASSERT_EQ(Selection.gccSuffix(), Suffix) << "Selection picked " << Selection
337                                             << " which was not expected ";
338  }
339}
340
341TEST(MultilibTest, SetCombineWith) {
342  MultilibSet Coffee;
343  Coffee.push_back(Multilib("coffee"));
344  MultilibSet Milk;
345  Milk.push_back(Multilib("milk"));
346  MultilibSet Latte;
347  ASSERT_EQ(Latte.size(), (unsigned)0);
348  Latte.combineWith(Coffee);
349  ASSERT_EQ(Latte.size(), (unsigned)1);
350  Latte.combineWith(Milk);
351  ASSERT_EQ(Latte.size(), (unsigned)2);
352}
353