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_TRUE(Index >= 0 && Index < 4); 571 Counts[Index]++; 572 } 573 EXPECT_EQ(1, Counts[0]); // a 574 EXPECT_EQ(1, Counts[1]); // b 575 EXPECT_EQ(1, Counts[2]); // c 576 EXPECT_EQ(1, Counts[3]); // d 577 } 578 579 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) { 580 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 581 582 TempDir _a(TestDirectory.path("a")); 583 TempDir _ab(TestDirectory.path("a/b")); 584 TempDir _c(TestDirectory.path("c")); 585 TempDir _cd(TestDirectory.path("c/d")); 586 TempDir _e(TestDirectory.path("e")); 587 TempDir _ef(TestDirectory.path("e/f")); 588 TempDir _g(TestDirectory.path("g")); 589 590 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 591 592 // Test that calling no_push on entries without subdirectories has no effect. 593 { 594 std::error_code EC; 595 auto I = 596 vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC); 597 ASSERT_FALSE(EC); 598 599 std::vector<std::string> Contents; 600 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 601 I.increment(EC)) { 602 Contents.push_back(std::string(I->path())); 603 char last = I->path().back(); 604 switch (last) { 605 case 'b': 606 case 'd': 607 case 'f': 608 case 'g': 609 I.no_push(); 610 break; 611 default: 612 break; 613 } 614 } 615 EXPECT_EQ(7U, Contents.size()); 616 } 617 618 // Test that calling no_push skips subdirectories. 619 { 620 std::error_code EC; 621 auto I = 622 vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC); 623 ASSERT_FALSE(EC); 624 625 std::vector<std::string> Contents; 626 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 627 I.increment(EC)) { 628 Contents.push_back(std::string(I->path())); 629 char last = I->path().back(); 630 switch (last) { 631 case 'a': 632 case 'c': 633 case 'e': 634 I.no_push(); 635 break; 636 default: 637 break; 638 } 639 } 640 641 // Check contents, which may be in any order 642 EXPECT_EQ(4U, Contents.size()); 643 int Counts[7] = {0, 0, 0, 0, 0, 0, 0}; 644 for (const std::string &Name : Contents) { 645 ASSERT_FALSE(Name.empty()); 646 int Index = Name[Name.size() - 1] - 'a'; 647 ASSERT_TRUE(Index >= 0 && Index < 7); 648 Counts[Index]++; 649 } 650 EXPECT_EQ(1, Counts[0]); // a 651 EXPECT_EQ(0, Counts[1]); // b 652 EXPECT_EQ(1, Counts[2]); // c 653 EXPECT_EQ(0, Counts[3]); // d 654 EXPECT_EQ(1, Counts[4]); // e 655 EXPECT_EQ(0, Counts[5]); // f 656 EXPECT_EQ(1, Counts[6]); // g 657 } 658 } 659 660 #ifdef LLVM_ON_UNIX 661 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) { 662 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 663 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 664 665 TempLink _a("no_such_file", TestDirectory.path("a")); 666 TempDir _b(TestDirectory.path("b")); 667 TempLink _ba("no_such_file", TestDirectory.path("b/a")); 668 TempDir _bb(TestDirectory.path("b/b")); 669 TempLink _bc("no_such_file", TestDirectory.path("b/c")); 670 TempLink _c("no_such_file", TestDirectory.path("c")); 671 TempDir _d(TestDirectory.path("d")); 672 TempDir _dd(TestDirectory.path("d/d")); 673 TempDir _ddd(TestDirectory.path("d/d/d")); 674 TempLink _e("no_such_file", TestDirectory.path("e")); 675 676 std::vector<std::string> VisitedBrokenSymlinks; 677 std::vector<std::string> VisitedNonBrokenSymlinks; 678 std::error_code EC; 679 for (vfs::recursive_directory_iterator 680 I(*FS, Twine(TestDirectory.path()), EC), 681 E; 682 I != E; I.increment(EC)) { 683 EXPECT_FALSE(EC); 684 (FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks) 685 .push_back(std::string(I->path())); 686 } 687 688 // Check visited file names. 689 EXPECT_THAT(VisitedBrokenSymlinks, 690 UnorderedElementsAre(_a.path().str(), _ba.path().str(), 691 _bc.path().str(), _c.path().str(), 692 _e.path().str())); 693 EXPECT_THAT(VisitedNonBrokenSymlinks, 694 UnorderedElementsAre(_b.path().str(), _bb.path().str(), 695 _d.path().str(), _dd.path().str(), 696 _ddd.path().str())); 697 } 698 #endif 699 700 template <typename DirIter> 701 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) { 702 std::error_code EC; 703 SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end()); 704 SmallVector<std::string, 4> InputToCheck; 705 706 // Do not rely on iteration order to check for contents, sort both 707 // content vectors before comparison. 708 for (DirIter E; !EC && I != E; I.increment(EC)) 709 InputToCheck.push_back(std::string(I->path())); 710 711 llvm::sort(InputToCheck); 712 llvm::sort(Expected); 713 EXPECT_EQ(InputToCheck.size(), Expected.size()); 714 715 unsigned LastElt = std::min(InputToCheck.size(), Expected.size()); 716 for (unsigned Idx = 0; Idx != LastElt; ++Idx) 717 EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]); 718 } 719 720 TEST(VirtualFileSystemTest, OverlayIteration) { 721 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 722 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 723 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 724 new vfs::OverlayFileSystem(Lower)); 725 O->pushOverlay(Upper); 726 727 std::error_code EC; 728 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); 729 730 Lower->addRegularFile("/file1"); 731 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1")); 732 733 Upper->addRegularFile("/file2"); 734 checkContents(O->dir_begin("/", EC), {"/file2", "/file1"}); 735 736 Lower->addDirectory("/dir1"); 737 Lower->addRegularFile("/dir1/foo"); 738 Upper->addDirectory("/dir2"); 739 Upper->addRegularFile("/dir2/foo"); 740 checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo")); 741 checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"}); 742 } 743 744 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) { 745 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 746 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 747 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 748 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 749 new vfs::OverlayFileSystem(Lower)); 750 O->pushOverlay(Middle); 751 O->pushOverlay(Upper); 752 753 std::error_code EC; 754 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 755 ArrayRef<StringRef>()); 756 757 Lower->addRegularFile("/file1"); 758 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 759 ArrayRef<StringRef>("/file1")); 760 761 Upper->addDirectory("/dir"); 762 Upper->addRegularFile("/dir/file2"); 763 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 764 {"/dir", "/dir/file2", "/file1"}); 765 766 Lower->addDirectory("/dir1"); 767 Lower->addRegularFile("/dir1/foo"); 768 Lower->addDirectory("/dir1/a"); 769 Lower->addRegularFile("/dir1/a/b"); 770 Middle->addDirectory("/a"); 771 Middle->addDirectory("/a/b"); 772 Middle->addDirectory("/a/b/c"); 773 Middle->addRegularFile("/a/b/c/d"); 774 Middle->addRegularFile("/hiddenByUp"); 775 Upper->addDirectory("/dir2"); 776 Upper->addRegularFile("/dir2/foo"); 777 Upper->addRegularFile("/hiddenByUp"); 778 checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC), 779 ArrayRef<StringRef>("/dir2/foo")); 780 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 781 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp", 782 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a", 783 "/dir1/a/b", "/dir1/foo", "/file1"}); 784 } 785 786 TEST(VirtualFileSystemTest, ThreeLevelIteration) { 787 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 788 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 789 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 790 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 791 new vfs::OverlayFileSystem(Lower)); 792 O->pushOverlay(Middle); 793 O->pushOverlay(Upper); 794 795 std::error_code EC; 796 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); 797 798 Middle->addRegularFile("/file2"); 799 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2")); 800 801 Lower->addRegularFile("/file1"); 802 Upper->addRegularFile("/file3"); 803 checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"}); 804 } 805 806 TEST(VirtualFileSystemTest, HiddenInIteration) { 807 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 808 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 809 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 810 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 811 new vfs::OverlayFileSystem(Lower)); 812 O->pushOverlay(Middle); 813 O->pushOverlay(Upper); 814 815 std::error_code EC; 816 Lower->addRegularFile("/onlyInLow"); 817 Lower->addDirectory("/hiddenByMid"); 818 Lower->addDirectory("/hiddenByUp"); 819 Middle->addRegularFile("/onlyInMid"); 820 Middle->addRegularFile("/hiddenByMid"); 821 Middle->addDirectory("/hiddenByUp"); 822 Upper->addRegularFile("/onlyInUp"); 823 Upper->addRegularFile("/hiddenByUp"); 824 checkContents( 825 O->dir_begin("/", EC), 826 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"}); 827 828 // Make sure we get the top-most entry 829 { 830 std::error_code EC; 831 vfs::directory_iterator I = O->dir_begin("/", EC), E; 832 for (; !EC && I != E; I.increment(EC)) 833 if (I->path() == "/hiddenByUp") 834 break; 835 ASSERT_NE(E, I); 836 EXPECT_EQ(sys::fs::file_type::regular_file, I->type()); 837 } 838 { 839 std::error_code EC; 840 vfs::directory_iterator I = O->dir_begin("/", EC), E; 841 for (; !EC && I != E; I.increment(EC)) 842 if (I->path() == "/hiddenByMid") 843 break; 844 ASSERT_NE(E, I); 845 EXPECT_EQ(sys::fs::file_type::regular_file, I->type()); 846 } 847 } 848 849 TEST(ProxyFileSystemTest, Basic) { 850 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> Base( 851 new vfs::InMemoryFileSystem()); 852 vfs::ProxyFileSystem PFS(Base); 853 854 Base->addFile("/a", 0, MemoryBuffer::getMemBuffer("test")); 855 856 auto Stat = PFS.status("/a"); 857 ASSERT_FALSE(Stat.getError()); 858 859 auto File = PFS.openFileForRead("/a"); 860 ASSERT_FALSE(File.getError()); 861 EXPECT_EQ("test", (*(*File)->getBuffer("ignored"))->getBuffer()); 862 863 std::error_code EC; 864 vfs::directory_iterator I = PFS.dir_begin("/", EC); 865 ASSERT_FALSE(EC); 866 ASSERT_EQ("/a", I->path()); 867 I.increment(EC); 868 ASSERT_FALSE(EC); 869 ASSERT_EQ(vfs::directory_iterator(), I); 870 871 ASSERT_FALSE(PFS.setCurrentWorkingDirectory("/")); 872 873 auto PWD = PFS.getCurrentWorkingDirectory(); 874 ASSERT_FALSE(PWD.getError()); 875 ASSERT_EQ("/", *PWD); 876 877 SmallString<16> Path; 878 ASSERT_FALSE(PFS.getRealPath("a", Path)); 879 ASSERT_EQ("/a", Path); 880 881 bool Local = true; 882 ASSERT_FALSE(PFS.isLocal("/a", Local)); 883 EXPECT_FALSE(Local); 884 } 885 886 class InMemoryFileSystemTest : public ::testing::Test { 887 protected: 888 llvm::vfs::InMemoryFileSystem FS; 889 llvm::vfs::InMemoryFileSystem NormalizedFS; 890 891 InMemoryFileSystemTest() 892 : FS(/*UseNormalizedPaths=*/false), 893 NormalizedFS(/*UseNormalizedPaths=*/true) {} 894 }; 895 896 MATCHER_P2(IsHardLinkTo, FS, Target, "") { 897 StringRef From = arg; 898 StringRef To = Target; 899 auto OpenedFrom = FS->openFileForRead(From); 900 auto OpenedTo = FS->openFileForRead(To); 901 return !OpenedFrom.getError() && !OpenedTo.getError() && 902 (*OpenedFrom)->status()->getUniqueID() == 903 (*OpenedTo)->status()->getUniqueID(); 904 } 905 906 TEST_F(InMemoryFileSystemTest, IsEmpty) { 907 auto Stat = FS.status("/a"); 908 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString(); 909 Stat = FS.status("/"); 910 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString(); 911 } 912 913 TEST_F(InMemoryFileSystemTest, WindowsPath) { 914 FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); 915 auto Stat = FS.status("c:"); 916 #if !defined(_WIN32) 917 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 918 #endif 919 Stat = FS.status("c:/windows/system128/foo.cpp"); 920 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 921 FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); 922 Stat = FS.status("d:/windows/foo.cpp"); 923 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 924 } 925 926 TEST_F(InMemoryFileSystemTest, OverlayFile) { 927 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 928 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 929 auto Stat = FS.status("/"); 930 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 931 Stat = FS.status("/."); 932 ASSERT_FALSE(Stat); 933 Stat = NormalizedFS.status("/."); 934 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 935 Stat = FS.status("/a"); 936 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 937 ASSERT_EQ("/a", Stat->getName()); 938 } 939 940 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) { 941 auto Buf = MemoryBuffer::getMemBuffer("a"); 942 FS.addFileNoOwn("/a", 0, *Buf); 943 auto Stat = FS.status("/a"); 944 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 945 ASSERT_EQ("/a", Stat->getName()); 946 } 947 948 TEST_F(InMemoryFileSystemTest, OpenFileForRead) { 949 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 950 FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); 951 FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); 952 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 953 NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); 954 NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); 955 auto File = FS.openFileForRead("/a"); 956 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 957 File = FS.openFileForRead("/a"); // Open again. 958 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 959 File = NormalizedFS.openFileForRead("/././a"); // Open again. 960 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 961 File = FS.openFileForRead("/"); 962 ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString(); 963 File = FS.openFileForRead("/b"); 964 ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString(); 965 File = FS.openFileForRead("./c"); 966 ASSERT_FALSE(File); 967 File = FS.openFileForRead("e/../d"); 968 ASSERT_FALSE(File); 969 File = NormalizedFS.openFileForRead("./c"); 970 ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer()); 971 File = NormalizedFS.openFileForRead("e/../d"); 972 ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer()); 973 } 974 975 TEST_F(InMemoryFileSystemTest, DuplicatedFile) { 976 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); 977 ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a"))); 978 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); 979 ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b"))); 980 } 981 982 TEST_F(InMemoryFileSystemTest, DirectoryIteration) { 983 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("")); 984 FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer("")); 985 986 std::error_code EC; 987 vfs::directory_iterator I = FS.dir_begin("/", EC); 988 ASSERT_FALSE(EC); 989 ASSERT_EQ("/a", I->path()); 990 I.increment(EC); 991 ASSERT_FALSE(EC); 992 ASSERT_EQ("/b", I->path()); 993 I.increment(EC); 994 ASSERT_FALSE(EC); 995 ASSERT_EQ(vfs::directory_iterator(), I); 996 997 I = FS.dir_begin("/b", EC); 998 ASSERT_FALSE(EC); 999 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix 1000 // path for the sake of the comparison. 1001 ASSERT_EQ("/b/c", getPosixPath(std::string(I->path()))); 1002 I.increment(EC); 1003 ASSERT_FALSE(EC); 1004 ASSERT_EQ(vfs::directory_iterator(), I); 1005 } 1006 1007 TEST_F(InMemoryFileSystemTest, WorkingDirectory) { 1008 FS.setCurrentWorkingDirectory("/b"); 1009 FS.addFile("c", 0, MemoryBuffer::getMemBuffer("")); 1010 1011 auto Stat = FS.status("/b/c"); 1012 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1013 ASSERT_EQ("/b/c", Stat->getName()); 1014 ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory()); 1015 1016 Stat = FS.status("c"); 1017 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1018 1019 NormalizedFS.setCurrentWorkingDirectory("/b/c"); 1020 NormalizedFS.setCurrentWorkingDirectory("."); 1021 ASSERT_EQ("/b/c", 1022 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get())); 1023 NormalizedFS.setCurrentWorkingDirectory(".."); 1024 ASSERT_EQ("/b", 1025 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get())); 1026 } 1027 1028 TEST_F(InMemoryFileSystemTest, IsLocal) { 1029 FS.setCurrentWorkingDirectory("/b"); 1030 FS.addFile("c", 0, MemoryBuffer::getMemBuffer("")); 1031 1032 std::error_code EC; 1033 bool IsLocal = true; 1034 EC = FS.isLocal("c", IsLocal); 1035 ASSERT_FALSE(EC); 1036 ASSERT_FALSE(IsLocal); 1037 } 1038 1039 #if !defined(_WIN32) 1040 TEST_F(InMemoryFileSystemTest, GetRealPath) { 1041 SmallString<16> Path; 1042 EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted); 1043 1044 auto GetRealPath = [this](StringRef P) { 1045 SmallString<16> Output; 1046 auto EC = FS.getRealPath(P, Output); 1047 EXPECT_FALSE(EC); 1048 return std::string(Output); 1049 }; 1050 1051 FS.setCurrentWorkingDirectory("a"); 1052 EXPECT_EQ(GetRealPath("b"), "a/b"); 1053 EXPECT_EQ(GetRealPath("../b"), "b"); 1054 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c"); 1055 1056 FS.setCurrentWorkingDirectory("/a"); 1057 EXPECT_EQ(GetRealPath("b"), "/a/b"); 1058 EXPECT_EQ(GetRealPath("../b"), "/b"); 1059 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c"); 1060 } 1061 #endif // _WIN32 1062 1063 TEST_F(InMemoryFileSystemTest, AddFileWithUser) { 1064 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE); 1065 auto Stat = FS.status("/a"); 1066 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1067 ASSERT_TRUE(Stat->isDirectory()); 1068 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 1069 Stat = FS.status("/a/b"); 1070 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1071 ASSERT_TRUE(Stat->isDirectory()); 1072 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 1073 Stat = FS.status("/a/b/c"); 1074 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1075 ASSERT_TRUE(Stat->isRegularFile()); 1076 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 1077 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 1078 } 1079 1080 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) { 1081 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00); 1082 auto Stat = FS.status("/a"); 1083 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1084 ASSERT_TRUE(Stat->isDirectory()); 1085 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 1086 Stat = FS.status("/a/b"); 1087 ASSERT_TRUE(Stat->isDirectory()); 1088 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1089 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 1090 Stat = FS.status("/a/b/c"); 1091 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1092 ASSERT_TRUE(Stat->isRegularFile()); 1093 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 1094 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 1095 } 1096 1097 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) { 1098 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, 1099 sys::fs::file_type::socket_file); 1100 auto Stat = FS.status("/a"); 1101 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1102 ASSERT_TRUE(Stat->isDirectory()); 1103 Stat = FS.status("/a/b"); 1104 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1105 ASSERT_TRUE(Stat->isDirectory()); 1106 Stat = FS.status("/a/b/c"); 1107 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1108 ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType()); 1109 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 1110 } 1111 1112 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) { 1113 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, None, 1114 sys::fs::perms::owner_read | sys::fs::perms::owner_write); 1115 auto Stat = FS.status("/a"); 1116 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1117 ASSERT_TRUE(Stat->isDirectory()); 1118 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write | 1119 sys::fs::perms::owner_exe, 1120 Stat->getPermissions()); 1121 Stat = FS.status("/a/b"); 1122 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1123 ASSERT_TRUE(Stat->isDirectory()); 1124 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write | 1125 sys::fs::perms::owner_exe, 1126 Stat->getPermissions()); 1127 Stat = FS.status("/a/b/c"); 1128 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1129 ASSERT_TRUE(Stat->isRegularFile()); 1130 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write, 1131 Stat->getPermissions()); 1132 } 1133 1134 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) { 1135 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None, 1136 /*Group=*/None, sys::fs::file_type::directory_file); 1137 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None, 1138 /*Group=*/None, sys::fs::file_type::regular_file); 1139 auto Stat = FS.status("/a"); 1140 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1141 ASSERT_TRUE(Stat->isDirectory()); 1142 Stat = FS.status("/a/b"); 1143 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1144 ASSERT_TRUE(Stat->isRegularFile()); 1145 } 1146 1147 // Test that the name returned by status() is in the same form as the path that 1148 // was requested (to match the behavior of RealFileSystem). 1149 TEST_F(InMemoryFileSystemTest, StatusName) { 1150 NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 1151 /*User=*/None, 1152 /*Group=*/None, sys::fs::file_type::regular_file); 1153 NormalizedFS.setCurrentWorkingDirectory("/a/b"); 1154 1155 // Access using InMemoryFileSystem::status. 1156 auto Stat = NormalizedFS.status("../b/c"); 1157 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" 1158 << NormalizedFS.toString(); 1159 ASSERT_TRUE(Stat->isRegularFile()); 1160 ASSERT_EQ("../b/c", Stat->getName()); 1161 1162 // Access using InMemoryFileAdaptor::status. 1163 auto File = NormalizedFS.openFileForRead("../b/c"); 1164 ASSERT_FALSE(File.getError()) << File.getError() << "\n" 1165 << NormalizedFS.toString(); 1166 Stat = (*File)->status(); 1167 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" 1168 << NormalizedFS.toString(); 1169 ASSERT_TRUE(Stat->isRegularFile()); 1170 ASSERT_EQ("../b/c", Stat->getName()); 1171 1172 // Access using a directory iterator. 1173 std::error_code EC; 1174 llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC); 1175 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix 1176 // path for the sake of the comparison. 1177 ASSERT_EQ("../b/c", getPosixPath(std::string(It->path()))); 1178 } 1179 1180 TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) { 1181 StringRef FromLink = "/path/to/FROM/link"; 1182 StringRef Target = "/path/to/TO/file"; 1183 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1184 EXPECT_TRUE(FS.addHardLink(FromLink, Target)); 1185 EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target)); 1186 EXPECT_TRUE(FS.status(FromLink)->getSize() == FS.status(Target)->getSize()); 1187 EXPECT_TRUE(FS.getBufferForFile(FromLink)->get()->getBuffer() == 1188 FS.getBufferForFile(Target)->get()->getBuffer()); 1189 } 1190 1191 TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) { 1192 StringRef Link0 = "/path/to/0/link"; 1193 StringRef Link1 = "/path/to/1/link"; 1194 StringRef Link2 = "/path/to/2/link"; 1195 StringRef Target = "/path/to/target"; 1196 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file")); 1197 EXPECT_TRUE(FS.addHardLink(Link2, Target)); 1198 EXPECT_TRUE(FS.addHardLink(Link1, Link2)); 1199 EXPECT_TRUE(FS.addHardLink(Link0, Link1)); 1200 EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target)); 1201 EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target)); 1202 EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target)); 1203 } 1204 1205 TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) { 1206 EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target")); 1207 } 1208 1209 TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) { 1210 StringRef Link = "/path/to/link"; 1211 StringRef Target = "/path/to/target"; 1212 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1213 FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link")); 1214 EXPECT_FALSE(FS.addHardLink(Link, Target)); 1215 } 1216 1217 TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) { 1218 StringRef Link = "/path/to/link"; 1219 StringRef Target = "/path/to/target"; 1220 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1221 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1222 EXPECT_FALSE(FS.addHardLink(Link, Target)); 1223 } 1224 1225 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) { 1226 StringRef Link = "/path/to/link"; 1227 StringRef Target = "/path/to/target"; 1228 StringRef Content = "content of target"; 1229 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1230 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1231 EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content))); 1232 } 1233 1234 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) { 1235 StringRef Link = "/path/to/link"; 1236 StringRef Target = "/path/to/target"; 1237 StringRef Content = "content of target"; 1238 StringRef LinkContent = "different content of link"; 1239 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1240 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1241 EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent))); 1242 } 1243 1244 TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) { 1245 StringRef Dir = "path/to/dummy/dir"; 1246 StringRef Link = "/path/to/link"; 1247 StringRef File = "path/to/dummy/dir/target"; 1248 StringRef Content = "content of target"; 1249 EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content))); 1250 EXPECT_FALSE(FS.addHardLink(Link, Dir)); 1251 } 1252 1253 TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) { 1254 StringRef Dir = "path/to/dummy/dir"; 1255 StringRef Target = "path/to/dummy/dir/target"; 1256 StringRef Content = "content of target"; 1257 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1258 EXPECT_FALSE(FS.addHardLink(Dir, Target)); 1259 } 1260 1261 TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) { 1262 StringRef CommonContent = "content string"; 1263 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent)); 1264 FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent)); 1265 EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b")); 1266 } 1267 1268 TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) { 1269 std::error_code EC; 1270 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string")); 1271 EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b")); 1272 auto I = vfs::recursive_directory_iterator(FS, "/", EC); 1273 ASSERT_FALSE(EC); 1274 std::vector<std::string> Nodes; 1275 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 1276 I.increment(EC)) { 1277 Nodes.push_back(getPosixPath(std::string(I->path()))); 1278 } 1279 EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d")); 1280 } 1281 1282 TEST_F(InMemoryFileSystemTest, UniqueID) { 1283 ASSERT_TRUE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text"))); 1284 ASSERT_TRUE(FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer("text"))); 1285 ASSERT_TRUE(FS.addHardLink("/e/f", "/a/b")); 1286 1287 EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/a/b")->getUniqueID()); 1288 EXPECT_NE(FS.status("/a/b")->getUniqueID(), FS.status("/c/d")->getUniqueID()); 1289 EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/e/f")->getUniqueID()); 1290 EXPECT_EQ(FS.status("/a")->getUniqueID(), FS.status("/a")->getUniqueID()); 1291 EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/c")->getUniqueID()); 1292 EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/e")->getUniqueID()); 1293 1294 // Recreating the "same" FS yields the same UniqueIDs. 1295 vfs::InMemoryFileSystem FS2; 1296 ASSERT_TRUE(FS2.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text"))); 1297 EXPECT_EQ(FS.status("/a/b")->getUniqueID(), 1298 FS2.status("/a/b")->getUniqueID()); 1299 EXPECT_EQ(FS.status("/a")->getUniqueID(), FS2.status("/a")->getUniqueID()); 1300 } 1301 1302 // NOTE: in the tests below, we use '//root/' as our root directory, since it is 1303 // a legal *absolute* path on Windows as well as *nix. 1304 class VFSFromYAMLTest : public ::testing::Test { 1305 public: 1306 int NumDiagnostics; 1307 1308 void SetUp() override { NumDiagnostics = 0; } 1309 1310 static void CountingDiagHandler(const SMDiagnostic &, void *Context) { 1311 VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context); 1312 ++Test->NumDiagnostics; 1313 } 1314 1315 std::unique_ptr<vfs::FileSystem> 1316 getFromYAMLRawString(StringRef Content, 1317 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) { 1318 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content); 1319 return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this, 1320 ExternalFS); 1321 } 1322 1323 std::unique_ptr<vfs::FileSystem> getFromYAMLString( 1324 StringRef Content, 1325 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) { 1326 std::string VersionPlusContent("{\n 'version':0,\n"); 1327 VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos); 1328 return getFromYAMLRawString(VersionPlusContent, ExternalFS); 1329 } 1330 1331 // This is intended as a "XFAIL" for windows hosts. 1332 bool supportsSameDirMultipleYAMLEntries() { 1333 Triple Host(Triple::normalize(sys::getProcessTriple())); 1334 return !Host.isOSWindows(); 1335 } 1336 }; 1337 1338 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) { 1339 IntrusiveRefCntPtr<vfs::FileSystem> FS; 1340 FS = getFromYAMLString(""); 1341 EXPECT_EQ(nullptr, FS.get()); 1342 FS = getFromYAMLString("[]"); 1343 EXPECT_EQ(nullptr, FS.get()); 1344 FS = getFromYAMLString("'string'"); 1345 EXPECT_EQ(nullptr, FS.get()); 1346 EXPECT_EQ(3, NumDiagnostics); 1347 } 1348 1349 TEST_F(VFSFromYAMLTest, MappedFiles) { 1350 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1351 Lower->addDirectory("//root/foo/bar"); 1352 Lower->addRegularFile("//root/foo/bar/a"); 1353 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1354 "{ 'roots': [\n" 1355 "{\n" 1356 " 'type': 'directory',\n" 1357 " 'name': '//root/',\n" 1358 " 'contents': [ {\n" 1359 " 'type': 'file',\n" 1360 " 'name': 'file1',\n" 1361 " 'external-contents': '//root/foo/bar/a'\n" 1362 " },\n" 1363 " {\n" 1364 " 'type': 'file',\n" 1365 " 'name': 'file2',\n" 1366 " 'external-contents': '//root/foo/b'\n" 1367 " },\n" 1368 " {\n" 1369 " 'type': 'directory-remap',\n" 1370 " 'name': 'mappeddir',\n" 1371 " 'external-contents': '//root/foo/bar'\n" 1372 " },\n" 1373 " {\n" 1374 " 'type': 'directory-remap',\n" 1375 " 'name': 'mappeddir2',\n" 1376 " 'use-external-name': false,\n" 1377 " 'external-contents': '//root/foo/bar'\n" 1378 " }\n" 1379 " ]\n" 1380 "}\n" 1381 "]\n" 1382 "}", 1383 Lower); 1384 ASSERT_TRUE(FS.get() != nullptr); 1385 1386 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1387 new vfs::OverlayFileSystem(Lower)); 1388 O->pushOverlay(FS); 1389 1390 // file 1391 ErrorOr<vfs::Status> S = O->status("//root/file1"); 1392 ASSERT_FALSE(S.getError()); 1393 EXPECT_EQ("//root/foo/bar/a", S->getName()); 1394 EXPECT_TRUE(S->IsVFSMapped); 1395 1396 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); 1397 EXPECT_EQ("//root/foo/bar/a", SLower->getName()); 1398 EXPECT_TRUE(S->equivalent(*SLower)); 1399 EXPECT_FALSE(SLower->IsVFSMapped); 1400 1401 // file after opening 1402 auto OpenedF = O->openFileForRead("//root/file1"); 1403 ASSERT_FALSE(OpenedF.getError()); 1404 auto OpenedS = (*OpenedF)->status(); 1405 ASSERT_FALSE(OpenedS.getError()); 1406 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 1407 EXPECT_TRUE(OpenedS->IsVFSMapped); 1408 1409 // directory 1410 S = O->status("//root/"); 1411 ASSERT_FALSE(S.getError()); 1412 EXPECT_TRUE(S->isDirectory()); 1413 EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID 1414 1415 // remapped directory 1416 S = O->status("//root/mappeddir"); 1417 ASSERT_FALSE(S.getError()); 1418 EXPECT_TRUE(S->isDirectory()); 1419 EXPECT_TRUE(S->IsVFSMapped); 1420 EXPECT_TRUE(S->equivalent(*O->status("//root/foo/bar"))); 1421 1422 SLower = O->status("//root/foo/bar"); 1423 EXPECT_EQ("//root/foo/bar", SLower->getName()); 1424 EXPECT_TRUE(S->equivalent(*SLower)); 1425 EXPECT_FALSE(SLower->IsVFSMapped); 1426 1427 // file in remapped directory 1428 S = O->status("//root/mappeddir/a"); 1429 ASSERT_FALSE(S.getError()); 1430 ASSERT_FALSE(S->isDirectory()); 1431 ASSERT_TRUE(S->IsVFSMapped); 1432 ASSERT_EQ("//root/foo/bar/a", S->getName()); 1433 1434 // file in remapped directory, with use-external-name=false 1435 S = O->status("//root/mappeddir2/a"); 1436 ASSERT_FALSE(S.getError()); 1437 ASSERT_FALSE(S->isDirectory()); 1438 ASSERT_TRUE(S->IsVFSMapped); 1439 ASSERT_EQ("//root/mappeddir2/a", S->getName()); 1440 1441 // file contents in remapped directory 1442 OpenedF = O->openFileForRead("//root/mappeddir/a"); 1443 ASSERT_FALSE(OpenedF.getError()); 1444 OpenedS = (*OpenedF)->status(); 1445 ASSERT_FALSE(OpenedS.getError()); 1446 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 1447 EXPECT_TRUE(OpenedS->IsVFSMapped); 1448 1449 // file contents in remapped directory, with use-external-name=false 1450 OpenedF = O->openFileForRead("//root/mappeddir2/a"); 1451 ASSERT_FALSE(OpenedF.getError()); 1452 OpenedS = (*OpenedF)->status(); 1453 ASSERT_FALSE(OpenedS.getError()); 1454 EXPECT_EQ("//root/mappeddir2/a", OpenedS->getName()); 1455 EXPECT_TRUE(OpenedS->IsVFSMapped); 1456 1457 // broken mapping 1458 EXPECT_EQ(O->status("//root/file2").getError(), 1459 llvm::errc::no_such_file_or_directory); 1460 EXPECT_EQ(0, NumDiagnostics); 1461 } 1462 1463 TEST_F(VFSFromYAMLTest, MappedRoot) { 1464 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1465 Lower->addDirectory("//root/foo/bar"); 1466 Lower->addRegularFile("//root/foo/bar/a"); 1467 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1468 getFromYAMLString("{ 'roots': [\n" 1469 "{\n" 1470 " 'type': 'directory-remap',\n" 1471 " 'name': '//mappedroot/',\n" 1472 " 'external-contents': '//root/foo/bar'\n" 1473 "}\n" 1474 "]\n" 1475 "}", 1476 Lower); 1477 ASSERT_TRUE(FS.get() != nullptr); 1478 1479 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1480 new vfs::OverlayFileSystem(Lower)); 1481 O->pushOverlay(FS); 1482 1483 // file 1484 ErrorOr<vfs::Status> S = O->status("//mappedroot/a"); 1485 ASSERT_FALSE(S.getError()); 1486 EXPECT_EQ("//root/foo/bar/a", S->getName()); 1487 EXPECT_TRUE(S->IsVFSMapped); 1488 1489 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); 1490 EXPECT_EQ("//root/foo/bar/a", SLower->getName()); 1491 EXPECT_TRUE(S->equivalent(*SLower)); 1492 EXPECT_FALSE(SLower->IsVFSMapped); 1493 1494 // file after opening 1495 auto OpenedF = O->openFileForRead("//mappedroot/a"); 1496 ASSERT_FALSE(OpenedF.getError()); 1497 auto OpenedS = (*OpenedF)->status(); 1498 ASSERT_FALSE(OpenedS.getError()); 1499 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 1500 EXPECT_TRUE(OpenedS->IsVFSMapped); 1501 1502 EXPECT_EQ(0, NumDiagnostics); 1503 } 1504 1505 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlay) { 1506 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1507 Lower->addDirectory("//root/foo"); 1508 Lower->addRegularFile("//root/foo/a"); 1509 Lower->addDirectory("//root/bar"); 1510 Lower->addRegularFile("//root/bar/b"); 1511 Lower->addRegularFile("//root/bar/c"); 1512 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1513 getFromYAMLString("{ 'roots': [\n" 1514 "{\n" 1515 " 'type': 'directory',\n" 1516 " 'name': '//root/',\n" 1517 " 'contents': [ {\n" 1518 " 'type': 'directory-remap',\n" 1519 " 'name': 'bar',\n" 1520 " 'external-contents': '//root/foo'\n" 1521 " }\n" 1522 " ]\n" 1523 "}]}", 1524 Lower); 1525 ASSERT_TRUE(FS.get() != nullptr); 1526 1527 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1528 new vfs::OverlayFileSystem(Lower)); 1529 O->pushOverlay(FS); 1530 1531 ErrorOr<vfs::Status> S = O->status("//root/foo"); 1532 ASSERT_FALSE(S.getError()); 1533 1534 ErrorOr<vfs::Status> SS = O->status("//root/bar"); 1535 ASSERT_FALSE(SS.getError()); 1536 EXPECT_TRUE(S->equivalent(*SS)); 1537 1538 std::error_code EC; 1539 checkContents(O->dir_begin("//root/bar", EC), 1540 {"//root/foo/a", "//root/bar/b", "//root/bar/c"}); 1541 1542 Lower->addRegularFile("//root/foo/b"); 1543 checkContents(O->dir_begin("//root/bar", EC), 1544 {"//root/foo/a", "//root/foo/b", "//root/bar/c"}); 1545 1546 EXPECT_EQ(0, NumDiagnostics); 1547 } 1548 1549 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoExternalNames) { 1550 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1551 Lower->addDirectory("//root/foo"); 1552 Lower->addRegularFile("//root/foo/a"); 1553 Lower->addDirectory("//root/bar"); 1554 Lower->addRegularFile("//root/bar/b"); 1555 Lower->addRegularFile("//root/bar/c"); 1556 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1557 getFromYAMLString("{ 'use-external-names': false,\n" 1558 " 'roots': [\n" 1559 "{\n" 1560 " 'type': 'directory',\n" 1561 " 'name': '//root/',\n" 1562 " 'contents': [ {\n" 1563 " 'type': 'directory-remap',\n" 1564 " 'name': 'bar',\n" 1565 " 'external-contents': '//root/foo'\n" 1566 " }\n" 1567 " ]\n" 1568 "}]}", 1569 Lower); 1570 ASSERT_TRUE(FS.get() != nullptr); 1571 1572 ErrorOr<vfs::Status> S = FS->status("//root/foo"); 1573 ASSERT_FALSE(S.getError()); 1574 1575 ErrorOr<vfs::Status> SS = FS->status("//root/bar"); 1576 ASSERT_FALSE(SS.getError()); 1577 EXPECT_TRUE(S->equivalent(*SS)); 1578 1579 std::error_code EC; 1580 checkContents(FS->dir_begin("//root/bar", EC), 1581 {"//root/bar/a", "//root/bar/b", "//root/bar/c"}); 1582 1583 Lower->addRegularFile("//root/foo/b"); 1584 checkContents(FS->dir_begin("//root/bar", EC), 1585 {"//root/bar/a", "//root/bar/b", "//root/bar/c"}); 1586 1587 EXPECT_EQ(0, NumDiagnostics); 1588 } 1589 1590 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoFallthrough) { 1591 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1592 Lower->addDirectory("//root/foo"); 1593 Lower->addRegularFile("//root/foo/a"); 1594 Lower->addDirectory("//root/bar"); 1595 Lower->addRegularFile("//root/bar/b"); 1596 Lower->addRegularFile("//root/bar/c"); 1597 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1598 getFromYAMLString("{ 'fallthrough': false,\n" 1599 " 'roots': [\n" 1600 "{\n" 1601 " 'type': 'directory',\n" 1602 " 'name': '//root/',\n" 1603 " 'contents': [ {\n" 1604 " 'type': 'directory-remap',\n" 1605 " 'name': 'bar',\n" 1606 " 'external-contents': '//root/foo'\n" 1607 " }\n" 1608 " ]\n" 1609 "}]}", 1610 Lower); 1611 ASSERT_TRUE(FS.get() != nullptr); 1612 1613 ErrorOr<vfs::Status> S = Lower->status("//root/foo"); 1614 ASSERT_FALSE(S.getError()); 1615 1616 ErrorOr<vfs::Status> SS = FS->status("//root/bar"); 1617 ASSERT_FALSE(SS.getError()); 1618 EXPECT_TRUE(S->equivalent(*SS)); 1619 1620 std::error_code EC; 1621 checkContents(FS->dir_begin("//root/bar", EC), {"//root/foo/a"}); 1622 1623 Lower->addRegularFile("//root/foo/b"); 1624 checkContents(FS->dir_begin("//root/bar", EC), 1625 {"//root/foo/a", "//root/foo/b"}); 1626 1627 EXPECT_EQ(0, NumDiagnostics); 1628 } 1629 1630 TEST_F(VFSFromYAMLTest, ReturnsRequestedPathVFSMiss) { 1631 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS( 1632 new vfs::InMemoryFileSystem); 1633 BaseFS->addFile("//root/foo/a", 0, 1634 MemoryBuffer::getMemBuffer("contents of a")); 1635 ASSERT_FALSE(BaseFS->setCurrentWorkingDirectory("//root/foo")); 1636 auto RemappedFS = vfs::RedirectingFileSystem::create( 1637 {}, /*UseExternalNames=*/false, *BaseFS); 1638 1639 auto OpenedF = RemappedFS->openFileForRead("a"); 1640 ASSERT_FALSE(OpenedF.getError()); 1641 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName(); 1642 ASSERT_FALSE(Name.getError()); 1643 EXPECT_EQ("a", Name.get()); 1644 1645 auto OpenedS = (*OpenedF)->status(); 1646 ASSERT_FALSE(OpenedS.getError()); 1647 EXPECT_EQ("a", OpenedS->getName()); 1648 EXPECT_FALSE(OpenedS->IsVFSMapped); 1649 1650 auto DirectS = RemappedFS->status("a"); 1651 ASSERT_FALSE(DirectS.getError()); 1652 EXPECT_EQ("a", DirectS->getName()); 1653 EXPECT_FALSE(DirectS->IsVFSMapped); 1654 1655 EXPECT_EQ(0, NumDiagnostics); 1656 } 1657 1658 TEST_F(VFSFromYAMLTest, ReturnsExternalPathVFSHit) { 1659 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS( 1660 new vfs::InMemoryFileSystem); 1661 BaseFS->addFile("//root/foo/realname", 0, 1662 MemoryBuffer::getMemBuffer("contents of a")); 1663 auto FS = 1664 getFromYAMLString("{ 'use-external-names': true,\n" 1665 " 'roots': [\n" 1666 "{\n" 1667 " 'type': 'directory',\n" 1668 " 'name': '//root/foo',\n" 1669 " 'contents': [ {\n" 1670 " 'type': 'file',\n" 1671 " 'name': 'vfsname',\n" 1672 " 'external-contents': 'realname'\n" 1673 " }\n" 1674 " ]\n" 1675 "}]}", 1676 BaseFS); 1677 ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo")); 1678 1679 auto OpenedF = FS->openFileForRead("vfsname"); 1680 ASSERT_FALSE(OpenedF.getError()); 1681 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName(); 1682 ASSERT_FALSE(Name.getError()); 1683 EXPECT_EQ("realname", Name.get()); 1684 1685 auto OpenedS = (*OpenedF)->status(); 1686 ASSERT_FALSE(OpenedS.getError()); 1687 EXPECT_EQ("realname", OpenedS->getName()); 1688 EXPECT_TRUE(OpenedS->IsVFSMapped); 1689 1690 auto DirectS = FS->status("vfsname"); 1691 ASSERT_FALSE(DirectS.getError()); 1692 EXPECT_EQ("realname", DirectS->getName()); 1693 EXPECT_TRUE(DirectS->IsVFSMapped); 1694 1695 EXPECT_EQ(0, NumDiagnostics); 1696 } 1697 1698 TEST_F(VFSFromYAMLTest, ReturnsInternalPathVFSHit) { 1699 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS( 1700 new vfs::InMemoryFileSystem); 1701 BaseFS->addFile("//root/foo/realname", 0, 1702 MemoryBuffer::getMemBuffer("contents of a")); 1703 auto FS = 1704 getFromYAMLString("{ 'use-external-names': false,\n" 1705 " 'roots': [\n" 1706 "{\n" 1707 " 'type': 'directory',\n" 1708 " 'name': '//root/foo',\n" 1709 " 'contents': [ {\n" 1710 " 'type': 'file',\n" 1711 " 'name': 'vfsname',\n" 1712 " 'external-contents': 'realname'\n" 1713 " }\n" 1714 " ]\n" 1715 "}]}", 1716 BaseFS); 1717 ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo")); 1718 1719 auto OpenedF = FS->openFileForRead("vfsname"); 1720 ASSERT_FALSE(OpenedF.getError()); 1721 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName(); 1722 ASSERT_FALSE(Name.getError()); 1723 EXPECT_EQ("vfsname", Name.get()); 1724 1725 auto OpenedS = (*OpenedF)->status(); 1726 ASSERT_FALSE(OpenedS.getError()); 1727 EXPECT_EQ("vfsname", OpenedS->getName()); 1728 EXPECT_TRUE(OpenedS->IsVFSMapped); 1729 1730 auto DirectS = FS->status("vfsname"); 1731 ASSERT_FALSE(DirectS.getError()); 1732 EXPECT_EQ("vfsname", DirectS->getName()); 1733 EXPECT_TRUE(DirectS->IsVFSMapped); 1734 1735 EXPECT_EQ(0, NumDiagnostics); 1736 } 1737 1738 TEST_F(VFSFromYAMLTest, CaseInsensitive) { 1739 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1740 Lower->addRegularFile("//root/foo/bar/a"); 1741 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1742 "{ 'case-sensitive': 'false',\n" 1743 " 'roots': [\n" 1744 "{\n" 1745 " 'type': 'directory',\n" 1746 " 'name': '//root/',\n" 1747 " 'contents': [ {\n" 1748 " 'type': 'file',\n" 1749 " 'name': 'XX',\n" 1750 " 'external-contents': '//root/foo/bar/a'\n" 1751 " }\n" 1752 " ]\n" 1753 "}]}", 1754 Lower); 1755 ASSERT_TRUE(FS.get() != nullptr); 1756 1757 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1758 new vfs::OverlayFileSystem(Lower)); 1759 O->pushOverlay(FS); 1760 1761 ErrorOr<vfs::Status> S = O->status("//root/XX"); 1762 ASSERT_FALSE(S.getError()); 1763 1764 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 1765 ASSERT_FALSE(SS.getError()); 1766 EXPECT_TRUE(S->equivalent(*SS)); 1767 SS = O->status("//root/xX"); 1768 EXPECT_TRUE(S->equivalent(*SS)); 1769 SS = O->status("//root/Xx"); 1770 EXPECT_TRUE(S->equivalent(*SS)); 1771 EXPECT_EQ(0, NumDiagnostics); 1772 } 1773 1774 TEST_F(VFSFromYAMLTest, CaseSensitive) { 1775 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1776 Lower->addRegularFile("//root/foo/bar/a"); 1777 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1778 "{ 'case-sensitive': 'true',\n" 1779 " 'roots': [\n" 1780 "{\n" 1781 " 'type': 'directory',\n" 1782 " 'name': '//root/',\n" 1783 " 'contents': [ {\n" 1784 " 'type': 'file',\n" 1785 " 'name': 'XX',\n" 1786 " 'external-contents': '//root/foo/bar/a'\n" 1787 " }\n" 1788 " ]\n" 1789 "}]}", 1790 Lower); 1791 ASSERT_TRUE(FS.get() != nullptr); 1792 1793 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1794 new vfs::OverlayFileSystem(Lower)); 1795 O->pushOverlay(FS); 1796 1797 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 1798 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 1799 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 EXPECT_EQ(0, NumDiagnostics); 1804 } 1805 1806 TEST_F(VFSFromYAMLTest, IllegalVFSFile) { 1807 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1808 1809 // invalid YAML at top-level 1810 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower); 1811 EXPECT_EQ(nullptr, FS.get()); 1812 // invalid YAML in roots 1813 FS = getFromYAMLString("{ 'roots':[}", Lower); 1814 // invalid YAML in directory 1815 FS = getFromYAMLString( 1816 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}", 1817 Lower); 1818 EXPECT_EQ(nullptr, FS.get()); 1819 1820 // invalid configuration 1821 FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower); 1822 EXPECT_EQ(nullptr, FS.get()); 1823 FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower); 1824 EXPECT_EQ(nullptr, FS.get()); 1825 1826 // invalid roots 1827 FS = getFromYAMLString("{ 'roots':'' }", Lower); 1828 EXPECT_EQ(nullptr, FS.get()); 1829 FS = getFromYAMLString("{ 'roots':{} }", Lower); 1830 EXPECT_EQ(nullptr, FS.get()); 1831 1832 // invalid entries 1833 FS = getFromYAMLString( 1834 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower); 1835 EXPECT_EQ(nullptr, FS.get()); 1836 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], " 1837 "'external-contents': 'other' }", 1838 Lower); 1839 EXPECT_EQ(nullptr, FS.get()); 1840 FS = getFromYAMLString( 1841 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }", 1842 Lower); 1843 EXPECT_EQ(nullptr, FS.get()); 1844 FS = getFromYAMLString( 1845 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }", 1846 Lower); 1847 EXPECT_EQ(nullptr, FS.get()); 1848 FS = getFromYAMLString( 1849 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }", 1850 Lower); 1851 EXPECT_EQ(nullptr, FS.get()); 1852 FS = getFromYAMLString( 1853 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }", 1854 Lower); 1855 EXPECT_EQ(nullptr, FS.get()); 1856 FS = getFromYAMLString( 1857 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }", 1858 Lower); 1859 EXPECT_EQ(nullptr, FS.get()); 1860 1861 // missing mandatory fields 1862 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower); 1863 EXPECT_EQ(nullptr, FS.get()); 1864 FS = getFromYAMLString( 1865 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower); 1866 EXPECT_EQ(nullptr, FS.get()); 1867 FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower); 1868 EXPECT_EQ(nullptr, FS.get()); 1869 1870 // duplicate keys 1871 FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower); 1872 EXPECT_EQ(nullptr, FS.get()); 1873 FS = getFromYAMLString( 1874 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }", 1875 Lower); 1876 EXPECT_EQ(nullptr, FS.get()); 1877 FS = 1878 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', " 1879 "'external-contents':'blah' } ] }", 1880 Lower); 1881 EXPECT_EQ(nullptr, FS.get()); 1882 1883 // missing version 1884 FS = getFromYAMLRawString("{ 'roots':[] }", Lower); 1885 EXPECT_EQ(nullptr, FS.get()); 1886 1887 // bad version number 1888 FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower); 1889 EXPECT_EQ(nullptr, FS.get()); 1890 FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower); 1891 EXPECT_EQ(nullptr, FS.get()); 1892 FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower); 1893 EXPECT_EQ(nullptr, FS.get()); 1894 1895 // both 'external-contents' and 'contents' specified 1896 Lower->addDirectory("//root/external/dir"); 1897 FS = getFromYAMLString( 1898 "{ 'roots':[ \n" 1899 "{ 'type': 'directory', 'name': '//root/A', 'contents': [],\n" 1900 " 'external-contents': '//root/external/dir'}]}", 1901 Lower); 1902 EXPECT_EQ(nullptr, FS.get()); 1903 1904 // 'directory-remap' with 'contents' 1905 FS = getFromYAMLString( 1906 "{ 'roots':[ \n" 1907 "{ 'type': 'directory-remap', 'name': '//root/A', 'contents': [] }]}", 1908 Lower); 1909 EXPECT_EQ(nullptr, FS.get()); 1910 1911 EXPECT_EQ(26, NumDiagnostics); 1912 } 1913 1914 TEST_F(VFSFromYAMLTest, UseExternalName) { 1915 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1916 Lower->addRegularFile("//root/external/file"); 1917 1918 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1919 getFromYAMLString("{ 'roots': [\n" 1920 " { 'type': 'file', 'name': '//root/A',\n" 1921 " 'external-contents': '//root/external/file'\n" 1922 " },\n" 1923 " { 'type': 'file', 'name': '//root/B',\n" 1924 " 'use-external-name': true,\n" 1925 " 'external-contents': '//root/external/file'\n" 1926 " },\n" 1927 " { 'type': 'file', 'name': '//root/C',\n" 1928 " 'use-external-name': false,\n" 1929 " 'external-contents': '//root/external/file'\n" 1930 " }\n" 1931 "] }", 1932 Lower); 1933 ASSERT_TRUE(nullptr != FS.get()); 1934 1935 // default true 1936 EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName()); 1937 // explicit 1938 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 1939 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 1940 1941 // global configuration 1942 FS = getFromYAMLString("{ 'use-external-names': false,\n" 1943 " 'roots': [\n" 1944 " { 'type': 'file', 'name': '//root/A',\n" 1945 " 'external-contents': '//root/external/file'\n" 1946 " },\n" 1947 " { 'type': 'file', 'name': '//root/B',\n" 1948 " 'use-external-name': true,\n" 1949 " 'external-contents': '//root/external/file'\n" 1950 " },\n" 1951 " { 'type': 'file', 'name': '//root/C',\n" 1952 " 'use-external-name': false,\n" 1953 " 'external-contents': '//root/external/file'\n" 1954 " }\n" 1955 "] }", 1956 Lower); 1957 ASSERT_TRUE(nullptr != FS.get()); 1958 1959 // default 1960 EXPECT_EQ("//root/A", FS->status("//root/A")->getName()); 1961 // explicit 1962 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 1963 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 1964 } 1965 1966 TEST_F(VFSFromYAMLTest, MultiComponentPath) { 1967 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1968 Lower->addRegularFile("//root/other"); 1969 1970 // file in roots 1971 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1972 getFromYAMLString("{ 'roots': [\n" 1973 " { 'type': 'file', 'name': '//root/path/to/file',\n" 1974 " 'external-contents': '//root/other' }]\n" 1975 "}", 1976 Lower); 1977 ASSERT_TRUE(nullptr != FS.get()); 1978 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1979 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1980 EXPECT_FALSE(FS->status("//root/path").getError()); 1981 EXPECT_FALSE(FS->status("//root/").getError()); 1982 1983 // at the start 1984 FS = getFromYAMLString( 1985 "{ 'roots': [\n" 1986 " { 'type': 'directory', 'name': '//root/path/to',\n" 1987 " 'contents': [ { 'type': 'file', 'name': 'file',\n" 1988 " 'external-contents': '//root/other' }]}]\n" 1989 "}", 1990 Lower); 1991 ASSERT_TRUE(nullptr != FS.get()); 1992 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1993 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1994 EXPECT_FALSE(FS->status("//root/path").getError()); 1995 EXPECT_FALSE(FS->status("//root/").getError()); 1996 1997 // at the end 1998 FS = getFromYAMLString( 1999 "{ 'roots': [\n" 2000 " { 'type': 'directory', 'name': '//root/',\n" 2001 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n" 2002 " 'external-contents': '//root/other' }]}]\n" 2003 "}", 2004 Lower); 2005 ASSERT_TRUE(nullptr != FS.get()); 2006 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 2007 EXPECT_FALSE(FS->status("//root/path/to").getError()); 2008 EXPECT_FALSE(FS->status("//root/path").getError()); 2009 EXPECT_FALSE(FS->status("//root/").getError()); 2010 } 2011 2012 TEST_F(VFSFromYAMLTest, TrailingSlashes) { 2013 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2014 Lower->addRegularFile("//root/other"); 2015 2016 // file in roots 2017 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2018 "{ 'roots': [\n" 2019 " { 'type': 'directory', 'name': '//root/path/to////',\n" 2020 " 'contents': [ { 'type': 'file', 'name': 'file',\n" 2021 " 'external-contents': '//root/other' }]}]\n" 2022 "}", 2023 Lower); 2024 ASSERT_TRUE(nullptr != FS.get()); 2025 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 2026 EXPECT_FALSE(FS->status("//root/path/to").getError()); 2027 EXPECT_FALSE(FS->status("//root/path").getError()); 2028 EXPECT_FALSE(FS->status("//root/").getError()); 2029 } 2030 2031 TEST_F(VFSFromYAMLTest, DirectoryIteration) { 2032 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2033 Lower->addDirectory("//root/"); 2034 Lower->addDirectory("//root/foo"); 2035 Lower->addDirectory("//root/foo/bar"); 2036 Lower->addRegularFile("//root/foo/bar/a"); 2037 Lower->addRegularFile("//root/foo/bar/b"); 2038 Lower->addRegularFile("//root/file3"); 2039 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2040 "{ 'use-external-names': false,\n" 2041 " 'roots': [\n" 2042 "{\n" 2043 " 'type': 'directory',\n" 2044 " 'name': '//root/',\n" 2045 " 'contents': [ {\n" 2046 " 'type': 'file',\n" 2047 " 'name': 'file1',\n" 2048 " 'external-contents': '//root/foo/bar/a'\n" 2049 " },\n" 2050 " {\n" 2051 " 'type': 'file',\n" 2052 " 'name': 'file2',\n" 2053 " 'external-contents': '//root/foo/bar/b'\n" 2054 " }\n" 2055 " ]\n" 2056 "}\n" 2057 "]\n" 2058 "}", 2059 Lower); 2060 ASSERT_TRUE(FS.get() != nullptr); 2061 2062 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 2063 new vfs::OverlayFileSystem(Lower)); 2064 O->pushOverlay(FS); 2065 2066 std::error_code EC; 2067 checkContents(O->dir_begin("//root/", EC), 2068 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"}); 2069 2070 checkContents(O->dir_begin("//root/foo/bar", EC), 2071 {"//root/foo/bar/a", "//root/foo/bar/b"}); 2072 } 2073 2074 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) { 2075 // https://llvm.org/bugs/show_bug.cgi?id=27725 2076 if (!supportsSameDirMultipleYAMLEntries()) 2077 return; 2078 2079 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2080 Lower->addDirectory("//root/zab"); 2081 Lower->addDirectory("//root/baz"); 2082 Lower->addRegularFile("//root/zab/a"); 2083 Lower->addRegularFile("//root/zab/b"); 2084 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2085 "{ 'use-external-names': false,\n" 2086 " 'roots': [\n" 2087 "{\n" 2088 " 'type': 'directory',\n" 2089 " 'name': '//root/baz/',\n" 2090 " 'contents': [ {\n" 2091 " 'type': 'file',\n" 2092 " 'name': 'x',\n" 2093 " 'external-contents': '//root/zab/a'\n" 2094 " }\n" 2095 " ]\n" 2096 "},\n" 2097 "{\n" 2098 " 'type': 'directory',\n" 2099 " 'name': '//root/baz/',\n" 2100 " 'contents': [ {\n" 2101 " 'type': 'file',\n" 2102 " 'name': 'y',\n" 2103 " 'external-contents': '//root/zab/b'\n" 2104 " }\n" 2105 " ]\n" 2106 "}\n" 2107 "]\n" 2108 "}", 2109 Lower); 2110 ASSERT_TRUE(FS.get() != nullptr); 2111 2112 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 2113 new vfs::OverlayFileSystem(Lower)); 2114 O->pushOverlay(FS); 2115 2116 std::error_code EC; 2117 2118 checkContents(O->dir_begin("//root/baz/", EC), 2119 {"//root/baz/x", "//root/baz/y"}); 2120 } 2121 2122 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) { 2123 2124 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2125 Lower->addDirectory("//root/a"); 2126 Lower->addDirectory("//root/a/b"); 2127 Lower->addDirectory("//root/a/b/c"); 2128 Lower->addRegularFile("//root/a/b/c/file"); 2129 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2130 "{ 'use-external-names': false,\n" 2131 " 'roots': [\n" 2132 "{\n" 2133 " 'type': 'directory',\n" 2134 " 'name': '//root/a/b/c/',\n" 2135 " 'contents': [ {\n" 2136 " 'type': 'file',\n" 2137 " 'name': 'file',\n" 2138 " 'external-contents': '//root/a/b/c/file'\n" 2139 " }\n" 2140 " ]\n" 2141 "},\n" 2142 "]\n" 2143 "}", 2144 Lower); 2145 ASSERT_TRUE(FS.get() != nullptr); 2146 2147 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 2148 new vfs::OverlayFileSystem(Lower)); 2149 O->pushOverlay(FS); 2150 2151 std::error_code EC; 2152 2153 // Test recursive_directory_iterator level() 2154 vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator( 2155 *O, "//root", EC), 2156 E; 2157 ASSERT_FALSE(EC); 2158 for (int l = 0; I != E; I.increment(EC), ++l) { 2159 ASSERT_FALSE(EC); 2160 EXPECT_EQ(I.level(), l); 2161 } 2162 EXPECT_EQ(I, E); 2163 } 2164 2165 TEST_F(VFSFromYAMLTest, RelativePaths) { 2166 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2167 // Filename at root level without a parent directory. 2168 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2169 "{ 'roots': [\n" 2170 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n" 2171 " 'external-contents': '//root/external/file'\n" 2172 " }\n" 2173 "] }", 2174 Lower); 2175 EXPECT_EQ(nullptr, FS.get()); 2176 2177 // Relative file path. 2178 FS = getFromYAMLString("{ 'roots': [\n" 2179 " { 'type': 'file', 'name': 'relative/file/path.h',\n" 2180 " 'external-contents': '//root/external/file'\n" 2181 " }\n" 2182 "] }", 2183 Lower); 2184 EXPECT_EQ(nullptr, FS.get()); 2185 2186 // Relative directory path. 2187 FS = getFromYAMLString( 2188 "{ 'roots': [\n" 2189 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n" 2190 " 'contents': []\n" 2191 " }\n" 2192 "] }", 2193 Lower); 2194 EXPECT_EQ(nullptr, FS.get()); 2195 2196 EXPECT_EQ(3, NumDiagnostics); 2197 } 2198 2199 TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) { 2200 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2201 Lower->addDirectory("//root/"); 2202 Lower->addRegularFile("//root/a"); 2203 Lower->addRegularFile("//root/b"); 2204 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2205 "{ 'use-external-names': false,\n" 2206 " 'fallthrough': false,\n" 2207 " 'roots': [\n" 2208 "{\n" 2209 " 'type': 'directory',\n" 2210 " 'name': '//root/',\n" 2211 " 'contents': [ {\n" 2212 " 'type': 'file',\n" 2213 " 'name': 'c',\n" 2214 " 'external-contents': '//root/a'\n" 2215 " }\n" 2216 " ]\n" 2217 "}\n" 2218 "]\n" 2219 "}", 2220 Lower); 2221 ASSERT_TRUE(FS.get() != nullptr); 2222 2223 std::error_code EC; 2224 checkContents(FS->dir_begin("//root/", EC), 2225 {"//root/c"}); 2226 } 2227 2228 TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) { 2229 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2230 Lower->addDirectory("//root/"); 2231 Lower->addRegularFile("//root/a"); 2232 Lower->addRegularFile("//root/b"); 2233 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2234 "{ 'use-external-names': false,\n" 2235 " 'roots': [\n" 2236 "{\n" 2237 " 'type': 'directory',\n" 2238 " 'name': '//root/',\n" 2239 " 'contents': [ {\n" 2240 " 'type': 'file',\n" 2241 " 'name': 'a',\n" 2242 " 'external-contents': '//root/a'\n" 2243 " }\n" 2244 " ]\n" 2245 "}\n" 2246 "]\n" 2247 "}", 2248 Lower); 2249 ASSERT_TRUE(FS.get() != nullptr); 2250 2251 std::error_code EC; 2252 checkContents(FS->dir_begin("//root/", EC), 2253 {"//root/a", "//root/b"}); 2254 } 2255 2256 TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) { 2257 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2258 Lower->addDirectory("//root/"); 2259 Lower->addDirectory("//root/foo"); 2260 Lower->addRegularFile("//root/foo/a"); 2261 Lower->addRegularFile("//root/foo/b"); 2262 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2263 "{ 'use-external-names': false,\n" 2264 " 'roots': [\n" 2265 "{\n" 2266 " 'type': 'directory',\n" 2267 " 'name': '//root/',\n" 2268 " 'contents': [ {\n" 2269 " 'type': 'file',\n" 2270 " 'name': 'bar/a',\n" 2271 " 'external-contents': '//root/foo/a'\n" 2272 " }\n" 2273 " ]\n" 2274 "}\n" 2275 "]\n" 2276 "}", 2277 Lower); 2278 ASSERT_TRUE(FS.get() != nullptr); 2279 2280 std::error_code EC; 2281 checkContents(FS->dir_begin("//root/foo", EC), 2282 {"//root/foo/a", "//root/foo/b"}); 2283 } 2284 2285 TEST_F(VFSFromYAMLTest, GetRealPath) { 2286 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2287 Lower->addDirectory("//dir/"); 2288 Lower->addRegularFile("/foo"); 2289 Lower->addSymlink("/link"); 2290 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2291 "{ 'use-external-names': false,\n" 2292 " 'roots': [\n" 2293 "{\n" 2294 " 'type': 'directory',\n" 2295 " 'name': '//root/',\n" 2296 " 'contents': [ {\n" 2297 " 'type': 'file',\n" 2298 " 'name': 'bar',\n" 2299 " 'external-contents': '/link'\n" 2300 " }\n" 2301 " ]\n" 2302 "},\n" 2303 "{\n" 2304 " 'type': 'directory',\n" 2305 " 'name': '//dir/',\n" 2306 " 'contents': []\n" 2307 "}\n" 2308 "]\n" 2309 "}", 2310 Lower); 2311 ASSERT_TRUE(FS.get() != nullptr); 2312 2313 // Regular file present in underlying file system. 2314 SmallString<16> RealPath; 2315 EXPECT_FALSE(FS->getRealPath("/foo", RealPath)); 2316 EXPECT_EQ(RealPath.str(), "/foo"); 2317 2318 // File present in YAML pointing to symlink in underlying file system. 2319 EXPECT_FALSE(FS->getRealPath("//root/bar", RealPath)); 2320 EXPECT_EQ(RealPath.str(), "/symlink"); 2321 2322 // Directories should fall back to the underlying file system is possible. 2323 EXPECT_FALSE(FS->getRealPath("//dir/", RealPath)); 2324 EXPECT_EQ(RealPath.str(), "//dir/"); 2325 2326 // Try a non-existing file. 2327 EXPECT_EQ(FS->getRealPath("/non_existing", RealPath), 2328 errc::no_such_file_or_directory); 2329 } 2330 2331 TEST_F(VFSFromYAMLTest, WorkingDirectory) { 2332 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2333 Lower->addDirectory("//root/"); 2334 Lower->addDirectory("//root/foo"); 2335 Lower->addRegularFile("//root/foo/a"); 2336 Lower->addRegularFile("//root/foo/b"); 2337 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2338 "{ 'use-external-names': false,\n" 2339 " 'roots': [\n" 2340 "{\n" 2341 " 'type': 'directory',\n" 2342 " 'name': '//root/bar',\n" 2343 " 'contents': [ {\n" 2344 " 'type': 'file',\n" 2345 " 'name': 'a',\n" 2346 " 'external-contents': '//root/foo/a'\n" 2347 " }\n" 2348 " ]\n" 2349 "}\n" 2350 "]\n" 2351 "}", 2352 Lower); 2353 ASSERT_TRUE(FS.get() != nullptr); 2354 std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar"); 2355 ASSERT_FALSE(EC); 2356 2357 llvm::ErrorOr<std::string> WorkingDir = FS->getCurrentWorkingDirectory(); 2358 ASSERT_TRUE(WorkingDir); 2359 EXPECT_EQ(*WorkingDir, "//root/bar"); 2360 2361 llvm::ErrorOr<vfs::Status> Status = FS->status("./a"); 2362 ASSERT_FALSE(Status.getError()); 2363 EXPECT_TRUE(Status->isStatusKnown()); 2364 EXPECT_FALSE(Status->isDirectory()); 2365 EXPECT_TRUE(Status->isRegularFile()); 2366 EXPECT_FALSE(Status->isSymlink()); 2367 EXPECT_FALSE(Status->isOther()); 2368 EXPECT_TRUE(Status->exists()); 2369 2370 EC = FS->setCurrentWorkingDirectory("bogus"); 2371 ASSERT_TRUE(EC); 2372 WorkingDir = FS->getCurrentWorkingDirectory(); 2373 ASSERT_TRUE(WorkingDir); 2374 EXPECT_EQ(*WorkingDir, "//root/bar"); 2375 2376 EC = FS->setCurrentWorkingDirectory("//root/"); 2377 ASSERT_FALSE(EC); 2378 WorkingDir = FS->getCurrentWorkingDirectory(); 2379 ASSERT_TRUE(WorkingDir); 2380 EXPECT_EQ(*WorkingDir, "//root/"); 2381 2382 EC = FS->setCurrentWorkingDirectory("bar"); 2383 ASSERT_FALSE(EC); 2384 WorkingDir = FS->getCurrentWorkingDirectory(); 2385 ASSERT_TRUE(WorkingDir); 2386 EXPECT_EQ(*WorkingDir, "//root/bar"); 2387 } 2388 2389 TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthrough) { 2390 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 2391 Lower->addDirectory("//root/"); 2392 Lower->addDirectory("//root/foo"); 2393 Lower->addRegularFile("//root/foo/a"); 2394 Lower->addRegularFile("//root/foo/b"); 2395 Lower->addRegularFile("//root/c"); 2396 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2397 "{ 'use-external-names': false,\n" 2398 " 'roots': [\n" 2399 "{\n" 2400 " 'type': 'directory',\n" 2401 " 'name': '//root/bar',\n" 2402 " 'contents': [ {\n" 2403 " 'type': 'file',\n" 2404 " 'name': 'a',\n" 2405 " 'external-contents': '//root/foo/a'\n" 2406 " }\n" 2407 " ]\n" 2408 "},\n" 2409 "{\n" 2410 " 'type': 'directory',\n" 2411 " 'name': '//root/bar/baz',\n" 2412 " 'contents': [ {\n" 2413 " 'type': 'file',\n" 2414 " 'name': 'a',\n" 2415 " 'external-contents': '//root/foo/a'\n" 2416 " }\n" 2417 " ]\n" 2418 "}\n" 2419 "]\n" 2420 "}", 2421 Lower); 2422 ASSERT_TRUE(FS.get() != nullptr); 2423 std::error_code EC = FS->setCurrentWorkingDirectory("//root/"); 2424 ASSERT_FALSE(EC); 2425 ASSERT_TRUE(FS.get() != nullptr); 2426 2427 llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a"); 2428 ASSERT_FALSE(Status.getError()); 2429 EXPECT_TRUE(Status->exists()); 2430 2431 Status = FS->status("foo/a"); 2432 ASSERT_FALSE(Status.getError()); 2433 EXPECT_TRUE(Status->exists()); 2434 2435 EC = FS->setCurrentWorkingDirectory("//root/bar"); 2436 ASSERT_FALSE(EC); 2437 2438 Status = FS->status("./a"); 2439 ASSERT_FALSE(Status.getError()); 2440 EXPECT_TRUE(Status->exists()); 2441 2442 Status = FS->status("./b"); 2443 ASSERT_TRUE(Status.getError()); 2444 2445 Status = FS->status("./c"); 2446 ASSERT_TRUE(Status.getError()); 2447 2448 EC = FS->setCurrentWorkingDirectory("//root/"); 2449 ASSERT_FALSE(EC); 2450 2451 Status = FS->status("c"); 2452 ASSERT_FALSE(Status.getError()); 2453 EXPECT_TRUE(Status->exists()); 2454 2455 Status = FS->status("./bar/baz/a"); 2456 ASSERT_FALSE(Status.getError()); 2457 EXPECT_TRUE(Status->exists()); 2458 2459 EC = FS->setCurrentWorkingDirectory("//root/bar"); 2460 ASSERT_FALSE(EC); 2461 2462 Status = FS->status("./baz/a"); 2463 ASSERT_FALSE(Status.getError()); 2464 EXPECT_TRUE(Status->exists()); 2465 2466 Status = FS->status("../bar/baz/a"); 2467 ASSERT_FALSE(Status.getError()); 2468 EXPECT_TRUE(Status->exists()); 2469 } 2470 2471 TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthroughInvalid) { 2472 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2473 Lower->addDirectory("//root/"); 2474 Lower->addDirectory("//root/foo"); 2475 Lower->addRegularFile("//root/foo/a"); 2476 Lower->addRegularFile("//root/foo/b"); 2477 Lower->addRegularFile("//root/c"); 2478 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2479 "{ 'use-external-names': false,\n" 2480 " 'roots': [\n" 2481 "{\n" 2482 " 'type': 'directory',\n" 2483 " 'name': '//root/bar',\n" 2484 " 'contents': [ {\n" 2485 " 'type': 'file',\n" 2486 " 'name': 'a',\n" 2487 " 'external-contents': '//root/foo/a'\n" 2488 " }\n" 2489 " ]\n" 2490 "}\n" 2491 "]\n" 2492 "}", 2493 Lower); 2494 ASSERT_TRUE(FS.get() != nullptr); 2495 std::error_code EC = FS->setCurrentWorkingDirectory("//root/"); 2496 ASSERT_FALSE(EC); 2497 ASSERT_TRUE(FS.get() != nullptr); 2498 2499 llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a"); 2500 ASSERT_FALSE(Status.getError()); 2501 EXPECT_TRUE(Status->exists()); 2502 2503 Status = FS->status("foo/a"); 2504 ASSERT_FALSE(Status.getError()); 2505 EXPECT_TRUE(Status->exists()); 2506 } 2507 2508 TEST_F(VFSFromYAMLTest, VirtualWorkingDirectory) { 2509 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2510 Lower->addDirectory("//root/"); 2511 Lower->addDirectory("//root/foo"); 2512 Lower->addRegularFile("//root/foo/a"); 2513 Lower->addRegularFile("//root/foo/b"); 2514 Lower->addRegularFile("//root/c"); 2515 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 2516 "{ 'use-external-names': false,\n" 2517 " 'roots': [\n" 2518 "{\n" 2519 " 'type': 'directory',\n" 2520 " 'name': '//root/bar',\n" 2521 " 'contents': [ {\n" 2522 " 'type': 'file',\n" 2523 " 'name': 'a',\n" 2524 " 'external-contents': '//root/foo/a'\n" 2525 " }\n" 2526 " ]\n" 2527 "}\n" 2528 "]\n" 2529 "}", 2530 Lower); 2531 ASSERT_TRUE(FS.get() != nullptr); 2532 std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar"); 2533 ASSERT_FALSE(EC); 2534 ASSERT_TRUE(FS.get() != nullptr); 2535 2536 llvm::ErrorOr<vfs::Status> Status = FS->status("a"); 2537 ASSERT_FALSE(Status.getError()); 2538 EXPECT_TRUE(Status->exists()); 2539 } 2540 2541 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest) { 2542 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2543 TempDir _a(TestDirectory.path("a")); 2544 TempFile _ab(TestDirectory.path("a, b")); 2545 TempDir _c(TestDirectory.path("c")); 2546 TempFile _cd(TestDirectory.path("c/d")); 2547 TempDir _e(TestDirectory.path("e")); 2548 TempDir _ef(TestDirectory.path("e/f")); 2549 TempFile _g(TestDirectory.path("g")); 2550 TempDir _h(TestDirectory.path("h")); 2551 2552 vfs::YAMLVFSWriter VFSWriter; 2553 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2554 VFSWriter.addFileMapping(_ab.path(), "//root/a/b"); 2555 VFSWriter.addFileMapping(_cd.path(), "//root/c/d"); 2556 VFSWriter.addDirectoryMapping(_e.path(), "//root/e"); 2557 VFSWriter.addDirectoryMapping(_ef.path(), "//root/e/f"); 2558 VFSWriter.addFileMapping(_g.path(), "//root/g"); 2559 VFSWriter.addDirectoryMapping(_h.path(), "//root/h"); 2560 2561 std::string Buffer; 2562 raw_string_ostream OS(Buffer); 2563 VFSWriter.write(OS); 2564 OS.flush(); 2565 2566 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2567 Lower->addDirectory("//root/"); 2568 Lower->addDirectory("//root/a"); 2569 Lower->addRegularFile("//root/a/b"); 2570 Lower->addDirectory("//root/b"); 2571 Lower->addDirectory("//root/c"); 2572 Lower->addRegularFile("//root/c/d"); 2573 Lower->addDirectory("//root/e"); 2574 Lower->addDirectory("//root/e/f"); 2575 Lower->addRegularFile("//root/g"); 2576 Lower->addDirectory("//root/h"); 2577 2578 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2579 ASSERT_TRUE(FS.get() != nullptr); 2580 2581 EXPECT_TRUE(FS->exists(_a.path())); 2582 EXPECT_TRUE(FS->exists(_ab.path())); 2583 EXPECT_TRUE(FS->exists(_c.path())); 2584 EXPECT_TRUE(FS->exists(_cd.path())); 2585 EXPECT_TRUE(FS->exists(_e.path())); 2586 EXPECT_TRUE(FS->exists(_ef.path())); 2587 EXPECT_TRUE(FS->exists(_g.path())); 2588 EXPECT_TRUE(FS->exists(_h.path())); 2589 } 2590 2591 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest2) { 2592 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2593 TempDir _a(TestDirectory.path("a")); 2594 TempFile _ab(TestDirectory.path("a/b")); 2595 TempDir _ac(TestDirectory.path("a/c")); 2596 TempFile _acd(TestDirectory.path("a/c/d")); 2597 TempFile _ace(TestDirectory.path("a/c/e")); 2598 TempFile _acf(TestDirectory.path("a/c/f")); 2599 TempDir _ag(TestDirectory.path("a/g")); 2600 TempFile _agh(TestDirectory.path("a/g/h")); 2601 2602 vfs::YAMLVFSWriter VFSWriter; 2603 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2604 VFSWriter.addFileMapping(_ab.path(), "//root/a/b"); 2605 VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c"); 2606 VFSWriter.addFileMapping(_acd.path(), "//root/a/c/d"); 2607 VFSWriter.addFileMapping(_ace.path(), "//root/a/c/e"); 2608 VFSWriter.addFileMapping(_acf.path(), "//root/a/c/f"); 2609 VFSWriter.addDirectoryMapping(_ag.path(), "//root/a/g"); 2610 VFSWriter.addFileMapping(_agh.path(), "//root/a/g/h"); 2611 2612 std::string Buffer; 2613 raw_string_ostream OS(Buffer); 2614 VFSWriter.write(OS); 2615 OS.flush(); 2616 2617 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2618 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2619 EXPECT_TRUE(FS.get() != nullptr); 2620 } 2621 2622 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest3) { 2623 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2624 TempDir _a(TestDirectory.path("a")); 2625 TempFile _ab(TestDirectory.path("a/b")); 2626 TempDir _ac(TestDirectory.path("a/c")); 2627 TempDir _acd(TestDirectory.path("a/c/d")); 2628 TempDir _acde(TestDirectory.path("a/c/d/e")); 2629 TempFile _acdef(TestDirectory.path("a/c/d/e/f")); 2630 TempFile _acdeg(TestDirectory.path("a/c/d/e/g")); 2631 TempDir _ah(TestDirectory.path("a/h")); 2632 TempFile _ahi(TestDirectory.path("a/h/i")); 2633 2634 vfs::YAMLVFSWriter VFSWriter; 2635 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2636 VFSWriter.addFileMapping(_ab.path(), "//root/a/b"); 2637 VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c"); 2638 VFSWriter.addDirectoryMapping(_acd.path(), "//root/a/c/d"); 2639 VFSWriter.addDirectoryMapping(_acde.path(), "//root/a/c/d/e"); 2640 VFSWriter.addFileMapping(_acdef.path(), "//root/a/c/d/e/f"); 2641 VFSWriter.addFileMapping(_acdeg.path(), "//root/a/c/d/e/g"); 2642 VFSWriter.addDirectoryMapping(_ahi.path(), "//root/a/h"); 2643 VFSWriter.addFileMapping(_ahi.path(), "//root/a/h/i"); 2644 2645 std::string Buffer; 2646 raw_string_ostream OS(Buffer); 2647 VFSWriter.write(OS); 2648 OS.flush(); 2649 2650 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2651 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2652 EXPECT_TRUE(FS.get() != nullptr); 2653 } 2654 2655 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTestHandleDirs) { 2656 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 2657 TempDir _a(TestDirectory.path("a")); 2658 TempDir _b(TestDirectory.path("b")); 2659 TempDir _c(TestDirectory.path("c")); 2660 2661 vfs::YAMLVFSWriter VFSWriter; 2662 VFSWriter.addDirectoryMapping(_a.path(), "//root/a"); 2663 VFSWriter.addDirectoryMapping(_b.path(), "//root/b"); 2664 VFSWriter.addDirectoryMapping(_c.path(), "//root/c"); 2665 2666 std::string Buffer; 2667 raw_string_ostream OS(Buffer); 2668 VFSWriter.write(OS); 2669 OS.flush(); 2670 2671 // We didn't add a single file - only directories. 2672 EXPECT_TRUE(Buffer.find("'type': 'file'") == std::string::npos); 2673 2674 IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); 2675 Lower->addDirectory("//root/a"); 2676 Lower->addDirectory("//root/b"); 2677 Lower->addDirectory("//root/c"); 2678 // canaries 2679 Lower->addRegularFile("//root/a/a"); 2680 Lower->addRegularFile("//root/b/b"); 2681 Lower->addRegularFile("//root/c/c"); 2682 2683 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower); 2684 ASSERT_TRUE(FS.get() != nullptr); 2685 2686 EXPECT_FALSE(FS->exists(_a.path("a"))); 2687 EXPECT_FALSE(FS->exists(_b.path("b"))); 2688 EXPECT_FALSE(FS->exists(_c.path("c"))); 2689 } 2690 2691 TEST(VFSFromRemappedFilesTest, Basic) { 2692 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 2693 new vfs::InMemoryFileSystem; 2694 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 2695 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 2696 2697 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 2698 {"//root/a/a", "//root/b"}, 2699 {"//root/a/b/c", "//root/c"}, 2700 }; 2701 auto RemappedFS = vfs::RedirectingFileSystem::create( 2702 RemappedFiles, /*UseExternalNames=*/false, *BaseFS); 2703 2704 auto StatA = RemappedFS->status("//root/a/a"); 2705 auto StatB = RemappedFS->status("//root/a/b/c"); 2706 ASSERT_TRUE(StatA); 2707 ASSERT_TRUE(StatB); 2708 EXPECT_EQ("//root/a/a", StatA->getName()); 2709 EXPECT_EQ("//root/a/b/c", StatB->getName()); 2710 2711 auto BufferA = RemappedFS->getBufferForFile("//root/a/a"); 2712 auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c"); 2713 ASSERT_TRUE(BufferA); 2714 ASSERT_TRUE(BufferB); 2715 EXPECT_EQ("contents of b", (*BufferA)->getBuffer()); 2716 EXPECT_EQ("contents of c", (*BufferB)->getBuffer()); 2717 } 2718 2719 TEST(VFSFromRemappedFilesTest, UseExternalNames) { 2720 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 2721 new vfs::InMemoryFileSystem; 2722 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 2723 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 2724 2725 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 2726 {"//root/a/a", "//root/b"}, 2727 {"//root/a/b/c", "//root/c"}, 2728 }; 2729 auto RemappedFS = vfs::RedirectingFileSystem::create( 2730 RemappedFiles, /*UseExternalNames=*/true, *BaseFS); 2731 2732 auto StatA = RemappedFS->status("//root/a/a"); 2733 auto StatB = RemappedFS->status("//root/a/b/c"); 2734 ASSERT_TRUE(StatA); 2735 ASSERT_TRUE(StatB); 2736 EXPECT_EQ("//root/b", StatA->getName()); 2737 EXPECT_EQ("//root/c", StatB->getName()); 2738 2739 auto BufferA = RemappedFS->getBufferForFile("//root/a/a"); 2740 auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c"); 2741 ASSERT_TRUE(BufferA); 2742 ASSERT_TRUE(BufferB); 2743 EXPECT_EQ("contents of b", (*BufferA)->getBuffer()); 2744 EXPECT_EQ("contents of c", (*BufferB)->getBuffer()); 2745 } 2746 2747 TEST(VFSFromRemappedFilesTest, LastMappingWins) { 2748 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS = 2749 new vfs::InMemoryFileSystem; 2750 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); 2751 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); 2752 2753 std::vector<std::pair<std::string, std::string>> RemappedFiles = { 2754 {"//root/a", "//root/b"}, 2755 {"//root/a", "//root/c"}, 2756 }; 2757 auto RemappedFSKeepName = vfs::RedirectingFileSystem::create( 2758 RemappedFiles, /*UseExternalNames=*/false, *BaseFS); 2759 auto RemappedFSExternalName = vfs::RedirectingFileSystem::create( 2760 RemappedFiles, /*UseExternalNames=*/true, *BaseFS); 2761 2762 auto StatKeepA = RemappedFSKeepName->status("//root/a"); 2763 auto StatExternalA = RemappedFSExternalName->status("//root/a"); 2764 ASSERT_TRUE(StatKeepA); 2765 ASSERT_TRUE(StatExternalA); 2766 EXPECT_EQ("//root/a", StatKeepA->getName()); 2767 EXPECT_EQ("//root/c", StatExternalA->getName()); 2768 2769 auto BufferKeepA = RemappedFSKeepName->getBufferForFile("//root/a"); 2770 auto BufferExternalA = RemappedFSExternalName->getBufferForFile("//root/a"); 2771 ASSERT_TRUE(BufferKeepA); 2772 ASSERT_TRUE(BufferExternalA); 2773 EXPECT_EQ("contents of c", (*BufferKeepA)->getBuffer()); 2774 EXPECT_EQ("contents of c", (*BufferExternalA)->getBuffer()); 2775 } 2776