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