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.substr(Content.find('{') + 1); 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 2931 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2932 Lower->addDirectory("//root/"); 2933 Lower->addDirectory("//root/a"); 2934 Lower->addRegularFile("//root/a/b"); 2935 Lower->addDirectory("//root/b"); 2936 Lower->addDirectory("//root/c"); 2937 Lower->addRegularFile("//root/c/d"); 2938 Lower->addDirectory("//root/e"); 2939 Lower->addDirectory("//root/e/f"); 2940 Lower->addRegularFile("//root/g"); 2941 Lower->addDirectory("//root/h"); 2942 2943 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2944 ASSERT_NE(FS.get(), nullptr); 2945 2946 EXPECT_TRUE(FS->exists(_a.path())); 2947 EXPECT_TRUE(FS->exists(_ab.path())); 2948 EXPECT_TRUE(FS->exists(_c.path())); 2949 EXPECT_TRUE(FS->exists(_cd.path())); 2950 EXPECT_TRUE(FS->exists(_e.path())); 2951 EXPECT_TRUE(FS->exists(_ef.path())); 2952 EXPECT_TRUE(FS->exists(_g.path())); 2953 EXPECT_TRUE(FS->exists(_h.path())); 2954 } 2955 2956 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest2) { 2957 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2958 TempDir _a(TestDirectory.path("a")); 2959 TempFile _ab(TestDirectory.path("a/b")); 2960 TempDir _ac(TestDirectory.path("a/c")); 2961 TempFile _acd(TestDirectory.path("a/c/d")); 2962 TempFile _ace(TestDirectory.path("a/c/e")); 2963 TempFile _acf(TestDirectory.path("a/c/f")); 2964 TempDir _ag(TestDirectory.path("a/g")); 2965 TempFile _agh(TestDirectory.path("a/g/h")); 2966 2967 vfs::YAMLVFSWriter VFSWriter; 2968 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2969 VFSWriter.addFileMapping(_ab.path(), "//root/a/b"); 2970 VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c"); 2971 VFSWriter.addFileMapping(_acd.path(), "//root/a/c/d"); 2972 VFSWriter.addFileMapping(_ace.path(), "//root/a/c/e"); 2973 VFSWriter.addFileMapping(_acf.path(), "//root/a/c/f"); 2974 VFSWriter.addDirectoryMapping(_ag.path(), "//root/a/g"); 2975 VFSWriter.addFileMapping(_agh.path(), "//root/a/g/h"); 2976 2977 std::string Buffer; 2978 raw_string_ostream OS(Buffer); 2979 VFSWriter.write(OS); 2980 2981 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2982 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2983 EXPECT_NE(FS.get(), nullptr); 2984 } 2985 2986 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest3) { 2987 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2988 TempDir _a(TestDirectory.path("a")); 2989 TempFile _ab(TestDirectory.path("a/b")); 2990 TempDir _ac(TestDirectory.path("a/c")); 2991 TempDir _acd(TestDirectory.path("a/c/d")); 2992 TempDir _acde(TestDirectory.path("a/c/d/e")); 2993 TempFile _acdef(TestDirectory.path("a/c/d/e/f")); 2994 TempFile _acdeg(TestDirectory.path("a/c/d/e/g")); 2995 TempDir _ah(TestDirectory.path("a/h")); 2996 TempFile _ahi(TestDirectory.path("a/h/i")); 2997 2998 vfs::YAMLVFSWriter VFSWriter; 2999 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 3000 VFSWriter.addFileMapping(_ab.path(), "//root/a/b"); 3001 VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c"); 3002 VFSWriter.addDirectoryMapping(_acd.path(), "//root/a/c/d"); 3003 VFSWriter.addDirectoryMapping(_acde.path(), "//root/a/c/d/e"); 3004 VFSWriter.addFileMapping(_acdef.path(), "//root/a/c/d/e/f"); 3005 VFSWriter.addFileMapping(_acdeg.path(), "//root/a/c/d/e/g"); 3006 VFSWriter.addDirectoryMapping(_ahi.path(), "//root/a/h"); 3007 VFSWriter.addFileMapping(_ahi.path(), "//root/a/h/i"); 3008 3009 std::string Buffer; 3010 raw_string_ostream OS(Buffer); 3011 VFSWriter.write(OS); 3012 3013 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 3014 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 3015 EXPECT_NE(FS.get(), nullptr); 3016 } 3017 3018 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTestHandleDirs) { 3019 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 3020 TempDir _a(TestDirectory.path("a")); 3021 TempDir _b(TestDirectory.path("b")); 3022 TempDir _c(TestDirectory.path("c")); 3023 3024 vfs::YAMLVFSWriter VFSWriter; 3025 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 3026 VFSWriter.addDirectoryMapping(_b.path(), "//root/b"); 3027 VFSWriter.addDirectoryMapping(_c.path(), "//root/c"); 3028 3029 std::string Buffer; 3030 raw_string_ostream OS(Buffer); 3031 VFSWriter.write(OS); 3032 3033 // We didn't add a single file - only directories. 3034 EXPECT_EQ(Buffer.find("'type': 'file'"), std::string::npos); 3035 3036 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 3037 Lower->addDirectory("//root/a"); 3038 Lower->addDirectory("//root/b"); 3039 Lower->addDirectory("//root/c"); 3040 // canaries 3041 Lower->addRegularFile("//root/a/a"); 3042 Lower->addRegularFile("//root/b/b"); 3043 Lower->addRegularFile("//root/c/c"); 3044 3045 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 3046 ASSERT_NE(FS.get(), nullptr); 3047 3048 EXPECT_FALSE(FS->exists(_a.path("a"))); 3049 EXPECT_FALSE(FS->exists(_b.path("b"))); 3050 EXPECT_FALSE(FS->exists(_c.path("c"))); 3051 } 3052 3053 TEST_F(VFSFromYAMLTest, RedirectingWith) { 3054 IntrusiveRefCntPtr<DummyFileSystem> Both(new DummyFileSystem()); 3055 Both->addDirectory("//root/a"); 3056 Both->addRegularFile("//root/a/f"); 3057 Both->addDirectory("//root/b"); 3058 Both->addRegularFile("//root/b/f"); 3059 3060 IntrusiveRefCntPtr<DummyFileSystem> AOnly(new DummyFileSystem()); 3061 AOnly->addDirectory("//root/a"); 3062 AOnly->addRegularFile("//root/a/f"); 3063 3064 IntrusiveRefCntPtr<DummyFileSystem> BOnly(new DummyFileSystem()); 3065 BOnly->addDirectory("//root/b"); 3066 BOnly->addRegularFile("//root/b/f"); 3067 3068 auto BaseStr = std::string(" 'roots': [\n" 3069 " {\n" 3070 " 'type': 'directory-remap',\n" 3071 " 'name': '//root/a',\n" 3072 " 'external-contents': '//root/b'\n" 3073 " }\n" 3074 " ]\n" 3075 "}"); 3076 auto FallthroughStr = "{ 'redirecting-with': 'fallthrough',\n" + BaseStr; 3077 auto FallbackStr = "{ 'redirecting-with': 'fallback',\n" + BaseStr; 3078 auto RedirectOnlyStr = "{ 'redirecting-with': 'redirect-only',\n" + BaseStr; 3079 3080 auto ExpectPath = [&](vfs::FileSystem &FS, StringRef Expected, 3081 StringRef Message) { 3082 auto AF = FS.openFileForRead("//root/a/f"); 3083 ASSERT_FALSE(AF.getError()) << Message; 3084 auto AFName = (*AF)->getName(); 3085 ASSERT_FALSE(AFName.getError()) << Message; 3086 EXPECT_EQ(Expected.str(), AFName.get()) << Message; 3087 3088 auto AS = FS.status("//root/a/f"); 3089 ASSERT_FALSE(AS.getError()) << Message; 3090 EXPECT_EQ(Expected.str(), AS->getName()) << Message; 3091 }; 3092 3093 auto ExpectFailure = [&](vfs::FileSystem &FS, StringRef Message) { 3094 EXPECT_TRUE(FS.openFileForRead("//root/a/f").getError()) << Message; 3095 EXPECT_TRUE(FS.status("//root/a/f").getError()) << Message; 3096 }; 3097 3098 { 3099 // `f` in both `a` and `b` 3100 3101 // `fallthrough` tries `external-name` first, so should be `b` 3102 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough = 3103 getFromYAMLString(FallthroughStr, Both); 3104 ASSERT_TRUE(Fallthrough.get() != nullptr); 3105 ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, both exist"); 3106 3107 // `fallback` tries the original name first, so should be `a` 3108 IntrusiveRefCntPtr<vfs::FileSystem> Fallback = 3109 getFromYAMLString(FallbackStr, Both); 3110 ASSERT_TRUE(Fallback.get() != nullptr); 3111 ExpectPath(*Fallback, "//root/a/f", "fallback, both exist"); 3112 3113 // `redirect-only` is the same as `fallthrough` but doesn't try the 3114 // original on failure, so no change here (ie. `b`) 3115 IntrusiveRefCntPtr<vfs::FileSystem> Redirect = 3116 getFromYAMLString(RedirectOnlyStr, Both); 3117 ASSERT_TRUE(Redirect.get() != nullptr); 3118 ExpectPath(*Redirect, "//root/b/f", "redirect-only, both exist"); 3119 } 3120 3121 { 3122 // `f` in `a` only 3123 3124 // Fallthrough to the original path, `a` 3125 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough = 3126 getFromYAMLString(FallthroughStr, AOnly); 3127 ASSERT_TRUE(Fallthrough.get() != nullptr); 3128 ExpectPath(*Fallthrough, "//root/a/f", "fallthrough, a only"); 3129 3130 // Original first, so still `a` 3131 IntrusiveRefCntPtr<vfs::FileSystem> Fallback = 3132 getFromYAMLString(FallbackStr, AOnly); 3133 ASSERT_TRUE(Fallback.get() != nullptr); 3134 ExpectPath(*Fallback, "//root/a/f", "fallback, a only"); 3135 3136 // Fails since no fallthrough 3137 IntrusiveRefCntPtr<vfs::FileSystem> Redirect = 3138 getFromYAMLString(RedirectOnlyStr, AOnly); 3139 ASSERT_TRUE(Redirect.get() != nullptr); 3140 ExpectFailure(*Redirect, "redirect-only, a only"); 3141 } 3142 3143 { 3144 // `f` in `b` only 3145 3146 // Tries `b` first (no fallthrough) 3147 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough = 3148 getFromYAMLString(FallthroughStr, BOnly); 3149 ASSERT_TRUE(Fallthrough.get() != nullptr); 3150 ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, b only"); 3151 3152 // Tries original first but then fallsback to `b` 3153 IntrusiveRefCntPtr<vfs::FileSystem> Fallback = 3154 getFromYAMLString(FallbackStr, BOnly); 3155 ASSERT_TRUE(Fallback.get() != nullptr); 3156 ExpectPath(*Fallback, "//root/b/f", "fallback, b only"); 3157 3158 // Redirect exists, so uses it (`b`) 3159 IntrusiveRefCntPtr<vfs::FileSystem> Redirect = 3160 getFromYAMLString(RedirectOnlyStr, BOnly); 3161 ASSERT_TRUE(Redirect.get() != nullptr); 3162 ExpectPath(*Redirect, "//root/b/f", "redirect-only, b only"); 3163 } 3164 3165 EXPECT_EQ(0, NumDiagnostics); 3166 } 3167 3168 TEST(VFSFromRemappedFilesTest, Basic) { 3169 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 3170 new vfs::InMemoryFileSystem; 3171 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 3172 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 3173 3174 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 3175 {"//root/a/a", "//root/b"}, 3176 {"//root/a/b/c", "//root/c"}, 3177 }; 3178 auto RemappedFS = vfs::RedirectingFileSystem::create( 3179 RemappedFiles, /*UseExternalNames=*/false, *BaseFS); 3180 3181 auto StatA = RemappedFS->status("//root/a/a"); 3182 auto StatB = RemappedFS->status("//root/a/b/c"); 3183 ASSERT_TRUE(StatA); 3184 ASSERT_TRUE(StatB); 3185 EXPECT_EQ("//root/a/a", StatA->getName()); 3186 EXPECT_EQ("//root/a/b/c", StatB->getName()); 3187 3188 auto BufferA = RemappedFS->getBufferForFile("//root/a/a"); 3189 auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c"); 3190 ASSERT_TRUE(BufferA); 3191 ASSERT_TRUE(BufferB); 3192 EXPECT_EQ("contents of b", (*BufferA)->getBuffer()); 3193 EXPECT_EQ("contents of c", (*BufferB)->getBuffer()); 3194 } 3195 3196 TEST(VFSFromRemappedFilesTest, UseExternalNames) { 3197 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 3198 new vfs::InMemoryFileSystem; 3199 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 3200 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 3201 3202 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 3203 {"//root/a/a", "//root/b"}, 3204 {"//root/a/b/c", "//root/c"}, 3205 }; 3206 auto RemappedFS = vfs::RedirectingFileSystem::create( 3207 RemappedFiles, /*UseExternalNames=*/true, *BaseFS); 3208 3209 auto StatA = RemappedFS->status("//root/a/a"); 3210 auto StatB = RemappedFS->status("//root/a/b/c"); 3211 ASSERT_TRUE(StatA); 3212 ASSERT_TRUE(StatB); 3213 EXPECT_EQ("//root/b", StatA->getName()); 3214 EXPECT_EQ("//root/c", StatB->getName()); 3215 3216 auto BufferA = RemappedFS->getBufferForFile("//root/a/a"); 3217 auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c"); 3218 ASSERT_TRUE(BufferA); 3219 ASSERT_TRUE(BufferB); 3220 EXPECT_EQ("contents of b", (*BufferA)->getBuffer()); 3221 EXPECT_EQ("contents of c", (*BufferB)->getBuffer()); 3222 } 3223 3224 TEST(VFSFromRemappedFilesTest, LastMappingWins) { 3225 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 3226 new vfs::InMemoryFileSystem; 3227 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 3228 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 3229 3230 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 3231 {"//root/a", "//root/b"}, 3232 {"//root/a", "//root/c"}, 3233 }; 3234 auto RemappedFSKeepName = vfs::RedirectingFileSystem::create( 3235 RemappedFiles, /*UseExternalNames=*/false, *BaseFS); 3236 auto RemappedFSExternalName = vfs::RedirectingFileSystem::create( 3237 RemappedFiles, /*UseExternalNames=*/true, *BaseFS); 3238 3239 auto StatKeepA = RemappedFSKeepName->status("//root/a"); 3240 auto StatExternalA = RemappedFSExternalName->status("//root/a"); 3241 ASSERT_TRUE(StatKeepA); 3242 ASSERT_TRUE(StatExternalA); 3243 EXPECT_EQ("//root/a", StatKeepA->getName()); 3244 EXPECT_EQ("//root/c", StatExternalA->getName()); 3245 3246 auto BufferKeepA = RemappedFSKeepName->getBufferForFile("//root/a"); 3247 auto BufferExternalA = RemappedFSExternalName->getBufferForFile("//root/a"); 3248 ASSERT_TRUE(BufferKeepA); 3249 ASSERT_TRUE(BufferExternalA); 3250 EXPECT_EQ("contents of c", (*BufferKeepA)->getBuffer()); 3251 EXPECT_EQ("contents of c", (*BufferExternalA)->getBuffer()); 3252 } 3253 3254 TEST(RedirectingFileSystemTest, PrintOutput) { 3255 auto Buffer = 3256 MemoryBuffer::getMemBuffer("{\n" 3257 " 'version': 0,\n" 3258 " 'roots': [\n" 3259 " {\n" 3260 " 'type': 'directory-remap',\n" 3261 " 'name': '/dremap',\n" 3262 " 'external-contents': '/a',\n" 3263 " }," 3264 " {\n" 3265 " 'type': 'directory',\n" 3266 " 'name': '/vdir',\n" 3267 " 'contents': [" 3268 " {\n" 3269 " 'type': 'directory-remap',\n" 3270 " 'name': 'dremap',\n" 3271 " 'external-contents': '/b'\n" 3272 " 'use-external-name': 'true'\n" 3273 " },\n" 3274 " {\n" 3275 " 'type': 'file',\n" 3276 " 'name': 'vfile',\n" 3277 " 'external-contents': '/c'\n" 3278 " 'use-external-name': 'false'\n" 3279 " }]\n" 3280 " }]\n" 3281 "}"); 3282 3283 auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>(); 3284 auto Redirecting = vfs::RedirectingFileSystem::create( 3285 std::move(Buffer), nullptr, "", nullptr, Dummy); 3286 3287 SmallString<0> Output; 3288 raw_svector_ostream OuputStream{Output}; 3289 3290 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Summary); 3291 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n", Output); 3292 3293 Output.clear(); 3294 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents); 3295 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n" 3296 "'/'\n" 3297 " 'dremap' -> '/a'\n" 3298 " 'vdir'\n" 3299 " 'dremap' -> '/b' (UseExternalName: true)\n" 3300 " 'vfile' -> '/c' (UseExternalName: false)\n" 3301 "ExternalFS:\n" 3302 " DummyFileSystem (Summary)\n", 3303 Output); 3304 3305 Output.clear(); 3306 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents, 1); 3307 ASSERT_EQ(" RedirectingFileSystem (UseExternalNames: true)\n" 3308 " '/'\n" 3309 " 'dremap' -> '/a'\n" 3310 " 'vdir'\n" 3311 " 'dremap' -> '/b' (UseExternalName: true)\n" 3312 " 'vfile' -> '/c' (UseExternalName: false)\n" 3313 " ExternalFS:\n" 3314 " DummyFileSystem (Summary)\n", 3315 Output); 3316 3317 Output.clear(); 3318 Redirecting->print(OuputStream, 3319 vfs::FileSystem::PrintType::RecursiveContents); 3320 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n" 3321 "'/'\n" 3322 " 'dremap' -> '/a'\n" 3323 " 'vdir'\n" 3324 " 'dremap' -> '/b' (UseExternalName: true)\n" 3325 " 'vfile' -> '/c' (UseExternalName: false)\n" 3326 "ExternalFS:\n" 3327 " DummyFileSystem (RecursiveContents)\n", 3328 Output); 3329 } 3330 3331 TEST(RedirectingFileSystemTest, Used) { 3332 auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>(); 3333 auto YAML1 = 3334 MemoryBuffer::getMemBuffer("{\n" 3335 " 'version': 0,\n" 3336 " 'redirecting-with': 'fallthrough',\n" 3337 " 'roots': [\n" 3338 " {\n" 3339 " 'type': 'file',\n" 3340 " 'name': '/vfile1',\n" 3341 " 'external-contents': '/a',\n" 3342 " }," 3343 " ]\n" 3344 "}"); 3345 auto YAML2 = 3346 MemoryBuffer::getMemBuffer("{\n" 3347 " 'version': 0,\n" 3348 " 'redirecting-with': 'fallthrough',\n" 3349 " 'roots': [\n" 3350 " {\n" 3351 " 'type': 'file',\n" 3352 " 'name': '/vfile2',\n" 3353 " 'external-contents': '/b',\n" 3354 " }," 3355 " ]\n" 3356 "}"); 3357 3358 Dummy->addRegularFile("/a"); 3359 Dummy->addRegularFile("/b"); 3360 3361 IntrusiveRefCntPtr<vfs::RedirectingFileSystem> Redirecting1 = 3362 vfs::RedirectingFileSystem::create(std::move(YAML1), nullptr, "", nullptr, 3363 Dummy) 3364 .release(); 3365 auto Redirecting2 = vfs::RedirectingFileSystem::create( 3366 std::move(YAML2), nullptr, "", nullptr, Redirecting1); 3367 3368 Redirecting1->setUsageTrackingActive(true); 3369 Redirecting2->setUsageTrackingActive(true); 3370 EXPECT_TRUE(Redirecting2->exists("/vfile1")); 3371 EXPECT_TRUE(Redirecting2->exists("/b")); 3372 EXPECT_TRUE(Redirecting1->hasBeenUsed()); 3373 EXPECT_FALSE(Redirecting2->hasBeenUsed()); 3374 } 3375 3376 // Check that paths looked up in the external filesystem are unmodified, except 3377 // potentially to add the working directory. We cannot canonicalize away .. 3378 // in the presence of symlinks in the external filesystem. 3379 TEST(RedirectingFileSystemTest, ExternalPaths) { 3380 struct InterceptorFS : llvm::vfs::ProxyFileSystem { 3381 std::vector<std::string> SeenPaths; 3382 3383 InterceptorFS(IntrusiveRefCntPtr<FileSystem> UnderlyingFS) 3384 : ProxyFileSystem(UnderlyingFS) {} 3385 3386 llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override { 3387 SeenPaths.push_back(Path.str()); 3388 return ProxyFileSystem::status(Path); 3389 } 3390 3391 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> 3392 openFileForRead(const Twine &Path) override { 3393 SeenPaths.push_back(Path.str()); 3394 return ProxyFileSystem::openFileForRead(Path); 3395 } 3396 3397 std::error_code isLocal(const Twine &Path, bool &Result) override { 3398 SeenPaths.push_back(Path.str()); 3399 return ProxyFileSystem::isLocal(Path, Result); 3400 } 3401 3402 vfs::directory_iterator dir_begin(const Twine &Dir, 3403 std::error_code &EC) override { 3404 SeenPaths.push_back(Dir.str()); 3405 return ProxyFileSystem::dir_begin(Dir, EC); 3406 } 3407 3408 bool exists(const Twine &Path) override { 3409 SeenPaths.push_back(Path.str()); 3410 return ProxyFileSystem::exists(Path); 3411 } 3412 }; 3413 3414 std::error_code EC; 3415 auto BaseFS = makeIntrusiveRefCnt<DummyFileSystem>(); 3416 BaseFS->setCurrentWorkingDirectory("/cwd"); 3417 auto CheckFS = makeIntrusiveRefCnt<InterceptorFS>(BaseFS); 3418 auto FS = vfs::RedirectingFileSystem::create({}, /*UseExternalNames=*/false, 3419 *CheckFS); 3420 3421 FS->status("/a/../b"); 3422 FS->openFileForRead("c"); 3423 FS->exists("./d"); 3424 bool IsLocal = false; 3425 FS->isLocal("/e/./../f", IsLocal); 3426 FS->dir_begin(".././g", EC); 3427 3428 std::vector<std::string> Expected{"/a/../b", "/cwd/c", "/cwd/./d", 3429 "/e/./../f", "/cwd/.././g"}; 3430 3431 EXPECT_EQ(CheckFS->SeenPaths, Expected); 3432 3433 CheckFS->SeenPaths.clear(); 3434 FS->setRedirection(vfs::RedirectingFileSystem::RedirectKind::Fallback); 3435 FS->status("/a/../b"); 3436 FS->openFileForRead("c"); 3437 FS->exists("./d"); 3438 FS->isLocal("/e/./../f", IsLocal); 3439 FS->dir_begin(".././g", EC); 3440 3441 EXPECT_EQ(CheckFS->SeenPaths, Expected); 3442 } 3443 3444 TEST(RedirectingFileSystemTest, Exists) { 3445 IntrusiveRefCntPtr<DummyFileSystem> Dummy(new NoStatusDummyFileSystem()); 3446 auto YAML = 3447 MemoryBuffer::getMemBuffer("{\n" 3448 " 'version': 0,\n" 3449 " 'roots': [\n" 3450 " {\n" 3451 " 'type': 'directory-remap',\n" 3452 " 'name': '/dremap',\n" 3453 " 'external-contents': '/a',\n" 3454 " }," 3455 " {\n" 3456 " 'type': 'directory-remap',\n" 3457 " 'name': '/dmissing',\n" 3458 " 'external-contents': '/dmissing',\n" 3459 " }," 3460 " {\n" 3461 " 'type': 'directory',\n" 3462 " 'name': '/both',\n" 3463 " 'contents': [\n" 3464 " {\n" 3465 " 'type': 'file',\n" 3466 " 'name': 'vfile',\n" 3467 " 'external-contents': '/c'\n" 3468 " }\n" 3469 " ]\n" 3470 " },\n" 3471 " {\n" 3472 " 'type': 'directory',\n" 3473 " 'name': '/vdir',\n" 3474 " 'contents': [" 3475 " {\n" 3476 " 'type': 'directory-remap',\n" 3477 " 'name': 'dremap',\n" 3478 " 'external-contents': '/b'\n" 3479 " },\n" 3480 " {\n" 3481 " 'type': 'file',\n" 3482 " 'name': 'missing',\n" 3483 " 'external-contents': '/missing'\n" 3484 " },\n" 3485 " {\n" 3486 " 'type': 'file',\n" 3487 " 'name': 'vfile',\n" 3488 " 'external-contents': '/c'\n" 3489 " }]\n" 3490 " }]\n" 3491 "}"); 3492 3493 Dummy->addDirectory("/a"); 3494 Dummy->addRegularFile("/a/foo"); 3495 Dummy->addDirectory("/b"); 3496 Dummy->addRegularFile("/c"); 3497 Dummy->addRegularFile("/both/foo"); 3498 3499 auto Redirecting = vfs::RedirectingFileSystem::create( 3500 std::move(YAML), nullptr, "", nullptr, Dummy); 3501 3502 EXPECT_TRUE(Redirecting->exists("/dremap")); 3503 EXPECT_FALSE(Redirecting->exists("/dmissing")); 3504 EXPECT_FALSE(Redirecting->exists("/unknown")); 3505 EXPECT_TRUE(Redirecting->exists("/both")); 3506 EXPECT_TRUE(Redirecting->exists("/both/foo")); 3507 EXPECT_TRUE(Redirecting->exists("/both/vfile")); 3508 EXPECT_TRUE(Redirecting->exists("/vdir")); 3509 EXPECT_TRUE(Redirecting->exists("/vdir/dremap")); 3510 EXPECT_FALSE(Redirecting->exists("/vdir/missing")); 3511 EXPECT_TRUE(Redirecting->exists("/vdir/vfile")); 3512 EXPECT_FALSE(Redirecting->exists("/vdir/unknown")); 3513 } 3514 3515 TEST(RedirectingFileSystemTest, ExistsFallback) { 3516 IntrusiveRefCntPtr<DummyFileSystem> Dummy(new NoStatusDummyFileSystem()); 3517 auto YAML = 3518 MemoryBuffer::getMemBuffer("{\n" 3519 " 'version': 0,\n" 3520 " 'redirecting-with': 'fallback',\n" 3521 " 'roots': [\n" 3522 " {\n" 3523 " 'type': 'file',\n" 3524 " 'name': '/fallback',\n" 3525 " 'external-contents': '/missing',\n" 3526 " }," 3527 " ]\n" 3528 "}"); 3529 3530 Dummy->addRegularFile("/fallback"); 3531 3532 auto Redirecting = vfs::RedirectingFileSystem::create( 3533 std::move(YAML), nullptr, "", nullptr, Dummy); 3534 3535 EXPECT_TRUE(Redirecting->exists("/fallback")); 3536 EXPECT_FALSE(Redirecting->exists("/missing")); 3537 } 3538 3539 TEST(RedirectingFileSystemTest, ExistsRedirectOnly) { 3540 IntrusiveRefCntPtr<DummyFileSystem> Dummy(new NoStatusDummyFileSystem()); 3541 auto YAML = 3542 MemoryBuffer::getMemBuffer("{\n" 3543 " 'version': 0,\n" 3544 " 'redirecting-with': 'redirect-only',\n" 3545 " 'roots': [\n" 3546 " {\n" 3547 " 'type': 'file',\n" 3548 " 'name': '/vfile',\n" 3549 " 'external-contents': '/a',\n" 3550 " }," 3551 " ]\n" 3552 "}"); 3553 3554 Dummy->addRegularFile("/a"); 3555 Dummy->addRegularFile("/b"); 3556 3557 auto Redirecting = vfs::RedirectingFileSystem::create( 3558 std::move(YAML), nullptr, "", nullptr, Dummy); 3559 3560 EXPECT_FALSE(Redirecting->exists("/a")); 3561 EXPECT_FALSE(Redirecting->exists("/b")); 3562 EXPECT_TRUE(Redirecting->exists("/vfile")); 3563 } 3564 3565 TEST(TracingFileSystemTest, TracingWorks) { 3566 auto InMemoryFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>(); 3567 auto TracingFS = 3568 makeIntrusiveRefCnt<vfs::TracingFileSystem>(std::move(InMemoryFS)); 3569 3570 EXPECT_EQ(TracingFS->NumStatusCalls, 0u); 3571 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 0u); 3572 EXPECT_EQ(TracingFS->NumDirBeginCalls, 0u); 3573 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u); 3574 EXPECT_EQ(TracingFS->NumExistsCalls, 0u); 3575 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u); 3576 3577 (void)TracingFS->status("/foo"); 3578 EXPECT_EQ(TracingFS->NumStatusCalls, 1u); 3579 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 0u); 3580 EXPECT_EQ(TracingFS->NumDirBeginCalls, 0u); 3581 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u); 3582 EXPECT_EQ(TracingFS->NumExistsCalls, 0u); 3583 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u); 3584 3585 (void)TracingFS->openFileForRead("/foo"); 3586 EXPECT_EQ(TracingFS->NumStatusCalls, 1u); 3587 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u); 3588 EXPECT_EQ(TracingFS->NumDirBeginCalls, 0u); 3589 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u); 3590 EXPECT_EQ(TracingFS->NumExistsCalls, 0u); 3591 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u); 3592 3593 std::error_code EC; 3594 (void)TracingFS->dir_begin("/foo", EC); 3595 EXPECT_EQ(TracingFS->NumStatusCalls, 1u); 3596 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u); 3597 EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u); 3598 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u); 3599 EXPECT_EQ(TracingFS->NumExistsCalls, 0u); 3600 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u); 3601 3602 SmallString<128> RealPath; 3603 (void)TracingFS->getRealPath("/foo", RealPath); 3604 EXPECT_EQ(TracingFS->NumStatusCalls, 1u); 3605 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u); 3606 EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u); 3607 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 1u); 3608 EXPECT_EQ(TracingFS->NumExistsCalls, 0u); 3609 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u); 3610 3611 (void)TracingFS->exists("/foo"); 3612 EXPECT_EQ(TracingFS->NumStatusCalls, 1u); 3613 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u); 3614 EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u); 3615 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 1u); 3616 EXPECT_EQ(TracingFS->NumExistsCalls, 1u); 3617 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u); 3618 3619 bool IsLocal; 3620 (void)TracingFS->isLocal("/foo", IsLocal); 3621 EXPECT_EQ(TracingFS->NumStatusCalls, 1u); 3622 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u); 3623 EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u); 3624 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 1u); 3625 EXPECT_EQ(TracingFS->NumExistsCalls, 1u); 3626 EXPECT_EQ(TracingFS->NumIsLocalCalls, 1u); 3627 } 3628 3629 TEST(TracingFileSystemTest, PrintOutput) { 3630 auto InMemoryFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>(); 3631 auto TracingFS = 3632 makeIntrusiveRefCnt<vfs::TracingFileSystem>(std::move(InMemoryFS)); 3633 3634 (void)TracingFS->status("/foo"); 3635 3636 (void)TracingFS->openFileForRead("/foo"); 3637 (void)TracingFS->openFileForRead("/foo"); 3638 3639 std::error_code EC; 3640 (void)TracingFS->dir_begin("/foo", EC); 3641 (void)TracingFS->dir_begin("/foo", EC); 3642 (void)TracingFS->dir_begin("/foo", EC); 3643 3644 llvm::SmallString<128> RealPath; 3645 (void)TracingFS->getRealPath("/foo", RealPath); 3646 (void)TracingFS->getRealPath("/foo", RealPath); 3647 (void)TracingFS->getRealPath("/foo", RealPath); 3648 (void)TracingFS->getRealPath("/foo", RealPath); 3649 3650 (void)TracingFS->exists("/foo"); 3651 (void)TracingFS->exists("/foo"); 3652 (void)TracingFS->exists("/foo"); 3653 (void)TracingFS->exists("/foo"); 3654 (void)TracingFS->exists("/foo"); 3655 3656 bool IsLocal; 3657 (void)TracingFS->isLocal("/foo", IsLocal); 3658 (void)TracingFS->isLocal("/foo", IsLocal); 3659 (void)TracingFS->isLocal("/foo", IsLocal); 3660 (void)TracingFS->isLocal("/foo", IsLocal); 3661 (void)TracingFS->isLocal("/foo", IsLocal); 3662 (void)TracingFS->isLocal("/foo", IsLocal); 3663 3664 std::string Output; 3665 llvm::raw_string_ostream OS(Output); 3666 TracingFS->print(OS); 3667 3668 ASSERT_EQ("TracingFileSystem\n" 3669 "NumStatusCalls=1\n" 3670 "NumOpenFileForReadCalls=2\n" 3671 "NumDirBeginCalls=3\n" 3672 "NumGetRealPathCalls=4\n" 3673 "NumExistsCalls=5\n" 3674 "NumIsLocalCalls=6\n" 3675 " InMemoryFileSystem\n", 3676 Output); 3677 } 3678