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