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