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