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