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