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 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 42 TEST(MultilibTest, OpEqReflexivity1) { 43 Multilib M; 44 ASSERT_TRUE(M == M) << "Multilib::operator==() is not reflexive"; 45 } 46 47 TEST(MultilibTest, OpEqReflexivity2) { 48 ASSERT_TRUE(Multilib() == Multilib()) 49 << "Separately constructed default multilibs are not equal"; 50 } 51 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 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 68 TEST(MultilibTest, OpEqInequivalence2) { 69 Multilib M1, M2; 70 M2.flag("+foo"); 71 ASSERT_FALSE(M1 == M2) << "Flags make Multilibs different"; 72 } 73 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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