1 //===- unittests/Support/VirtualFileSystem.cpp -------------- VFS 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 #include "llvm/Support/VirtualFileSystem.h" 10 #include "llvm/ADT/Triple.h" 11 #include "llvm/Config/llvm-config.h" 12 #include "llvm/Support/Errc.h" 13 #include "llvm/Support/Host.h" 14 #include "llvm/Support/MemoryBuffer.h" 15 #include "llvm/Support/Path.h" 16 #include "llvm/Support/SourceMgr.h" 17 #include "llvm/Testing/Support/SupportHelpers.h" 18 #include "gmock/gmock.h" 19 #include "gtest/gtest.h" 20 #include <map> 21 #include <string> 22 23 using namespace llvm; 24 using llvm::sys::fs::UniqueID; 25 using llvm::unittest::TempDir; 26 using llvm::unittest::TempFile; 27 using llvm::unittest::TempLink; 28 using testing::ElementsAre; 29 using testing::Pair; 30 using testing::UnorderedElementsAre; 31 32 namespace { 33 struct DummyFile : public vfs::File { 34 vfs::Status S; 35 explicit DummyFile(vfs::Status S) : S(S) {} 36 llvm::ErrorOr<vfs::Status> status() override { return S; } 37 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 38 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 39 bool IsVolatile) override { 40 llvm_unreachable("unimplemented"); 41 } 42 std::error_code close() override { return std::error_code(); } 43 }; 44 45 class DummyFileSystem : public vfs::FileSystem { 46 int FSID; // used to produce UniqueIDs 47 int FileID; // used to produce UniqueIDs 48 std::string WorkingDirectory; 49 std::map<std::string, vfs::Status> FilesAndDirs; 50 typedef std::map<std::string, vfs::Status>::const_iterator const_iterator; 51 52 static int getNextFSID() { 53 static int Count = 0; 54 return Count++; 55 } 56 57 public: 58 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {} 59 60 ErrorOr<vfs::Status> status(const Twine &Path) override { 61 auto I = findEntry(Path); 62 if (I == FilesAndDirs.end()) 63 return make_error_code(llvm::errc::no_such_file_or_directory); 64 return I->second; 65 } 66 ErrorOr<std::unique_ptr<vfs::File>> 67 openFileForRead(const Twine &Path) override { 68 auto S = status(Path); 69 if (S) 70 return std::unique_ptr<vfs::File>(new DummyFile{*S}); 71 return S.getError(); 72 } 73 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 74 return WorkingDirectory; 75 } 76 std::error_code setCurrentWorkingDirectory(const Twine &Path) override { 77 WorkingDirectory = Path.str(); 78 return std::error_code(); 79 } 80 // Map any symlink to "/symlink". 81 std::error_code getRealPath(const Twine &Path, 82 SmallVectorImpl<char> &Output) const override { 83 auto I = findEntry(Path); 84 if (I == FilesAndDirs.end()) 85 return make_error_code(llvm::errc::no_such_file_or_directory); 86 if (I->second.isSymlink()) { 87 Output.clear(); 88 Twine("/symlink").toVector(Output); 89 return std::error_code(); 90 } 91 Output.clear(); 92 Path.toVector(Output); 93 return std::error_code(); 94 } 95 96 struct DirIterImpl : public llvm::vfs::detail::DirIterImpl { 97 std::map<std::string, vfs::Status> &FilesAndDirs; 98 std::map<std::string, vfs::Status>::iterator I; 99 std::string Path; 100 bool isInPath(StringRef S) { 101 if (Path.size() < S.size() && S.find(Path) == 0) { 102 auto LastSep = S.find_last_of('/'); 103 if (LastSep == Path.size() || LastSep == Path.size() - 1) 104 return true; 105 } 106 return false; 107 } 108 DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs, 109 const Twine &_Path) 110 : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()), 111 Path(_Path.str()) { 112 for (; I != FilesAndDirs.end(); ++I) { 113 if (isInPath(I->first)) { 114 CurrentEntry = vfs::directory_entry(std::string(I->second.getName()), 115 I->second.getType()); 116 break; 117 } 118 } 119 } 120 std::error_code increment() override { 121 ++I; 122 for (; I != FilesAndDirs.end(); ++I) { 123 if (isInPath(I->first)) { 124 CurrentEntry = vfs::directory_entry(std::string(I->second.getName()), 125 I->second.getType()); 126 break; 127 } 128 } 129 if (I == FilesAndDirs.end()) 130 CurrentEntry = vfs::directory_entry(); 131 return std::error_code(); 132 } 133 }; 134 135 vfs::directory_iterator dir_begin(const Twine &Dir, 136 std::error_code &EC) override { 137 return vfs::directory_iterator( 138 std::make_shared<DirIterImpl>(FilesAndDirs, Dir)); 139 } 140 141 void addEntry(StringRef Path, const vfs::Status &Status) { 142 FilesAndDirs[std::string(Path)] = Status; 143 } 144 145 const_iterator findEntry(const Twine &Path) const { 146 SmallString<128> P; 147 Path.toVector(P); 148 std::error_code EC = makeAbsolute(P); 149 assert(!EC); 150 (void)EC; 151 return FilesAndDirs.find(std::string(P.str())); 152 } 153 154 void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { 155 vfs::Status S(Path, UniqueID(FSID, FileID++), 156 std::chrono::system_clock::now(), 0, 0, 1024, 157 sys::fs::file_type::regular_file, Perms); 158 addEntry(Path, S); 159 } 160 161 void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { 162 vfs::Status S(Path, UniqueID(FSID, FileID++), 163 std::chrono::system_clock::now(), 0, 0, 0, 164 sys::fs::file_type::directory_file, Perms); 165 addEntry(Path, S); 166 } 167 168 void addSymlink(StringRef Path) { 169 vfs::Status S(Path, UniqueID(FSID, FileID++), 170 std::chrono::system_clock::now(), 0, 0, 0, 171 sys::fs::file_type::symlink_file, sys::fs::all_all); 172 addEntry(Path, S); 173 } 174 175 protected: 176 void printImpl(raw_ostream &OS, PrintType Type, 177 unsigned IndentLevel) const override { 178 printIndent(OS, IndentLevel); 179 OS << "DummyFileSystem ("; 180 switch (Type) { 181 case vfs::FileSystem::PrintType::Summary: 182 OS << "Summary"; 183 break; 184 case vfs::FileSystem::PrintType::Contents: 185 OS << "Contents"; 186 break; 187 case vfs::FileSystem::PrintType::RecursiveContents: 188 OS << "RecursiveContents"; 189 break; 190 } 191 OS << ")\n"; 192 } 193 }; 194 195 class ErrorDummyFileSystem : public DummyFileSystem { 196 std::error_code setCurrentWorkingDirectory(const Twine &Path) override { 197 return llvm::errc::no_such_file_or_directory; 198 } 199 }; 200 201 /// Replace back-slashes by front-slashes. 202 std::string getPosixPath(const Twine &S) { 203 SmallString<128> Result; 204 llvm::sys::path::native(S, Result, llvm::sys::path::Style::posix); 205 return std::string(Result.str()); 206 } 207 } // end anonymous namespace 208 209 TEST(VirtualFileSystemTest, StatusQueries) { 210 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); 211 ErrorOr<vfs::Status> Status((std::error_code())); 212 213 D->addRegularFile("/foo"); 214 Status = D->status("/foo"); 215 ASSERT_FALSE(Status.getError()); 216 EXPECT_TRUE(Status->isStatusKnown()); 217 EXPECT_FALSE(Status->isDirectory()); 218 EXPECT_TRUE(Status->isRegularFile()); 219 EXPECT_FALSE(Status->isSymlink()); 220 EXPECT_FALSE(Status->isOther()); 221 EXPECT_TRUE(Status->exists()); 222 223 D->addDirectory("/bar"); 224 Status = D->status("/bar"); 225 ASSERT_FALSE(Status.getError()); 226 EXPECT_TRUE(Status->isStatusKnown()); 227 EXPECT_TRUE(Status->isDirectory()); 228 EXPECT_FALSE(Status->isRegularFile()); 229 EXPECT_FALSE(Status->isSymlink()); 230 EXPECT_FALSE(Status->isOther()); 231 EXPECT_TRUE(Status->exists()); 232 233 D->addSymlink("/baz"); 234 Status = D->status("/baz"); 235 ASSERT_FALSE(Status.getError()); 236 EXPECT_TRUE(Status->isStatusKnown()); 237 EXPECT_FALSE(Status->isDirectory()); 238 EXPECT_FALSE(Status->isRegularFile()); 239 EXPECT_TRUE(Status->isSymlink()); 240 EXPECT_FALSE(Status->isOther()); 241 EXPECT_TRUE(Status->exists()); 242 243 EXPECT_TRUE(Status->equivalent(*Status)); 244 ErrorOr<vfs::Status> Status2 = D->status("/foo"); 245 ASSERT_FALSE(Status2.getError()); 246 EXPECT_FALSE(Status->equivalent(*Status2)); 247 } 248 249 TEST(VirtualFileSystemTest, BaseOnlyOverlay) { 250 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); 251 ErrorOr<vfs::Status> Status((std::error_code())); 252 EXPECT_FALSE(Status = D->status("/foo")); 253 254 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D)); 255 EXPECT_FALSE(Status = O->status("/foo")); 256 257 D->addRegularFile("/foo"); 258 Status = D->status("/foo"); 259 EXPECT_FALSE(Status.getError()); 260 261 ErrorOr<vfs::Status> Status2((std::error_code())); 262 Status2 = O->status("/foo"); 263 EXPECT_FALSE(Status2.getError()); 264 EXPECT_TRUE(Status->equivalent(*Status2)); 265 } 266 267 TEST(VirtualFileSystemTest, GetRealPathInOverlay) { 268 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 269 Lower->addRegularFile("/foo"); 270 Lower->addSymlink("/lower_link"); 271 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 272 273 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 274 new vfs::OverlayFileSystem(Lower)); 275 O->pushOverlay(Upper); 276 277 // Regular file. 278 SmallString<16> RealPath; 279 EXPECT_FALSE(O->getRealPath("/foo", RealPath)); 280 EXPECT_EQ(RealPath.str(), "/foo"); 281 282 // Expect no error getting real path for symlink in lower overlay. 283 EXPECT_FALSE(O->getRealPath("/lower_link", RealPath)); 284 EXPECT_EQ(RealPath.str(), "/symlink"); 285 286 // Try a non-existing link. 287 EXPECT_EQ(O->getRealPath("/upper_link", RealPath), 288 errc::no_such_file_or_directory); 289 290 // Add a new symlink in upper. 291 Upper->addSymlink("/upper_link"); 292 EXPECT_FALSE(O->getRealPath("/upper_link", RealPath)); 293 EXPECT_EQ(RealPath.str(), "/symlink"); 294 } 295 296 TEST(VirtualFileSystemTest, OverlayFiles) { 297 IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem()); 298 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 299 IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem()); 300 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 301 new vfs::OverlayFileSystem(Base)); 302 O->pushOverlay(Middle); 303 O->pushOverlay(Top); 304 305 ErrorOr<vfs::Status> Status1((std::error_code())), 306 Status2((std::error_code())), Status3((std::error_code())), 307 StatusB((std::error_code())), StatusM((std::error_code())), 308 StatusT((std::error_code())); 309 310 Base->addRegularFile("/foo"); 311 StatusB = Base->status("/foo"); 312 ASSERT_FALSE(StatusB.getError()); 313 Status1 = O->status("/foo"); 314 ASSERT_FALSE(Status1.getError()); 315 Middle->addRegularFile("/foo"); 316 StatusM = Middle->status("/foo"); 317 ASSERT_FALSE(StatusM.getError()); 318 Status2 = O->status("/foo"); 319 ASSERT_FALSE(Status2.getError()); 320 Top->addRegularFile("/foo"); 321 StatusT = Top->status("/foo"); 322 ASSERT_FALSE(StatusT.getError()); 323 Status3 = O->status("/foo"); 324 ASSERT_FALSE(Status3.getError()); 325 326 EXPECT_TRUE(Status1->equivalent(*StatusB)); 327 EXPECT_TRUE(Status2->equivalent(*StatusM)); 328 EXPECT_TRUE(Status3->equivalent(*StatusT)); 329 330 EXPECT_FALSE(Status1->equivalent(*Status2)); 331 EXPECT_FALSE(Status2->equivalent(*Status3)); 332 EXPECT_FALSE(Status1->equivalent(*Status3)); 333 } 334 335 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) { 336 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 337 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 338 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 339 new vfs::OverlayFileSystem(Lower)); 340 O->pushOverlay(Upper); 341 342 Lower->addDirectory("/lower-only"); 343 Upper->addDirectory("/upper-only"); 344 345 // non-merged paths should be the same 346 ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only"); 347 ASSERT_FALSE(Status1.getError()); 348 ErrorOr<vfs::Status> Status2 = O->status("/lower-only"); 349 ASSERT_FALSE(Status2.getError()); 350 EXPECT_TRUE(Status1->equivalent(*Status2)); 351 352 Status1 = Upper->status("/upper-only"); 353 ASSERT_FALSE(Status1.getError()); 354 Status2 = O->status("/upper-only"); 355 ASSERT_FALSE(Status2.getError()); 356 EXPECT_TRUE(Status1->equivalent(*Status2)); 357 } 358 359 TEST(VirtualFileSystemTest, MergedDirPermissions) { 360 // merged directories get the permissions of the upper dir 361 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 362 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 363 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 364 new vfs::OverlayFileSystem(Lower)); 365 O->pushOverlay(Upper); 366 367 ErrorOr<vfs::Status> Status((std::error_code())); 368 Lower->addDirectory("/both", sys::fs::owner_read); 369 Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read); 370 Status = O->status("/both"); 371 ASSERT_FALSE(Status.getError()); 372 EXPECT_EQ(0740, Status->getPermissions()); 373 374 // permissions (as usual) are not recursively applied 375 Lower->addRegularFile("/both/foo", sys::fs::owner_read); 376 Upper->addRegularFile("/both/bar", sys::fs::owner_write); 377 Status = O->status("/both/foo"); 378 ASSERT_FALSE(Status.getError()); 379 EXPECT_EQ(0400, Status->getPermissions()); 380 Status = O->status("/both/bar"); 381 ASSERT_FALSE(Status.getError()); 382 EXPECT_EQ(0200, Status->getPermissions()); 383 } 384 385 TEST(VirtualFileSystemTest, OverlayIterator) { 386 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 387 Lower->addRegularFile("/foo"); 388 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 389 390 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 391 new vfs::OverlayFileSystem(Lower)); 392 O->pushOverlay(Upper); 393 394 ErrorOr<vfs::Status> Status((std::error_code())); 395 { 396 auto it = O->overlays_begin(); 397 auto end = O->overlays_end(); 398 399 EXPECT_NE(it, end); 400 401 Status = (*it)->status("/foo"); 402 ASSERT_TRUE(Status.getError()); 403 404 it++; 405 EXPECT_NE(it, end); 406 407 Status = (*it)->status("/foo"); 408 ASSERT_FALSE(Status.getError()); 409 EXPECT_TRUE(Status->exists()); 410 411 it++; 412 EXPECT_EQ(it, end); 413 } 414 415 { 416 auto it = O->overlays_rbegin(); 417 auto end = O->overlays_rend(); 418 419 EXPECT_NE(it, end); 420 421 Status = (*it)->status("/foo"); 422 ASSERT_FALSE(Status.getError()); 423 EXPECT_TRUE(Status->exists()); 424 425 it++; 426 EXPECT_NE(it, end); 427 428 Status = (*it)->status("/foo"); 429 ASSERT_TRUE(Status.getError()); 430 431 it++; 432 EXPECT_EQ(it, end); 433 } 434 } 435 436 TEST(VirtualFileSystemTest, BasicRealFSIteration) { 437 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 438 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 439 440 std::error_code EC; 441 vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory.path()), EC); 442 ASSERT_FALSE(EC); 443 EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty 444 445 TempDir _a(TestDirectory.path("a")); 446 TempDir _ab(TestDirectory.path("a/b")); 447 TempDir _c(TestDirectory.path("c")); 448 TempDir _cd(TestDirectory.path("c/d")); 449 450 I = FS->dir_begin(Twine(TestDirectory.path()), EC); 451 ASSERT_FALSE(EC); 452 ASSERT_NE(vfs::directory_iterator(), I); 453 // Check either a or c, since we can't rely on the iteration order. 454 EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c")); 455 I.increment(EC); 456 ASSERT_FALSE(EC); 457 ASSERT_NE(vfs::directory_iterator(), I); 458 EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c")); 459 I.increment(EC); 460 EXPECT_EQ(vfs::directory_iterator(), I); 461 } 462 463 #ifdef LLVM_ON_UNIX 464 TEST(VirtualFileSystemTest, MultipleWorkingDirs) { 465 // Our root contains a/aa, b/bb, c, where c is a link to a/. 466 // Run tests both in root/b/ and root/c/ (to test "normal" and symlink dirs). 467 // Interleave operations to show the working directories are independent. 468 TempDir Root("r", /*Unique*/ true); 469 TempDir ADir(Root.path("a")); 470 TempDir BDir(Root.path("b")); 471 TempLink C(ADir.path(), Root.path("c")); 472 TempFile AA(ADir.path("aa"), "", "aaaa"); 473 TempFile BB(BDir.path("bb"), "", "bbbb"); 474 std::unique_ptr<vfs::FileSystem> BFS = vfs::createPhysicalFileSystem(), 475 CFS = vfs::createPhysicalFileSystem(); 476 477 ASSERT_FALSE(BFS->setCurrentWorkingDirectory(BDir.path())); 478 ASSERT_FALSE(CFS->setCurrentWorkingDirectory(C.path())); 479 EXPECT_EQ(BDir.path(), *BFS->getCurrentWorkingDirectory()); 480 EXPECT_EQ(C.path(), *CFS->getCurrentWorkingDirectory()); 481 482 // openFileForRead(), indirectly. 483 auto BBuf = BFS->getBufferForFile("bb"); 484 ASSERT_TRUE(BBuf); 485 EXPECT_EQ("bbbb", (*BBuf)->getBuffer()); 486 487 auto ABuf = CFS->getBufferForFile("aa"); 488 ASSERT_TRUE(ABuf); 489 EXPECT_EQ("aaaa", (*ABuf)->getBuffer()); 490 491 // status() 492 auto BStat = BFS->status("bb"); 493 ASSERT_TRUE(BStat); 494 EXPECT_EQ("bb", BStat->getName()); 495 496 auto AStat = CFS->status("aa"); 497 ASSERT_TRUE(AStat); 498 EXPECT_EQ("aa", AStat->getName()); // unresolved name 499 500 // getRealPath() 501 SmallString<128> BPath; 502 ASSERT_FALSE(BFS->getRealPath("bb", BPath)); 503 EXPECT_EQ(BB.path(), BPath); 504 505 SmallString<128> APath; 506 ASSERT_FALSE(CFS->getRealPath("aa", APath)); 507 EXPECT_EQ(AA.path(), APath); // Reports resolved name. 508 509 // dir_begin 510 std::error_code EC; 511 auto BIt = BFS->dir_begin(".", EC); 512 ASSERT_FALSE(EC); 513 ASSERT_NE(BIt, vfs::directory_iterator()); 514 EXPECT_EQ((BDir.path() + "/./bb").str(), BIt->path()); 515 BIt.increment(EC); 516 ASSERT_FALSE(EC); 517 ASSERT_EQ(BIt, vfs::directory_iterator()); 518 519 auto CIt = CFS->dir_begin(".", EC); 520 ASSERT_FALSE(EC); 521 ASSERT_NE(CIt, vfs::directory_iterator()); 522 EXPECT_EQ((ADir.path() + "/./aa").str(), 523 CIt->path()); // Partly resolved name! 524 CIt.increment(EC); // Because likely to read through this path. 525 ASSERT_FALSE(EC); 526 ASSERT_EQ(CIt, vfs::directory_iterator()); 527 } 528 529 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) { 530 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 531 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 532 533 TempLink _a("no_such_file", TestDirectory.path("a")); 534 TempDir _b(TestDirectory.path("b")); 535 TempLink _c("no_such_file", TestDirectory.path("c")); 536 537 // Should get no iteration error, but a stat error for the broken symlinks. 538 std::map<std::string, std::error_code> StatResults; 539 std::error_code EC; 540 for (vfs::directory_iterator 541 I = FS->dir_begin(Twine(TestDirectory.path()), EC), 542 E; 543 I != E; I.increment(EC)) { 544 EXPECT_FALSE(EC); 545 StatResults[std::string(sys::path::filename(I->path()))] = 546 FS->status(I->path()).getError(); 547 } 548 EXPECT_THAT( 549 StatResults, 550 ElementsAre( 551 Pair("a", std::make_error_code(std::errc::no_such_file_or_directory)), 552 Pair("b", std::error_code()), 553 Pair("c", 554 std::make_error_code(std::errc::no_such_file_or_directory)))); 555 } 556 #endif 557 558 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) { 559 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 560 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 561 562 std::error_code EC; 563 auto I = 564 vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC); 565 ASSERT_FALSE(EC); 566 EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty 567 568 TempDir _a(TestDirectory.path("a")); 569 TempDir _ab(TestDirectory.path("a/b")); 570 TempDir _c(TestDirectory.path("c")); 571 TempDir _cd(TestDirectory.path("c/d")); 572 573 I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC); 574 ASSERT_FALSE(EC); 575 ASSERT_NE(vfs::recursive_directory_iterator(), I); 576 577 std::vector<std::string> Contents; 578 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 579 I.increment(EC)) { 580 Contents.push_back(std::string(I->path())); 581 } 582 583 // Check contents, which may be in any order 584 EXPECT_EQ(4U, Contents.size()); 585 int Counts[4] = {0, 0, 0, 0}; 586 for (const std::string &Name : Contents) { 587 ASSERT_FALSE(Name.empty()); 588 int Index = Name[Name.size() - 1] - 'a'; 589 ASSERT_GE(Index, 0); 590 ASSERT_LT(Index, 4); 591 Counts[Index]++; 592 } 593 EXPECT_EQ(1, Counts[0]); // a 594 EXPECT_EQ(1, Counts[1]); // b 595 EXPECT_EQ(1, Counts[2]); // c 596 EXPECT_EQ(1, Counts[3]); // d 597 } 598 599 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) { 600 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 601 602 TempDir _a(TestDirectory.path("a")); 603 TempDir _ab(TestDirectory.path("a/b")); 604 TempDir _c(TestDirectory.path("c")); 605 TempDir _cd(TestDirectory.path("c/d")); 606 TempDir _e(TestDirectory.path("e")); 607 TempDir _ef(TestDirectory.path("e/f")); 608 TempDir _g(TestDirectory.path("g")); 609 610 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 611 612 // Test that calling no_push on entries without subdirectories has no effect. 613 { 614 std::error_code EC; 615 auto I = 616 vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC); 617 ASSERT_FALSE(EC); 618 619 std::vector<std::string> Contents; 620 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 621 I.increment(EC)) { 622 Contents.push_back(std::string(I->path())); 623 char last = I->path().back(); 624 switch (last) { 625 case 'b': 626 case 'd': 627 case 'f': 628 case 'g': 629 I.no_push(); 630 break; 631 default: 632 break; 633 } 634 } 635 EXPECT_EQ(7U, Contents.size()); 636 } 637 638 // Test that calling no_push skips subdirectories. 639 { 640 std::error_code EC; 641 auto I = 642 vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC); 643 ASSERT_FALSE(EC); 644 645 std::vector<std::string> Contents; 646 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 647 I.increment(EC)) { 648 Contents.push_back(std::string(I->path())); 649 char last = I->path().back(); 650 switch (last) { 651 case 'a': 652 case 'c': 653 case 'e': 654 I.no_push(); 655 break; 656 default: 657 break; 658 } 659 } 660 661 // Check contents, which may be in any order 662 EXPECT_EQ(4U, Contents.size()); 663 int Counts[7] = {0, 0, 0, 0, 0, 0, 0}; 664 for (const std::string &Name : Contents) { 665 ASSERT_FALSE(Name.empty()); 666 int Index = Name[Name.size() - 1] - 'a'; 667 ASSERT_GE(Index, 0); 668 ASSERT_LT(Index, 7); 669 Counts[Index]++; 670 } 671 EXPECT_EQ(1, Counts[0]); // a 672 EXPECT_EQ(0, Counts[1]); // b 673 EXPECT_EQ(1, Counts[2]); // c 674 EXPECT_EQ(0, Counts[3]); // d 675 EXPECT_EQ(1, Counts[4]); // e 676 EXPECT_EQ(0, Counts[5]); // f 677 EXPECT_EQ(1, Counts[6]); // g 678 } 679 } 680 681 #ifdef LLVM_ON_UNIX 682 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) { 683 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 684 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 685 686 TempLink _a("no_such_file", TestDirectory.path("a")); 687 TempDir _b(TestDirectory.path("b")); 688 TempLink _ba("no_such_file", TestDirectory.path("b/a")); 689 TempDir _bb(TestDirectory.path("b/b")); 690 TempLink _bc("no_such_file", TestDirectory.path("b/c")); 691 TempLink _c("no_such_file", TestDirectory.path("c")); 692 TempDir _d(TestDirectory.path("d")); 693 TempDir _dd(TestDirectory.path("d/d")); 694 TempDir _ddd(TestDirectory.path("d/d/d")); 695 TempLink _e("no_such_file", TestDirectory.path("e")); 696 697 std::vector<std::string> VisitedBrokenSymlinks; 698 std::vector<std::string> VisitedNonBrokenSymlinks; 699 std::error_code EC; 700 for (vfs::recursive_directory_iterator 701 I(*FS, Twine(TestDirectory.path()), EC), 702 E; 703 I != E; I.increment(EC)) { 704 EXPECT_FALSE(EC); 705 (FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks) 706 .push_back(std::string(I->path())); 707 } 708 709 // Check visited file names. 710 EXPECT_THAT(VisitedBrokenSymlinks, 711 UnorderedElementsAre(_a.path().str(), _ba.path().str(), 712 _bc.path().str(), _c.path().str(), 713 _e.path().str())); 714 EXPECT_THAT(VisitedNonBrokenSymlinks, 715 UnorderedElementsAre(_b.path().str(), _bb.path().str(), 716 _d.path().str(), _dd.path().str(), 717 _ddd.path().str())); 718 } 719 #endif 720 721 template <typename DirIter> 722 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) { 723 std::error_code EC; 724 SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end()); 725 SmallVector<std::string, 4> InputToCheck; 726 727 // Do not rely on iteration order to check for contents, sort both 728 // content vectors before comparison. 729 for (DirIter E; !EC && I != E; I.increment(EC)) 730 InputToCheck.push_back(std::string(I->path())); 731 732 llvm::sort(InputToCheck); 733 llvm::sort(Expected); 734 EXPECT_EQ(InputToCheck.size(), Expected.size()); 735 736 unsigned LastElt = std::min(InputToCheck.size(), Expected.size()); 737 for (unsigned Idx = 0; Idx != LastElt; ++Idx) 738 EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]); 739 } 740 741 TEST(VirtualFileSystemTest, OverlayIteration) { 742 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 743 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 744 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 745 new vfs::OverlayFileSystem(Lower)); 746 O->pushOverlay(Upper); 747 748 std::error_code EC; 749 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); 750 751 Lower->addRegularFile("/file1"); 752 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1")); 753 754 Upper->addRegularFile("/file2"); 755 checkContents(O->dir_begin("/", EC), {"/file2", "/file1"}); 756 757 Lower->addDirectory("/dir1"); 758 Lower->addRegularFile("/dir1/foo"); 759 Upper->addDirectory("/dir2"); 760 Upper->addRegularFile("/dir2/foo"); 761 checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo")); 762 checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"}); 763 } 764 765 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) { 766 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 767 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 768 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 769 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 770 new vfs::OverlayFileSystem(Lower)); 771 O->pushOverlay(Middle); 772 O->pushOverlay(Upper); 773 774 std::error_code EC; 775 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 776 ArrayRef<StringRef>()); 777 778 Lower->addRegularFile("/file1"); 779 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 780 ArrayRef<StringRef>("/file1")); 781 782 Upper->addDirectory("/dir"); 783 Upper->addRegularFile("/dir/file2"); 784 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 785 {"/dir", "/dir/file2", "/file1"}); 786 787 Lower->addDirectory("/dir1"); 788 Lower->addRegularFile("/dir1/foo"); 789 Lower->addDirectory("/dir1/a"); 790 Lower->addRegularFile("/dir1/a/b"); 791 Middle->addDirectory("/a"); 792 Middle->addDirectory("/a/b"); 793 Middle->addDirectory("/a/b/c"); 794 Middle->addRegularFile("/a/b/c/d"); 795 Middle->addRegularFile("/hiddenByUp"); 796 Upper->addDirectory("/dir2"); 797 Upper->addRegularFile("/dir2/foo"); 798 Upper->addRegularFile("/hiddenByUp"); 799 checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC), 800 ArrayRef<StringRef>("/dir2/foo")); 801 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 802 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp", 803 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a", 804 "/dir1/a/b", "/dir1/foo", "/file1"}); 805 } 806 807 TEST(VirtualFileSystemTest, ThreeLevelIteration) { 808 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 809 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 810 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 811 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 812 new vfs::OverlayFileSystem(Lower)); 813 O->pushOverlay(Middle); 814 O->pushOverlay(Upper); 815 816 std::error_code EC; 817 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); 818 819 Middle->addRegularFile("/file2"); 820 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2")); 821 822 Lower->addRegularFile("/file1"); 823 Upper->addRegularFile("/file3"); 824 checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"}); 825 } 826 827 TEST(VirtualFileSystemTest, HiddenInIteration) { 828 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 829 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 830 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 831 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 832 new vfs::OverlayFileSystem(Lower)); 833 O->pushOverlay(Middle); 834 O->pushOverlay(Upper); 835 836 std::error_code EC; 837 Lower->addRegularFile("/onlyInLow"); 838 Lower->addDirectory("/hiddenByMid"); 839 Lower->addDirectory("/hiddenByUp"); 840 Middle->addRegularFile("/onlyInMid"); 841 Middle->addRegularFile("/hiddenByMid"); 842 Middle->addDirectory("/hiddenByUp"); 843 Upper->addRegularFile("/onlyInUp"); 844 Upper->addRegularFile("/hiddenByUp"); 845 checkContents( 846 O->dir_begin("/", EC), 847 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"}); 848 849 // Make sure we get the top-most entry 850 { 851 std::error_code EC; 852 vfs::directory_iterator I = O->dir_begin("/", EC), E; 853 for (; !EC && I != E; I.increment(EC)) 854 if (I->path() == "/hiddenByUp") 855 break; 856 ASSERT_NE(E, I); 857 EXPECT_EQ(sys::fs::file_type::regular_file, I->type()); 858 } 859 { 860 std::error_code EC; 861 vfs::directory_iterator I = O->dir_begin("/", EC), E; 862 for (; !EC && I != E; I.increment(EC)) 863 if (I->path() == "/hiddenByMid") 864 break; 865 ASSERT_NE(E, I); 866 EXPECT_EQ(sys::fs::file_type::regular_file, I->type()); 867 } 868 } 869 870 TEST(OverlayFileSystemTest, PrintOutput) { 871 auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>(); 872 auto Overlay1 = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Dummy); 873 Overlay1->pushOverlay(Dummy); 874 auto Overlay2 = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Overlay1); 875 Overlay2->pushOverlay(Dummy); 876 877 SmallString<0> Output; 878 raw_svector_ostream OuputStream{Output}; 879 880 Overlay2->print(OuputStream, vfs::FileSystem::PrintType::Summary); 881 ASSERT_EQ("OverlayFileSystem\n", Output); 882 883 Output.clear(); 884 Overlay2->print(OuputStream, vfs::FileSystem::PrintType::Contents); 885 ASSERT_EQ("OverlayFileSystem\n" 886 " DummyFileSystem (Summary)\n" 887 " OverlayFileSystem\n", 888 Output); 889 890 Output.clear(); 891 Overlay2->print(OuputStream, vfs::FileSystem::PrintType::RecursiveContents); 892 ASSERT_EQ("OverlayFileSystem\n" 893 " DummyFileSystem (RecursiveContents)\n" 894 " OverlayFileSystem\n" 895 " DummyFileSystem (RecursiveContents)\n" 896 " DummyFileSystem (RecursiveContents)\n", 897 Output); 898 } 899 900 TEST(ProxyFileSystemTest, Basic) { 901 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> Base( 902 new vfs::InMemoryFileSystem()); 903 vfs::ProxyFileSystem PFS(Base); 904 905 Base->addFile("/a", 0, MemoryBuffer::getMemBuffer("test")); 906 907 auto Stat = PFS.status("/a"); 908 ASSERT_FALSE(Stat.getError()); 909 910 auto File = PFS.openFileForRead("/a"); 911 ASSERT_FALSE(File.getError()); 912 EXPECT_EQ("test", (*(*File)->getBuffer("ignored"))->getBuffer()); 913 914 std::error_code EC; 915 vfs::directory_iterator I = PFS.dir_begin("/", EC); 916 ASSERT_FALSE(EC); 917 ASSERT_EQ("/a", I->path()); 918 I.increment(EC); 919 ASSERT_FALSE(EC); 920 ASSERT_EQ(vfs::directory_iterator(), I); 921 922 ASSERT_FALSE(PFS.setCurrentWorkingDirectory("/")); 923 924 auto PWD = PFS.getCurrentWorkingDirectory(); 925 ASSERT_FALSE(PWD.getError()); 926 ASSERT_EQ("/", getPosixPath(*PWD)); 927 928 SmallString<16> Path; 929 ASSERT_FALSE(PFS.getRealPath("a", Path)); 930 ASSERT_EQ("/a", getPosixPath(Path)); 931 932 bool Local = true; 933 ASSERT_FALSE(PFS.isLocal("/a", Local)); 934 EXPECT_FALSE(Local); 935 } 936 937 class InMemoryFileSystemTest : public ::testing::Test { 938 protected: 939 llvm::vfs::InMemoryFileSystem FS; 940 llvm::vfs::InMemoryFileSystem NormalizedFS; 941 942 InMemoryFileSystemTest() 943 : FS(/*UseNormalizedPaths=*/false), 944 NormalizedFS(/*UseNormalizedPaths=*/true) {} 945 }; 946 947 MATCHER_P2(IsHardLinkTo, FS, Target, "") { 948 StringRef From = arg; 949 StringRef To = Target; 950 auto OpenedFrom = FS->openFileForRead(From); 951 auto OpenedTo = FS->openFileForRead(To); 952 return !OpenedFrom.getError() && !OpenedTo.getError() && 953 (*OpenedFrom)->status()->getUniqueID() == 954 (*OpenedTo)->status()->getUniqueID(); 955 } 956 957 TEST_F(InMemoryFileSystemTest, IsEmpty) { 958 auto Stat = FS.status("/a"); 959 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString(); 960 Stat = FS.status("/"); 961 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString(); 962 } 963 964 TEST_F(InMemoryFileSystemTest, WindowsPath) { 965 FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); 966 auto Stat = FS.status("c:"); 967 #if !defined(_WIN32) 968 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 969 #endif 970 Stat = FS.status("c:/windows/system128/foo.cpp"); 971 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 972 FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); 973 Stat = FS.status("d:/windows/foo.cpp"); 974 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 975 } 976 977 TEST_F(InMemoryFileSystemTest, OverlayFile) { 978 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 979 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 980 auto Stat = FS.status("/"); 981 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 982 Stat = FS.status("/."); 983 ASSERT_FALSE(Stat); 984 Stat = NormalizedFS.status("/."); 985 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 986 Stat = FS.status("/a"); 987 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 988 ASSERT_EQ("/a", Stat->getName()); 989 } 990 991 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) { 992 auto Buf = MemoryBuffer::getMemBuffer("a"); 993 FS.addFileNoOwn("/a", 0, *Buf); 994 auto Stat = FS.status("/a"); 995 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 996 ASSERT_EQ("/a", Stat->getName()); 997 } 998 999 TEST_F(InMemoryFileSystemTest, OpenFileForRead) { 1000 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 1001 FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); 1002 FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); 1003 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 1004 NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); 1005 NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); 1006 auto File = FS.openFileForRead("/a"); 1007 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 1008 File = FS.openFileForRead("/a"); // Open again. 1009 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 1010 File = NormalizedFS.openFileForRead("/././a"); // Open again. 1011 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 1012 File = FS.openFileForRead("/"); 1013 ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString(); 1014 File = FS.openFileForRead("/b"); 1015 ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString(); 1016 File = FS.openFileForRead("./c"); 1017 ASSERT_FALSE(File); 1018 File = FS.openFileForRead("e/../d"); 1019 ASSERT_FALSE(File); 1020 File = NormalizedFS.openFileForRead("./c"); 1021 ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer()); 1022 File = NormalizedFS.openFileForRead("e/../d"); 1023 ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer()); 1024 } 1025 1026 TEST_F(InMemoryFileSystemTest, DuplicatedFile) { 1027 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); 1028 ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a"))); 1029 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); 1030 ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b"))); 1031 } 1032 1033 TEST_F(InMemoryFileSystemTest, DirectoryIteration) { 1034 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("")); 1035 FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer("")); 1036 1037 std::error_code EC; 1038 vfs::directory_iterator I = FS.dir_begin("/", EC); 1039 ASSERT_FALSE(EC); 1040 ASSERT_EQ("/a", I->path()); 1041 I.increment(EC); 1042 ASSERT_FALSE(EC); 1043 ASSERT_EQ("/b", I->path()); 1044 I.increment(EC); 1045 ASSERT_FALSE(EC); 1046 ASSERT_EQ(vfs::directory_iterator(), I); 1047 1048 I = FS.dir_begin("/b", EC); 1049 ASSERT_FALSE(EC); 1050 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix 1051 // path for the sake of the comparison. 1052 ASSERT_EQ("/b/c", getPosixPath(std::string(I->path()))); 1053 I.increment(EC); 1054 ASSERT_FALSE(EC); 1055 ASSERT_EQ(vfs::directory_iterator(), I); 1056 } 1057 1058 TEST_F(InMemoryFileSystemTest, WorkingDirectory) { 1059 FS.setCurrentWorkingDirectory("/b"); 1060 FS.addFile("c", 0, MemoryBuffer::getMemBuffer("")); 1061 1062 auto Stat = FS.status("/b/c"); 1063 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1064 ASSERT_EQ("/b/c", Stat->getName()); 1065 ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory()); 1066 1067 Stat = FS.status("c"); 1068 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1069 1070 NormalizedFS.setCurrentWorkingDirectory("/b/c"); 1071 NormalizedFS.setCurrentWorkingDirectory("."); 1072 ASSERT_EQ("/b/c", 1073 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get())); 1074 NormalizedFS.setCurrentWorkingDirectory(".."); 1075 ASSERT_EQ("/b", 1076 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get())); 1077 } 1078 1079 TEST_F(InMemoryFileSystemTest, IsLocal) { 1080 FS.setCurrentWorkingDirectory("/b"); 1081 FS.addFile("c", 0, MemoryBuffer::getMemBuffer("")); 1082 1083 std::error_code EC; 1084 bool IsLocal = true; 1085 EC = FS.isLocal("c", IsLocal); 1086 ASSERT_FALSE(EC); 1087 ASSERT_FALSE(IsLocal); 1088 } 1089 1090 #if !defined(_WIN32) 1091 TEST_F(InMemoryFileSystemTest, GetRealPath) { 1092 SmallString<16> Path; 1093 EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted); 1094 1095 auto GetRealPath = [this](StringRef P) { 1096 SmallString<16> Output; 1097 auto EC = FS.getRealPath(P, Output); 1098 EXPECT_FALSE(EC); 1099 return std::string(Output); 1100 }; 1101 1102 FS.setCurrentWorkingDirectory("a"); 1103 EXPECT_EQ(GetRealPath("b"), "a/b"); 1104 EXPECT_EQ(GetRealPath("../b"), "b"); 1105 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c"); 1106 1107 FS.setCurrentWorkingDirectory("/a"); 1108 EXPECT_EQ(GetRealPath("b"), "/a/b"); 1109 EXPECT_EQ(GetRealPath("../b"), "/b"); 1110 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c"); 1111 } 1112 #endif // _WIN32 1113 1114 TEST_F(InMemoryFileSystemTest, AddFileWithUser) { 1115 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE); 1116 auto Stat = FS.status("/a"); 1117 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1118 ASSERT_TRUE(Stat->isDirectory()); 1119 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 1120 Stat = FS.status("/a/b"); 1121 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1122 ASSERT_TRUE(Stat->isDirectory()); 1123 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 1124 Stat = FS.status("/a/b/c"); 1125 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1126 ASSERT_TRUE(Stat->isRegularFile()); 1127 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 1128 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 1129 } 1130 1131 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) { 1132 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt, 1133 0xDABBAD00); 1134 auto Stat = FS.status("/a"); 1135 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1136 ASSERT_TRUE(Stat->isDirectory()); 1137 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 1138 Stat = FS.status("/a/b"); 1139 ASSERT_TRUE(Stat->isDirectory()); 1140 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1141 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 1142 Stat = FS.status("/a/b/c"); 1143 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1144 ASSERT_TRUE(Stat->isRegularFile()); 1145 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 1146 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 1147 } 1148 1149 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) { 1150 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt, 1151 std::nullopt, sys::fs::file_type::socket_file); 1152 auto Stat = FS.status("/a"); 1153 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1154 ASSERT_TRUE(Stat->isDirectory()); 1155 Stat = FS.status("/a/b"); 1156 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1157 ASSERT_TRUE(Stat->isDirectory()); 1158 Stat = FS.status("/a/b/c"); 1159 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1160 ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType()); 1161 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 1162 } 1163 1164 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) { 1165 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt, 1166 std::nullopt, std::nullopt, 1167 sys::fs::perms::owner_read | sys::fs::perms::owner_write); 1168 auto Stat = FS.status("/a"); 1169 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1170 ASSERT_TRUE(Stat->isDirectory()); 1171 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write | 1172 sys::fs::perms::owner_exe, 1173 Stat->getPermissions()); 1174 Stat = FS.status("/a/b"); 1175 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1176 ASSERT_TRUE(Stat->isDirectory()); 1177 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write | 1178 sys::fs::perms::owner_exe, 1179 Stat->getPermissions()); 1180 Stat = FS.status("/a/b/c"); 1181 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1182 ASSERT_TRUE(Stat->isRegularFile()); 1183 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write, 1184 Stat->getPermissions()); 1185 } 1186 1187 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) { 1188 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/std::nullopt, 1189 /*Group=*/std::nullopt, sys::fs::file_type::directory_file); 1190 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), 1191 /*User=*/std::nullopt, 1192 /*Group=*/std::nullopt, sys::fs::file_type::regular_file); 1193 auto Stat = FS.status("/a"); 1194 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1195 ASSERT_TRUE(Stat->isDirectory()); 1196 Stat = FS.status("/a/b"); 1197 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1198 ASSERT_TRUE(Stat->isRegularFile()); 1199 } 1200 1201 // Test that the name returned by status() is in the same form as the path that 1202 // was requested (to match the behavior of RealFileSystem). 1203 TEST_F(InMemoryFileSystemTest, StatusName) { 1204 NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 1205 /*User=*/std::nullopt, 1206 /*Group=*/std::nullopt, 1207 sys::fs::file_type::regular_file); 1208 NormalizedFS.setCurrentWorkingDirectory("/a/b"); 1209 1210 // Access using InMemoryFileSystem::status. 1211 auto Stat = NormalizedFS.status("../b/c"); 1212 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" 1213 << NormalizedFS.toString(); 1214 ASSERT_TRUE(Stat->isRegularFile()); 1215 ASSERT_EQ("../b/c", Stat->getName()); 1216 1217 // Access using InMemoryFileAdaptor::status. 1218 auto File = NormalizedFS.openFileForRead("../b/c"); 1219 ASSERT_FALSE(File.getError()) << File.getError() << "\n" 1220 << NormalizedFS.toString(); 1221 Stat = (*File)->status(); 1222 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" 1223 << NormalizedFS.toString(); 1224 ASSERT_TRUE(Stat->isRegularFile()); 1225 ASSERT_EQ("../b/c", Stat->getName()); 1226 1227 // Access using a directory iterator. 1228 std::error_code EC; 1229 llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC); 1230 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix 1231 // path for the sake of the comparison. 1232 ASSERT_EQ("../b/c", getPosixPath(std::string(It->path()))); 1233 } 1234 1235 TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) { 1236 StringRef FromLink = "/path/to/FROM/link"; 1237 StringRef Target = "/path/to/TO/file"; 1238 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1239 EXPECT_TRUE(FS.addHardLink(FromLink, Target)); 1240 EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target)); 1241 EXPECT_EQ(FS.status(FromLink)->getSize(), FS.status(Target)->getSize()); 1242 EXPECT_EQ(FS.getBufferForFile(FromLink)->get()->getBuffer(), 1243 FS.getBufferForFile(Target)->get()->getBuffer()); 1244 } 1245 1246 TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) { 1247 StringRef Link0 = "/path/to/0/link"; 1248 StringRef Link1 = "/path/to/1/link"; 1249 StringRef Link2 = "/path/to/2/link"; 1250 StringRef Target = "/path/to/target"; 1251 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file")); 1252 EXPECT_TRUE(FS.addHardLink(Link2, Target)); 1253 EXPECT_TRUE(FS.addHardLink(Link1, Link2)); 1254 EXPECT_TRUE(FS.addHardLink(Link0, Link1)); 1255 EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target)); 1256 EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target)); 1257 EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target)); 1258 } 1259 1260 TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) { 1261 EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target")); 1262 } 1263 1264 TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) { 1265 StringRef Link = "/path/to/link"; 1266 StringRef Target = "/path/to/target"; 1267 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1268 FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link")); 1269 EXPECT_FALSE(FS.addHardLink(Link, Target)); 1270 } 1271 1272 TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) { 1273 StringRef Link = "/path/to/link"; 1274 StringRef Target = "/path/to/target"; 1275 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1276 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1277 EXPECT_FALSE(FS.addHardLink(Link, Target)); 1278 } 1279 1280 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) { 1281 StringRef Link = "/path/to/link"; 1282 StringRef Target = "/path/to/target"; 1283 StringRef Content = "content of target"; 1284 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1285 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1286 EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content))); 1287 } 1288 1289 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) { 1290 StringRef Link = "/path/to/link"; 1291 StringRef Target = "/path/to/target"; 1292 StringRef Content = "content of target"; 1293 StringRef LinkContent = "different content of link"; 1294 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1295 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1296 EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent))); 1297 } 1298 1299 TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) { 1300 StringRef Dir = "path/to/dummy/dir"; 1301 StringRef Link = "/path/to/link"; 1302 StringRef File = "path/to/dummy/dir/target"; 1303 StringRef Content = "content of target"; 1304 EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content))); 1305 EXPECT_FALSE(FS.addHardLink(Link, Dir)); 1306 } 1307 1308 TEST_F(InMemoryFileSystemTest, AddHardLinkToASymlink) { 1309 EXPECT_TRUE(FS.addFile("/file", 0, MemoryBuffer::getMemBuffer("content"))); 1310 EXPECT_TRUE(FS.addSymbolicLink("/symlink", "/file", 0)); 1311 EXPECT_TRUE(FS.addHardLink("/hardlink", "/symlink")); 1312 EXPECT_EQ((*FS.getBufferForFile("/hardlink"))->getBuffer(), "content"); 1313 } 1314 1315 TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) { 1316 StringRef Dir = "path/to/dummy/dir"; 1317 StringRef Target = "path/to/dummy/dir/target"; 1318 StringRef Content = "content of target"; 1319 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1320 EXPECT_FALSE(FS.addHardLink(Dir, Target)); 1321 } 1322 1323 TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) { 1324 StringRef CommonContent = "content string"; 1325 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent)); 1326 FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent)); 1327 EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b")); 1328 } 1329 1330 TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) { 1331 std::error_code EC; 1332 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string")); 1333 EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b")); 1334 auto I = vfs::recursive_directory_iterator(FS, "/", EC); 1335 ASSERT_FALSE(EC); 1336 std::vector<std::string> Nodes; 1337 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 1338 I.increment(EC)) { 1339 Nodes.push_back(getPosixPath(std::string(I->path()))); 1340 } 1341 EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d")); 1342 } 1343 1344 TEST_F(InMemoryFileSystemTest, UniqueID) { 1345 ASSERT_TRUE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text"))); 1346 ASSERT_TRUE(FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer("text"))); 1347 ASSERT_TRUE(FS.addHardLink("/e/f", "/a/b")); 1348 1349 EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/a/b")->getUniqueID()); 1350 EXPECT_NE(FS.status("/a/b")->getUniqueID(), FS.status("/c/d")->getUniqueID()); 1351 EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/e/f")->getUniqueID()); 1352 EXPECT_EQ(FS.status("/a")->getUniqueID(), FS.status("/a")->getUniqueID()); 1353 EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/c")->getUniqueID()); 1354 EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/e")->getUniqueID()); 1355 1356 // Recreating the "same" FS yields the same UniqueIDs. 1357 // Note: FS2 should match FS with respect to path normalization. 1358 vfs::InMemoryFileSystem FS2(/*UseNormalizedPath=*/false); 1359 ASSERT_TRUE(FS2.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text"))); 1360 EXPECT_EQ(FS.status("/a/b")->getUniqueID(), 1361 FS2.status("/a/b")->getUniqueID()); 1362 EXPECT_EQ(FS.status("/a")->getUniqueID(), FS2.status("/a")->getUniqueID()); 1363 } 1364 1365 TEST_F(InMemoryFileSystemTest, AddSymlinkToAFile) { 1366 EXPECT_TRUE( 1367 FS.addFile("/some/file", 0, MemoryBuffer::getMemBuffer("contents"))); 1368 EXPECT_TRUE(FS.addSymbolicLink("/other/file/link", "/some/file", 0)); 1369 ErrorOr<vfs::Status> Stat = FS.status("/some/file"); 1370 EXPECT_TRUE(Stat->isRegularFile()); 1371 } 1372 1373 TEST_F(InMemoryFileSystemTest, AddSymlinkToADirectory) { 1374 EXPECT_TRUE(FS.addSymbolicLink("/link", "/target", 0)); 1375 EXPECT_TRUE( 1376 FS.addFile("/target/foo.h", 0, MemoryBuffer::getMemBuffer("foo"))); 1377 ErrorOr<vfs::Status> Stat = FS.status("/link/foo.h"); 1378 EXPECT_TRUE(Stat); 1379 EXPECT_EQ((*Stat).getName(), "/link/foo.h"); 1380 EXPECT_TRUE(Stat->isRegularFile()); 1381 } 1382 1383 TEST_F(InMemoryFileSystemTest, AddSymlinkToASymlink) { 1384 EXPECT_TRUE(FS.addSymbolicLink("/first", "/second", 0)); 1385 EXPECT_TRUE(FS.addSymbolicLink("/second", "/third", 0)); 1386 EXPECT_TRUE(FS.addFile("/third", 0, MemoryBuffer::getMemBuffer(""))); 1387 ErrorOr<vfs::Status> Stat = FS.status("/first"); 1388 EXPECT_TRUE(Stat); 1389 EXPECT_EQ((*Stat).getName(), "/first"); 1390 // Follow-through symlinks by default. This matches RealFileSystem's 1391 // semantics. 1392 EXPECT_TRUE(Stat->isRegularFile()); 1393 Stat = FS.status("/second"); 1394 EXPECT_TRUE(Stat); 1395 EXPECT_EQ((*Stat).getName(), "/second"); 1396 EXPECT_TRUE(Stat->isRegularFile()); 1397 Stat = FS.status("/third"); 1398 EXPECT_TRUE(Stat); 1399 EXPECT_EQ((*Stat).getName(), "/third"); 1400 EXPECT_TRUE(Stat->isRegularFile()); 1401 } 1402 1403 TEST_F(InMemoryFileSystemTest, AddRecursiveSymlink) { 1404 EXPECT_TRUE(FS.addSymbolicLink("/link-a", "/link-b", 0)); 1405 EXPECT_TRUE(FS.addSymbolicLink("/link-b", "/link-a", 0)); 1406 ErrorOr<vfs::Status> Stat = FS.status("/link-a/foo"); 1407 EXPECT_FALSE(Stat); 1408 EXPECT_EQ(Stat.getError(), errc::no_such_file_or_directory); 1409 } 1410 1411 TEST_F(InMemoryFileSystemTest, DirectoryIteratorWithSymlinkToAFile) { 1412 std::error_code EC; 1413 1414 EXPECT_TRUE(FS.addFile("/file", 0, MemoryBuffer::getMemBuffer(""))); 1415 EXPECT_TRUE(FS.addSymbolicLink("/symlink", "/file", 0)); 1416 1417 vfs::directory_iterator I = FS.dir_begin("/", EC), E; 1418 ASSERT_FALSE(EC); 1419 1420 std::vector<std::string> Nodes; 1421 for (; !EC && I != E; I.increment(EC)) 1422 Nodes.push_back(getPosixPath(std::string(I->path()))); 1423 1424 EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/file", "/file")); 1425 } 1426 1427 TEST_F(InMemoryFileSystemTest, RecursiveDirectoryIteratorWithSymlinkToADir) { 1428 std::error_code EC; 1429 1430 EXPECT_TRUE(FS.addFile("/dir/file", 0, MemoryBuffer::getMemBuffer(""))); 1431 EXPECT_TRUE(FS.addSymbolicLink("/dir_symlink", "/dir", 0)); 1432 1433 vfs::recursive_directory_iterator I(FS, "/", EC), E; 1434 ASSERT_FALSE(EC); 1435 1436 std::vector<std::string> Nodes; 1437 for (; !EC && I != E; I.increment(EC)) 1438 Nodes.push_back(getPosixPath(std::string(I->path()))); 1439 1440 EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/dir", "/dir/file", "/dir", 1441 "/dir/file")); 1442 } 1443 1444 // NOTE: in the tests below, we use '//root/' as our root directory, since it is 1445 // a legal *absolute* path on Windows as well as *nix. 1446 class VFSFromYAMLTest : public ::testing::Test { 1447 public: 1448 int NumDiagnostics; 1449 1450 void SetUp() override { NumDiagnostics = 0; } 1451 1452 static void CountingDiagHandler(const SMDiagnostic &, void *Context) { 1453 VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context); 1454 ++Test->NumDiagnostics; 1455 } 1456 1457 std::unique_ptr<vfs::FileSystem> 1458 getFromYAMLRawString(StringRef Content, 1459 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS, 1460 StringRef YAMLFilePath = "") { 1461 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content); 1462 return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, YAMLFilePath, 1463 this, ExternalFS); 1464 } 1465 1466 std::unique_ptr<vfs::FileSystem> getFromYAMLString( 1467 StringRef Content, 1468 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem(), 1469 StringRef YAMLFilePath = "") { 1470 std::string VersionPlusContent("{\n 'version':0,\n"); 1471 VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos); 1472 return getFromYAMLRawString(VersionPlusContent, ExternalFS, YAMLFilePath); 1473 } 1474 1475 // This is intended as a "XFAIL" for windows hosts. 1476 bool supportsSameDirMultipleYAMLEntries() { 1477 Triple Host(Triple::normalize(sys::getProcessTriple())); 1478 return !Host.isOSWindows(); 1479 } 1480 }; 1481 1482 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) { 1483 IntrusiveRefCntPtr<vfs::FileSystem> FS; 1484 FS = getFromYAMLString(""); 1485 EXPECT_EQ(nullptr, FS.get()); 1486 FS = getFromYAMLString("[]"); 1487 EXPECT_EQ(nullptr, FS.get()); 1488 FS = getFromYAMLString("'string'"); 1489 EXPECT_EQ(nullptr, FS.get()); 1490 EXPECT_EQ(3, NumDiagnostics); 1491 } 1492 1493 TEST_F(VFSFromYAMLTest, MappedFiles) { 1494 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1495 Lower->addDirectory("//root/foo/bar"); 1496 Lower->addRegularFile("//root/foo/bar/a"); 1497 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1498 "{ 'roots': [\n" 1499 "{\n" 1500 " 'type': 'directory',\n" 1501 " 'name': '//root/',\n" 1502 " 'contents': [ {\n" 1503 " 'type': 'file',\n" 1504 " 'name': 'file1',\n" 1505 " 'external-contents': '//root/foo/bar/a'\n" 1506 " },\n" 1507 " {\n" 1508 " 'type': 'file',\n" 1509 " 'name': 'file2',\n" 1510 " 'external-contents': '//root/foo/b'\n" 1511 " },\n" 1512 " {\n" 1513 " 'type': 'directory-remap',\n" 1514 " 'name': 'mappeddir',\n" 1515 " 'external-contents': '//root/foo/bar'\n" 1516 " },\n" 1517 " {\n" 1518 " 'type': 'directory-remap',\n" 1519 " 'name': 'mappeddir2',\n" 1520 " 'use-external-name': false,\n" 1521 " 'external-contents': '//root/foo/bar'\n" 1522 " }\n" 1523 " ]\n" 1524 "}\n" 1525 "]\n" 1526 "}", 1527 Lower); 1528 ASSERT_NE(FS.get(), nullptr); 1529 1530 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1531 new vfs::OverlayFileSystem(Lower)); 1532 O->pushOverlay(FS); 1533 1534 // file 1535 ErrorOr<vfs::Status> S = O->status("//root/file1"); 1536 ASSERT_FALSE(S.getError()); 1537 EXPECT_EQ("//root/foo/bar/a", S->getName()); 1538 EXPECT_TRUE(S->IsVFSMapped); 1539 EXPECT_TRUE(S->ExposesExternalVFSPath); 1540 1541 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); 1542 EXPECT_EQ("//root/foo/bar/a", SLower->getName()); 1543 EXPECT_TRUE(S->equivalent(*SLower)); 1544 EXPECT_FALSE(SLower->IsVFSMapped); 1545 EXPECT_FALSE(SLower->ExposesExternalVFSPath); 1546 1547 // file after opening 1548 auto OpenedF = O->openFileForRead("//root/file1"); 1549 ASSERT_FALSE(OpenedF.getError()); 1550 auto OpenedS = (*OpenedF)->status(); 1551 ASSERT_FALSE(OpenedS.getError()); 1552 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 1553 EXPECT_TRUE(OpenedS->IsVFSMapped); 1554 EXPECT_TRUE(OpenedS->ExposesExternalVFSPath); 1555 1556 // directory 1557 S = O->status("//root/"); 1558 ASSERT_FALSE(S.getError()); 1559 EXPECT_TRUE(S->isDirectory()); 1560 EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID 1561 1562 // remapped directory 1563 S = O->status("//root/mappeddir"); 1564 ASSERT_FALSE(S.getError()); 1565 EXPECT_TRUE(S->isDirectory()); 1566 EXPECT_TRUE(S->IsVFSMapped); 1567 EXPECT_TRUE(S->ExposesExternalVFSPath); 1568 EXPECT_TRUE(S->equivalent(*O->status("//root/foo/bar"))); 1569 1570 SLower = O->status("//root/foo/bar"); 1571 EXPECT_EQ("//root/foo/bar", SLower->getName()); 1572 EXPECT_TRUE(S->equivalent(*SLower)); 1573 EXPECT_FALSE(SLower->IsVFSMapped); 1574 EXPECT_FALSE(SLower->ExposesExternalVFSPath); 1575 1576 // file in remapped directory 1577 S = O->status("//root/mappeddir/a"); 1578 ASSERT_FALSE(S.getError()); 1579 EXPECT_FALSE(S->isDirectory()); 1580 EXPECT_TRUE(S->IsVFSMapped); 1581 EXPECT_TRUE(S->ExposesExternalVFSPath); 1582 EXPECT_EQ("//root/foo/bar/a", S->getName()); 1583 1584 // file in remapped directory, with use-external-name=false 1585 S = O->status("//root/mappeddir2/a"); 1586 ASSERT_FALSE(S.getError()); 1587 EXPECT_FALSE(S->isDirectory()); 1588 EXPECT_TRUE(S->IsVFSMapped); 1589 EXPECT_FALSE(S->ExposesExternalVFSPath); 1590 EXPECT_EQ("//root/mappeddir2/a", S->getName()); 1591 1592 // file contents in remapped directory 1593 OpenedF = O->openFileForRead("//root/mappeddir/a"); 1594 ASSERT_FALSE(OpenedF.getError()); 1595 OpenedS = (*OpenedF)->status(); 1596 ASSERT_FALSE(OpenedS.getError()); 1597 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 1598 EXPECT_TRUE(OpenedS->IsVFSMapped); 1599 EXPECT_TRUE(OpenedS->ExposesExternalVFSPath); 1600 1601 // file contents in remapped directory, with use-external-name=false 1602 OpenedF = O->openFileForRead("//root/mappeddir2/a"); 1603 ASSERT_FALSE(OpenedF.getError()); 1604 OpenedS = (*OpenedF)->status(); 1605 ASSERT_FALSE(OpenedS.getError()); 1606 EXPECT_EQ("//root/mappeddir2/a", OpenedS->getName()); 1607 EXPECT_TRUE(OpenedS->IsVFSMapped); 1608 EXPECT_FALSE(OpenedS->ExposesExternalVFSPath); 1609 1610 // broken mapping 1611 EXPECT_EQ(O->status("//root/file2").getError(), 1612 llvm::errc::no_such_file_or_directory); 1613 EXPECT_EQ(0, NumDiagnostics); 1614 } 1615 1616 TEST_F(VFSFromYAMLTest, MappedRoot) { 1617 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1618 Lower->addDirectory("//root/foo/bar"); 1619 Lower->addRegularFile("//root/foo/bar/a"); 1620 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1621 getFromYAMLString("{ 'roots': [\n" 1622 "{\n" 1623 " 'type': 'directory-remap',\n" 1624 " 'name': '//mappedroot/',\n" 1625 " 'external-contents': '//root/foo/bar'\n" 1626 "}\n" 1627 "]\n" 1628 "}", 1629 Lower); 1630 ASSERT_NE(FS.get(), nullptr); 1631 1632 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1633 new vfs::OverlayFileSystem(Lower)); 1634 O->pushOverlay(FS); 1635 1636 // file 1637 ErrorOr<vfs::Status> S = O->status("//mappedroot/a"); 1638 ASSERT_FALSE(S.getError()); 1639 EXPECT_EQ("//root/foo/bar/a", S->getName()); 1640 EXPECT_TRUE(S->IsVFSMapped); 1641 EXPECT_TRUE(S->ExposesExternalVFSPath); 1642 1643 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); 1644 EXPECT_EQ("//root/foo/bar/a", SLower->getName()); 1645 EXPECT_TRUE(S->equivalent(*SLower)); 1646 EXPECT_FALSE(SLower->IsVFSMapped); 1647 EXPECT_FALSE(SLower->ExposesExternalVFSPath); 1648 1649 // file after opening 1650 auto OpenedF = O->openFileForRead("//mappedroot/a"); 1651 ASSERT_FALSE(OpenedF.getError()); 1652 auto OpenedS = (*OpenedF)->status(); 1653 ASSERT_FALSE(OpenedS.getError()); 1654 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 1655 EXPECT_TRUE(OpenedS->IsVFSMapped); 1656 EXPECT_TRUE(OpenedS->ExposesExternalVFSPath); 1657 1658 EXPECT_EQ(0, NumDiagnostics); 1659 } 1660 1661 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlay) { 1662 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1663 Lower->addDirectory("//root/foo"); 1664 Lower->addRegularFile("//root/foo/a"); 1665 Lower->addDirectory("//root/bar"); 1666 Lower->addRegularFile("//root/bar/b"); 1667 Lower->addRegularFile("//root/bar/c"); 1668 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1669 getFromYAMLString("{ 'roots': [\n" 1670 "{\n" 1671 " 'type': 'directory',\n" 1672 " 'name': '//root/',\n" 1673 " 'contents': [ {\n" 1674 " 'type': 'directory-remap',\n" 1675 " 'name': 'bar',\n" 1676 " 'external-contents': '//root/foo'\n" 1677 " }\n" 1678 " ]\n" 1679 "}]}", 1680 Lower); 1681 ASSERT_NE(FS.get(), nullptr); 1682 1683 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1684 new vfs::OverlayFileSystem(Lower)); 1685 O->pushOverlay(FS); 1686 1687 ErrorOr<vfs::Status> S = O->status("//root/foo"); 1688 ASSERT_FALSE(S.getError()); 1689 1690 ErrorOr<vfs::Status> SS = O->status("//root/bar"); 1691 ASSERT_FALSE(SS.getError()); 1692 EXPECT_TRUE(S->equivalent(*SS)); 1693 1694 std::error_code EC; 1695 checkContents(O->dir_begin("//root/bar", EC), 1696 {"//root/foo/a", "//root/bar/b", "//root/bar/c"}); 1697 1698 Lower->addRegularFile("//root/foo/b"); 1699 checkContents(O->dir_begin("//root/bar", EC), 1700 {"//root/foo/a", "//root/foo/b", "//root/bar/c"}); 1701 1702 EXPECT_EQ(0, NumDiagnostics); 1703 } 1704 1705 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoExternalNames) { 1706 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1707 Lower->addDirectory("//root/foo"); 1708 Lower->addRegularFile("//root/foo/a"); 1709 Lower->addDirectory("//root/bar"); 1710 Lower->addRegularFile("//root/bar/b"); 1711 Lower->addRegularFile("//root/bar/c"); 1712 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1713 getFromYAMLString("{ 'use-external-names': false,\n" 1714 " 'roots': [\n" 1715 "{\n" 1716 " 'type': 'directory',\n" 1717 " 'name': '//root/',\n" 1718 " 'contents': [ {\n" 1719 " 'type': 'directory-remap',\n" 1720 " 'name': 'bar',\n" 1721 " 'external-contents': '//root/foo'\n" 1722 " }\n" 1723 " ]\n" 1724 "}]}", 1725 Lower); 1726 ASSERT_NE(FS.get(), nullptr); 1727 1728 ErrorOr<vfs::Status> S = FS->status("//root/foo"); 1729 ASSERT_FALSE(S.getError()); 1730 1731 ErrorOr<vfs::Status> SS = FS->status("//root/bar"); 1732 ASSERT_FALSE(SS.getError()); 1733 EXPECT_TRUE(S->equivalent(*SS)); 1734 1735 std::error_code EC; 1736 checkContents(FS->dir_begin("//root/bar", EC), 1737 {"//root/bar/a", "//root/bar/b", "//root/bar/c"}); 1738 1739 Lower->addRegularFile("//root/foo/b"); 1740 checkContents(FS->dir_begin("//root/bar", EC), 1741 {"//root/bar/a", "//root/bar/b", "//root/bar/c"}); 1742 1743 EXPECT_EQ(0, NumDiagnostics); 1744 } 1745 1746 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoFallthrough) { 1747 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1748 Lower->addDirectory("//root/foo"); 1749 Lower->addRegularFile("//root/foo/a"); 1750 Lower->addDirectory("//root/bar"); 1751 Lower->addRegularFile("//root/bar/b"); 1752 Lower->addRegularFile("//root/bar/c"); 1753 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1754 getFromYAMLString("{ 'fallthrough': false,\n" 1755 " 'roots': [\n" 1756 "{\n" 1757 " 'type': 'directory',\n" 1758 " 'name': '//root/',\n" 1759 " 'contents': [ {\n" 1760 " 'type': 'directory-remap',\n" 1761 " 'name': 'bar',\n" 1762 " 'external-contents': '//root/foo'\n" 1763 " }\n" 1764 " ]\n" 1765 "}]}", 1766 Lower); 1767 ASSERT_NE(FS.get(), nullptr); 1768 1769 ErrorOr<vfs::Status> S = Lower->status("//root/foo"); 1770 ASSERT_FALSE(S.getError()); 1771 1772 ErrorOr<vfs::Status> SS = FS->status("//root/bar"); 1773 ASSERT_FALSE(SS.getError()); 1774 EXPECT_TRUE(S->equivalent(*SS)); 1775 1776 std::error_code EC; 1777 checkContents(FS->dir_begin("//root/bar", EC), {"//root/foo/a"}); 1778 1779 Lower->addRegularFile("//root/foo/b"); 1780 checkContents(FS->dir_begin("//root/bar", EC), 1781 {"//root/foo/a", "//root/foo/b"}); 1782 1783 EXPECT_EQ(0, NumDiagnostics); 1784 } 1785 1786 TEST_F(VFSFromYAMLTest, ReturnsRequestedPathVFSMiss) { 1787 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS( 1788 new vfs::InMemoryFileSystem); 1789 BaseFS->addFile("//root/foo/a", 0, 1790 MemoryBuffer::getMemBuffer("contents of a")); 1791 ASSERT_FALSE(BaseFS->setCurrentWorkingDirectory("//root/foo")); 1792 auto RemappedFS = vfs::RedirectingFileSystem::create( 1793 {}, /*UseExternalNames=*/false, *BaseFS); 1794 1795 auto OpenedF = RemappedFS->openFileForRead("a"); 1796 ASSERT_FALSE(OpenedF.getError()); 1797 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName(); 1798 ASSERT_FALSE(Name.getError()); 1799 EXPECT_EQ("a", Name.get()); 1800 1801 auto OpenedS = (*OpenedF)->status(); 1802 ASSERT_FALSE(OpenedS.getError()); 1803 EXPECT_EQ("a", OpenedS->getName()); 1804 EXPECT_FALSE(OpenedS->IsVFSMapped); 1805 EXPECT_FALSE(OpenedS->ExposesExternalVFSPath); 1806 1807 auto DirectS = RemappedFS->status("a"); 1808 ASSERT_FALSE(DirectS.getError()); 1809 EXPECT_EQ("a", DirectS->getName()); 1810 EXPECT_FALSE(DirectS->IsVFSMapped); 1811 EXPECT_FALSE(DirectS->ExposesExternalVFSPath); 1812 1813 EXPECT_EQ(0, NumDiagnostics); 1814 } 1815 1816 TEST_F(VFSFromYAMLTest, ReturnsExternalPathVFSHit) { 1817 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS( 1818 new vfs::InMemoryFileSystem); 1819 BaseFS->addFile("//root/foo/realname", 0, 1820 MemoryBuffer::getMemBuffer("contents of a")); 1821 auto FS = 1822 getFromYAMLString("{ 'use-external-names': true,\n" 1823 " 'roots': [\n" 1824 "{\n" 1825 " 'type': 'directory',\n" 1826 " 'name': '//root/foo',\n" 1827 " 'contents': [ {\n" 1828 " 'type': 'file',\n" 1829 " 'name': 'vfsname',\n" 1830 " 'external-contents': 'realname'\n" 1831 " }\n" 1832 " ]\n" 1833 "}]}", 1834 BaseFS); 1835 ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo")); 1836 1837 auto OpenedF = FS->openFileForRead("vfsname"); 1838 ASSERT_FALSE(OpenedF.getError()); 1839 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName(); 1840 ASSERT_FALSE(Name.getError()); 1841 EXPECT_EQ("realname", Name.get()); 1842 1843 auto OpenedS = (*OpenedF)->status(); 1844 ASSERT_FALSE(OpenedS.getError()); 1845 EXPECT_EQ("realname", OpenedS->getName()); 1846 EXPECT_TRUE(OpenedS->IsVFSMapped); 1847 EXPECT_TRUE(OpenedS->ExposesExternalVFSPath); 1848 1849 auto DirectS = FS->status("vfsname"); 1850 ASSERT_FALSE(DirectS.getError()); 1851 EXPECT_EQ("realname", DirectS->getName()); 1852 EXPECT_TRUE(DirectS->IsVFSMapped); 1853 EXPECT_TRUE(DirectS->ExposesExternalVFSPath); 1854 1855 EXPECT_EQ(0, NumDiagnostics); 1856 } 1857 1858 TEST_F(VFSFromYAMLTest, RootRelativeTest) { 1859 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1860 Lower->addDirectory("//root/foo/bar"); 1861 Lower->addRegularFile("//root/foo/bar/a"); 1862 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1863 getFromYAMLString("{\n" 1864 " 'case-sensitive': false,\n" 1865 " 'root-relative': 'overlay-dir',\n" 1866 " 'roots': [\n" 1867 " { 'name': 'b', 'type': 'file',\n" 1868 " 'external-contents': '//root/foo/bar/a'\n" 1869 " }\n" 1870 " ]\n" 1871 "}", 1872 Lower, "//root/foo/bar/overlay"); 1873 1874 ASSERT_NE(FS.get(), nullptr); 1875 ErrorOr<vfs::Status> S = FS->status("//root/foo/bar/b"); 1876 ASSERT_FALSE(S.getError()); 1877 EXPECT_EQ("//root/foo/bar/a", S->getName()); 1878 1879 // On Windows, with overlay-relative set to true, the relative 1880 // path in external-contents field will be prepend by OverlayDir 1881 // with native path separator, regardless of the actual path separator 1882 // used in YAMLFilePath field. 1883 #ifndef _WIN32 1884 FS = getFromYAMLString("{\n" 1885 " 'case-sensitive': false,\n" 1886 " 'overlay-relative': true,\n" 1887 " 'root-relative': 'overlay-dir',\n" 1888 " 'roots': [\n" 1889 " { 'name': 'b', 'type': 'file',\n" 1890 " 'external-contents': 'a'\n" 1891 " }\n" 1892 " ]\n" 1893 "}", 1894 Lower, "//root/foo/bar/overlay"); 1895 ASSERT_NE(FS.get(), nullptr); 1896 S = FS->status("//root/foo/bar/b"); 1897 ASSERT_FALSE(S.getError()); 1898 EXPECT_EQ("//root/foo/bar/a", S->getName()); 1899 #else 1900 IntrusiveRefCntPtr<DummyFileSystem> LowerWindows(new DummyFileSystem()); 1901 LowerWindows->addDirectory("\\\\root\\foo\\bar"); 1902 LowerWindows->addRegularFile("\\\\root\\foo\\bar\\a"); 1903 FS = getFromYAMLString("{\n" 1904 " 'case-sensitive': false,\n" 1905 " 'overlay-relative': true,\n" 1906 " 'root-relative': 'overlay-dir',\n" 1907 " 'roots': [\n" 1908 " { 'name': 'b', 'type': 'file',\n" 1909 " 'external-contents': 'a'\n" 1910 " }\n" 1911 " ]\n" 1912 "}", 1913 LowerWindows, "\\\\root\\foo\\bar\\overlay"); 1914 ASSERT_NE(FS.get(), nullptr); 1915 S = FS->status("\\\\root\\foo\\bar\\b"); 1916 ASSERT_FALSE(S.getError()); 1917 EXPECT_EQ("\\\\root\\foo\\bar\\a", S->getName()); 1918 #endif 1919 } 1920 1921 TEST_F(VFSFromYAMLTest, ReturnsInternalPathVFSHit) { 1922 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS( 1923 new vfs::InMemoryFileSystem); 1924 BaseFS->addFile("//root/foo/realname", 0, 1925 MemoryBuffer::getMemBuffer("contents of a")); 1926 auto FS = 1927 getFromYAMLString("{ 'use-external-names': false,\n" 1928 " 'roots': [\n" 1929 "{\n" 1930 " 'type': 'directory',\n" 1931 " 'name': '//root/foo',\n" 1932 " 'contents': [ {\n" 1933 " 'type': 'file',\n" 1934 " 'name': 'vfsname',\n" 1935 " 'external-contents': 'realname'\n" 1936 " }\n" 1937 " ]\n" 1938 "}]}", 1939 BaseFS); 1940 ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo")); 1941 1942 auto OpenedF = FS->openFileForRead("vfsname"); 1943 ASSERT_FALSE(OpenedF.getError()); 1944 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName(); 1945 ASSERT_FALSE(Name.getError()); 1946 EXPECT_EQ("vfsname", Name.get()); 1947 1948 auto OpenedS = (*OpenedF)->status(); 1949 ASSERT_FALSE(OpenedS.getError()); 1950 EXPECT_EQ("vfsname", OpenedS->getName()); 1951 EXPECT_TRUE(OpenedS->IsVFSMapped); 1952 EXPECT_FALSE(OpenedS->ExposesExternalVFSPath); 1953 1954 auto DirectS = FS->status("vfsname"); 1955 ASSERT_FALSE(DirectS.getError()); 1956 EXPECT_EQ("vfsname", DirectS->getName()); 1957 EXPECT_TRUE(DirectS->IsVFSMapped); 1958 EXPECT_FALSE(DirectS->ExposesExternalVFSPath); 1959 1960 EXPECT_EQ(0, NumDiagnostics); 1961 } 1962 1963 TEST_F(VFSFromYAMLTest, CaseInsensitive) { 1964 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1965 Lower->addRegularFile("//root/foo/bar/a"); 1966 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1967 "{ 'case-sensitive': 'false',\n" 1968 " 'roots': [\n" 1969 "{\n" 1970 " 'type': 'directory',\n" 1971 " 'name': '//root/',\n" 1972 " 'contents': [ {\n" 1973 " 'type': 'file',\n" 1974 " 'name': 'XX',\n" 1975 " 'external-contents': '//root/foo/bar/a'\n" 1976 " }\n" 1977 " ]\n" 1978 "}]}", 1979 Lower); 1980 ASSERT_NE(FS.get(), nullptr); 1981 1982 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1983 new vfs::OverlayFileSystem(Lower)); 1984 O->pushOverlay(FS); 1985 1986 ErrorOr<vfs::Status> S = O->status("//root/XX"); 1987 ASSERT_FALSE(S.getError()); 1988 1989 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 1990 ASSERT_FALSE(SS.getError()); 1991 EXPECT_TRUE(S->equivalent(*SS)); 1992 SS = O->status("//root/xX"); 1993 EXPECT_TRUE(S->equivalent(*SS)); 1994 SS = O->status("//root/Xx"); 1995 EXPECT_TRUE(S->equivalent(*SS)); 1996 EXPECT_EQ(0, NumDiagnostics); 1997 } 1998 1999 TEST_F(VFSFromYAMLTest, CaseSensitive) { 2000 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2001 Lower->addRegularFile("//root/foo/bar/a"); 2002 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2003 "{ 'case-sensitive': 'true',\n" 2004 " 'roots': [\n" 2005 "{\n" 2006 " 'type': 'directory',\n" 2007 " 'name': '//root/',\n" 2008 " 'contents': [ {\n" 2009 " 'type': 'file',\n" 2010 " 'name': 'XX',\n" 2011 " 'external-contents': '//root/foo/bar/a'\n" 2012 " }\n" 2013 " ]\n" 2014 "}]}", 2015 Lower); 2016 ASSERT_NE(FS.get(), nullptr); 2017 2018 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 2019 new vfs::OverlayFileSystem(Lower)); 2020 O->pushOverlay(FS); 2021 2022 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 2023 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 2024 SS = O->status("//root/xX"); 2025 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 2026 SS = O->status("//root/Xx"); 2027 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 2028 EXPECT_EQ(0, NumDiagnostics); 2029 } 2030 2031 TEST_F(VFSFromYAMLTest, IllegalVFSFile) { 2032 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2033 2034 // invalid YAML at top-level 2035 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower); 2036 EXPECT_EQ(nullptr, FS.get()); 2037 // invalid YAML in roots 2038 FS = getFromYAMLString("{ 'roots':[}", Lower); 2039 // invalid YAML in directory 2040 FS = getFromYAMLString( 2041 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}", 2042 Lower); 2043 EXPECT_EQ(nullptr, FS.get()); 2044 2045 // invalid configuration 2046 FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower); 2047 EXPECT_EQ(nullptr, FS.get()); 2048 FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower); 2049 EXPECT_EQ(nullptr, FS.get()); 2050 2051 // invalid roots 2052 FS = getFromYAMLString("{ 'roots':'' }", Lower); 2053 EXPECT_EQ(nullptr, FS.get()); 2054 FS = getFromYAMLString("{ 'roots':{} }", Lower); 2055 EXPECT_EQ(nullptr, FS.get()); 2056 2057 // invalid entries 2058 FS = getFromYAMLString( 2059 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower); 2060 EXPECT_EQ(nullptr, FS.get()); 2061 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], " 2062 "'external-contents': 'other' }", 2063 Lower); 2064 EXPECT_EQ(nullptr, FS.get()); 2065 FS = getFromYAMLString( 2066 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }", 2067 Lower); 2068 EXPECT_EQ(nullptr, FS.get()); 2069 FS = getFromYAMLString( 2070 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }", 2071 Lower); 2072 EXPECT_EQ(nullptr, FS.get()); 2073 FS = getFromYAMLString( 2074 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }", 2075 Lower); 2076 EXPECT_EQ(nullptr, FS.get()); 2077 FS = getFromYAMLString( 2078 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }", 2079 Lower); 2080 EXPECT_EQ(nullptr, FS.get()); 2081 FS = getFromYAMLString( 2082 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }", 2083 Lower); 2084 EXPECT_EQ(nullptr, FS.get()); 2085 2086 // missing mandatory fields 2087 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower); 2088 EXPECT_EQ(nullptr, FS.get()); 2089 FS = getFromYAMLString( 2090 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower); 2091 EXPECT_EQ(nullptr, FS.get()); 2092 FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower); 2093 EXPECT_EQ(nullptr, FS.get()); 2094 2095 // duplicate keys 2096 FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower); 2097 EXPECT_EQ(nullptr, FS.get()); 2098 FS = getFromYAMLString( 2099 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }", 2100 Lower); 2101 EXPECT_EQ(nullptr, FS.get()); 2102 FS = 2103 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', " 2104 "'external-contents':'blah' } ] }", 2105 Lower); 2106 EXPECT_EQ(nullptr, FS.get()); 2107 2108 // missing version 2109 FS = getFromYAMLRawString("{ 'roots':[] }", Lower); 2110 EXPECT_EQ(nullptr, FS.get()); 2111 2112 // bad version number 2113 FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower); 2114 EXPECT_EQ(nullptr, FS.get()); 2115 FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower); 2116 EXPECT_EQ(nullptr, FS.get()); 2117 FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower); 2118 EXPECT_EQ(nullptr, FS.get()); 2119 2120 // both 'external-contents' and 'contents' specified 2121 Lower->addDirectory("//root/external/dir"); 2122 FS = getFromYAMLString( 2123 "{ 'roots':[ \n" 2124 "{ 'type': 'directory', 'name': '//root/A', 'contents': [],\n" 2125 " 'external-contents': '//root/external/dir'}]}", 2126 Lower); 2127 EXPECT_EQ(nullptr, FS.get()); 2128 2129 // 'directory-remap' with 'contents' 2130 FS = getFromYAMLString( 2131 "{ 'roots':[ \n" 2132 "{ 'type': 'directory-remap', 'name': '//root/A', 'contents': [] }]}", 2133 Lower); 2134 EXPECT_EQ(nullptr, FS.get()); 2135 2136 // invalid redirect kind 2137 FS = getFromYAMLString("{ 'redirecting-with': 'none', 'roots': [{\n" 2138 " 'type': 'directory-remap',\n" 2139 " 'name': '//root/A',\n" 2140 " 'external-contents': '//root/B' }]}", 2141 Lower); 2142 EXPECT_EQ(nullptr, FS.get()); 2143 2144 // redirect and fallthrough passed 2145 FS = getFromYAMLString("{ 'redirecting-with': 'fallthrough',\n" 2146 " 'fallthrough': true,\n" 2147 " 'roots': [{\n" 2148 " 'type': 'directory-remap',\n" 2149 " 'name': '//root/A',\n" 2150 " 'external-contents': '//root/B' }]}", 2151 Lower); 2152 EXPECT_EQ(nullptr, FS.get()); 2153 2154 EXPECT_EQ(28, NumDiagnostics); 2155 } 2156 2157 TEST_F(VFSFromYAMLTest, UseExternalName) { 2158 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2159 Lower->addRegularFile("//root/external/file"); 2160 2161 IntrusiveRefCntPtr<vfs::FileSystem> FS = 2162 getFromYAMLString("{ 'roots': [\n" 2163 " { 'type': 'file', 'name': '//root/A',\n" 2164 " 'external-contents': '//root/external/file'\n" 2165 " },\n" 2166 " { 'type': 'file', 'name': '//root/B',\n" 2167 " 'use-external-name': true,\n" 2168 " 'external-contents': '//root/external/file'\n" 2169 " },\n" 2170 " { 'type': 'file', 'name': '//root/C',\n" 2171 " 'use-external-name': false,\n" 2172 " 'external-contents': '//root/external/file'\n" 2173 " }\n" 2174 "] }", 2175 Lower); 2176 ASSERT_NE(nullptr, FS.get()); 2177 2178 // default true 2179 EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName()); 2180 // explicit 2181 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 2182 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 2183 2184 // global configuration 2185 FS = getFromYAMLString("{ 'use-external-names': false,\n" 2186 " 'roots': [\n" 2187 " { 'type': 'file', 'name': '//root/A',\n" 2188 " 'external-contents': '//root/external/file'\n" 2189 " },\n" 2190 " { 'type': 'file', 'name': '//root/B',\n" 2191 " 'use-external-name': true,\n" 2192 " 'external-contents': '//root/external/file'\n" 2193 " },\n" 2194 " { 'type': 'file', 'name': '//root/C',\n" 2195 " 'use-external-name': false,\n" 2196 " 'external-contents': '//root/external/file'\n" 2197 " }\n" 2198 "] }", 2199 Lower); 2200 ASSERT_NE(nullptr, FS.get()); 2201 2202 // default 2203 EXPECT_EQ("//root/A", FS->status("//root/A")->getName()); 2204 // explicit 2205 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 2206 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 2207 } 2208 2209 TEST_F(VFSFromYAMLTest, MultiComponentPath) { 2210 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2211 Lower->addRegularFile("//root/other"); 2212 2213 // file in roots 2214 IntrusiveRefCntPtr<vfs::FileSystem> FS = 2215 getFromYAMLString("{ 'roots': [\n" 2216 " { 'type': 'file', 'name': '//root/path/to/file',\n" 2217 " 'external-contents': '//root/other' }]\n" 2218 "}", 2219 Lower); 2220 ASSERT_NE(nullptr, FS.get()); 2221 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 2222 EXPECT_FALSE(FS->status("//root/path/to").getError()); 2223 EXPECT_FALSE(FS->status("//root/path").getError()); 2224 EXPECT_FALSE(FS->status("//root/").getError()); 2225 2226 // at the start 2227 FS = getFromYAMLString( 2228 "{ 'roots': [\n" 2229 " { 'type': 'directory', 'name': '//root/path/to',\n" 2230 " 'contents': [ { 'type': 'file', 'name': 'file',\n" 2231 " 'external-contents': '//root/other' }]}]\n" 2232 "}", 2233 Lower); 2234 ASSERT_NE(nullptr, FS.get()); 2235 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 2236 EXPECT_FALSE(FS->status("//root/path/to").getError()); 2237 EXPECT_FALSE(FS->status("//root/path").getError()); 2238 EXPECT_FALSE(FS->status("//root/").getError()); 2239 2240 // at the end 2241 FS = getFromYAMLString( 2242 "{ 'roots': [\n" 2243 " { 'type': 'directory', 'name': '//root/',\n" 2244 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n" 2245 " 'external-contents': '//root/other' }]}]\n" 2246 "}", 2247 Lower); 2248 ASSERT_NE(nullptr, FS.get()); 2249 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 2250 EXPECT_FALSE(FS->status("//root/path/to").getError()); 2251 EXPECT_FALSE(FS->status("//root/path").getError()); 2252 EXPECT_FALSE(FS->status("//root/").getError()); 2253 } 2254 2255 TEST_F(VFSFromYAMLTest, TrailingSlashes) { 2256 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2257 Lower->addRegularFile("//root/other"); 2258 2259 // file in roots 2260 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2261 "{ 'roots': [\n" 2262 " { 'type': 'directory', 'name': '//root/path/to////',\n" 2263 " 'contents': [ { 'type': 'file', 'name': 'file',\n" 2264 " 'external-contents': '//root/other' }]}]\n" 2265 "}", 2266 Lower); 2267 ASSERT_NE(nullptr, FS.get()); 2268 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 2269 EXPECT_FALSE(FS->status("//root/path/to").getError()); 2270 EXPECT_FALSE(FS->status("//root/path").getError()); 2271 EXPECT_FALSE(FS->status("//root/").getError()); 2272 } 2273 2274 TEST_F(VFSFromYAMLTest, DirectoryIteration) { 2275 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2276 Lower->addDirectory("//root/"); 2277 Lower->addDirectory("//root/foo"); 2278 Lower->addDirectory("//root/foo/bar"); 2279 Lower->addRegularFile("//root/foo/bar/a"); 2280 Lower->addRegularFile("//root/foo/bar/b"); 2281 Lower->addRegularFile("//root/file3"); 2282 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2283 "{ 'use-external-names': false,\n" 2284 " 'roots': [\n" 2285 "{\n" 2286 " 'type': 'directory',\n" 2287 " 'name': '//root/',\n" 2288 " 'contents': [ {\n" 2289 " 'type': 'file',\n" 2290 " 'name': 'file1',\n" 2291 " 'external-contents': '//root/foo/bar/a'\n" 2292 " },\n" 2293 " {\n" 2294 " 'type': 'file',\n" 2295 " 'name': 'file2',\n" 2296 " 'external-contents': '//root/foo/bar/b'\n" 2297 " }\n" 2298 " ]\n" 2299 "}\n" 2300 "]\n" 2301 "}", 2302 Lower); 2303 ASSERT_NE(FS.get(), nullptr); 2304 2305 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 2306 new vfs::OverlayFileSystem(Lower)); 2307 O->pushOverlay(FS); 2308 2309 std::error_code EC; 2310 checkContents(O->dir_begin("//root/", EC), 2311 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"}); 2312 2313 checkContents(O->dir_begin("//root/foo/bar", EC), 2314 {"//root/foo/bar/a", "//root/foo/bar/b"}); 2315 } 2316 2317 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) { 2318 // https://llvm.org/bugs/show_bug.cgi?id=27725 2319 if (!supportsSameDirMultipleYAMLEntries()) 2320 GTEST_SKIP(); 2321 2322 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2323 Lower->addDirectory("//root/zab"); 2324 Lower->addDirectory("//root/baz"); 2325 Lower->addRegularFile("//root/zab/a"); 2326 Lower->addRegularFile("//root/zab/b"); 2327 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2328 "{ 'use-external-names': false,\n" 2329 " 'roots': [\n" 2330 "{\n" 2331 " 'type': 'directory',\n" 2332 " 'name': '//root/baz/',\n" 2333 " 'contents': [ {\n" 2334 " 'type': 'file',\n" 2335 " 'name': 'x',\n" 2336 " 'external-contents': '//root/zab/a'\n" 2337 " }\n" 2338 " ]\n" 2339 "},\n" 2340 "{\n" 2341 " 'type': 'directory',\n" 2342 " 'name': '//root/baz/',\n" 2343 " 'contents': [ {\n" 2344 " 'type': 'file',\n" 2345 " 'name': 'y',\n" 2346 " 'external-contents': '//root/zab/b'\n" 2347 " }\n" 2348 " ]\n" 2349 "}\n" 2350 "]\n" 2351 "}", 2352 Lower); 2353 ASSERT_NE(FS.get(), nullptr); 2354 2355 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 2356 new vfs::OverlayFileSystem(Lower)); 2357 O->pushOverlay(FS); 2358 2359 std::error_code EC; 2360 2361 checkContents(O->dir_begin("//root/baz/", EC), 2362 {"//root/baz/x", "//root/baz/y"}); 2363 } 2364 2365 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) { 2366 2367 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2368 Lower->addDirectory("//root/a"); 2369 Lower->addDirectory("//root/a/b"); 2370 Lower->addDirectory("//root/a/b/c"); 2371 Lower->addRegularFile("//root/a/b/c/file"); 2372 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2373 "{ 'use-external-names': false,\n" 2374 " 'roots': [\n" 2375 "{\n" 2376 " 'type': 'directory',\n" 2377 " 'name': '//root/a/b/c/',\n" 2378 " 'contents': [ {\n" 2379 " 'type': 'file',\n" 2380 " 'name': 'file',\n" 2381 " 'external-contents': '//root/a/b/c/file'\n" 2382 " }\n" 2383 " ]\n" 2384 "},\n" 2385 "]\n" 2386 "}", 2387 Lower); 2388 ASSERT_NE(FS.get(), nullptr); 2389 2390 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 2391 new vfs::OverlayFileSystem(Lower)); 2392 O->pushOverlay(FS); 2393 2394 std::error_code EC; 2395 2396 // Test recursive_directory_iterator level() 2397 vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator( 2398 *O, "//root", EC), 2399 E; 2400 ASSERT_FALSE(EC); 2401 for (int l = 0; I != E; I.increment(EC), ++l) { 2402 ASSERT_FALSE(EC); 2403 EXPECT_EQ(I.level(), l); 2404 } 2405 EXPECT_EQ(I, E); 2406 } 2407 2408 TEST_F(VFSFromYAMLTest, RelativePaths) { 2409 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2410 std::error_code EC; 2411 SmallString<128> CWD; 2412 EC = llvm::sys::fs::current_path(CWD); 2413 ASSERT_FALSE(EC); 2414 2415 // Filename at root level without a parent directory. 2416 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2417 "{ 'roots': [\n" 2418 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n" 2419 " 'external-contents': '//root/external/file'\n" 2420 " }\n" 2421 "] }", 2422 Lower); 2423 ASSERT_TRUE(FS.get() != nullptr); 2424 SmallString<128> ExpectedPathNotInDir("file-not-in-directory.h"); 2425 llvm::sys::fs::make_absolute(ExpectedPathNotInDir); 2426 checkContents(FS->dir_begin(CWD, EC), {ExpectedPathNotInDir}); 2427 2428 // Relative file path. 2429 FS = getFromYAMLString("{ 'roots': [\n" 2430 " { 'type': 'file', 'name': 'relative/path.h',\n" 2431 " 'external-contents': '//root/external/file'\n" 2432 " }\n" 2433 "] }", 2434 Lower); 2435 ASSERT_TRUE(FS.get() != nullptr); 2436 SmallString<128> Parent("relative"); 2437 llvm::sys::fs::make_absolute(Parent); 2438 auto I = FS->dir_begin(Parent, EC); 2439 ASSERT_FALSE(EC); 2440 // Convert to POSIX path for comparison of windows paths 2441 ASSERT_EQ("relative/path.h", 2442 getPosixPath(std::string(I->path().substr(CWD.size() + 1)))); 2443 2444 // Relative directory path. 2445 FS = getFromYAMLString( 2446 "{ 'roots': [\n" 2447 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n" 2448 " 'contents': []\n" 2449 " }\n" 2450 "] }", 2451 Lower); 2452 ASSERT_TRUE(FS.get() != nullptr); 2453 SmallString<128> Root("relative/directory"); 2454 llvm::sys::fs::make_absolute(Root); 2455 I = FS->dir_begin(Root, EC); 2456 ASSERT_FALSE(EC); 2457 ASSERT_EQ("path.h", std::string(I->path().substr(Root.size() + 1))); 2458 2459 EXPECT_EQ(0, NumDiagnostics); 2460 } 2461 2462 TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) { 2463 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2464 Lower->addDirectory("//root/"); 2465 Lower->addRegularFile("//root/a"); 2466 Lower->addRegularFile("//root/b"); 2467 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2468 "{ 'use-external-names': false,\n" 2469 " 'fallthrough': false,\n" 2470 " 'roots': [\n" 2471 "{\n" 2472 " 'type': 'directory',\n" 2473 " 'name': '//root/',\n" 2474 " 'contents': [ {\n" 2475 " 'type': 'file',\n" 2476 " 'name': 'c',\n" 2477 " 'external-contents': '//root/a'\n" 2478 " }\n" 2479 " ]\n" 2480 "}\n" 2481 "]\n" 2482 "}", 2483 Lower); 2484 ASSERT_NE(FS.get(), nullptr); 2485 2486 std::error_code EC; 2487 checkContents(FS->dir_begin("//root/", EC), 2488 {"//root/c"}); 2489 } 2490 2491 TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) { 2492 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2493 Lower->addDirectory("//root/"); 2494 Lower->addRegularFile("//root/a"); 2495 Lower->addRegularFile("//root/b"); 2496 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2497 "{ 'use-external-names': false,\n" 2498 " 'roots': [\n" 2499 "{\n" 2500 " 'type': 'directory',\n" 2501 " 'name': '//root/',\n" 2502 " 'contents': [ {\n" 2503 " 'type': 'file',\n" 2504 " 'name': 'a',\n" 2505 " 'external-contents': '//root/a'\n" 2506 " }\n" 2507 " ]\n" 2508 "}\n" 2509 "]\n" 2510 "}", 2511 Lower); 2512 ASSERT_NE(FS.get(), nullptr); 2513 2514 std::error_code EC; 2515 checkContents(FS->dir_begin("//root/", EC), 2516 {"//root/a", "//root/b"}); 2517 } 2518 2519 TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) { 2520 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2521 Lower->addDirectory("//root/"); 2522 Lower->addDirectory("//root/foo"); 2523 Lower->addRegularFile("//root/foo/a"); 2524 Lower->addRegularFile("//root/foo/b"); 2525 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2526 "{ 'use-external-names': false,\n" 2527 " 'roots': [\n" 2528 "{\n" 2529 " 'type': 'directory',\n" 2530 " 'name': '//root/',\n" 2531 " 'contents': [ {\n" 2532 " 'type': 'file',\n" 2533 " 'name': 'bar/a',\n" 2534 " 'external-contents': '//root/foo/a'\n" 2535 " }\n" 2536 " ]\n" 2537 "}\n" 2538 "]\n" 2539 "}", 2540 Lower); 2541 ASSERT_NE(FS.get(), nullptr); 2542 2543 std::error_code EC; 2544 checkContents(FS->dir_begin("//root/foo", EC), 2545 {"//root/foo/a", "//root/foo/b"}); 2546 } 2547 2548 TEST_F(VFSFromYAMLTest, GetRealPath) { 2549 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2550 Lower->addDirectory("//dir/"); 2551 Lower->addRegularFile("/foo"); 2552 Lower->addSymlink("/link"); 2553 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2554 "{ 'use-external-names': false,\n" 2555 " 'roots': [\n" 2556 "{\n" 2557 " 'type': 'directory',\n" 2558 " 'name': '//root/',\n" 2559 " 'contents': [ {\n" 2560 " 'type': 'file',\n" 2561 " 'name': 'bar',\n" 2562 " 'external-contents': '/link'\n" 2563 " }\n" 2564 " ]\n" 2565 "},\n" 2566 "{\n" 2567 " 'type': 'directory',\n" 2568 " 'name': '//dir/',\n" 2569 " 'contents': []\n" 2570 "}\n" 2571 "]\n" 2572 "}", 2573 Lower); 2574 ASSERT_NE(FS.get(), nullptr); 2575 2576 // Regular file present in underlying file system. 2577 SmallString<16> RealPath; 2578 EXPECT_FALSE(FS->getRealPath("/foo", RealPath)); 2579 EXPECT_EQ(RealPath.str(), "/foo"); 2580 2581 // File present in YAML pointing to symlink in underlying file system. 2582 EXPECT_FALSE(FS->getRealPath("//root/bar", RealPath)); 2583 EXPECT_EQ(RealPath.str(), "/symlink"); 2584 2585 // Directories should fall back to the underlying file system is possible. 2586 EXPECT_FALSE(FS->getRealPath("//dir/", RealPath)); 2587 EXPECT_EQ(RealPath.str(), "//dir/"); 2588 2589 // Try a non-existing file. 2590 EXPECT_EQ(FS->getRealPath("/non_existing", RealPath), 2591 errc::no_such_file_or_directory); 2592 } 2593 2594 TEST_F(VFSFromYAMLTest, WorkingDirectory) { 2595 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2596 Lower->addDirectory("//root/"); 2597 Lower->addDirectory("//root/foo"); 2598 Lower->addRegularFile("//root/foo/a"); 2599 Lower->addRegularFile("//root/foo/b"); 2600 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2601 "{ 'use-external-names': false,\n" 2602 " 'roots': [\n" 2603 "{\n" 2604 " 'type': 'directory',\n" 2605 " 'name': '//root/bar',\n" 2606 " 'contents': [ {\n" 2607 " 'type': 'file',\n" 2608 " 'name': 'a',\n" 2609 " 'external-contents': '//root/foo/a'\n" 2610 " }\n" 2611 " ]\n" 2612 "}\n" 2613 "]\n" 2614 "}", 2615 Lower); 2616 ASSERT_NE(FS.get(), nullptr); 2617 std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar"); 2618 ASSERT_FALSE(EC); 2619 2620 llvm::ErrorOr<std::string> WorkingDir = FS->getCurrentWorkingDirectory(); 2621 ASSERT_TRUE(WorkingDir); 2622 EXPECT_EQ(*WorkingDir, "//root/bar"); 2623 2624 llvm::ErrorOr<vfs::Status> Status = FS->status("./a"); 2625 ASSERT_FALSE(Status.getError()); 2626 EXPECT_TRUE(Status->isStatusKnown()); 2627 EXPECT_FALSE(Status->isDirectory()); 2628 EXPECT_TRUE(Status->isRegularFile()); 2629 EXPECT_FALSE(Status->isSymlink()); 2630 EXPECT_FALSE(Status->isOther()); 2631 EXPECT_TRUE(Status->exists()); 2632 2633 EC = FS->setCurrentWorkingDirectory("bogus"); 2634 ASSERT_TRUE(EC); 2635 WorkingDir = FS->getCurrentWorkingDirectory(); 2636 ASSERT_TRUE(WorkingDir); 2637 EXPECT_EQ(*WorkingDir, "//root/bar"); 2638 2639 EC = FS->setCurrentWorkingDirectory("//root/"); 2640 ASSERT_FALSE(EC); 2641 WorkingDir = FS->getCurrentWorkingDirectory(); 2642 ASSERT_TRUE(WorkingDir); 2643 EXPECT_EQ(*WorkingDir, "//root/"); 2644 2645 EC = FS->setCurrentWorkingDirectory("bar"); 2646 ASSERT_FALSE(EC); 2647 WorkingDir = FS->getCurrentWorkingDirectory(); 2648 ASSERT_TRUE(WorkingDir); 2649 EXPECT_EQ(*WorkingDir, "//root/bar"); 2650 } 2651 2652 TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthrough) { 2653 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2654 Lower->addDirectory("//root/"); 2655 Lower->addDirectory("//root/foo"); 2656 Lower->addRegularFile("//root/foo/a"); 2657 Lower->addRegularFile("//root/foo/b"); 2658 Lower->addRegularFile("//root/c"); 2659 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2660 "{ 'use-external-names': false,\n" 2661 " 'roots': [\n" 2662 "{\n" 2663 " 'type': 'directory',\n" 2664 " 'name': '//root/bar',\n" 2665 " 'contents': [ {\n" 2666 " 'type': 'file',\n" 2667 " 'name': 'a',\n" 2668 " 'external-contents': '//root/foo/a'\n" 2669 " }\n" 2670 " ]\n" 2671 "},\n" 2672 "{\n" 2673 " 'type': 'directory',\n" 2674 " 'name': '//root/bar/baz',\n" 2675 " 'contents': [ {\n" 2676 " 'type': 'file',\n" 2677 " 'name': 'a',\n" 2678 " 'external-contents': '//root/foo/a'\n" 2679 " }\n" 2680 " ]\n" 2681 "}\n" 2682 "]\n" 2683 "}", 2684 Lower); 2685 ASSERT_NE(FS.get(), nullptr); 2686 std::error_code EC = FS->setCurrentWorkingDirectory("//root/"); 2687 ASSERT_FALSE(EC); 2688 ASSERT_NE(FS.get(), nullptr); 2689 2690 llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a"); 2691 ASSERT_FALSE(Status.getError()); 2692 EXPECT_TRUE(Status->exists()); 2693 2694 Status = FS->status("foo/a"); 2695 ASSERT_FALSE(Status.getError()); 2696 EXPECT_TRUE(Status->exists()); 2697 2698 EC = FS->setCurrentWorkingDirectory("//root/bar"); 2699 ASSERT_FALSE(EC); 2700 2701 Status = FS->status("./a"); 2702 ASSERT_FALSE(Status.getError()); 2703 EXPECT_TRUE(Status->exists()); 2704 2705 Status = FS->status("./b"); 2706 ASSERT_TRUE(Status.getError()); 2707 2708 Status = FS->status("./c"); 2709 ASSERT_TRUE(Status.getError()); 2710 2711 EC = FS->setCurrentWorkingDirectory("//root/"); 2712 ASSERT_FALSE(EC); 2713 2714 Status = FS->status("c"); 2715 ASSERT_FALSE(Status.getError()); 2716 EXPECT_TRUE(Status->exists()); 2717 2718 Status = FS->status("./bar/baz/a"); 2719 ASSERT_FALSE(Status.getError()); 2720 EXPECT_TRUE(Status->exists()); 2721 2722 EC = FS->setCurrentWorkingDirectory("//root/bar"); 2723 ASSERT_FALSE(EC); 2724 2725 Status = FS->status("./baz/a"); 2726 ASSERT_FALSE(Status.getError()); 2727 EXPECT_TRUE(Status->exists()); 2728 2729 Status = FS->status("../bar/baz/a"); 2730 ASSERT_FALSE(Status.getError()); 2731 EXPECT_TRUE(Status->exists()); 2732 } 2733 2734 TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthroughInvalid) { 2735 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2736 Lower->addDirectory("//root/"); 2737 Lower->addDirectory("//root/foo"); 2738 Lower->addRegularFile("//root/foo/a"); 2739 Lower->addRegularFile("//root/foo/b"); 2740 Lower->addRegularFile("//root/c"); 2741 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2742 "{ 'use-external-names': false,\n" 2743 " 'roots': [\n" 2744 "{\n" 2745 " 'type': 'directory',\n" 2746 " 'name': '//root/bar',\n" 2747 " 'contents': [ {\n" 2748 " 'type': 'file',\n" 2749 " 'name': 'a',\n" 2750 " 'external-contents': '//root/foo/a'\n" 2751 " }\n" 2752 " ]\n" 2753 "}\n" 2754 "]\n" 2755 "}", 2756 Lower); 2757 ASSERT_NE(FS.get(), nullptr); 2758 std::error_code EC = FS->setCurrentWorkingDirectory("//root/"); 2759 ASSERT_FALSE(EC); 2760 ASSERT_NE(FS.get(), nullptr); 2761 2762 llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a"); 2763 ASSERT_FALSE(Status.getError()); 2764 EXPECT_TRUE(Status->exists()); 2765 2766 Status = FS->status("foo/a"); 2767 ASSERT_FALSE(Status.getError()); 2768 EXPECT_TRUE(Status->exists()); 2769 } 2770 2771 TEST_F(VFSFromYAMLTest, VirtualWorkingDirectory) { 2772 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2773 Lower->addDirectory("//root/"); 2774 Lower->addDirectory("//root/foo"); 2775 Lower->addRegularFile("//root/foo/a"); 2776 Lower->addRegularFile("//root/foo/b"); 2777 Lower->addRegularFile("//root/c"); 2778 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2779 "{ 'use-external-names': false,\n" 2780 " 'roots': [\n" 2781 "{\n" 2782 " 'type': 'directory',\n" 2783 " 'name': '//root/bar',\n" 2784 " 'contents': [ {\n" 2785 " 'type': 'file',\n" 2786 " 'name': 'a',\n" 2787 " 'external-contents': '//root/foo/a'\n" 2788 " }\n" 2789 " ]\n" 2790 "}\n" 2791 "]\n" 2792 "}", 2793 Lower); 2794 ASSERT_NE(FS.get(), nullptr); 2795 std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar"); 2796 ASSERT_FALSE(EC); 2797 ASSERT_NE(FS.get(), nullptr); 2798 2799 llvm::ErrorOr<vfs::Status> Status = FS->status("a"); 2800 ASSERT_FALSE(Status.getError()); 2801 EXPECT_TRUE(Status->exists()); 2802 } 2803 2804 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest) { 2805 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2806 TempDir _a(TestDirectory.path("a")); 2807 TempFile _ab(TestDirectory.path("a, b")); 2808 TempDir _c(TestDirectory.path("c")); 2809 TempFile _cd(TestDirectory.path("c/d")); 2810 TempDir _e(TestDirectory.path("e")); 2811 TempDir _ef(TestDirectory.path("e/f")); 2812 TempFile _g(TestDirectory.path("g")); 2813 TempDir _h(TestDirectory.path("h")); 2814 2815 vfs::YAMLVFSWriter VFSWriter; 2816 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2817 VFSWriter.addFileMapping(_ab.path(), "//root/a/b"); 2818 VFSWriter.addFileMapping(_cd.path(), "//root/c/d"); 2819 VFSWriter.addDirectoryMapping(_e.path(), "//root/e"); 2820 VFSWriter.addDirectoryMapping(_ef.path(), "//root/e/f"); 2821 VFSWriter.addFileMapping(_g.path(), "//root/g"); 2822 VFSWriter.addDirectoryMapping(_h.path(), "//root/h"); 2823 2824 std::string Buffer; 2825 raw_string_ostream OS(Buffer); 2826 VFSWriter.write(OS); 2827 OS.flush(); 2828 2829 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2830 Lower->addDirectory("//root/"); 2831 Lower->addDirectory("//root/a"); 2832 Lower->addRegularFile("//root/a/b"); 2833 Lower->addDirectory("//root/b"); 2834 Lower->addDirectory("//root/c"); 2835 Lower->addRegularFile("//root/c/d"); 2836 Lower->addDirectory("//root/e"); 2837 Lower->addDirectory("//root/e/f"); 2838 Lower->addRegularFile("//root/g"); 2839 Lower->addDirectory("//root/h"); 2840 2841 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2842 ASSERT_NE(FS.get(), nullptr); 2843 2844 EXPECT_TRUE(FS->exists(_a.path())); 2845 EXPECT_TRUE(FS->exists(_ab.path())); 2846 EXPECT_TRUE(FS->exists(_c.path())); 2847 EXPECT_TRUE(FS->exists(_cd.path())); 2848 EXPECT_TRUE(FS->exists(_e.path())); 2849 EXPECT_TRUE(FS->exists(_ef.path())); 2850 EXPECT_TRUE(FS->exists(_g.path())); 2851 EXPECT_TRUE(FS->exists(_h.path())); 2852 } 2853 2854 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest2) { 2855 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2856 TempDir _a(TestDirectory.path("a")); 2857 TempFile _ab(TestDirectory.path("a/b")); 2858 TempDir _ac(TestDirectory.path("a/c")); 2859 TempFile _acd(TestDirectory.path("a/c/d")); 2860 TempFile _ace(TestDirectory.path("a/c/e")); 2861 TempFile _acf(TestDirectory.path("a/c/f")); 2862 TempDir _ag(TestDirectory.path("a/g")); 2863 TempFile _agh(TestDirectory.path("a/g/h")); 2864 2865 vfs::YAMLVFSWriter VFSWriter; 2866 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2867 VFSWriter.addFileMapping(_ab.path(), "//root/a/b"); 2868 VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c"); 2869 VFSWriter.addFileMapping(_acd.path(), "//root/a/c/d"); 2870 VFSWriter.addFileMapping(_ace.path(), "//root/a/c/e"); 2871 VFSWriter.addFileMapping(_acf.path(), "//root/a/c/f"); 2872 VFSWriter.addDirectoryMapping(_ag.path(), "//root/a/g"); 2873 VFSWriter.addFileMapping(_agh.path(), "//root/a/g/h"); 2874 2875 std::string Buffer; 2876 raw_string_ostream OS(Buffer); 2877 VFSWriter.write(OS); 2878 OS.flush(); 2879 2880 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2881 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2882 EXPECT_NE(FS.get(), nullptr); 2883 } 2884 2885 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest3) { 2886 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2887 TempDir _a(TestDirectory.path("a")); 2888 TempFile _ab(TestDirectory.path("a/b")); 2889 TempDir _ac(TestDirectory.path("a/c")); 2890 TempDir _acd(TestDirectory.path("a/c/d")); 2891 TempDir _acde(TestDirectory.path("a/c/d/e")); 2892 TempFile _acdef(TestDirectory.path("a/c/d/e/f")); 2893 TempFile _acdeg(TestDirectory.path("a/c/d/e/g")); 2894 TempDir _ah(TestDirectory.path("a/h")); 2895 TempFile _ahi(TestDirectory.path("a/h/i")); 2896 2897 vfs::YAMLVFSWriter VFSWriter; 2898 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2899 VFSWriter.addFileMapping(_ab.path(), "//root/a/b"); 2900 VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c"); 2901 VFSWriter.addDirectoryMapping(_acd.path(), "//root/a/c/d"); 2902 VFSWriter.addDirectoryMapping(_acde.path(), "//root/a/c/d/e"); 2903 VFSWriter.addFileMapping(_acdef.path(), "//root/a/c/d/e/f"); 2904 VFSWriter.addFileMapping(_acdeg.path(), "//root/a/c/d/e/g"); 2905 VFSWriter.addDirectoryMapping(_ahi.path(), "//root/a/h"); 2906 VFSWriter.addFileMapping(_ahi.path(), "//root/a/h/i"); 2907 2908 std::string Buffer; 2909 raw_string_ostream OS(Buffer); 2910 VFSWriter.write(OS); 2911 OS.flush(); 2912 2913 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2914 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2915 EXPECT_NE(FS.get(), nullptr); 2916 } 2917 2918 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTestHandleDirs) { 2919 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2920 TempDir _a(TestDirectory.path("a")); 2921 TempDir _b(TestDirectory.path("b")); 2922 TempDir _c(TestDirectory.path("c")); 2923 2924 vfs::YAMLVFSWriter VFSWriter; 2925 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2926 VFSWriter.addDirectoryMapping(_b.path(), "//root/b"); 2927 VFSWriter.addDirectoryMapping(_c.path(), "//root/c"); 2928 2929 std::string Buffer; 2930 raw_string_ostream OS(Buffer); 2931 VFSWriter.write(OS); 2932 OS.flush(); 2933 2934 // We didn't add a single file - only directories. 2935 EXPECT_EQ(Buffer.find("'type': 'file'"), std::string::npos); 2936 2937 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2938 Lower->addDirectory("//root/a"); 2939 Lower->addDirectory("//root/b"); 2940 Lower->addDirectory("//root/c"); 2941 // canaries 2942 Lower->addRegularFile("//root/a/a"); 2943 Lower->addRegularFile("//root/b/b"); 2944 Lower->addRegularFile("//root/c/c"); 2945 2946 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2947 ASSERT_NE(FS.get(), nullptr); 2948 2949 EXPECT_FALSE(FS->exists(_a.path("a"))); 2950 EXPECT_FALSE(FS->exists(_b.path("b"))); 2951 EXPECT_FALSE(FS->exists(_c.path("c"))); 2952 } 2953 2954 TEST_F(VFSFromYAMLTest, RedirectingWith) { 2955 IntrusiveRefCntPtr<DummyFileSystem> Both(new DummyFileSystem()); 2956 Both->addDirectory("//root/a"); 2957 Both->addRegularFile("//root/a/f"); 2958 Both->addDirectory("//root/b"); 2959 Both->addRegularFile("//root/b/f"); 2960 2961 IntrusiveRefCntPtr<DummyFileSystem> AOnly(new DummyFileSystem()); 2962 AOnly->addDirectory("//root/a"); 2963 AOnly->addRegularFile("//root/a/f"); 2964 2965 IntrusiveRefCntPtr<DummyFileSystem> BOnly(new DummyFileSystem()); 2966 BOnly->addDirectory("//root/b"); 2967 BOnly->addRegularFile("//root/b/f"); 2968 2969 auto BaseStr = std::string(" 'roots': [\n" 2970 " {\n" 2971 " 'type': 'directory-remap',\n" 2972 " 'name': '//root/a',\n" 2973 " 'external-contents': '//root/b'\n" 2974 " }\n" 2975 " ]\n" 2976 "}"); 2977 auto FallthroughStr = "{ 'redirecting-with': 'fallthrough',\n" + BaseStr; 2978 auto FallbackStr = "{ 'redirecting-with': 'fallback',\n" + BaseStr; 2979 auto RedirectOnlyStr = "{ 'redirecting-with': 'redirect-only',\n" + BaseStr; 2980 2981 auto ExpectPath = [&](vfs::FileSystem &FS, StringRef Expected, 2982 StringRef Message) { 2983 auto AF = FS.openFileForRead("//root/a/f"); 2984 ASSERT_FALSE(AF.getError()) << Message; 2985 auto AFName = (*AF)->getName(); 2986 ASSERT_FALSE(AFName.getError()) << Message; 2987 EXPECT_EQ(Expected.str(), AFName.get()) << Message; 2988 2989 auto AS = FS.status("//root/a/f"); 2990 ASSERT_FALSE(AS.getError()) << Message; 2991 EXPECT_EQ(Expected.str(), AS->getName()) << Message; 2992 }; 2993 2994 auto ExpectFailure = [&](vfs::FileSystem &FS, StringRef Message) { 2995 EXPECT_TRUE(FS.openFileForRead("//root/a/f").getError()) << Message; 2996 EXPECT_TRUE(FS.status("//root/a/f").getError()) << Message; 2997 }; 2998 2999 { 3000 // `f` in both `a` and `b` 3001 3002 // `fallthrough` tries `external-name` first, so should be `b` 3003 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough = 3004 getFromYAMLString(FallthroughStr, Both); 3005 ASSERT_TRUE(Fallthrough.get() != nullptr); 3006 ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, both exist"); 3007 3008 // `fallback` tries the original name first, so should be `a` 3009 IntrusiveRefCntPtr<vfs::FileSystem> Fallback = 3010 getFromYAMLString(FallbackStr, Both); 3011 ASSERT_TRUE(Fallback.get() != nullptr); 3012 ExpectPath(*Fallback, "//root/a/f", "fallback, both exist"); 3013 3014 // `redirect-only` is the same as `fallthrough` but doesn't try the 3015 // original on failure, so no change here (ie. `b`) 3016 IntrusiveRefCntPtr<vfs::FileSystem> Redirect = 3017 getFromYAMLString(RedirectOnlyStr, Both); 3018 ASSERT_TRUE(Redirect.get() != nullptr); 3019 ExpectPath(*Redirect, "//root/b/f", "redirect-only, both exist"); 3020 } 3021 3022 { 3023 // `f` in `a` only 3024 3025 // Fallthrough to the original path, `a` 3026 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough = 3027 getFromYAMLString(FallthroughStr, AOnly); 3028 ASSERT_TRUE(Fallthrough.get() != nullptr); 3029 ExpectPath(*Fallthrough, "//root/a/f", "fallthrough, a only"); 3030 3031 // Original first, so still `a` 3032 IntrusiveRefCntPtr<vfs::FileSystem> Fallback = 3033 getFromYAMLString(FallbackStr, AOnly); 3034 ASSERT_TRUE(Fallback.get() != nullptr); 3035 ExpectPath(*Fallback, "//root/a/f", "fallback, a only"); 3036 3037 // Fails since no fallthrough 3038 IntrusiveRefCntPtr<vfs::FileSystem> Redirect = 3039 getFromYAMLString(RedirectOnlyStr, AOnly); 3040 ASSERT_TRUE(Redirect.get() != nullptr); 3041 ExpectFailure(*Redirect, "redirect-only, a only"); 3042 } 3043 3044 { 3045 // `f` in `b` only 3046 3047 // Tries `b` first (no fallthrough) 3048 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough = 3049 getFromYAMLString(FallthroughStr, BOnly); 3050 ASSERT_TRUE(Fallthrough.get() != nullptr); 3051 ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, b only"); 3052 3053 // Tries original first but then fallsback to `b` 3054 IntrusiveRefCntPtr<vfs::FileSystem> Fallback = 3055 getFromYAMLString(FallbackStr, BOnly); 3056 ASSERT_TRUE(Fallback.get() != nullptr); 3057 ExpectPath(*Fallback, "//root/b/f", "fallback, b only"); 3058 3059 // Redirect exists, so uses it (`b`) 3060 IntrusiveRefCntPtr<vfs::FileSystem> Redirect = 3061 getFromYAMLString(RedirectOnlyStr, BOnly); 3062 ASSERT_TRUE(Redirect.get() != nullptr); 3063 ExpectPath(*Redirect, "//root/b/f", "redirect-only, b only"); 3064 } 3065 3066 EXPECT_EQ(0, NumDiagnostics); 3067 } 3068 3069 TEST(VFSFromRemappedFilesTest, Basic) { 3070 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 3071 new vfs::InMemoryFileSystem; 3072 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 3073 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 3074 3075 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 3076 {"//root/a/a", "//root/b"}, 3077 {"//root/a/b/c", "//root/c"}, 3078 }; 3079 auto RemappedFS = vfs::RedirectingFileSystem::create( 3080 RemappedFiles, /*UseExternalNames=*/false, *BaseFS); 3081 3082 auto StatA = RemappedFS->status("//root/a/a"); 3083 auto StatB = RemappedFS->status("//root/a/b/c"); 3084 ASSERT_TRUE(StatA); 3085 ASSERT_TRUE(StatB); 3086 EXPECT_EQ("//root/a/a", StatA->getName()); 3087 EXPECT_EQ("//root/a/b/c", StatB->getName()); 3088 3089 auto BufferA = RemappedFS->getBufferForFile("//root/a/a"); 3090 auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c"); 3091 ASSERT_TRUE(BufferA); 3092 ASSERT_TRUE(BufferB); 3093 EXPECT_EQ("contents of b", (*BufferA)->getBuffer()); 3094 EXPECT_EQ("contents of c", (*BufferB)->getBuffer()); 3095 } 3096 3097 TEST(VFSFromRemappedFilesTest, UseExternalNames) { 3098 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 3099 new vfs::InMemoryFileSystem; 3100 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 3101 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 3102 3103 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 3104 {"//root/a/a", "//root/b"}, 3105 {"//root/a/b/c", "//root/c"}, 3106 }; 3107 auto RemappedFS = vfs::RedirectingFileSystem::create( 3108 RemappedFiles, /*UseExternalNames=*/true, *BaseFS); 3109 3110 auto StatA = RemappedFS->status("//root/a/a"); 3111 auto StatB = RemappedFS->status("//root/a/b/c"); 3112 ASSERT_TRUE(StatA); 3113 ASSERT_TRUE(StatB); 3114 EXPECT_EQ("//root/b", StatA->getName()); 3115 EXPECT_EQ("//root/c", StatB->getName()); 3116 3117 auto BufferA = RemappedFS->getBufferForFile("//root/a/a"); 3118 auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c"); 3119 ASSERT_TRUE(BufferA); 3120 ASSERT_TRUE(BufferB); 3121 EXPECT_EQ("contents of b", (*BufferA)->getBuffer()); 3122 EXPECT_EQ("contents of c", (*BufferB)->getBuffer()); 3123 } 3124 3125 TEST(VFSFromRemappedFilesTest, LastMappingWins) { 3126 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 3127 new vfs::InMemoryFileSystem; 3128 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 3129 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 3130 3131 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 3132 {"//root/a", "//root/b"}, 3133 {"//root/a", "//root/c"}, 3134 }; 3135 auto RemappedFSKeepName = vfs::RedirectingFileSystem::create( 3136 RemappedFiles, /*UseExternalNames=*/false, *BaseFS); 3137 auto RemappedFSExternalName = vfs::RedirectingFileSystem::create( 3138 RemappedFiles, /*UseExternalNames=*/true, *BaseFS); 3139 3140 auto StatKeepA = RemappedFSKeepName->status("//root/a"); 3141 auto StatExternalA = RemappedFSExternalName->status("//root/a"); 3142 ASSERT_TRUE(StatKeepA); 3143 ASSERT_TRUE(StatExternalA); 3144 EXPECT_EQ("//root/a", StatKeepA->getName()); 3145 EXPECT_EQ("//root/c", StatExternalA->getName()); 3146 3147 auto BufferKeepA = RemappedFSKeepName->getBufferForFile("//root/a"); 3148 auto BufferExternalA = RemappedFSExternalName->getBufferForFile("//root/a"); 3149 ASSERT_TRUE(BufferKeepA); 3150 ASSERT_TRUE(BufferExternalA); 3151 EXPECT_EQ("contents of c", (*BufferKeepA)->getBuffer()); 3152 EXPECT_EQ("contents of c", (*BufferExternalA)->getBuffer()); 3153 } 3154 3155 TEST(RedirectingFileSystemTest, PrintOutput) { 3156 auto Buffer = 3157 MemoryBuffer::getMemBuffer("{\n" 3158 " 'version': 0,\n" 3159 " 'roots': [\n" 3160 " {\n" 3161 " 'type': 'directory-remap',\n" 3162 " 'name': '/dremap',\n" 3163 " 'external-contents': '/a',\n" 3164 " }," 3165 " {\n" 3166 " 'type': 'directory',\n" 3167 " 'name': '/vdir',\n" 3168 " 'contents': [" 3169 " {\n" 3170 " 'type': 'directory-remap',\n" 3171 " 'name': 'dremap',\n" 3172 " 'external-contents': '/b'\n" 3173 " 'use-external-name': 'true'\n" 3174 " },\n" 3175 " {\n" 3176 " 'type': 'file',\n" 3177 " 'name': 'vfile',\n" 3178 " 'external-contents': '/c'\n" 3179 " 'use-external-name': 'false'\n" 3180 " }]\n" 3181 " }]\n" 3182 "}"); 3183 3184 auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>(); 3185 auto Redirecting = vfs::RedirectingFileSystem::create( 3186 std::move(Buffer), nullptr, "", nullptr, Dummy); 3187 3188 SmallString<0> Output; 3189 raw_svector_ostream OuputStream{Output}; 3190 3191 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Summary); 3192 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n", Output); 3193 3194 Output.clear(); 3195 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents); 3196 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n" 3197 "'/'\n" 3198 " 'dremap' -> '/a'\n" 3199 " 'vdir'\n" 3200 " 'dremap' -> '/b' (UseExternalName: true)\n" 3201 " 'vfile' -> '/c' (UseExternalName: false)\n" 3202 "ExternalFS:\n" 3203 " DummyFileSystem (Summary)\n", 3204 Output); 3205 3206 Output.clear(); 3207 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents, 1); 3208 ASSERT_EQ(" RedirectingFileSystem (UseExternalNames: true)\n" 3209 " '/'\n" 3210 " 'dremap' -> '/a'\n" 3211 " 'vdir'\n" 3212 " 'dremap' -> '/b' (UseExternalName: true)\n" 3213 " 'vfile' -> '/c' (UseExternalName: false)\n" 3214 " ExternalFS:\n" 3215 " DummyFileSystem (Summary)\n", 3216 Output); 3217 3218 Output.clear(); 3219 Redirecting->print(OuputStream, 3220 vfs::FileSystem::PrintType::RecursiveContents); 3221 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n" 3222 "'/'\n" 3223 " 'dremap' -> '/a'\n" 3224 " 'vdir'\n" 3225 " 'dremap' -> '/b' (UseExternalName: true)\n" 3226 " 'vfile' -> '/c' (UseExternalName: false)\n" 3227 "ExternalFS:\n" 3228 " DummyFileSystem (RecursiveContents)\n", 3229 Output); 3230 } 3231