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