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