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