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