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(std::string 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("/", *PWD); 927 928 SmallString<16> Path; 929 ASSERT_FALSE(PFS.getRealPath("a", Path)); 930 ASSERT_EQ("/a", 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 vfs::InMemoryFileSystem FS2; 1347 ASSERT_TRUE(FS2.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text"))); 1348 EXPECT_EQ(FS.status("/a/b")->getUniqueID(), 1349 FS2.status("/a/b")->getUniqueID()); 1350 EXPECT_EQ(FS.status("/a")->getUniqueID(), FS2.status("/a")->getUniqueID()); 1351 } 1352 1353 // NOTE: in the tests below, we use '//root/' as our root directory, since it is 1354 // a legal *absolute* path on Windows as well as *nix. 1355 class VFSFromYAMLTest : public ::testing::Test { 1356 public: 1357 int NumDiagnostics; 1358 1359 void SetUp() override { NumDiagnostics = 0; } 1360 1361 static void CountingDiagHandler(const SMDiagnostic &, void *Context) { 1362 VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context); 1363 ++Test->NumDiagnostics; 1364 } 1365 1366 std::unique_ptr<vfs::FileSystem> 1367 getFromYAMLRawString(StringRef Content, 1368 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) { 1369 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content); 1370 return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this, 1371 ExternalFS); 1372 } 1373 1374 std::unique_ptr<vfs::FileSystem> getFromYAMLString( 1375 StringRef Content, 1376 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) { 1377 std::string VersionPlusContent("{\n 'version':0,\n"); 1378 VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos); 1379 return getFromYAMLRawString(VersionPlusContent, ExternalFS); 1380 } 1381 1382 // This is intended as a "XFAIL" for windows hosts. 1383 bool supportsSameDirMultipleYAMLEntries() { 1384 Triple Host(Triple::normalize(sys::getProcessTriple())); 1385 return !Host.isOSWindows(); 1386 } 1387 }; 1388 1389 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) { 1390 IntrusiveRefCntPtr<vfs::FileSystem> FS; 1391 FS = getFromYAMLString(""); 1392 EXPECT_EQ(nullptr, FS.get()); 1393 FS = getFromYAMLString("[]"); 1394 EXPECT_EQ(nullptr, FS.get()); 1395 FS = getFromYAMLString("'string'"); 1396 EXPECT_EQ(nullptr, FS.get()); 1397 EXPECT_EQ(3, NumDiagnostics); 1398 } 1399 1400 TEST_F(VFSFromYAMLTest, MappedFiles) { 1401 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1402 Lower->addDirectory("//root/foo/bar"); 1403 Lower->addRegularFile("//root/foo/bar/a"); 1404 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1405 "{ 'roots': [\n" 1406 "{\n" 1407 " 'type': 'directory',\n" 1408 " 'name': '//root/',\n" 1409 " 'contents': [ {\n" 1410 " 'type': 'file',\n" 1411 " 'name': 'file1',\n" 1412 " 'external-contents': '//root/foo/bar/a'\n" 1413 " },\n" 1414 " {\n" 1415 " 'type': 'file',\n" 1416 " 'name': 'file2',\n" 1417 " 'external-contents': '//root/foo/b'\n" 1418 " },\n" 1419 " {\n" 1420 " 'type': 'directory-remap',\n" 1421 " 'name': 'mappeddir',\n" 1422 " 'external-contents': '//root/foo/bar'\n" 1423 " },\n" 1424 " {\n" 1425 " 'type': 'directory-remap',\n" 1426 " 'name': 'mappeddir2',\n" 1427 " 'use-external-name': false,\n" 1428 " 'external-contents': '//root/foo/bar'\n" 1429 " }\n" 1430 " ]\n" 1431 "}\n" 1432 "]\n" 1433 "}", 1434 Lower); 1435 ASSERT_NE(FS.get(), nullptr); 1436 1437 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1438 new vfs::OverlayFileSystem(Lower)); 1439 O->pushOverlay(FS); 1440 1441 // file 1442 ErrorOr<vfs::Status> S = O->status("//root/file1"); 1443 ASSERT_FALSE(S.getError()); 1444 EXPECT_EQ("//root/foo/bar/a", S->getName()); 1445 EXPECT_TRUE(S->IsVFSMapped); 1446 1447 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); 1448 EXPECT_EQ("//root/foo/bar/a", SLower->getName()); 1449 EXPECT_TRUE(S->equivalent(*SLower)); 1450 EXPECT_FALSE(SLower->IsVFSMapped); 1451 1452 // file after opening 1453 auto OpenedF = O->openFileForRead("//root/file1"); 1454 ASSERT_FALSE(OpenedF.getError()); 1455 auto OpenedS = (*OpenedF)->status(); 1456 ASSERT_FALSE(OpenedS.getError()); 1457 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 1458 EXPECT_TRUE(OpenedS->IsVFSMapped); 1459 1460 // directory 1461 S = O->status("//root/"); 1462 ASSERT_FALSE(S.getError()); 1463 EXPECT_TRUE(S->isDirectory()); 1464 EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID 1465 1466 // remapped directory 1467 S = O->status("//root/mappeddir"); 1468 ASSERT_FALSE(S.getError()); 1469 EXPECT_TRUE(S->isDirectory()); 1470 EXPECT_TRUE(S->IsVFSMapped); 1471 EXPECT_TRUE(S->equivalent(*O->status("//root/foo/bar"))); 1472 1473 SLower = O->status("//root/foo/bar"); 1474 EXPECT_EQ("//root/foo/bar", SLower->getName()); 1475 EXPECT_TRUE(S->equivalent(*SLower)); 1476 EXPECT_FALSE(SLower->IsVFSMapped); 1477 1478 // file in remapped directory 1479 S = O->status("//root/mappeddir/a"); 1480 ASSERT_FALSE(S.getError()); 1481 ASSERT_FALSE(S->isDirectory()); 1482 ASSERT_TRUE(S->IsVFSMapped); 1483 ASSERT_EQ("//root/foo/bar/a", S->getName()); 1484 1485 // file in remapped directory, with use-external-name=false 1486 S = O->status("//root/mappeddir2/a"); 1487 ASSERT_FALSE(S.getError()); 1488 ASSERT_FALSE(S->isDirectory()); 1489 ASSERT_TRUE(S->IsVFSMapped); 1490 ASSERT_EQ("//root/mappeddir2/a", S->getName()); 1491 1492 // file contents in remapped directory 1493 OpenedF = O->openFileForRead("//root/mappeddir/a"); 1494 ASSERT_FALSE(OpenedF.getError()); 1495 OpenedS = (*OpenedF)->status(); 1496 ASSERT_FALSE(OpenedS.getError()); 1497 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 1498 EXPECT_TRUE(OpenedS->IsVFSMapped); 1499 1500 // file contents in remapped directory, with use-external-name=false 1501 OpenedF = O->openFileForRead("//root/mappeddir2/a"); 1502 ASSERT_FALSE(OpenedF.getError()); 1503 OpenedS = (*OpenedF)->status(); 1504 ASSERT_FALSE(OpenedS.getError()); 1505 EXPECT_EQ("//root/mappeddir2/a", OpenedS->getName()); 1506 EXPECT_TRUE(OpenedS->IsVFSMapped); 1507 1508 // broken mapping 1509 EXPECT_EQ(O->status("//root/file2").getError(), 1510 llvm::errc::no_such_file_or_directory); 1511 EXPECT_EQ(0, NumDiagnostics); 1512 } 1513 1514 TEST_F(VFSFromYAMLTest, MappedRoot) { 1515 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1516 Lower->addDirectory("//root/foo/bar"); 1517 Lower->addRegularFile("//root/foo/bar/a"); 1518 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1519 getFromYAMLString("{ 'roots': [\n" 1520 "{\n" 1521 " 'type': 'directory-remap',\n" 1522 " 'name': '//mappedroot/',\n" 1523 " 'external-contents': '//root/foo/bar'\n" 1524 "}\n" 1525 "]\n" 1526 "}", 1527 Lower); 1528 ASSERT_NE(FS.get(), nullptr); 1529 1530 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1531 new vfs::OverlayFileSystem(Lower)); 1532 O->pushOverlay(FS); 1533 1534 // file 1535 ErrorOr<vfs::Status> S = O->status("//mappedroot/a"); 1536 ASSERT_FALSE(S.getError()); 1537 EXPECT_EQ("//root/foo/bar/a", S->getName()); 1538 EXPECT_TRUE(S->IsVFSMapped); 1539 1540 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); 1541 EXPECT_EQ("//root/foo/bar/a", SLower->getName()); 1542 EXPECT_TRUE(S->equivalent(*SLower)); 1543 EXPECT_FALSE(SLower->IsVFSMapped); 1544 1545 // file after opening 1546 auto OpenedF = O->openFileForRead("//mappedroot/a"); 1547 ASSERT_FALSE(OpenedF.getError()); 1548 auto OpenedS = (*OpenedF)->status(); 1549 ASSERT_FALSE(OpenedS.getError()); 1550 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 1551 EXPECT_TRUE(OpenedS->IsVFSMapped); 1552 1553 EXPECT_EQ(0, NumDiagnostics); 1554 } 1555 1556 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlay) { 1557 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1558 Lower->addDirectory("//root/foo"); 1559 Lower->addRegularFile("//root/foo/a"); 1560 Lower->addDirectory("//root/bar"); 1561 Lower->addRegularFile("//root/bar/b"); 1562 Lower->addRegularFile("//root/bar/c"); 1563 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1564 getFromYAMLString("{ 'roots': [\n" 1565 "{\n" 1566 " 'type': 'directory',\n" 1567 " 'name': '//root/',\n" 1568 " 'contents': [ {\n" 1569 " 'type': 'directory-remap',\n" 1570 " 'name': 'bar',\n" 1571 " 'external-contents': '//root/foo'\n" 1572 " }\n" 1573 " ]\n" 1574 "}]}", 1575 Lower); 1576 ASSERT_NE(FS.get(), nullptr); 1577 1578 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1579 new vfs::OverlayFileSystem(Lower)); 1580 O->pushOverlay(FS); 1581 1582 ErrorOr<vfs::Status> S = O->status("//root/foo"); 1583 ASSERT_FALSE(S.getError()); 1584 1585 ErrorOr<vfs::Status> SS = O->status("//root/bar"); 1586 ASSERT_FALSE(SS.getError()); 1587 EXPECT_TRUE(S->equivalent(*SS)); 1588 1589 std::error_code EC; 1590 checkContents(O->dir_begin("//root/bar", EC), 1591 {"//root/foo/a", "//root/bar/b", "//root/bar/c"}); 1592 1593 Lower->addRegularFile("//root/foo/b"); 1594 checkContents(O->dir_begin("//root/bar", EC), 1595 {"//root/foo/a", "//root/foo/b", "//root/bar/c"}); 1596 1597 EXPECT_EQ(0, NumDiagnostics); 1598 } 1599 1600 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoExternalNames) { 1601 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1602 Lower->addDirectory("//root/foo"); 1603 Lower->addRegularFile("//root/foo/a"); 1604 Lower->addDirectory("//root/bar"); 1605 Lower->addRegularFile("//root/bar/b"); 1606 Lower->addRegularFile("//root/bar/c"); 1607 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1608 getFromYAMLString("{ 'use-external-names': false,\n" 1609 " 'roots': [\n" 1610 "{\n" 1611 " 'type': 'directory',\n" 1612 " 'name': '//root/',\n" 1613 " 'contents': [ {\n" 1614 " 'type': 'directory-remap',\n" 1615 " 'name': 'bar',\n" 1616 " 'external-contents': '//root/foo'\n" 1617 " }\n" 1618 " ]\n" 1619 "}]}", 1620 Lower); 1621 ASSERT_NE(FS.get(), nullptr); 1622 1623 ErrorOr<vfs::Status> S = FS->status("//root/foo"); 1624 ASSERT_FALSE(S.getError()); 1625 1626 ErrorOr<vfs::Status> SS = FS->status("//root/bar"); 1627 ASSERT_FALSE(SS.getError()); 1628 EXPECT_TRUE(S->equivalent(*SS)); 1629 1630 std::error_code EC; 1631 checkContents(FS->dir_begin("//root/bar", EC), 1632 {"//root/bar/a", "//root/bar/b", "//root/bar/c"}); 1633 1634 Lower->addRegularFile("//root/foo/b"); 1635 checkContents(FS->dir_begin("//root/bar", EC), 1636 {"//root/bar/a", "//root/bar/b", "//root/bar/c"}); 1637 1638 EXPECT_EQ(0, NumDiagnostics); 1639 } 1640 1641 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoFallthrough) { 1642 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1643 Lower->addDirectory("//root/foo"); 1644 Lower->addRegularFile("//root/foo/a"); 1645 Lower->addDirectory("//root/bar"); 1646 Lower->addRegularFile("//root/bar/b"); 1647 Lower->addRegularFile("//root/bar/c"); 1648 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1649 getFromYAMLString("{ 'fallthrough': false,\n" 1650 " 'roots': [\n" 1651 "{\n" 1652 " 'type': 'directory',\n" 1653 " 'name': '//root/',\n" 1654 " 'contents': [ {\n" 1655 " 'type': 'directory-remap',\n" 1656 " 'name': 'bar',\n" 1657 " 'external-contents': '//root/foo'\n" 1658 " }\n" 1659 " ]\n" 1660 "}]}", 1661 Lower); 1662 ASSERT_NE(FS.get(), nullptr); 1663 1664 ErrorOr<vfs::Status> S = Lower->status("//root/foo"); 1665 ASSERT_FALSE(S.getError()); 1666 1667 ErrorOr<vfs::Status> SS = FS->status("//root/bar"); 1668 ASSERT_FALSE(SS.getError()); 1669 EXPECT_TRUE(S->equivalent(*SS)); 1670 1671 std::error_code EC; 1672 checkContents(FS->dir_begin("//root/bar", EC), {"//root/foo/a"}); 1673 1674 Lower->addRegularFile("//root/foo/b"); 1675 checkContents(FS->dir_begin("//root/bar", EC), 1676 {"//root/foo/a", "//root/foo/b"}); 1677 1678 EXPECT_EQ(0, NumDiagnostics); 1679 } 1680 1681 TEST_F(VFSFromYAMLTest, ReturnsRequestedPathVFSMiss) { 1682 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS( 1683 new vfs::InMemoryFileSystem); 1684 BaseFS->addFile("//root/foo/a", 0, 1685 MemoryBuffer::getMemBuffer("contents of a")); 1686 ASSERT_FALSE(BaseFS->setCurrentWorkingDirectory("//root/foo")); 1687 auto RemappedFS = vfs::RedirectingFileSystem::create( 1688 {}, /*UseExternalNames=*/false, *BaseFS); 1689 1690 auto OpenedF = RemappedFS->openFileForRead("a"); 1691 ASSERT_FALSE(OpenedF.getError()); 1692 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName(); 1693 ASSERT_FALSE(Name.getError()); 1694 EXPECT_EQ("a", Name.get()); 1695 1696 auto OpenedS = (*OpenedF)->status(); 1697 ASSERT_FALSE(OpenedS.getError()); 1698 EXPECT_EQ("a", OpenedS->getName()); 1699 EXPECT_FALSE(OpenedS->IsVFSMapped); 1700 1701 auto DirectS = RemappedFS->status("a"); 1702 ASSERT_FALSE(DirectS.getError()); 1703 EXPECT_EQ("a", DirectS->getName()); 1704 EXPECT_FALSE(DirectS->IsVFSMapped); 1705 1706 EXPECT_EQ(0, NumDiagnostics); 1707 } 1708 1709 TEST_F(VFSFromYAMLTest, ReturnsExternalPathVFSHit) { 1710 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS( 1711 new vfs::InMemoryFileSystem); 1712 BaseFS->addFile("//root/foo/realname", 0, 1713 MemoryBuffer::getMemBuffer("contents of a")); 1714 auto FS = 1715 getFromYAMLString("{ 'use-external-names': true,\n" 1716 " 'roots': [\n" 1717 "{\n" 1718 " 'type': 'directory',\n" 1719 " 'name': '//root/foo',\n" 1720 " 'contents': [ {\n" 1721 " 'type': 'file',\n" 1722 " 'name': 'vfsname',\n" 1723 " 'external-contents': 'realname'\n" 1724 " }\n" 1725 " ]\n" 1726 "}]}", 1727 BaseFS); 1728 ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo")); 1729 1730 auto OpenedF = FS->openFileForRead("vfsname"); 1731 ASSERT_FALSE(OpenedF.getError()); 1732 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName(); 1733 ASSERT_FALSE(Name.getError()); 1734 EXPECT_EQ("realname", Name.get()); 1735 1736 auto OpenedS = (*OpenedF)->status(); 1737 ASSERT_FALSE(OpenedS.getError()); 1738 EXPECT_EQ("realname", OpenedS->getName()); 1739 EXPECT_TRUE(OpenedS->IsVFSMapped); 1740 1741 auto DirectS = FS->status("vfsname"); 1742 ASSERT_FALSE(DirectS.getError()); 1743 EXPECT_EQ("realname", DirectS->getName()); 1744 EXPECT_TRUE(DirectS->IsVFSMapped); 1745 1746 EXPECT_EQ(0, NumDiagnostics); 1747 } 1748 1749 TEST_F(VFSFromYAMLTest, ReturnsInternalPathVFSHit) { 1750 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS( 1751 new vfs::InMemoryFileSystem); 1752 BaseFS->addFile("//root/foo/realname", 0, 1753 MemoryBuffer::getMemBuffer("contents of a")); 1754 auto FS = 1755 getFromYAMLString("{ 'use-external-names': false,\n" 1756 " 'roots': [\n" 1757 "{\n" 1758 " 'type': 'directory',\n" 1759 " 'name': '//root/foo',\n" 1760 " 'contents': [ {\n" 1761 " 'type': 'file',\n" 1762 " 'name': 'vfsname',\n" 1763 " 'external-contents': 'realname'\n" 1764 " }\n" 1765 " ]\n" 1766 "}]}", 1767 BaseFS); 1768 ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo")); 1769 1770 auto OpenedF = FS->openFileForRead("vfsname"); 1771 ASSERT_FALSE(OpenedF.getError()); 1772 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName(); 1773 ASSERT_FALSE(Name.getError()); 1774 EXPECT_EQ("vfsname", Name.get()); 1775 1776 auto OpenedS = (*OpenedF)->status(); 1777 ASSERT_FALSE(OpenedS.getError()); 1778 EXPECT_EQ("vfsname", OpenedS->getName()); 1779 EXPECT_TRUE(OpenedS->IsVFSMapped); 1780 1781 auto DirectS = FS->status("vfsname"); 1782 ASSERT_FALSE(DirectS.getError()); 1783 EXPECT_EQ("vfsname", DirectS->getName()); 1784 EXPECT_TRUE(DirectS->IsVFSMapped); 1785 1786 EXPECT_EQ(0, NumDiagnostics); 1787 } 1788 1789 TEST_F(VFSFromYAMLTest, CaseInsensitive) { 1790 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1791 Lower->addRegularFile("//root/foo/bar/a"); 1792 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1793 "{ 'case-sensitive': 'false',\n" 1794 " 'roots': [\n" 1795 "{\n" 1796 " 'type': 'directory',\n" 1797 " 'name': '//root/',\n" 1798 " 'contents': [ {\n" 1799 " 'type': 'file',\n" 1800 " 'name': 'XX',\n" 1801 " 'external-contents': '//root/foo/bar/a'\n" 1802 " }\n" 1803 " ]\n" 1804 "}]}", 1805 Lower); 1806 ASSERT_NE(FS.get(), nullptr); 1807 1808 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1809 new vfs::OverlayFileSystem(Lower)); 1810 O->pushOverlay(FS); 1811 1812 ErrorOr<vfs::Status> S = O->status("//root/XX"); 1813 ASSERT_FALSE(S.getError()); 1814 1815 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 1816 ASSERT_FALSE(SS.getError()); 1817 EXPECT_TRUE(S->equivalent(*SS)); 1818 SS = O->status("//root/xX"); 1819 EXPECT_TRUE(S->equivalent(*SS)); 1820 SS = O->status("//root/Xx"); 1821 EXPECT_TRUE(S->equivalent(*SS)); 1822 EXPECT_EQ(0, NumDiagnostics); 1823 } 1824 1825 TEST_F(VFSFromYAMLTest, CaseSensitive) { 1826 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1827 Lower->addRegularFile("//root/foo/bar/a"); 1828 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1829 "{ 'case-sensitive': 'true',\n" 1830 " 'roots': [\n" 1831 "{\n" 1832 " 'type': 'directory',\n" 1833 " 'name': '//root/',\n" 1834 " 'contents': [ {\n" 1835 " 'type': 'file',\n" 1836 " 'name': 'XX',\n" 1837 " 'external-contents': '//root/foo/bar/a'\n" 1838 " }\n" 1839 " ]\n" 1840 "}]}", 1841 Lower); 1842 ASSERT_NE(FS.get(), nullptr); 1843 1844 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1845 new vfs::OverlayFileSystem(Lower)); 1846 O->pushOverlay(FS); 1847 1848 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 1849 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 1850 SS = O->status("//root/xX"); 1851 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 1852 SS = O->status("//root/Xx"); 1853 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 1854 EXPECT_EQ(0, NumDiagnostics); 1855 } 1856 1857 TEST_F(VFSFromYAMLTest, IllegalVFSFile) { 1858 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1859 1860 // invalid YAML at top-level 1861 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower); 1862 EXPECT_EQ(nullptr, FS.get()); 1863 // invalid YAML in roots 1864 FS = getFromYAMLString("{ 'roots':[}", Lower); 1865 // invalid YAML in directory 1866 FS = getFromYAMLString( 1867 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}", 1868 Lower); 1869 EXPECT_EQ(nullptr, FS.get()); 1870 1871 // invalid configuration 1872 FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower); 1873 EXPECT_EQ(nullptr, FS.get()); 1874 FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower); 1875 EXPECT_EQ(nullptr, FS.get()); 1876 1877 // invalid roots 1878 FS = getFromYAMLString("{ 'roots':'' }", Lower); 1879 EXPECT_EQ(nullptr, FS.get()); 1880 FS = getFromYAMLString("{ 'roots':{} }", Lower); 1881 EXPECT_EQ(nullptr, FS.get()); 1882 1883 // invalid entries 1884 FS = getFromYAMLString( 1885 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower); 1886 EXPECT_EQ(nullptr, FS.get()); 1887 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], " 1888 "'external-contents': 'other' }", 1889 Lower); 1890 EXPECT_EQ(nullptr, FS.get()); 1891 FS = getFromYAMLString( 1892 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }", 1893 Lower); 1894 EXPECT_EQ(nullptr, FS.get()); 1895 FS = getFromYAMLString( 1896 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }", 1897 Lower); 1898 EXPECT_EQ(nullptr, FS.get()); 1899 FS = getFromYAMLString( 1900 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }", 1901 Lower); 1902 EXPECT_EQ(nullptr, FS.get()); 1903 FS = getFromYAMLString( 1904 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }", 1905 Lower); 1906 EXPECT_EQ(nullptr, FS.get()); 1907 FS = getFromYAMLString( 1908 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }", 1909 Lower); 1910 EXPECT_EQ(nullptr, FS.get()); 1911 1912 // missing mandatory fields 1913 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower); 1914 EXPECT_EQ(nullptr, FS.get()); 1915 FS = getFromYAMLString( 1916 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower); 1917 EXPECT_EQ(nullptr, FS.get()); 1918 FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower); 1919 EXPECT_EQ(nullptr, FS.get()); 1920 1921 // duplicate keys 1922 FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower); 1923 EXPECT_EQ(nullptr, FS.get()); 1924 FS = getFromYAMLString( 1925 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }", 1926 Lower); 1927 EXPECT_EQ(nullptr, FS.get()); 1928 FS = 1929 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', " 1930 "'external-contents':'blah' } ] }", 1931 Lower); 1932 EXPECT_EQ(nullptr, FS.get()); 1933 1934 // missing version 1935 FS = getFromYAMLRawString("{ 'roots':[] }", Lower); 1936 EXPECT_EQ(nullptr, FS.get()); 1937 1938 // bad version number 1939 FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower); 1940 EXPECT_EQ(nullptr, FS.get()); 1941 FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower); 1942 EXPECT_EQ(nullptr, FS.get()); 1943 FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower); 1944 EXPECT_EQ(nullptr, FS.get()); 1945 1946 // both 'external-contents' and 'contents' specified 1947 Lower->addDirectory("//root/external/dir"); 1948 FS = getFromYAMLString( 1949 "{ 'roots':[ \n" 1950 "{ 'type': 'directory', 'name': '//root/A', 'contents': [],\n" 1951 " 'external-contents': '//root/external/dir'}]}", 1952 Lower); 1953 EXPECT_EQ(nullptr, FS.get()); 1954 1955 // 'directory-remap' with 'contents' 1956 FS = getFromYAMLString( 1957 "{ 'roots':[ \n" 1958 "{ 'type': 'directory-remap', 'name': '//root/A', 'contents': [] }]}", 1959 Lower); 1960 EXPECT_EQ(nullptr, FS.get()); 1961 1962 // invalid redirect kind 1963 FS = getFromYAMLString("{ 'redirecting-with': 'none', 'roots': [{\n" 1964 " 'type': 'directory-remap',\n" 1965 " 'name': '//root/A',\n" 1966 " 'external-contents': '//root/B' }]}", 1967 Lower); 1968 EXPECT_EQ(nullptr, FS.get()); 1969 1970 // redirect and fallthrough passed 1971 FS = getFromYAMLString("{ 'redirecting-with': 'fallthrough',\n" 1972 " 'fallthrough': true,\n" 1973 " 'roots': [{\n" 1974 " 'type': 'directory-remap',\n" 1975 " 'name': '//root/A',\n" 1976 " 'external-contents': '//root/B' }]}", 1977 Lower); 1978 EXPECT_EQ(nullptr, FS.get()); 1979 1980 EXPECT_EQ(28, NumDiagnostics); 1981 } 1982 1983 TEST_F(VFSFromYAMLTest, UseExternalName) { 1984 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1985 Lower->addRegularFile("//root/external/file"); 1986 1987 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1988 getFromYAMLString("{ 'roots': [\n" 1989 " { 'type': 'file', 'name': '//root/A',\n" 1990 " 'external-contents': '//root/external/file'\n" 1991 " },\n" 1992 " { 'type': 'file', 'name': '//root/B',\n" 1993 " 'use-external-name': true,\n" 1994 " 'external-contents': '//root/external/file'\n" 1995 " },\n" 1996 " { 'type': 'file', 'name': '//root/C',\n" 1997 " 'use-external-name': false,\n" 1998 " 'external-contents': '//root/external/file'\n" 1999 " }\n" 2000 "] }", 2001 Lower); 2002 ASSERT_NE(nullptr, FS.get()); 2003 2004 // default true 2005 EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName()); 2006 // explicit 2007 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 2008 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 2009 2010 // global configuration 2011 FS = getFromYAMLString("{ 'use-external-names': false,\n" 2012 " 'roots': [\n" 2013 " { 'type': 'file', 'name': '//root/A',\n" 2014 " 'external-contents': '//root/external/file'\n" 2015 " },\n" 2016 " { 'type': 'file', 'name': '//root/B',\n" 2017 " 'use-external-name': true,\n" 2018 " 'external-contents': '//root/external/file'\n" 2019 " },\n" 2020 " { 'type': 'file', 'name': '//root/C',\n" 2021 " 'use-external-name': false,\n" 2022 " 'external-contents': '//root/external/file'\n" 2023 " }\n" 2024 "] }", 2025 Lower); 2026 ASSERT_NE(nullptr, FS.get()); 2027 2028 // default 2029 EXPECT_EQ("//root/A", FS->status("//root/A")->getName()); 2030 // explicit 2031 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 2032 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 2033 } 2034 2035 TEST_F(VFSFromYAMLTest, MultiComponentPath) { 2036 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2037 Lower->addRegularFile("//root/other"); 2038 2039 // file in roots 2040 IntrusiveRefCntPtr<vfs::FileSystem> FS = 2041 getFromYAMLString("{ 'roots': [\n" 2042 " { 'type': 'file', 'name': '//root/path/to/file',\n" 2043 " 'external-contents': '//root/other' }]\n" 2044 "}", 2045 Lower); 2046 ASSERT_NE(nullptr, FS.get()); 2047 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 2048 EXPECT_FALSE(FS->status("//root/path/to").getError()); 2049 EXPECT_FALSE(FS->status("//root/path").getError()); 2050 EXPECT_FALSE(FS->status("//root/").getError()); 2051 2052 // at the start 2053 FS = getFromYAMLString( 2054 "{ 'roots': [\n" 2055 " { 'type': 'directory', 'name': '//root/path/to',\n" 2056 " 'contents': [ { 'type': 'file', 'name': 'file',\n" 2057 " 'external-contents': '//root/other' }]}]\n" 2058 "}", 2059 Lower); 2060 ASSERT_NE(nullptr, FS.get()); 2061 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 2062 EXPECT_FALSE(FS->status("//root/path/to").getError()); 2063 EXPECT_FALSE(FS->status("//root/path").getError()); 2064 EXPECT_FALSE(FS->status("//root/").getError()); 2065 2066 // at the end 2067 FS = getFromYAMLString( 2068 "{ 'roots': [\n" 2069 " { 'type': 'directory', 'name': '//root/',\n" 2070 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n" 2071 " 'external-contents': '//root/other' }]}]\n" 2072 "}", 2073 Lower); 2074 ASSERT_NE(nullptr, FS.get()); 2075 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 2076 EXPECT_FALSE(FS->status("//root/path/to").getError()); 2077 EXPECT_FALSE(FS->status("//root/path").getError()); 2078 EXPECT_FALSE(FS->status("//root/").getError()); 2079 } 2080 2081 TEST_F(VFSFromYAMLTest, TrailingSlashes) { 2082 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2083 Lower->addRegularFile("//root/other"); 2084 2085 // file in roots 2086 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2087 "{ 'roots': [\n" 2088 " { 'type': 'directory', 'name': '//root/path/to////',\n" 2089 " 'contents': [ { 'type': 'file', 'name': '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, DirectoryIteration) { 2101 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2102 Lower->addDirectory("//root/"); 2103 Lower->addDirectory("//root/foo"); 2104 Lower->addDirectory("//root/foo/bar"); 2105 Lower->addRegularFile("//root/foo/bar/a"); 2106 Lower->addRegularFile("//root/foo/bar/b"); 2107 Lower->addRegularFile("//root/file3"); 2108 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2109 "{ 'use-external-names': false,\n" 2110 " 'roots': [\n" 2111 "{\n" 2112 " 'type': 'directory',\n" 2113 " 'name': '//root/',\n" 2114 " 'contents': [ {\n" 2115 " 'type': 'file',\n" 2116 " 'name': 'file1',\n" 2117 " 'external-contents': '//root/foo/bar/a'\n" 2118 " },\n" 2119 " {\n" 2120 " 'type': 'file',\n" 2121 " 'name': 'file2',\n" 2122 " 'external-contents': '//root/foo/bar/b'\n" 2123 " }\n" 2124 " ]\n" 2125 "}\n" 2126 "]\n" 2127 "}", 2128 Lower); 2129 ASSERT_NE(FS.get(), nullptr); 2130 2131 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 2132 new vfs::OverlayFileSystem(Lower)); 2133 O->pushOverlay(FS); 2134 2135 std::error_code EC; 2136 checkContents(O->dir_begin("//root/", EC), 2137 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"}); 2138 2139 checkContents(O->dir_begin("//root/foo/bar", EC), 2140 {"//root/foo/bar/a", "//root/foo/bar/b"}); 2141 } 2142 2143 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) { 2144 // https://llvm.org/bugs/show_bug.cgi?id=27725 2145 if (!supportsSameDirMultipleYAMLEntries()) 2146 return; 2147 2148 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2149 Lower->addDirectory("//root/zab"); 2150 Lower->addDirectory("//root/baz"); 2151 Lower->addRegularFile("//root/zab/a"); 2152 Lower->addRegularFile("//root/zab/b"); 2153 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2154 "{ 'use-external-names': false,\n" 2155 " 'roots': [\n" 2156 "{\n" 2157 " 'type': 'directory',\n" 2158 " 'name': '//root/baz/',\n" 2159 " 'contents': [ {\n" 2160 " 'type': 'file',\n" 2161 " 'name': 'x',\n" 2162 " 'external-contents': '//root/zab/a'\n" 2163 " }\n" 2164 " ]\n" 2165 "},\n" 2166 "{\n" 2167 " 'type': 'directory',\n" 2168 " 'name': '//root/baz/',\n" 2169 " 'contents': [ {\n" 2170 " 'type': 'file',\n" 2171 " 'name': 'y',\n" 2172 " 'external-contents': '//root/zab/b'\n" 2173 " }\n" 2174 " ]\n" 2175 "}\n" 2176 "]\n" 2177 "}", 2178 Lower); 2179 ASSERT_NE(FS.get(), nullptr); 2180 2181 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 2182 new vfs::OverlayFileSystem(Lower)); 2183 O->pushOverlay(FS); 2184 2185 std::error_code EC; 2186 2187 checkContents(O->dir_begin("//root/baz/", EC), 2188 {"//root/baz/x", "//root/baz/y"}); 2189 } 2190 2191 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) { 2192 2193 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2194 Lower->addDirectory("//root/a"); 2195 Lower->addDirectory("//root/a/b"); 2196 Lower->addDirectory("//root/a/b/c"); 2197 Lower->addRegularFile("//root/a/b/c/file"); 2198 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2199 "{ 'use-external-names': false,\n" 2200 " 'roots': [\n" 2201 "{\n" 2202 " 'type': 'directory',\n" 2203 " 'name': '//root/a/b/c/',\n" 2204 " 'contents': [ {\n" 2205 " 'type': 'file',\n" 2206 " 'name': 'file',\n" 2207 " 'external-contents': '//root/a/b/c/file'\n" 2208 " }\n" 2209 " ]\n" 2210 "},\n" 2211 "]\n" 2212 "}", 2213 Lower); 2214 ASSERT_NE(FS.get(), nullptr); 2215 2216 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 2217 new vfs::OverlayFileSystem(Lower)); 2218 O->pushOverlay(FS); 2219 2220 std::error_code EC; 2221 2222 // Test recursive_directory_iterator level() 2223 vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator( 2224 *O, "//root", EC), 2225 E; 2226 ASSERT_FALSE(EC); 2227 for (int l = 0; I != E; I.increment(EC), ++l) { 2228 ASSERT_FALSE(EC); 2229 EXPECT_EQ(I.level(), l); 2230 } 2231 EXPECT_EQ(I, E); 2232 } 2233 2234 TEST_F(VFSFromYAMLTest, RelativePaths) { 2235 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2236 std::error_code EC; 2237 SmallString<128> CWD; 2238 EC = llvm::sys::fs::current_path(CWD); 2239 ASSERT_FALSE(EC); 2240 2241 // Filename at root level without a parent directory. 2242 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2243 "{ 'roots': [\n" 2244 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n" 2245 " 'external-contents': '//root/external/file'\n" 2246 " }\n" 2247 "] }", 2248 Lower); 2249 ASSERT_TRUE(FS.get() != nullptr); 2250 SmallString<128> ExpectedPathNotInDir("file-not-in-directory.h"); 2251 llvm::sys::fs::make_absolute(ExpectedPathNotInDir); 2252 checkContents(FS->dir_begin(CWD, EC), {ExpectedPathNotInDir}); 2253 2254 // Relative file path. 2255 FS = getFromYAMLString("{ 'roots': [\n" 2256 " { 'type': 'file', 'name': 'relative/path.h',\n" 2257 " 'external-contents': '//root/external/file'\n" 2258 " }\n" 2259 "] }", 2260 Lower); 2261 ASSERT_TRUE(FS.get() != nullptr); 2262 SmallString<128> Parent("relative"); 2263 llvm::sys::fs::make_absolute(Parent); 2264 auto I = FS->dir_begin(Parent, EC); 2265 ASSERT_FALSE(EC); 2266 // Convert to POSIX path for comparison of windows paths 2267 ASSERT_EQ("relative/path.h", 2268 getPosixPath(std::string(I->path().substr(CWD.size() + 1)))); 2269 2270 // Relative directory path. 2271 FS = getFromYAMLString( 2272 "{ 'roots': [\n" 2273 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n" 2274 " 'contents': []\n" 2275 " }\n" 2276 "] }", 2277 Lower); 2278 ASSERT_TRUE(FS.get() != nullptr); 2279 SmallString<128> Root("relative/directory"); 2280 llvm::sys::fs::make_absolute(Root); 2281 I = FS->dir_begin(Root, EC); 2282 ASSERT_FALSE(EC); 2283 ASSERT_EQ("path.h", std::string(I->path().substr(Root.size() + 1))); 2284 2285 EXPECT_EQ(0, NumDiagnostics); 2286 } 2287 2288 TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) { 2289 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2290 Lower->addDirectory("//root/"); 2291 Lower->addRegularFile("//root/a"); 2292 Lower->addRegularFile("//root/b"); 2293 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2294 "{ 'use-external-names': false,\n" 2295 " 'fallthrough': false,\n" 2296 " 'roots': [\n" 2297 "{\n" 2298 " 'type': 'directory',\n" 2299 " 'name': '//root/',\n" 2300 " 'contents': [ {\n" 2301 " 'type': 'file',\n" 2302 " 'name': 'c',\n" 2303 " 'external-contents': '//root/a'\n" 2304 " }\n" 2305 " ]\n" 2306 "}\n" 2307 "]\n" 2308 "}", 2309 Lower); 2310 ASSERT_NE(FS.get(), nullptr); 2311 2312 std::error_code EC; 2313 checkContents(FS->dir_begin("//root/", EC), 2314 {"//root/c"}); 2315 } 2316 2317 TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) { 2318 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2319 Lower->addDirectory("//root/"); 2320 Lower->addRegularFile("//root/a"); 2321 Lower->addRegularFile("//root/b"); 2322 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2323 "{ 'use-external-names': false,\n" 2324 " 'roots': [\n" 2325 "{\n" 2326 " 'type': 'directory',\n" 2327 " 'name': '//root/',\n" 2328 " 'contents': [ {\n" 2329 " 'type': 'file',\n" 2330 " 'name': 'a',\n" 2331 " 'external-contents': '//root/a'\n" 2332 " }\n" 2333 " ]\n" 2334 "}\n" 2335 "]\n" 2336 "}", 2337 Lower); 2338 ASSERT_NE(FS.get(), nullptr); 2339 2340 std::error_code EC; 2341 checkContents(FS->dir_begin("//root/", EC), 2342 {"//root/a", "//root/b"}); 2343 } 2344 2345 TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) { 2346 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2347 Lower->addDirectory("//root/"); 2348 Lower->addDirectory("//root/foo"); 2349 Lower->addRegularFile("//root/foo/a"); 2350 Lower->addRegularFile("//root/foo/b"); 2351 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2352 "{ 'use-external-names': false,\n" 2353 " 'roots': [\n" 2354 "{\n" 2355 " 'type': 'directory',\n" 2356 " 'name': '//root/',\n" 2357 " 'contents': [ {\n" 2358 " 'type': 'file',\n" 2359 " 'name': 'bar/a',\n" 2360 " 'external-contents': '//root/foo/a'\n" 2361 " }\n" 2362 " ]\n" 2363 "}\n" 2364 "]\n" 2365 "}", 2366 Lower); 2367 ASSERT_NE(FS.get(), nullptr); 2368 2369 std::error_code EC; 2370 checkContents(FS->dir_begin("//root/foo", EC), 2371 {"//root/foo/a", "//root/foo/b"}); 2372 } 2373 2374 TEST_F(VFSFromYAMLTest, GetRealPath) { 2375 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2376 Lower->addDirectory("//dir/"); 2377 Lower->addRegularFile("/foo"); 2378 Lower->addSymlink("/link"); 2379 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2380 "{ 'use-external-names': false,\n" 2381 " 'roots': [\n" 2382 "{\n" 2383 " 'type': 'directory',\n" 2384 " 'name': '//root/',\n" 2385 " 'contents': [ {\n" 2386 " 'type': 'file',\n" 2387 " 'name': 'bar',\n" 2388 " 'external-contents': '/link'\n" 2389 " }\n" 2390 " ]\n" 2391 "},\n" 2392 "{\n" 2393 " 'type': 'directory',\n" 2394 " 'name': '//dir/',\n" 2395 " 'contents': []\n" 2396 "}\n" 2397 "]\n" 2398 "}", 2399 Lower); 2400 ASSERT_NE(FS.get(), nullptr); 2401 2402 // Regular file present in underlying file system. 2403 SmallString<16> RealPath; 2404 EXPECT_FALSE(FS->getRealPath("/foo", RealPath)); 2405 EXPECT_EQ(RealPath.str(), "/foo"); 2406 2407 // File present in YAML pointing to symlink in underlying file system. 2408 EXPECT_FALSE(FS->getRealPath("//root/bar", RealPath)); 2409 EXPECT_EQ(RealPath.str(), "/symlink"); 2410 2411 // Directories should fall back to the underlying file system is possible. 2412 EXPECT_FALSE(FS->getRealPath("//dir/", RealPath)); 2413 EXPECT_EQ(RealPath.str(), "//dir/"); 2414 2415 // Try a non-existing file. 2416 EXPECT_EQ(FS->getRealPath("/non_existing", RealPath), 2417 errc::no_such_file_or_directory); 2418 } 2419 2420 TEST_F(VFSFromYAMLTest, WorkingDirectory) { 2421 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2422 Lower->addDirectory("//root/"); 2423 Lower->addDirectory("//root/foo"); 2424 Lower->addRegularFile("//root/foo/a"); 2425 Lower->addRegularFile("//root/foo/b"); 2426 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2427 "{ 'use-external-names': false,\n" 2428 " 'roots': [\n" 2429 "{\n" 2430 " 'type': 'directory',\n" 2431 " 'name': '//root/bar',\n" 2432 " 'contents': [ {\n" 2433 " 'type': 'file',\n" 2434 " 'name': 'a',\n" 2435 " 'external-contents': '//root/foo/a'\n" 2436 " }\n" 2437 " ]\n" 2438 "}\n" 2439 "]\n" 2440 "}", 2441 Lower); 2442 ASSERT_NE(FS.get(), nullptr); 2443 std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar"); 2444 ASSERT_FALSE(EC); 2445 2446 llvm::ErrorOr<std::string> WorkingDir = FS->getCurrentWorkingDirectory(); 2447 ASSERT_TRUE(WorkingDir); 2448 EXPECT_EQ(*WorkingDir, "//root/bar"); 2449 2450 llvm::ErrorOr<vfs::Status> Status = FS->status("./a"); 2451 ASSERT_FALSE(Status.getError()); 2452 EXPECT_TRUE(Status->isStatusKnown()); 2453 EXPECT_FALSE(Status->isDirectory()); 2454 EXPECT_TRUE(Status->isRegularFile()); 2455 EXPECT_FALSE(Status->isSymlink()); 2456 EXPECT_FALSE(Status->isOther()); 2457 EXPECT_TRUE(Status->exists()); 2458 2459 EC = FS->setCurrentWorkingDirectory("bogus"); 2460 ASSERT_TRUE(EC); 2461 WorkingDir = FS->getCurrentWorkingDirectory(); 2462 ASSERT_TRUE(WorkingDir); 2463 EXPECT_EQ(*WorkingDir, "//root/bar"); 2464 2465 EC = FS->setCurrentWorkingDirectory("//root/"); 2466 ASSERT_FALSE(EC); 2467 WorkingDir = FS->getCurrentWorkingDirectory(); 2468 ASSERT_TRUE(WorkingDir); 2469 EXPECT_EQ(*WorkingDir, "//root/"); 2470 2471 EC = FS->setCurrentWorkingDirectory("bar"); 2472 ASSERT_FALSE(EC); 2473 WorkingDir = FS->getCurrentWorkingDirectory(); 2474 ASSERT_TRUE(WorkingDir); 2475 EXPECT_EQ(*WorkingDir, "//root/bar"); 2476 } 2477 2478 TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthrough) { 2479 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2480 Lower->addDirectory("//root/"); 2481 Lower->addDirectory("//root/foo"); 2482 Lower->addRegularFile("//root/foo/a"); 2483 Lower->addRegularFile("//root/foo/b"); 2484 Lower->addRegularFile("//root/c"); 2485 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2486 "{ 'use-external-names': false,\n" 2487 " 'roots': [\n" 2488 "{\n" 2489 " 'type': 'directory',\n" 2490 " 'name': '//root/bar',\n" 2491 " 'contents': [ {\n" 2492 " 'type': 'file',\n" 2493 " 'name': 'a',\n" 2494 " 'external-contents': '//root/foo/a'\n" 2495 " }\n" 2496 " ]\n" 2497 "},\n" 2498 "{\n" 2499 " 'type': 'directory',\n" 2500 " 'name': '//root/bar/baz',\n" 2501 " 'contents': [ {\n" 2502 " 'type': 'file',\n" 2503 " 'name': 'a',\n" 2504 " 'external-contents': '//root/foo/a'\n" 2505 " }\n" 2506 " ]\n" 2507 "}\n" 2508 "]\n" 2509 "}", 2510 Lower); 2511 ASSERT_NE(FS.get(), nullptr); 2512 std::error_code EC = FS->setCurrentWorkingDirectory("//root/"); 2513 ASSERT_FALSE(EC); 2514 ASSERT_NE(FS.get(), nullptr); 2515 2516 llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a"); 2517 ASSERT_FALSE(Status.getError()); 2518 EXPECT_TRUE(Status->exists()); 2519 2520 Status = FS->status("foo/a"); 2521 ASSERT_FALSE(Status.getError()); 2522 EXPECT_TRUE(Status->exists()); 2523 2524 EC = FS->setCurrentWorkingDirectory("//root/bar"); 2525 ASSERT_FALSE(EC); 2526 2527 Status = FS->status("./a"); 2528 ASSERT_FALSE(Status.getError()); 2529 EXPECT_TRUE(Status->exists()); 2530 2531 Status = FS->status("./b"); 2532 ASSERT_TRUE(Status.getError()); 2533 2534 Status = FS->status("./c"); 2535 ASSERT_TRUE(Status.getError()); 2536 2537 EC = FS->setCurrentWorkingDirectory("//root/"); 2538 ASSERT_FALSE(EC); 2539 2540 Status = FS->status("c"); 2541 ASSERT_FALSE(Status.getError()); 2542 EXPECT_TRUE(Status->exists()); 2543 2544 Status = FS->status("./bar/baz/a"); 2545 ASSERT_FALSE(Status.getError()); 2546 EXPECT_TRUE(Status->exists()); 2547 2548 EC = FS->setCurrentWorkingDirectory("//root/bar"); 2549 ASSERT_FALSE(EC); 2550 2551 Status = FS->status("./baz/a"); 2552 ASSERT_FALSE(Status.getError()); 2553 EXPECT_TRUE(Status->exists()); 2554 2555 Status = FS->status("../bar/baz/a"); 2556 ASSERT_FALSE(Status.getError()); 2557 EXPECT_TRUE(Status->exists()); 2558 } 2559 2560 TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthroughInvalid) { 2561 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2562 Lower->addDirectory("//root/"); 2563 Lower->addDirectory("//root/foo"); 2564 Lower->addRegularFile("//root/foo/a"); 2565 Lower->addRegularFile("//root/foo/b"); 2566 Lower->addRegularFile("//root/c"); 2567 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2568 "{ 'use-external-names': false,\n" 2569 " 'roots': [\n" 2570 "{\n" 2571 " 'type': 'directory',\n" 2572 " 'name': '//root/bar',\n" 2573 " 'contents': [ {\n" 2574 " 'type': 'file',\n" 2575 " 'name': 'a',\n" 2576 " 'external-contents': '//root/foo/a'\n" 2577 " }\n" 2578 " ]\n" 2579 "}\n" 2580 "]\n" 2581 "}", 2582 Lower); 2583 ASSERT_NE(FS.get(), nullptr); 2584 std::error_code EC = FS->setCurrentWorkingDirectory("//root/"); 2585 ASSERT_FALSE(EC); 2586 ASSERT_NE(FS.get(), nullptr); 2587 2588 llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a"); 2589 ASSERT_FALSE(Status.getError()); 2590 EXPECT_TRUE(Status->exists()); 2591 2592 Status = FS->status("foo/a"); 2593 ASSERT_FALSE(Status.getError()); 2594 EXPECT_TRUE(Status->exists()); 2595 } 2596 2597 TEST_F(VFSFromYAMLTest, VirtualWorkingDirectory) { 2598 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2599 Lower->addDirectory("//root/"); 2600 Lower->addDirectory("//root/foo"); 2601 Lower->addRegularFile("//root/foo/a"); 2602 Lower->addRegularFile("//root/foo/b"); 2603 Lower->addRegularFile("//root/c"); 2604 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2605 "{ 'use-external-names': false,\n" 2606 " 'roots': [\n" 2607 "{\n" 2608 " 'type': 'directory',\n" 2609 " 'name': '//root/bar',\n" 2610 " 'contents': [ {\n" 2611 " 'type': 'file',\n" 2612 " 'name': 'a',\n" 2613 " 'external-contents': '//root/foo/a'\n" 2614 " }\n" 2615 " ]\n" 2616 "}\n" 2617 "]\n" 2618 "}", 2619 Lower); 2620 ASSERT_NE(FS.get(), nullptr); 2621 std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar"); 2622 ASSERT_FALSE(EC); 2623 ASSERT_NE(FS.get(), nullptr); 2624 2625 llvm::ErrorOr<vfs::Status> Status = FS->status("a"); 2626 ASSERT_FALSE(Status.getError()); 2627 EXPECT_TRUE(Status->exists()); 2628 } 2629 2630 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest) { 2631 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2632 TempDir _a(TestDirectory.path("a")); 2633 TempFile _ab(TestDirectory.path("a, b")); 2634 TempDir _c(TestDirectory.path("c")); 2635 TempFile _cd(TestDirectory.path("c/d")); 2636 TempDir _e(TestDirectory.path("e")); 2637 TempDir _ef(TestDirectory.path("e/f")); 2638 TempFile _g(TestDirectory.path("g")); 2639 TempDir _h(TestDirectory.path("h")); 2640 2641 vfs::YAMLVFSWriter VFSWriter; 2642 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2643 VFSWriter.addFileMapping(_ab.path(), "//root/a/b"); 2644 VFSWriter.addFileMapping(_cd.path(), "//root/c/d"); 2645 VFSWriter.addDirectoryMapping(_e.path(), "//root/e"); 2646 VFSWriter.addDirectoryMapping(_ef.path(), "//root/e/f"); 2647 VFSWriter.addFileMapping(_g.path(), "//root/g"); 2648 VFSWriter.addDirectoryMapping(_h.path(), "//root/h"); 2649 2650 std::string Buffer; 2651 raw_string_ostream OS(Buffer); 2652 VFSWriter.write(OS); 2653 OS.flush(); 2654 2655 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2656 Lower->addDirectory("//root/"); 2657 Lower->addDirectory("//root/a"); 2658 Lower->addRegularFile("//root/a/b"); 2659 Lower->addDirectory("//root/b"); 2660 Lower->addDirectory("//root/c"); 2661 Lower->addRegularFile("//root/c/d"); 2662 Lower->addDirectory("//root/e"); 2663 Lower->addDirectory("//root/e/f"); 2664 Lower->addRegularFile("//root/g"); 2665 Lower->addDirectory("//root/h"); 2666 2667 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2668 ASSERT_NE(FS.get(), nullptr); 2669 2670 EXPECT_TRUE(FS->exists(_a.path())); 2671 EXPECT_TRUE(FS->exists(_ab.path())); 2672 EXPECT_TRUE(FS->exists(_c.path())); 2673 EXPECT_TRUE(FS->exists(_cd.path())); 2674 EXPECT_TRUE(FS->exists(_e.path())); 2675 EXPECT_TRUE(FS->exists(_ef.path())); 2676 EXPECT_TRUE(FS->exists(_g.path())); 2677 EXPECT_TRUE(FS->exists(_h.path())); 2678 } 2679 2680 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest2) { 2681 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2682 TempDir _a(TestDirectory.path("a")); 2683 TempFile _ab(TestDirectory.path("a/b")); 2684 TempDir _ac(TestDirectory.path("a/c")); 2685 TempFile _acd(TestDirectory.path("a/c/d")); 2686 TempFile _ace(TestDirectory.path("a/c/e")); 2687 TempFile _acf(TestDirectory.path("a/c/f")); 2688 TempDir _ag(TestDirectory.path("a/g")); 2689 TempFile _agh(TestDirectory.path("a/g/h")); 2690 2691 vfs::YAMLVFSWriter VFSWriter; 2692 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2693 VFSWriter.addFileMapping(_ab.path(), "//root/a/b"); 2694 VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c"); 2695 VFSWriter.addFileMapping(_acd.path(), "//root/a/c/d"); 2696 VFSWriter.addFileMapping(_ace.path(), "//root/a/c/e"); 2697 VFSWriter.addFileMapping(_acf.path(), "//root/a/c/f"); 2698 VFSWriter.addDirectoryMapping(_ag.path(), "//root/a/g"); 2699 VFSWriter.addFileMapping(_agh.path(), "//root/a/g/h"); 2700 2701 std::string Buffer; 2702 raw_string_ostream OS(Buffer); 2703 VFSWriter.write(OS); 2704 OS.flush(); 2705 2706 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2707 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2708 EXPECT_NE(FS.get(), nullptr); 2709 } 2710 2711 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest3) { 2712 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2713 TempDir _a(TestDirectory.path("a")); 2714 TempFile _ab(TestDirectory.path("a/b")); 2715 TempDir _ac(TestDirectory.path("a/c")); 2716 TempDir _acd(TestDirectory.path("a/c/d")); 2717 TempDir _acde(TestDirectory.path("a/c/d/e")); 2718 TempFile _acdef(TestDirectory.path("a/c/d/e/f")); 2719 TempFile _acdeg(TestDirectory.path("a/c/d/e/g")); 2720 TempDir _ah(TestDirectory.path("a/h")); 2721 TempFile _ahi(TestDirectory.path("a/h/i")); 2722 2723 vfs::YAMLVFSWriter VFSWriter; 2724 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2725 VFSWriter.addFileMapping(_ab.path(), "//root/a/b"); 2726 VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c"); 2727 VFSWriter.addDirectoryMapping(_acd.path(), "//root/a/c/d"); 2728 VFSWriter.addDirectoryMapping(_acde.path(), "//root/a/c/d/e"); 2729 VFSWriter.addFileMapping(_acdef.path(), "//root/a/c/d/e/f"); 2730 VFSWriter.addFileMapping(_acdeg.path(), "//root/a/c/d/e/g"); 2731 VFSWriter.addDirectoryMapping(_ahi.path(), "//root/a/h"); 2732 VFSWriter.addFileMapping(_ahi.path(), "//root/a/h/i"); 2733 2734 std::string Buffer; 2735 raw_string_ostream OS(Buffer); 2736 VFSWriter.write(OS); 2737 OS.flush(); 2738 2739 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2740 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2741 EXPECT_NE(FS.get(), nullptr); 2742 } 2743 2744 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTestHandleDirs) { 2745 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2746 TempDir _a(TestDirectory.path("a")); 2747 TempDir _b(TestDirectory.path("b")); 2748 TempDir _c(TestDirectory.path("c")); 2749 2750 vfs::YAMLVFSWriter VFSWriter; 2751 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2752 VFSWriter.addDirectoryMapping(_b.path(), "//root/b"); 2753 VFSWriter.addDirectoryMapping(_c.path(), "//root/c"); 2754 2755 std::string Buffer; 2756 raw_string_ostream OS(Buffer); 2757 VFSWriter.write(OS); 2758 OS.flush(); 2759 2760 // We didn't add a single file - only directories. 2761 EXPECT_EQ(Buffer.find("'type': 'file'"), std::string::npos); 2762 2763 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2764 Lower->addDirectory("//root/a"); 2765 Lower->addDirectory("//root/b"); 2766 Lower->addDirectory("//root/c"); 2767 // canaries 2768 Lower->addRegularFile("//root/a/a"); 2769 Lower->addRegularFile("//root/b/b"); 2770 Lower->addRegularFile("//root/c/c"); 2771 2772 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2773 ASSERT_NE(FS.get(), nullptr); 2774 2775 EXPECT_FALSE(FS->exists(_a.path("a"))); 2776 EXPECT_FALSE(FS->exists(_b.path("b"))); 2777 EXPECT_FALSE(FS->exists(_c.path("c"))); 2778 } 2779 2780 TEST_F(VFSFromYAMLTest, RedirectingWith) { 2781 IntrusiveRefCntPtr<DummyFileSystem> Both(new DummyFileSystem()); 2782 Both->addDirectory("//root/a"); 2783 Both->addRegularFile("//root/a/f"); 2784 Both->addDirectory("//root/b"); 2785 Both->addRegularFile("//root/b/f"); 2786 2787 IntrusiveRefCntPtr<DummyFileSystem> AOnly(new DummyFileSystem()); 2788 AOnly->addDirectory("//root/a"); 2789 AOnly->addRegularFile("//root/a/f"); 2790 2791 IntrusiveRefCntPtr<DummyFileSystem> BOnly(new DummyFileSystem()); 2792 BOnly->addDirectory("//root/b"); 2793 BOnly->addRegularFile("//root/b/f"); 2794 2795 auto BaseStr = std::string(" 'roots': [\n" 2796 " {\n" 2797 " 'type': 'directory-remap',\n" 2798 " 'name': '//root/a',\n" 2799 " 'external-contents': '//root/b'\n" 2800 " }\n" 2801 " ]\n" 2802 "}"); 2803 auto FallthroughStr = "{ 'redirecting-with': 'fallthrough',\n" + BaseStr; 2804 auto FallbackStr = "{ 'redirecting-with': 'fallback',\n" + BaseStr; 2805 auto RedirectOnlyStr = "{ 'redirecting-with': 'redirect-only',\n" + BaseStr; 2806 2807 auto ExpectPath = [&](vfs::FileSystem &FS, StringRef Expected, 2808 StringRef Message) { 2809 auto AF = FS.openFileForRead("//root/a/f"); 2810 ASSERT_FALSE(AF.getError()) << Message; 2811 auto AFName = (*AF)->getName(); 2812 ASSERT_FALSE(AFName.getError()) << Message; 2813 EXPECT_EQ(Expected.str(), AFName.get()) << Message; 2814 2815 auto AS = FS.status("//root/a/f"); 2816 ASSERT_FALSE(AS.getError()) << Message; 2817 EXPECT_EQ(Expected.str(), AS->getName()) << Message; 2818 }; 2819 2820 auto ExpectFailure = [&](vfs::FileSystem &FS, StringRef Message) { 2821 EXPECT_TRUE(FS.openFileForRead("//root/a/f").getError()) << Message; 2822 EXPECT_TRUE(FS.status("//root/a/f").getError()) << Message; 2823 }; 2824 2825 { 2826 // `f` in both `a` and `b` 2827 2828 // `fallthrough` tries `external-name` first, so should be `b` 2829 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough = 2830 getFromYAMLString(FallthroughStr, Both); 2831 ASSERT_TRUE(Fallthrough.get() != nullptr); 2832 ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, both exist"); 2833 2834 // `fallback` tries the original name first, so should be `a` 2835 IntrusiveRefCntPtr<vfs::FileSystem> Fallback = 2836 getFromYAMLString(FallbackStr, Both); 2837 ASSERT_TRUE(Fallback.get() != nullptr); 2838 ExpectPath(*Fallback, "//root/a/f", "fallback, both exist"); 2839 2840 // `redirect-only` is the same as `fallthrough` but doesn't try the 2841 // original on failure, so no change here (ie. `b`) 2842 IntrusiveRefCntPtr<vfs::FileSystem> Redirect = 2843 getFromYAMLString(RedirectOnlyStr, Both); 2844 ASSERT_TRUE(Redirect.get() != nullptr); 2845 ExpectPath(*Redirect, "//root/b/f", "redirect-only, both exist"); 2846 } 2847 2848 { 2849 // `f` in `a` only 2850 2851 // Fallthrough to the original path, `a` 2852 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough = 2853 getFromYAMLString(FallthroughStr, AOnly); 2854 ASSERT_TRUE(Fallthrough.get() != nullptr); 2855 ExpectPath(*Fallthrough, "//root/a/f", "fallthrough, a only"); 2856 2857 // Original first, so still `a` 2858 IntrusiveRefCntPtr<vfs::FileSystem> Fallback = 2859 getFromYAMLString(FallbackStr, AOnly); 2860 ASSERT_TRUE(Fallback.get() != nullptr); 2861 ExpectPath(*Fallback, "//root/a/f", "fallback, a only"); 2862 2863 // Fails since no fallthrough 2864 IntrusiveRefCntPtr<vfs::FileSystem> Redirect = 2865 getFromYAMLString(RedirectOnlyStr, AOnly); 2866 ASSERT_TRUE(Redirect.get() != nullptr); 2867 ExpectFailure(*Redirect, "redirect-only, a only"); 2868 } 2869 2870 { 2871 // `f` in `b` only 2872 2873 // Tries `b` first (no fallthrough) 2874 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough = 2875 getFromYAMLString(FallthroughStr, BOnly); 2876 ASSERT_TRUE(Fallthrough.get() != nullptr); 2877 ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, b only"); 2878 2879 // Tries original first but then fallsback to `b` 2880 IntrusiveRefCntPtr<vfs::FileSystem> Fallback = 2881 getFromYAMLString(FallbackStr, BOnly); 2882 ASSERT_TRUE(Fallback.get() != nullptr); 2883 ExpectPath(*Fallback, "//root/b/f", "fallback, b only"); 2884 2885 // Redirect exists, so uses it (`b`) 2886 IntrusiveRefCntPtr<vfs::FileSystem> Redirect = 2887 getFromYAMLString(RedirectOnlyStr, BOnly); 2888 ASSERT_TRUE(Redirect.get() != nullptr); 2889 ExpectPath(*Redirect, "//root/b/f", "redirect-only, b only"); 2890 } 2891 2892 EXPECT_EQ(0, NumDiagnostics); 2893 } 2894 2895 TEST(VFSFromRemappedFilesTest, Basic) { 2896 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 2897 new vfs::InMemoryFileSystem; 2898 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 2899 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 2900 2901 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 2902 {"//root/a/a", "//root/b"}, 2903 {"//root/a/b/c", "//root/c"}, 2904 }; 2905 auto RemappedFS = vfs::RedirectingFileSystem::create( 2906 RemappedFiles, /*UseExternalNames=*/false, *BaseFS); 2907 2908 auto StatA = RemappedFS->status("//root/a/a"); 2909 auto StatB = RemappedFS->status("//root/a/b/c"); 2910 ASSERT_TRUE(StatA); 2911 ASSERT_TRUE(StatB); 2912 EXPECT_EQ("//root/a/a", StatA->getName()); 2913 EXPECT_EQ("//root/a/b/c", StatB->getName()); 2914 2915 auto BufferA = RemappedFS->getBufferForFile("//root/a/a"); 2916 auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c"); 2917 ASSERT_TRUE(BufferA); 2918 ASSERT_TRUE(BufferB); 2919 EXPECT_EQ("contents of b", (*BufferA)->getBuffer()); 2920 EXPECT_EQ("contents of c", (*BufferB)->getBuffer()); 2921 } 2922 2923 TEST(VFSFromRemappedFilesTest, UseExternalNames) { 2924 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 2925 new vfs::InMemoryFileSystem; 2926 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 2927 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 2928 2929 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 2930 {"//root/a/a", "//root/b"}, 2931 {"//root/a/b/c", "//root/c"}, 2932 }; 2933 auto RemappedFS = vfs::RedirectingFileSystem::create( 2934 RemappedFiles, /*UseExternalNames=*/true, *BaseFS); 2935 2936 auto StatA = RemappedFS->status("//root/a/a"); 2937 auto StatB = RemappedFS->status("//root/a/b/c"); 2938 ASSERT_TRUE(StatA); 2939 ASSERT_TRUE(StatB); 2940 EXPECT_EQ("//root/b", StatA->getName()); 2941 EXPECT_EQ("//root/c", StatB->getName()); 2942 2943 auto BufferA = RemappedFS->getBufferForFile("//root/a/a"); 2944 auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c"); 2945 ASSERT_TRUE(BufferA); 2946 ASSERT_TRUE(BufferB); 2947 EXPECT_EQ("contents of b", (*BufferA)->getBuffer()); 2948 EXPECT_EQ("contents of c", (*BufferB)->getBuffer()); 2949 } 2950 2951 TEST(VFSFromRemappedFilesTest, LastMappingWins) { 2952 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 2953 new vfs::InMemoryFileSystem; 2954 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 2955 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 2956 2957 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 2958 {"//root/a", "//root/b"}, 2959 {"//root/a", "//root/c"}, 2960 }; 2961 auto RemappedFSKeepName = vfs::RedirectingFileSystem::create( 2962 RemappedFiles, /*UseExternalNames=*/false, *BaseFS); 2963 auto RemappedFSExternalName = vfs::RedirectingFileSystem::create( 2964 RemappedFiles, /*UseExternalNames=*/true, *BaseFS); 2965 2966 auto StatKeepA = RemappedFSKeepName->status("//root/a"); 2967 auto StatExternalA = RemappedFSExternalName->status("//root/a"); 2968 ASSERT_TRUE(StatKeepA); 2969 ASSERT_TRUE(StatExternalA); 2970 EXPECT_EQ("//root/a", StatKeepA->getName()); 2971 EXPECT_EQ("//root/c", StatExternalA->getName()); 2972 2973 auto BufferKeepA = RemappedFSKeepName->getBufferForFile("//root/a"); 2974 auto BufferExternalA = RemappedFSExternalName->getBufferForFile("//root/a"); 2975 ASSERT_TRUE(BufferKeepA); 2976 ASSERT_TRUE(BufferExternalA); 2977 EXPECT_EQ("contents of c", (*BufferKeepA)->getBuffer()); 2978 EXPECT_EQ("contents of c", (*BufferExternalA)->getBuffer()); 2979 } 2980 2981 TEST(RedirectingFileSystemTest, PrintOutput) { 2982 auto Buffer = 2983 MemoryBuffer::getMemBuffer("{\n" 2984 " 'version': 0,\n" 2985 " 'roots': [\n" 2986 " {\n" 2987 " 'type': 'directory-remap',\n" 2988 " 'name': '/dremap',\n" 2989 " 'external-contents': '/a',\n" 2990 " }," 2991 " {\n" 2992 " 'type': 'directory',\n" 2993 " 'name': '/vdir',\n" 2994 " 'contents': [" 2995 " {\n" 2996 " 'type': 'directory-remap',\n" 2997 " 'name': 'dremap',\n" 2998 " 'external-contents': '/b'\n" 2999 " 'use-external-name': 'true'\n" 3000 " },\n" 3001 " {\n" 3002 " 'type': 'file',\n" 3003 " 'name': 'vfile',\n" 3004 " 'external-contents': '/c'\n" 3005 " 'use-external-name': 'false'\n" 3006 " }]\n" 3007 " }]\n" 3008 "}"); 3009 3010 auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>(); 3011 auto Redirecting = vfs::RedirectingFileSystem::create( 3012 std::move(Buffer), nullptr, "", nullptr, Dummy); 3013 3014 SmallString<0> Output; 3015 raw_svector_ostream OuputStream{Output}; 3016 3017 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Summary); 3018 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n", Output); 3019 3020 Output.clear(); 3021 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents); 3022 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n" 3023 "'/'\n" 3024 " 'dremap' -> '/a'\n" 3025 " 'vdir'\n" 3026 " 'dremap' -> '/b' (UseExternalName: true)\n" 3027 " 'vfile' -> '/c' (UseExternalName: false)\n" 3028 "ExternalFS:\n" 3029 " DummyFileSystem (Summary)\n", 3030 Output); 3031 3032 Output.clear(); 3033 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents, 1); 3034 ASSERT_EQ(" RedirectingFileSystem (UseExternalNames: true)\n" 3035 " '/'\n" 3036 " 'dremap' -> '/a'\n" 3037 " 'vdir'\n" 3038 " 'dremap' -> '/b' (UseExternalName: true)\n" 3039 " 'vfile' -> '/c' (UseExternalName: false)\n" 3040 " ExternalFS:\n" 3041 " DummyFileSystem (Summary)\n", 3042 Output); 3043 3044 Output.clear(); 3045 Redirecting->print(OuputStream, 3046 vfs::FileSystem::PrintType::RecursiveContents); 3047 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n" 3048 "'/'\n" 3049 " 'dremap' -> '/a'\n" 3050 " 'vdir'\n" 3051 " 'dremap' -> '/b' (UseExternalName: true)\n" 3052 " 'vfile' -> '/c' (UseExternalName: false)\n" 3053 "ExternalFS:\n" 3054 " DummyFileSystem (RecursiveContents)\n", 3055 Output); 3056 } 3057