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