1 //===- unittests/Support/VirtualFileSystem.cpp -------------- VFS tests ---===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "llvm/Support/VirtualFileSystem.h" 11 #include "llvm/ADT/Triple.h" 12 #include "llvm/Config/llvm-config.h" 13 #include "llvm/Support/Errc.h" 14 #include "llvm/Support/Host.h" 15 #include "llvm/Support/MemoryBuffer.h" 16 #include "llvm/Support/Path.h" 17 #include "llvm/Support/SourceMgr.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 testing::ElementsAre; 26 using testing::Pair; 27 using testing::UnorderedElementsAre; 28 29 namespace { 30 struct DummyFile : public vfs::File { 31 vfs::Status S; 32 explicit DummyFile(vfs::Status S) : S(S) {} 33 llvm::ErrorOr<vfs::Status> status() override { return S; } 34 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 35 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 36 bool IsVolatile) override { 37 llvm_unreachable("unimplemented"); 38 } 39 std::error_code close() override { return std::error_code(); } 40 }; 41 42 class DummyFileSystem : public vfs::FileSystem { 43 int FSID; // used to produce UniqueIDs 44 int FileID; // used to produce UniqueIDs 45 std::map<std::string, vfs::Status> FilesAndDirs; 46 47 static int getNextFSID() { 48 static int Count = 0; 49 return Count++; 50 } 51 52 public: 53 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {} 54 55 ErrorOr<vfs::Status> status(const Twine &Path) override { 56 std::map<std::string, vfs::Status>::iterator I = 57 FilesAndDirs.find(Path.str()); 58 if (I == FilesAndDirs.end()) 59 return make_error_code(llvm::errc::no_such_file_or_directory); 60 return I->second; 61 } 62 ErrorOr<std::unique_ptr<vfs::File>> 63 openFileForRead(const Twine &Path) override { 64 auto S = status(Path); 65 if (S) 66 return std::unique_ptr<vfs::File>(new DummyFile{*S}); 67 return S.getError(); 68 } 69 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 70 return std::string(); 71 } 72 std::error_code setCurrentWorkingDirectory(const Twine &Path) override { 73 return std::error_code(); 74 } 75 // Map any symlink to "/symlink". 76 std::error_code getRealPath(const Twine &Path, 77 SmallVectorImpl<char> &Output) const override { 78 auto I = FilesAndDirs.find(Path.str()); 79 if (I == FilesAndDirs.end()) 80 return make_error_code(llvm::errc::no_such_file_or_directory); 81 if (I->second.isSymlink()) { 82 Output.clear(); 83 Twine("/symlink").toVector(Output); 84 return std::error_code(); 85 } 86 Output.clear(); 87 Path.toVector(Output); 88 return std::error_code(); 89 } 90 91 struct DirIterImpl : public llvm::vfs::detail::DirIterImpl { 92 std::map<std::string, vfs::Status> &FilesAndDirs; 93 std::map<std::string, vfs::Status>::iterator I; 94 std::string Path; 95 bool isInPath(StringRef S) { 96 if (Path.size() < S.size() && S.find(Path) == 0) { 97 auto LastSep = S.find_last_of('/'); 98 if (LastSep == Path.size() || LastSep == Path.size() - 1) 99 return true; 100 } 101 return false; 102 } 103 DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs, 104 const Twine &_Path) 105 : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()), 106 Path(_Path.str()) { 107 for (; I != FilesAndDirs.end(); ++I) { 108 if (isInPath(I->first)) { 109 CurrentEntry = 110 vfs::directory_entry(I->second.getName(), I->second.getType()); 111 break; 112 } 113 } 114 } 115 std::error_code increment() override { 116 ++I; 117 for (; I != FilesAndDirs.end(); ++I) { 118 if (isInPath(I->first)) { 119 CurrentEntry = 120 vfs::directory_entry(I->second.getName(), I->second.getType()); 121 break; 122 } 123 } 124 if (I == FilesAndDirs.end()) 125 CurrentEntry = vfs::directory_entry(); 126 return std::error_code(); 127 } 128 }; 129 130 vfs::directory_iterator dir_begin(const Twine &Dir, 131 std::error_code &EC) override { 132 return vfs::directory_iterator( 133 std::make_shared<DirIterImpl>(FilesAndDirs, Dir)); 134 } 135 136 void addEntry(StringRef Path, const vfs::Status &Status) { 137 FilesAndDirs[Path] = Status; 138 } 139 140 void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { 141 vfs::Status S(Path, UniqueID(FSID, FileID++), 142 std::chrono::system_clock::now(), 0, 0, 1024, 143 sys::fs::file_type::regular_file, Perms); 144 addEntry(Path, S); 145 } 146 147 void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { 148 vfs::Status S(Path, UniqueID(FSID, FileID++), 149 std::chrono::system_clock::now(), 0, 0, 0, 150 sys::fs::file_type::directory_file, Perms); 151 addEntry(Path, S); 152 } 153 154 void addSymlink(StringRef Path) { 155 vfs::Status S(Path, UniqueID(FSID, FileID++), 156 std::chrono::system_clock::now(), 0, 0, 0, 157 sys::fs::file_type::symlink_file, sys::fs::all_all); 158 addEntry(Path, S); 159 } 160 }; 161 162 /// Replace back-slashes by front-slashes. 163 std::string getPosixPath(std::string S) { 164 SmallString<128> Result; 165 llvm::sys::path::native(S, Result, llvm::sys::path::Style::posix); 166 return Result.str(); 167 } 168 } // end anonymous namespace 169 170 TEST(VirtualFileSystemTest, StatusQueries) { 171 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); 172 ErrorOr<vfs::Status> Status((std::error_code())); 173 174 D->addRegularFile("/foo"); 175 Status = D->status("/foo"); 176 ASSERT_FALSE(Status.getError()); 177 EXPECT_TRUE(Status->isStatusKnown()); 178 EXPECT_FALSE(Status->isDirectory()); 179 EXPECT_TRUE(Status->isRegularFile()); 180 EXPECT_FALSE(Status->isSymlink()); 181 EXPECT_FALSE(Status->isOther()); 182 EXPECT_TRUE(Status->exists()); 183 184 D->addDirectory("/bar"); 185 Status = D->status("/bar"); 186 ASSERT_FALSE(Status.getError()); 187 EXPECT_TRUE(Status->isStatusKnown()); 188 EXPECT_TRUE(Status->isDirectory()); 189 EXPECT_FALSE(Status->isRegularFile()); 190 EXPECT_FALSE(Status->isSymlink()); 191 EXPECT_FALSE(Status->isOther()); 192 EXPECT_TRUE(Status->exists()); 193 194 D->addSymlink("/baz"); 195 Status = D->status("/baz"); 196 ASSERT_FALSE(Status.getError()); 197 EXPECT_TRUE(Status->isStatusKnown()); 198 EXPECT_FALSE(Status->isDirectory()); 199 EXPECT_FALSE(Status->isRegularFile()); 200 EXPECT_TRUE(Status->isSymlink()); 201 EXPECT_FALSE(Status->isOther()); 202 EXPECT_TRUE(Status->exists()); 203 204 EXPECT_TRUE(Status->equivalent(*Status)); 205 ErrorOr<vfs::Status> Status2 = D->status("/foo"); 206 ASSERT_FALSE(Status2.getError()); 207 EXPECT_FALSE(Status->equivalent(*Status2)); 208 } 209 210 TEST(VirtualFileSystemTest, BaseOnlyOverlay) { 211 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); 212 ErrorOr<vfs::Status> Status((std::error_code())); 213 EXPECT_FALSE(Status = D->status("/foo")); 214 215 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D)); 216 EXPECT_FALSE(Status = O->status("/foo")); 217 218 D->addRegularFile("/foo"); 219 Status = D->status("/foo"); 220 EXPECT_FALSE(Status.getError()); 221 222 ErrorOr<vfs::Status> Status2((std::error_code())); 223 Status2 = O->status("/foo"); 224 EXPECT_FALSE(Status2.getError()); 225 EXPECT_TRUE(Status->equivalent(*Status2)); 226 } 227 228 TEST(VirtualFileSystemTest, GetRealPathInOverlay) { 229 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 230 Lower->addRegularFile("/foo"); 231 Lower->addSymlink("/lower_link"); 232 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 233 234 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 235 new vfs::OverlayFileSystem(Lower)); 236 O->pushOverlay(Upper); 237 238 // Regular file. 239 SmallString<16> RealPath; 240 EXPECT_FALSE(O->getRealPath("/foo", RealPath)); 241 EXPECT_EQ(RealPath.str(), "/foo"); 242 243 // Expect no error getting real path for symlink in lower overlay. 244 EXPECT_FALSE(O->getRealPath("/lower_link", RealPath)); 245 EXPECT_EQ(RealPath.str(), "/symlink"); 246 247 // Try a non-existing link. 248 EXPECT_EQ(O->getRealPath("/upper_link", RealPath), 249 errc::no_such_file_or_directory); 250 251 // Add a new symlink in upper. 252 Upper->addSymlink("/upper_link"); 253 EXPECT_FALSE(O->getRealPath("/upper_link", RealPath)); 254 EXPECT_EQ(RealPath.str(), "/symlink"); 255 } 256 257 TEST(VirtualFileSystemTest, OverlayFiles) { 258 IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem()); 259 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 260 IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem()); 261 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 262 new vfs::OverlayFileSystem(Base)); 263 O->pushOverlay(Middle); 264 O->pushOverlay(Top); 265 266 ErrorOr<vfs::Status> Status1((std::error_code())), 267 Status2((std::error_code())), Status3((std::error_code())), 268 StatusB((std::error_code())), StatusM((std::error_code())), 269 StatusT((std::error_code())); 270 271 Base->addRegularFile("/foo"); 272 StatusB = Base->status("/foo"); 273 ASSERT_FALSE(StatusB.getError()); 274 Status1 = O->status("/foo"); 275 ASSERT_FALSE(Status1.getError()); 276 Middle->addRegularFile("/foo"); 277 StatusM = Middle->status("/foo"); 278 ASSERT_FALSE(StatusM.getError()); 279 Status2 = O->status("/foo"); 280 ASSERT_FALSE(Status2.getError()); 281 Top->addRegularFile("/foo"); 282 StatusT = Top->status("/foo"); 283 ASSERT_FALSE(StatusT.getError()); 284 Status3 = O->status("/foo"); 285 ASSERT_FALSE(Status3.getError()); 286 287 EXPECT_TRUE(Status1->equivalent(*StatusB)); 288 EXPECT_TRUE(Status2->equivalent(*StatusM)); 289 EXPECT_TRUE(Status3->equivalent(*StatusT)); 290 291 EXPECT_FALSE(Status1->equivalent(*Status2)); 292 EXPECT_FALSE(Status2->equivalent(*Status3)); 293 EXPECT_FALSE(Status1->equivalent(*Status3)); 294 } 295 296 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) { 297 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 298 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 299 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 300 new vfs::OverlayFileSystem(Lower)); 301 O->pushOverlay(Upper); 302 303 Lower->addDirectory("/lower-only"); 304 Upper->addDirectory("/upper-only"); 305 306 // non-merged paths should be the same 307 ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only"); 308 ASSERT_FALSE(Status1.getError()); 309 ErrorOr<vfs::Status> Status2 = O->status("/lower-only"); 310 ASSERT_FALSE(Status2.getError()); 311 EXPECT_TRUE(Status1->equivalent(*Status2)); 312 313 Status1 = Upper->status("/upper-only"); 314 ASSERT_FALSE(Status1.getError()); 315 Status2 = O->status("/upper-only"); 316 ASSERT_FALSE(Status2.getError()); 317 EXPECT_TRUE(Status1->equivalent(*Status2)); 318 } 319 320 TEST(VirtualFileSystemTest, MergedDirPermissions) { 321 // merged directories get the permissions of the upper dir 322 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 323 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 324 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 325 new vfs::OverlayFileSystem(Lower)); 326 O->pushOverlay(Upper); 327 328 ErrorOr<vfs::Status> Status((std::error_code())); 329 Lower->addDirectory("/both", sys::fs::owner_read); 330 Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read); 331 Status = O->status("/both"); 332 ASSERT_FALSE(Status.getError()); 333 EXPECT_EQ(0740, Status->getPermissions()); 334 335 // permissions (as usual) are not recursively applied 336 Lower->addRegularFile("/both/foo", sys::fs::owner_read); 337 Upper->addRegularFile("/both/bar", sys::fs::owner_write); 338 Status = O->status("/both/foo"); 339 ASSERT_FALSE(Status.getError()); 340 EXPECT_EQ(0400, Status->getPermissions()); 341 Status = O->status("/both/bar"); 342 ASSERT_FALSE(Status.getError()); 343 EXPECT_EQ(0200, Status->getPermissions()); 344 } 345 346 namespace { 347 struct ScopedDir { 348 SmallString<128> Path; 349 ScopedDir(const Twine &Name, bool Unique = false) { 350 std::error_code EC; 351 if (Unique) { 352 EC = llvm::sys::fs::createUniqueDirectory(Name, Path); 353 } else { 354 Path = Name.str(); 355 EC = llvm::sys::fs::create_directory(Twine(Path)); 356 } 357 if (EC) 358 Path = ""; 359 EXPECT_FALSE(EC); 360 } 361 ~ScopedDir() { 362 if (Path != "") { 363 EXPECT_FALSE(llvm::sys::fs::remove(Path.str())); 364 } 365 } 366 operator StringRef() { return Path.str(); } 367 }; 368 369 struct ScopedLink { 370 SmallString<128> Path; 371 ScopedLink(const Twine &To, const Twine &From) { 372 Path = From.str(); 373 std::error_code EC = sys::fs::create_link(To, From); 374 if (EC) 375 Path = ""; 376 EXPECT_FALSE(EC); 377 } 378 ~ScopedLink() { 379 if (Path != "") { 380 EXPECT_FALSE(llvm::sys::fs::remove(Path.str())); 381 } 382 } 383 operator StringRef() { return Path.str(); } 384 }; 385 } // end anonymous namespace 386 387 TEST(VirtualFileSystemTest, BasicRealFSIteration) { 388 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 389 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 390 391 std::error_code EC; 392 vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC); 393 ASSERT_FALSE(EC); 394 EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty 395 396 ScopedDir _a(TestDirectory + "/a"); 397 ScopedDir _ab(TestDirectory + "/a/b"); 398 ScopedDir _c(TestDirectory + "/c"); 399 ScopedDir _cd(TestDirectory + "/c/d"); 400 401 I = FS->dir_begin(Twine(TestDirectory), EC); 402 ASSERT_FALSE(EC); 403 ASSERT_NE(vfs::directory_iterator(), I); 404 // Check either a or c, since we can't rely on the iteration order. 405 EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c")); 406 I.increment(EC); 407 ASSERT_FALSE(EC); 408 ASSERT_NE(vfs::directory_iterator(), I); 409 EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c")); 410 I.increment(EC); 411 EXPECT_EQ(vfs::directory_iterator(), I); 412 } 413 414 #ifdef LLVM_ON_UNIX 415 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) { 416 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 417 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 418 419 ScopedLink _a("no_such_file", TestDirectory + "/a"); 420 ScopedDir _b(TestDirectory + "/b"); 421 ScopedLink _c("no_such_file", TestDirectory + "/c"); 422 423 // Should get no iteration error, but a stat error for the broken symlinks. 424 std::map<std::string, std::error_code> StatResults; 425 std::error_code EC; 426 for (vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC), E; 427 I != E; I.increment(EC)) { 428 EXPECT_FALSE(EC); 429 StatResults[sys::path::filename(I->path())] = 430 FS->status(I->path()).getError(); 431 } 432 EXPECT_THAT( 433 StatResults, 434 ElementsAre( 435 Pair("a", std::make_error_code(std::errc::no_such_file_or_directory)), 436 Pair("b", std::error_code()), 437 Pair("c", 438 std::make_error_code(std::errc::no_such_file_or_directory)))); 439 } 440 #endif 441 442 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) { 443 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 444 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 445 446 std::error_code EC; 447 auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); 448 ASSERT_FALSE(EC); 449 EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty 450 451 ScopedDir _a(TestDirectory + "/a"); 452 ScopedDir _ab(TestDirectory + "/a/b"); 453 ScopedDir _c(TestDirectory + "/c"); 454 ScopedDir _cd(TestDirectory + "/c/d"); 455 456 I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); 457 ASSERT_FALSE(EC); 458 ASSERT_NE(vfs::recursive_directory_iterator(), I); 459 460 std::vector<std::string> Contents; 461 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 462 I.increment(EC)) { 463 Contents.push_back(I->path()); 464 } 465 466 // Check contents, which may be in any order 467 EXPECT_EQ(4U, Contents.size()); 468 int Counts[4] = {0, 0, 0, 0}; 469 for (const std::string &Name : Contents) { 470 ASSERT_FALSE(Name.empty()); 471 int Index = Name[Name.size() - 1] - 'a'; 472 ASSERT_TRUE(Index >= 0 && Index < 4); 473 Counts[Index]++; 474 } 475 EXPECT_EQ(1, Counts[0]); // a 476 EXPECT_EQ(1, Counts[1]); // b 477 EXPECT_EQ(1, Counts[2]); // c 478 EXPECT_EQ(1, Counts[3]); // d 479 } 480 481 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) { 482 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 483 484 ScopedDir _a(TestDirectory + "/a"); 485 ScopedDir _ab(TestDirectory + "/a/b"); 486 ScopedDir _c(TestDirectory + "/c"); 487 ScopedDir _cd(TestDirectory + "/c/d"); 488 ScopedDir _e(TestDirectory + "/e"); 489 ScopedDir _ef(TestDirectory + "/e/f"); 490 ScopedDir _g(TestDirectory + "/g"); 491 492 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 493 494 // Test that calling no_push on entries without subdirectories has no effect. 495 { 496 std::error_code EC; 497 auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); 498 ASSERT_FALSE(EC); 499 500 std::vector<std::string> Contents; 501 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 502 I.increment(EC)) { 503 Contents.push_back(I->path()); 504 char last = I->path().back(); 505 switch (last) { 506 case 'b': 507 case 'd': 508 case 'f': 509 case 'g': 510 I.no_push(); 511 break; 512 default: 513 break; 514 } 515 } 516 EXPECT_EQ(7U, Contents.size()); 517 } 518 519 // Test that calling no_push skips subdirectories. 520 { 521 std::error_code EC; 522 auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); 523 ASSERT_FALSE(EC); 524 525 std::vector<std::string> Contents; 526 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 527 I.increment(EC)) { 528 Contents.push_back(I->path()); 529 char last = I->path().back(); 530 switch (last) { 531 case 'a': 532 case 'c': 533 case 'e': 534 I.no_push(); 535 break; 536 default: 537 break; 538 } 539 } 540 541 // Check contents, which may be in any order 542 EXPECT_EQ(4U, Contents.size()); 543 int Counts[7] = {0, 0, 0, 0, 0, 0, 0}; 544 for (const std::string &Name : Contents) { 545 ASSERT_FALSE(Name.empty()); 546 int Index = Name[Name.size() - 1] - 'a'; 547 ASSERT_TRUE(Index >= 0 && Index < 7); 548 Counts[Index]++; 549 } 550 EXPECT_EQ(1, Counts[0]); // a 551 EXPECT_EQ(0, Counts[1]); // b 552 EXPECT_EQ(1, Counts[2]); // c 553 EXPECT_EQ(0, Counts[3]); // d 554 EXPECT_EQ(1, Counts[4]); // e 555 EXPECT_EQ(0, Counts[5]); // f 556 EXPECT_EQ(1, Counts[6]); // g 557 } 558 } 559 560 #ifdef LLVM_ON_UNIX 561 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) { 562 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 563 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 564 565 ScopedLink _a("no_such_file", TestDirectory + "/a"); 566 ScopedDir _b(TestDirectory + "/b"); 567 ScopedLink _ba("no_such_file", TestDirectory + "/b/a"); 568 ScopedDir _bb(TestDirectory + "/b/b"); 569 ScopedLink _bc("no_such_file", TestDirectory + "/b/c"); 570 ScopedLink _c("no_such_file", TestDirectory + "/c"); 571 ScopedDir _d(TestDirectory + "/d"); 572 ScopedDir _dd(TestDirectory + "/d/d"); 573 ScopedDir _ddd(TestDirectory + "/d/d/d"); 574 ScopedLink _e("no_such_file", TestDirectory + "/e"); 575 576 std::vector<std::string> VisitedBrokenSymlinks; 577 std::vector<std::string> VisitedNonBrokenSymlinks; 578 std::error_code EC; 579 for (vfs::recursive_directory_iterator I(*FS, Twine(TestDirectory), EC), E; 580 I != E; I.increment(EC)) { 581 EXPECT_FALSE(EC); 582 (FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks) 583 .push_back(I->path()); 584 } 585 586 // Check visited file names. 587 EXPECT_THAT(VisitedBrokenSymlinks, 588 UnorderedElementsAre(StringRef(_a), StringRef(_ba), 589 StringRef(_bc), StringRef(_c), 590 StringRef(_e))); 591 EXPECT_THAT(VisitedNonBrokenSymlinks, 592 UnorderedElementsAre(StringRef(_b), StringRef(_bb), StringRef(_d), 593 StringRef(_dd), StringRef(_ddd))); 594 } 595 #endif 596 597 template <typename DirIter> 598 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) { 599 std::error_code EC; 600 SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end()); 601 SmallVector<std::string, 4> InputToCheck; 602 603 // Do not rely on iteration order to check for contents, sort both 604 // content vectors before comparison. 605 for (DirIter E; !EC && I != E; I.increment(EC)) 606 InputToCheck.push_back(I->path()); 607 608 llvm::sort(InputToCheck); 609 llvm::sort(Expected); 610 EXPECT_EQ(InputToCheck.size(), Expected.size()); 611 612 unsigned LastElt = std::min(InputToCheck.size(), Expected.size()); 613 for (unsigned Idx = 0; Idx != LastElt; ++Idx) 614 EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]); 615 } 616 617 TEST(VirtualFileSystemTest, OverlayIteration) { 618 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 619 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 620 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 621 new vfs::OverlayFileSystem(Lower)); 622 O->pushOverlay(Upper); 623 624 std::error_code EC; 625 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); 626 627 Lower->addRegularFile("/file1"); 628 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1")); 629 630 Upper->addRegularFile("/file2"); 631 checkContents(O->dir_begin("/", EC), {"/file2", "/file1"}); 632 633 Lower->addDirectory("/dir1"); 634 Lower->addRegularFile("/dir1/foo"); 635 Upper->addDirectory("/dir2"); 636 Upper->addRegularFile("/dir2/foo"); 637 checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo")); 638 checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"}); 639 } 640 641 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) { 642 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 643 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 644 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 645 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 646 new vfs::OverlayFileSystem(Lower)); 647 O->pushOverlay(Middle); 648 O->pushOverlay(Upper); 649 650 std::error_code EC; 651 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 652 ArrayRef<StringRef>()); 653 654 Lower->addRegularFile("/file1"); 655 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 656 ArrayRef<StringRef>("/file1")); 657 658 Upper->addDirectory("/dir"); 659 Upper->addRegularFile("/dir/file2"); 660 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 661 {"/dir", "/dir/file2", "/file1"}); 662 663 Lower->addDirectory("/dir1"); 664 Lower->addRegularFile("/dir1/foo"); 665 Lower->addDirectory("/dir1/a"); 666 Lower->addRegularFile("/dir1/a/b"); 667 Middle->addDirectory("/a"); 668 Middle->addDirectory("/a/b"); 669 Middle->addDirectory("/a/b/c"); 670 Middle->addRegularFile("/a/b/c/d"); 671 Middle->addRegularFile("/hiddenByUp"); 672 Upper->addDirectory("/dir2"); 673 Upper->addRegularFile("/dir2/foo"); 674 Upper->addRegularFile("/hiddenByUp"); 675 checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC), 676 ArrayRef<StringRef>("/dir2/foo")); 677 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 678 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp", 679 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a", 680 "/dir1/a/b", "/dir1/foo", "/file1"}); 681 } 682 683 TEST(VirtualFileSystemTest, ThreeLevelIteration) { 684 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 685 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 686 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 687 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 688 new vfs::OverlayFileSystem(Lower)); 689 O->pushOverlay(Middle); 690 O->pushOverlay(Upper); 691 692 std::error_code EC; 693 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); 694 695 Middle->addRegularFile("/file2"); 696 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2")); 697 698 Lower->addRegularFile("/file1"); 699 Upper->addRegularFile("/file3"); 700 checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"}); 701 } 702 703 TEST(VirtualFileSystemTest, HiddenInIteration) { 704 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 705 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 706 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 707 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 708 new vfs::OverlayFileSystem(Lower)); 709 O->pushOverlay(Middle); 710 O->pushOverlay(Upper); 711 712 std::error_code EC; 713 Lower->addRegularFile("/onlyInLow"); 714 Lower->addDirectory("/hiddenByMid"); 715 Lower->addDirectory("/hiddenByUp"); 716 Middle->addRegularFile("/onlyInMid"); 717 Middle->addRegularFile("/hiddenByMid"); 718 Middle->addDirectory("/hiddenByUp"); 719 Upper->addRegularFile("/onlyInUp"); 720 Upper->addRegularFile("/hiddenByUp"); 721 checkContents( 722 O->dir_begin("/", EC), 723 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"}); 724 725 // Make sure we get the top-most entry 726 { 727 std::error_code EC; 728 vfs::directory_iterator I = O->dir_begin("/", EC), E; 729 for (; !EC && I != E; I.increment(EC)) 730 if (I->path() == "/hiddenByUp") 731 break; 732 ASSERT_NE(E, I); 733 EXPECT_EQ(sys::fs::file_type::regular_file, I->type()); 734 } 735 { 736 std::error_code EC; 737 vfs::directory_iterator I = O->dir_begin("/", EC), E; 738 for (; !EC && I != E; I.increment(EC)) 739 if (I->path() == "/hiddenByMid") 740 break; 741 ASSERT_NE(E, I); 742 EXPECT_EQ(sys::fs::file_type::regular_file, I->type()); 743 } 744 } 745 746 TEST(ProxyFileSystemTest, Basic) { 747 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> Base( 748 new vfs::InMemoryFileSystem()); 749 vfs::ProxyFileSystem PFS(Base); 750 751 Base->addFile("/a", 0, MemoryBuffer::getMemBuffer("test")); 752 753 auto Stat = PFS.status("/a"); 754 ASSERT_FALSE(Stat.getError()); 755 756 auto File = PFS.openFileForRead("/a"); 757 ASSERT_FALSE(File.getError()); 758 EXPECT_EQ("test", (*(*File)->getBuffer("ignored"))->getBuffer()); 759 760 std::error_code EC; 761 vfs::directory_iterator I = PFS.dir_begin("/", EC); 762 ASSERT_FALSE(EC); 763 ASSERT_EQ("/a", I->path()); 764 I.increment(EC); 765 ASSERT_FALSE(EC); 766 ASSERT_EQ(vfs::directory_iterator(), I); 767 768 ASSERT_FALSE(PFS.setCurrentWorkingDirectory("/")); 769 770 auto PWD = PFS.getCurrentWorkingDirectory(); 771 ASSERT_FALSE(PWD.getError()); 772 ASSERT_EQ("/", *PWD); 773 774 SmallString<16> Path; 775 ASSERT_FALSE(PFS.getRealPath("a", Path)); 776 ASSERT_EQ("/a", Path); 777 778 bool Local = true; 779 ASSERT_FALSE(PFS.isLocal("/a", Local)); 780 ASSERT_EQ(false, Local); 781 } 782 783 class InMemoryFileSystemTest : public ::testing::Test { 784 protected: 785 llvm::vfs::InMemoryFileSystem FS; 786 llvm::vfs::InMemoryFileSystem NormalizedFS; 787 788 InMemoryFileSystemTest() 789 : FS(/*UseNormalizedPaths=*/false), 790 NormalizedFS(/*UseNormalizedPaths=*/true) {} 791 }; 792 793 MATCHER_P2(IsHardLinkTo, FS, Target, "") { 794 StringRef From = arg; 795 StringRef To = Target; 796 auto OpenedFrom = FS->openFileForRead(From); 797 auto OpenedTo = FS->openFileForRead(To); 798 return !OpenedFrom.getError() && !OpenedTo.getError() && 799 (*OpenedFrom)->status()->getUniqueID() == 800 (*OpenedTo)->status()->getUniqueID(); 801 } 802 803 TEST_F(InMemoryFileSystemTest, IsEmpty) { 804 auto Stat = FS.status("/a"); 805 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString(); 806 Stat = FS.status("/"); 807 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString(); 808 } 809 810 TEST_F(InMemoryFileSystemTest, WindowsPath) { 811 FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); 812 auto Stat = FS.status("c:"); 813 #if !defined(_WIN32) 814 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 815 #endif 816 Stat = FS.status("c:/windows/system128/foo.cpp"); 817 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 818 FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); 819 Stat = FS.status("d:/windows/foo.cpp"); 820 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 821 } 822 823 TEST_F(InMemoryFileSystemTest, OverlayFile) { 824 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 825 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 826 auto Stat = FS.status("/"); 827 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 828 Stat = FS.status("/."); 829 ASSERT_FALSE(Stat); 830 Stat = NormalizedFS.status("/."); 831 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 832 Stat = FS.status("/a"); 833 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 834 ASSERT_EQ("/a", Stat->getName()); 835 } 836 837 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) { 838 auto Buf = MemoryBuffer::getMemBuffer("a"); 839 FS.addFileNoOwn("/a", 0, Buf.get()); 840 auto Stat = FS.status("/a"); 841 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 842 ASSERT_EQ("/a", Stat->getName()); 843 } 844 845 TEST_F(InMemoryFileSystemTest, OpenFileForRead) { 846 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 847 FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); 848 FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); 849 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 850 NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); 851 NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); 852 auto File = FS.openFileForRead("/a"); 853 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 854 File = FS.openFileForRead("/a"); // Open again. 855 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 856 File = NormalizedFS.openFileForRead("/././a"); // Open again. 857 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 858 File = FS.openFileForRead("/"); 859 ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString(); 860 File = FS.openFileForRead("/b"); 861 ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString(); 862 File = FS.openFileForRead("./c"); 863 ASSERT_FALSE(File); 864 File = FS.openFileForRead("e/../d"); 865 ASSERT_FALSE(File); 866 File = NormalizedFS.openFileForRead("./c"); 867 ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer()); 868 File = NormalizedFS.openFileForRead("e/../d"); 869 ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer()); 870 } 871 872 TEST_F(InMemoryFileSystemTest, DuplicatedFile) { 873 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); 874 ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a"))); 875 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); 876 ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b"))); 877 } 878 879 TEST_F(InMemoryFileSystemTest, DirectoryIteration) { 880 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("")); 881 FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer("")); 882 883 std::error_code EC; 884 vfs::directory_iterator I = FS.dir_begin("/", EC); 885 ASSERT_FALSE(EC); 886 ASSERT_EQ("/a", I->path()); 887 I.increment(EC); 888 ASSERT_FALSE(EC); 889 ASSERT_EQ("/b", I->path()); 890 I.increment(EC); 891 ASSERT_FALSE(EC); 892 ASSERT_EQ(vfs::directory_iterator(), I); 893 894 I = FS.dir_begin("/b", EC); 895 ASSERT_FALSE(EC); 896 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix 897 // path for the sake of the comparison. 898 ASSERT_EQ("/b/c", getPosixPath(I->path())); 899 I.increment(EC); 900 ASSERT_FALSE(EC); 901 ASSERT_EQ(vfs::directory_iterator(), I); 902 } 903 904 TEST_F(InMemoryFileSystemTest, WorkingDirectory) { 905 FS.setCurrentWorkingDirectory("/b"); 906 FS.addFile("c", 0, MemoryBuffer::getMemBuffer("")); 907 908 auto Stat = FS.status("/b/c"); 909 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 910 ASSERT_EQ("/b/c", Stat->getName()); 911 ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory()); 912 913 Stat = FS.status("c"); 914 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 915 916 NormalizedFS.setCurrentWorkingDirectory("/b/c"); 917 NormalizedFS.setCurrentWorkingDirectory("."); 918 ASSERT_EQ("/b/c", 919 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get())); 920 NormalizedFS.setCurrentWorkingDirectory(".."); 921 ASSERT_EQ("/b", 922 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get())); 923 } 924 925 TEST_F(InMemoryFileSystemTest, IsLocal) { 926 FS.setCurrentWorkingDirectory("/b"); 927 FS.addFile("c", 0, MemoryBuffer::getMemBuffer("")); 928 929 std::error_code EC; 930 bool IsLocal = true; 931 EC = FS.isLocal("c", IsLocal); 932 ASSERT_FALSE(EC); 933 ASSERT_FALSE(IsLocal); 934 } 935 936 #if !defined(_WIN32) 937 TEST_F(InMemoryFileSystemTest, GetRealPath) { 938 SmallString<16> Path; 939 EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted); 940 941 auto GetRealPath = [this](StringRef P) { 942 SmallString<16> Output; 943 auto EC = FS.getRealPath(P, Output); 944 EXPECT_FALSE(EC); 945 return Output.str().str(); 946 }; 947 948 FS.setCurrentWorkingDirectory("a"); 949 EXPECT_EQ(GetRealPath("b"), "a/b"); 950 EXPECT_EQ(GetRealPath("../b"), "b"); 951 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c"); 952 953 FS.setCurrentWorkingDirectory("/a"); 954 EXPECT_EQ(GetRealPath("b"), "/a/b"); 955 EXPECT_EQ(GetRealPath("../b"), "/b"); 956 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c"); 957 } 958 #endif // _WIN32 959 960 TEST_F(InMemoryFileSystemTest, AddFileWithUser) { 961 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE); 962 auto Stat = FS.status("/a"); 963 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 964 ASSERT_TRUE(Stat->isDirectory()); 965 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 966 Stat = FS.status("/a/b"); 967 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 968 ASSERT_TRUE(Stat->isDirectory()); 969 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 970 Stat = FS.status("/a/b/c"); 971 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 972 ASSERT_TRUE(Stat->isRegularFile()); 973 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 974 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 975 } 976 977 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) { 978 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00); 979 auto Stat = FS.status("/a"); 980 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 981 ASSERT_TRUE(Stat->isDirectory()); 982 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 983 Stat = FS.status("/a/b"); 984 ASSERT_TRUE(Stat->isDirectory()); 985 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 986 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 987 Stat = FS.status("/a/b/c"); 988 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 989 ASSERT_TRUE(Stat->isRegularFile()); 990 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 991 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 992 } 993 994 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) { 995 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, 996 sys::fs::file_type::socket_file); 997 auto Stat = FS.status("/a"); 998 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 999 ASSERT_TRUE(Stat->isDirectory()); 1000 Stat = FS.status("/a/b"); 1001 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1002 ASSERT_TRUE(Stat->isDirectory()); 1003 Stat = FS.status("/a/b/c"); 1004 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1005 ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType()); 1006 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 1007 } 1008 1009 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) { 1010 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, None, 1011 sys::fs::perms::owner_read | sys::fs::perms::owner_write); 1012 auto Stat = FS.status("/a"); 1013 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1014 ASSERT_TRUE(Stat->isDirectory()); 1015 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write | 1016 sys::fs::perms::owner_exe, 1017 Stat->getPermissions()); 1018 Stat = FS.status("/a/b"); 1019 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1020 ASSERT_TRUE(Stat->isDirectory()); 1021 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write | 1022 sys::fs::perms::owner_exe, 1023 Stat->getPermissions()); 1024 Stat = FS.status("/a/b/c"); 1025 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1026 ASSERT_TRUE(Stat->isRegularFile()); 1027 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write, 1028 Stat->getPermissions()); 1029 } 1030 1031 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) { 1032 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None, 1033 /*Group=*/None, sys::fs::file_type::directory_file); 1034 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None, 1035 /*Group=*/None, sys::fs::file_type::regular_file); 1036 auto Stat = FS.status("/a"); 1037 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1038 ASSERT_TRUE(Stat->isDirectory()); 1039 Stat = FS.status("/a/b"); 1040 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1041 ASSERT_TRUE(Stat->isRegularFile()); 1042 } 1043 1044 // Test that the name returned by status() is in the same form as the path that 1045 // was requested (to match the behavior of RealFileSystem). 1046 TEST_F(InMemoryFileSystemTest, StatusName) { 1047 NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 1048 /*User=*/None, 1049 /*Group=*/None, sys::fs::file_type::regular_file); 1050 NormalizedFS.setCurrentWorkingDirectory("/a/b"); 1051 1052 // Access using InMemoryFileSystem::status. 1053 auto Stat = NormalizedFS.status("../b/c"); 1054 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" 1055 << NormalizedFS.toString(); 1056 ASSERT_TRUE(Stat->isRegularFile()); 1057 ASSERT_EQ("../b/c", Stat->getName()); 1058 1059 // Access using InMemoryFileAdaptor::status. 1060 auto File = NormalizedFS.openFileForRead("../b/c"); 1061 ASSERT_FALSE(File.getError()) << File.getError() << "\n" 1062 << NormalizedFS.toString(); 1063 Stat = (*File)->status(); 1064 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" 1065 << NormalizedFS.toString(); 1066 ASSERT_TRUE(Stat->isRegularFile()); 1067 ASSERT_EQ("../b/c", Stat->getName()); 1068 1069 // Access using a directory iterator. 1070 std::error_code EC; 1071 llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC); 1072 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix 1073 // path for the sake of the comparison. 1074 ASSERT_EQ("../b/c", getPosixPath(It->path())); 1075 } 1076 1077 TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) { 1078 StringRef FromLink = "/path/to/FROM/link"; 1079 StringRef Target = "/path/to/TO/file"; 1080 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1081 EXPECT_TRUE(FS.addHardLink(FromLink, Target)); 1082 EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target)); 1083 EXPECT_TRUE(FS.status(FromLink)->getSize() == FS.status(Target)->getSize()); 1084 EXPECT_TRUE(FS.getBufferForFile(FromLink)->get()->getBuffer() == 1085 FS.getBufferForFile(Target)->get()->getBuffer()); 1086 } 1087 1088 TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) { 1089 StringRef Link0 = "/path/to/0/link"; 1090 StringRef Link1 = "/path/to/1/link"; 1091 StringRef Link2 = "/path/to/2/link"; 1092 StringRef Target = "/path/to/target"; 1093 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file")); 1094 EXPECT_TRUE(FS.addHardLink(Link2, Target)); 1095 EXPECT_TRUE(FS.addHardLink(Link1, Link2)); 1096 EXPECT_TRUE(FS.addHardLink(Link0, Link1)); 1097 EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target)); 1098 EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target)); 1099 EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target)); 1100 } 1101 1102 TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) { 1103 EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target")); 1104 } 1105 1106 TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) { 1107 StringRef Link = "/path/to/link"; 1108 StringRef Target = "/path/to/target"; 1109 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1110 FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link")); 1111 EXPECT_FALSE(FS.addHardLink(Link, Target)); 1112 } 1113 1114 TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) { 1115 StringRef Link = "/path/to/link"; 1116 StringRef Target = "/path/to/target"; 1117 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1118 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1119 EXPECT_FALSE(FS.addHardLink(Link, Target)); 1120 } 1121 1122 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) { 1123 StringRef Link = "/path/to/link"; 1124 StringRef Target = "/path/to/target"; 1125 StringRef Content = "content of target"; 1126 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1127 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1128 EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content))); 1129 } 1130 1131 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) { 1132 StringRef Link = "/path/to/link"; 1133 StringRef Target = "/path/to/target"; 1134 StringRef Content = "content of target"; 1135 StringRef LinkContent = "different content of link"; 1136 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1137 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1138 EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent))); 1139 } 1140 1141 TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) { 1142 StringRef Dir = "path/to/dummy/dir"; 1143 StringRef Link = "/path/to/link"; 1144 StringRef File = "path/to/dummy/dir/target"; 1145 StringRef Content = "content of target"; 1146 EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content))); 1147 EXPECT_FALSE(FS.addHardLink(Link, Dir)); 1148 } 1149 1150 TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) { 1151 StringRef Dir = "path/to/dummy/dir"; 1152 StringRef Target = "path/to/dummy/dir/target"; 1153 StringRef Content = "content of target"; 1154 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1155 EXPECT_FALSE(FS.addHardLink(Dir, Target)); 1156 } 1157 1158 TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) { 1159 StringRef CommonContent = "content string"; 1160 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent)); 1161 FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent)); 1162 EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b")); 1163 } 1164 1165 TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) { 1166 std::error_code EC; 1167 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string")); 1168 EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b")); 1169 auto I = vfs::recursive_directory_iterator(FS, "/", EC); 1170 ASSERT_FALSE(EC); 1171 std::vector<std::string> Nodes; 1172 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 1173 I.increment(EC)) { 1174 Nodes.push_back(getPosixPath(I->path())); 1175 } 1176 EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d")); 1177 } 1178 1179 // NOTE: in the tests below, we use '//root/' as our root directory, since it is 1180 // a legal *absolute* path on Windows as well as *nix. 1181 class VFSFromYAMLTest : public ::testing::Test { 1182 public: 1183 int NumDiagnostics; 1184 1185 void SetUp() override { NumDiagnostics = 0; } 1186 1187 static void CountingDiagHandler(const SMDiagnostic &, void *Context) { 1188 VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context); 1189 ++Test->NumDiagnostics; 1190 } 1191 1192 IntrusiveRefCntPtr<vfs::FileSystem> 1193 getFromYAMLRawString(StringRef Content, 1194 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) { 1195 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content); 1196 return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this, 1197 ExternalFS); 1198 } 1199 1200 IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString( 1201 StringRef Content, 1202 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) { 1203 std::string VersionPlusContent("{\n 'version':0,\n"); 1204 VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos); 1205 return getFromYAMLRawString(VersionPlusContent, ExternalFS); 1206 } 1207 1208 // This is intended as a "XFAIL" for windows hosts. 1209 bool supportsSameDirMultipleYAMLEntries() { 1210 Triple Host(Triple::normalize(sys::getProcessTriple())); 1211 return !Host.isOSWindows(); 1212 } 1213 }; 1214 1215 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) { 1216 IntrusiveRefCntPtr<vfs::FileSystem> FS; 1217 FS = getFromYAMLString(""); 1218 EXPECT_EQ(nullptr, FS.get()); 1219 FS = getFromYAMLString("[]"); 1220 EXPECT_EQ(nullptr, FS.get()); 1221 FS = getFromYAMLString("'string'"); 1222 EXPECT_EQ(nullptr, FS.get()); 1223 EXPECT_EQ(3, NumDiagnostics); 1224 } 1225 1226 TEST_F(VFSFromYAMLTest, MappedFiles) { 1227 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1228 Lower->addRegularFile("//root/foo/bar/a"); 1229 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1230 "{ 'roots': [\n" 1231 "{\n" 1232 " 'type': 'directory',\n" 1233 " 'name': '//root/',\n" 1234 " 'contents': [ {\n" 1235 " 'type': 'file',\n" 1236 " 'name': 'file1',\n" 1237 " 'external-contents': '//root/foo/bar/a'\n" 1238 " },\n" 1239 " {\n" 1240 " 'type': 'file',\n" 1241 " 'name': 'file2',\n" 1242 " 'external-contents': '//root/foo/b'\n" 1243 " }\n" 1244 " ]\n" 1245 "}\n" 1246 "]\n" 1247 "}", 1248 Lower); 1249 ASSERT_TRUE(FS.get() != nullptr); 1250 1251 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1252 new vfs::OverlayFileSystem(Lower)); 1253 O->pushOverlay(FS); 1254 1255 // file 1256 ErrorOr<vfs::Status> S = O->status("//root/file1"); 1257 ASSERT_FALSE(S.getError()); 1258 EXPECT_EQ("//root/foo/bar/a", S->getName()); 1259 EXPECT_TRUE(S->IsVFSMapped); 1260 1261 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); 1262 EXPECT_EQ("//root/foo/bar/a", SLower->getName()); 1263 EXPECT_TRUE(S->equivalent(*SLower)); 1264 EXPECT_FALSE(SLower->IsVFSMapped); 1265 1266 // file after opening 1267 auto OpenedF = O->openFileForRead("//root/file1"); 1268 ASSERT_FALSE(OpenedF.getError()); 1269 auto OpenedS = (*OpenedF)->status(); 1270 ASSERT_FALSE(OpenedS.getError()); 1271 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 1272 EXPECT_TRUE(OpenedS->IsVFSMapped); 1273 1274 // directory 1275 S = O->status("//root/"); 1276 ASSERT_FALSE(S.getError()); 1277 EXPECT_TRUE(S->isDirectory()); 1278 EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID 1279 1280 // broken mapping 1281 EXPECT_EQ(O->status("//root/file2").getError(), 1282 llvm::errc::no_such_file_or_directory); 1283 EXPECT_EQ(0, NumDiagnostics); 1284 } 1285 1286 TEST_F(VFSFromYAMLTest, CaseInsensitive) { 1287 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1288 Lower->addRegularFile("//root/foo/bar/a"); 1289 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1290 "{ 'case-sensitive': 'false',\n" 1291 " 'roots': [\n" 1292 "{\n" 1293 " 'type': 'directory',\n" 1294 " 'name': '//root/',\n" 1295 " 'contents': [ {\n" 1296 " 'type': 'file',\n" 1297 " 'name': 'XX',\n" 1298 " 'external-contents': '//root/foo/bar/a'\n" 1299 " }\n" 1300 " ]\n" 1301 "}]}", 1302 Lower); 1303 ASSERT_TRUE(FS.get() != nullptr); 1304 1305 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1306 new vfs::OverlayFileSystem(Lower)); 1307 O->pushOverlay(FS); 1308 1309 ErrorOr<vfs::Status> S = O->status("//root/XX"); 1310 ASSERT_FALSE(S.getError()); 1311 1312 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 1313 ASSERT_FALSE(SS.getError()); 1314 EXPECT_TRUE(S->equivalent(*SS)); 1315 SS = O->status("//root/xX"); 1316 EXPECT_TRUE(S->equivalent(*SS)); 1317 SS = O->status("//root/Xx"); 1318 EXPECT_TRUE(S->equivalent(*SS)); 1319 EXPECT_EQ(0, NumDiagnostics); 1320 } 1321 1322 TEST_F(VFSFromYAMLTest, CaseSensitive) { 1323 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1324 Lower->addRegularFile("//root/foo/bar/a"); 1325 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1326 "{ 'case-sensitive': 'true',\n" 1327 " 'roots': [\n" 1328 "{\n" 1329 " 'type': 'directory',\n" 1330 " 'name': '//root/',\n" 1331 " 'contents': [ {\n" 1332 " 'type': 'file',\n" 1333 " 'name': 'XX',\n" 1334 " 'external-contents': '//root/foo/bar/a'\n" 1335 " }\n" 1336 " ]\n" 1337 "}]}", 1338 Lower); 1339 ASSERT_TRUE(FS.get() != nullptr); 1340 1341 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1342 new vfs::OverlayFileSystem(Lower)); 1343 O->pushOverlay(FS); 1344 1345 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 1346 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 1347 SS = O->status("//root/xX"); 1348 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 1349 SS = O->status("//root/Xx"); 1350 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 1351 EXPECT_EQ(0, NumDiagnostics); 1352 } 1353 1354 TEST_F(VFSFromYAMLTest, IllegalVFSFile) { 1355 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1356 1357 // invalid YAML at top-level 1358 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower); 1359 EXPECT_EQ(nullptr, FS.get()); 1360 // invalid YAML in roots 1361 FS = getFromYAMLString("{ 'roots':[}", Lower); 1362 // invalid YAML in directory 1363 FS = getFromYAMLString( 1364 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}", 1365 Lower); 1366 EXPECT_EQ(nullptr, FS.get()); 1367 1368 // invalid configuration 1369 FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower); 1370 EXPECT_EQ(nullptr, FS.get()); 1371 FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower); 1372 EXPECT_EQ(nullptr, FS.get()); 1373 1374 // invalid roots 1375 FS = getFromYAMLString("{ 'roots':'' }", Lower); 1376 EXPECT_EQ(nullptr, FS.get()); 1377 FS = getFromYAMLString("{ 'roots':{} }", Lower); 1378 EXPECT_EQ(nullptr, FS.get()); 1379 1380 // invalid entries 1381 FS = getFromYAMLString( 1382 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower); 1383 EXPECT_EQ(nullptr, FS.get()); 1384 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], " 1385 "'external-contents': 'other' }", 1386 Lower); 1387 EXPECT_EQ(nullptr, FS.get()); 1388 FS = getFromYAMLString( 1389 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }", 1390 Lower); 1391 EXPECT_EQ(nullptr, FS.get()); 1392 FS = getFromYAMLString( 1393 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }", 1394 Lower); 1395 EXPECT_EQ(nullptr, FS.get()); 1396 FS = getFromYAMLString( 1397 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }", 1398 Lower); 1399 EXPECT_EQ(nullptr, FS.get()); 1400 FS = getFromYAMLString( 1401 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }", 1402 Lower); 1403 EXPECT_EQ(nullptr, FS.get()); 1404 FS = getFromYAMLString( 1405 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }", 1406 Lower); 1407 EXPECT_EQ(nullptr, FS.get()); 1408 1409 // missing mandatory fields 1410 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower); 1411 EXPECT_EQ(nullptr, FS.get()); 1412 FS = getFromYAMLString( 1413 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower); 1414 EXPECT_EQ(nullptr, FS.get()); 1415 FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower); 1416 EXPECT_EQ(nullptr, FS.get()); 1417 1418 // duplicate keys 1419 FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower); 1420 EXPECT_EQ(nullptr, FS.get()); 1421 FS = getFromYAMLString( 1422 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }", 1423 Lower); 1424 EXPECT_EQ(nullptr, FS.get()); 1425 FS = 1426 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', " 1427 "'external-contents':'blah' } ] }", 1428 Lower); 1429 EXPECT_EQ(nullptr, FS.get()); 1430 1431 // missing version 1432 FS = getFromYAMLRawString("{ 'roots':[] }", Lower); 1433 EXPECT_EQ(nullptr, FS.get()); 1434 1435 // bad version number 1436 FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower); 1437 EXPECT_EQ(nullptr, FS.get()); 1438 FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower); 1439 EXPECT_EQ(nullptr, FS.get()); 1440 FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower); 1441 EXPECT_EQ(nullptr, FS.get()); 1442 EXPECT_EQ(24, NumDiagnostics); 1443 } 1444 1445 TEST_F(VFSFromYAMLTest, UseExternalName) { 1446 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1447 Lower->addRegularFile("//root/external/file"); 1448 1449 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1450 getFromYAMLString("{ 'roots': [\n" 1451 " { 'type': 'file', 'name': '//root/A',\n" 1452 " 'external-contents': '//root/external/file'\n" 1453 " },\n" 1454 " { 'type': 'file', 'name': '//root/B',\n" 1455 " 'use-external-name': true,\n" 1456 " 'external-contents': '//root/external/file'\n" 1457 " },\n" 1458 " { 'type': 'file', 'name': '//root/C',\n" 1459 " 'use-external-name': false,\n" 1460 " 'external-contents': '//root/external/file'\n" 1461 " }\n" 1462 "] }", 1463 Lower); 1464 ASSERT_TRUE(nullptr != FS.get()); 1465 1466 // default true 1467 EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName()); 1468 // explicit 1469 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 1470 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 1471 1472 // global configuration 1473 FS = getFromYAMLString("{ 'use-external-names': false,\n" 1474 " 'roots': [\n" 1475 " { 'type': 'file', 'name': '//root/A',\n" 1476 " 'external-contents': '//root/external/file'\n" 1477 " },\n" 1478 " { 'type': 'file', 'name': '//root/B',\n" 1479 " 'use-external-name': true,\n" 1480 " 'external-contents': '//root/external/file'\n" 1481 " },\n" 1482 " { 'type': 'file', 'name': '//root/C',\n" 1483 " 'use-external-name': false,\n" 1484 " 'external-contents': '//root/external/file'\n" 1485 " }\n" 1486 "] }", 1487 Lower); 1488 ASSERT_TRUE(nullptr != FS.get()); 1489 1490 // default 1491 EXPECT_EQ("//root/A", FS->status("//root/A")->getName()); 1492 // explicit 1493 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 1494 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 1495 } 1496 1497 TEST_F(VFSFromYAMLTest, MultiComponentPath) { 1498 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1499 Lower->addRegularFile("//root/other"); 1500 1501 // file in roots 1502 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1503 getFromYAMLString("{ 'roots': [\n" 1504 " { 'type': 'file', 'name': '//root/path/to/file',\n" 1505 " 'external-contents': '//root/other' }]\n" 1506 "}", 1507 Lower); 1508 ASSERT_TRUE(nullptr != FS.get()); 1509 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1510 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1511 EXPECT_FALSE(FS->status("//root/path").getError()); 1512 EXPECT_FALSE(FS->status("//root/").getError()); 1513 1514 // at the start 1515 FS = getFromYAMLString( 1516 "{ 'roots': [\n" 1517 " { 'type': 'directory', 'name': '//root/path/to',\n" 1518 " 'contents': [ { 'type': 'file', 'name': 'file',\n" 1519 " 'external-contents': '//root/other' }]}]\n" 1520 "}", 1521 Lower); 1522 ASSERT_TRUE(nullptr != FS.get()); 1523 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1524 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1525 EXPECT_FALSE(FS->status("//root/path").getError()); 1526 EXPECT_FALSE(FS->status("//root/").getError()); 1527 1528 // at the end 1529 FS = getFromYAMLString( 1530 "{ 'roots': [\n" 1531 " { 'type': 'directory', 'name': '//root/',\n" 1532 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n" 1533 " 'external-contents': '//root/other' }]}]\n" 1534 "}", 1535 Lower); 1536 ASSERT_TRUE(nullptr != FS.get()); 1537 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1538 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1539 EXPECT_FALSE(FS->status("//root/path").getError()); 1540 EXPECT_FALSE(FS->status("//root/").getError()); 1541 } 1542 1543 TEST_F(VFSFromYAMLTest, TrailingSlashes) { 1544 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1545 Lower->addRegularFile("//root/other"); 1546 1547 // file in roots 1548 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1549 "{ 'roots': [\n" 1550 " { 'type': 'directory', 'name': '//root/path/to////',\n" 1551 " 'contents': [ { 'type': 'file', 'name': 'file',\n" 1552 " 'external-contents': '//root/other' }]}]\n" 1553 "}", 1554 Lower); 1555 ASSERT_TRUE(nullptr != FS.get()); 1556 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1557 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1558 EXPECT_FALSE(FS->status("//root/path").getError()); 1559 EXPECT_FALSE(FS->status("//root/").getError()); 1560 } 1561 1562 TEST_F(VFSFromYAMLTest, DirectoryIteration) { 1563 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1564 Lower->addDirectory("//root/"); 1565 Lower->addDirectory("//root/foo"); 1566 Lower->addDirectory("//root/foo/bar"); 1567 Lower->addRegularFile("//root/foo/bar/a"); 1568 Lower->addRegularFile("//root/foo/bar/b"); 1569 Lower->addRegularFile("//root/file3"); 1570 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1571 "{ 'use-external-names': false,\n" 1572 " 'roots': [\n" 1573 "{\n" 1574 " 'type': 'directory',\n" 1575 " 'name': '//root/',\n" 1576 " 'contents': [ {\n" 1577 " 'type': 'file',\n" 1578 " 'name': 'file1',\n" 1579 " 'external-contents': '//root/foo/bar/a'\n" 1580 " },\n" 1581 " {\n" 1582 " 'type': 'file',\n" 1583 " 'name': 'file2',\n" 1584 " 'external-contents': '//root/foo/bar/b'\n" 1585 " }\n" 1586 " ]\n" 1587 "}\n" 1588 "]\n" 1589 "}", 1590 Lower); 1591 ASSERT_TRUE(FS.get() != nullptr); 1592 1593 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1594 new vfs::OverlayFileSystem(Lower)); 1595 O->pushOverlay(FS); 1596 1597 std::error_code EC; 1598 checkContents(O->dir_begin("//root/", EC), 1599 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"}); 1600 1601 checkContents(O->dir_begin("//root/foo/bar", EC), 1602 {"//root/foo/bar/a", "//root/foo/bar/b"}); 1603 } 1604 1605 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) { 1606 // https://llvm.org/bugs/show_bug.cgi?id=27725 1607 if (!supportsSameDirMultipleYAMLEntries()) 1608 return; 1609 1610 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1611 Lower->addDirectory("//root/zab"); 1612 Lower->addDirectory("//root/baz"); 1613 Lower->addRegularFile("//root/zab/a"); 1614 Lower->addRegularFile("//root/zab/b"); 1615 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1616 "{ 'use-external-names': false,\n" 1617 " 'roots': [\n" 1618 "{\n" 1619 " 'type': 'directory',\n" 1620 " 'name': '//root/baz/',\n" 1621 " 'contents': [ {\n" 1622 " 'type': 'file',\n" 1623 " 'name': 'x',\n" 1624 " 'external-contents': '//root/zab/a'\n" 1625 " }\n" 1626 " ]\n" 1627 "},\n" 1628 "{\n" 1629 " 'type': 'directory',\n" 1630 " 'name': '//root/baz/',\n" 1631 " 'contents': [ {\n" 1632 " 'type': 'file',\n" 1633 " 'name': 'y',\n" 1634 " 'external-contents': '//root/zab/b'\n" 1635 " }\n" 1636 " ]\n" 1637 "}\n" 1638 "]\n" 1639 "}", 1640 Lower); 1641 ASSERT_TRUE(FS.get() != nullptr); 1642 1643 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1644 new vfs::OverlayFileSystem(Lower)); 1645 O->pushOverlay(FS); 1646 1647 std::error_code EC; 1648 1649 checkContents(O->dir_begin("//root/baz/", EC), 1650 {"//root/baz/x", "//root/baz/y"}); 1651 } 1652 1653 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) { 1654 1655 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1656 Lower->addDirectory("//root/a"); 1657 Lower->addDirectory("//root/a/b"); 1658 Lower->addDirectory("//root/a/b/c"); 1659 Lower->addRegularFile("//root/a/b/c/file"); 1660 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1661 "{ 'use-external-names': false,\n" 1662 " 'roots': [\n" 1663 "{\n" 1664 " 'type': 'directory',\n" 1665 " 'name': '//root/a/b/c/',\n" 1666 " 'contents': [ {\n" 1667 " 'type': 'file',\n" 1668 " 'name': 'file',\n" 1669 " 'external-contents': '//root/a/b/c/file'\n" 1670 " }\n" 1671 " ]\n" 1672 "},\n" 1673 "]\n" 1674 "}", 1675 Lower); 1676 ASSERT_TRUE(FS.get() != nullptr); 1677 1678 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1679 new vfs::OverlayFileSystem(Lower)); 1680 O->pushOverlay(FS); 1681 1682 std::error_code EC; 1683 1684 // Test recursive_directory_iterator level() 1685 vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator( 1686 *O, "//root", EC), 1687 E; 1688 ASSERT_FALSE(EC); 1689 for (int l = 0; I != E; I.increment(EC), ++l) { 1690 ASSERT_FALSE(EC); 1691 EXPECT_EQ(I.level(), l); 1692 } 1693 EXPECT_EQ(I, E); 1694 } 1695 1696 TEST_F(VFSFromYAMLTest, RelativePaths) { 1697 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1698 // Filename at root level without a parent directory. 1699 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1700 "{ 'roots': [\n" 1701 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n" 1702 " 'external-contents': '//root/external/file'\n" 1703 " }\n" 1704 "] }", 1705 Lower); 1706 EXPECT_EQ(nullptr, FS.get()); 1707 1708 // Relative file path. 1709 FS = getFromYAMLString("{ 'roots': [\n" 1710 " { 'type': 'file', 'name': 'relative/file/path.h',\n" 1711 " 'external-contents': '//root/external/file'\n" 1712 " }\n" 1713 "] }", 1714 Lower); 1715 EXPECT_EQ(nullptr, FS.get()); 1716 1717 // Relative directory path. 1718 FS = getFromYAMLString( 1719 "{ 'roots': [\n" 1720 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n" 1721 " 'contents': []\n" 1722 " }\n" 1723 "] }", 1724 Lower); 1725 EXPECT_EQ(nullptr, FS.get()); 1726 1727 EXPECT_EQ(3, NumDiagnostics); 1728 } 1729 1730 TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) { 1731 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1732 Lower->addDirectory("//root/"); 1733 Lower->addRegularFile("//root/a"); 1734 Lower->addRegularFile("//root/b"); 1735 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1736 "{ 'use-external-names': false,\n" 1737 " 'fallthrough': false,\n" 1738 " 'roots': [\n" 1739 "{\n" 1740 " 'type': 'directory',\n" 1741 " 'name': '//root/',\n" 1742 " 'contents': [ {\n" 1743 " 'type': 'file',\n" 1744 " 'name': 'c',\n" 1745 " 'external-contents': '//root/a'\n" 1746 " }\n" 1747 " ]\n" 1748 "}\n" 1749 "]\n" 1750 "}", 1751 Lower); 1752 ASSERT_TRUE(FS.get() != nullptr); 1753 1754 std::error_code EC; 1755 checkContents(FS->dir_begin("//root/", EC), 1756 {"//root/c"}); 1757 } 1758 1759 TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) { 1760 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1761 Lower->addDirectory("//root/"); 1762 Lower->addRegularFile("//root/a"); 1763 Lower->addRegularFile("//root/b"); 1764 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1765 "{ 'use-external-names': false,\n" 1766 " 'roots': [\n" 1767 "{\n" 1768 " 'type': 'directory',\n" 1769 " 'name': '//root/',\n" 1770 " 'contents': [ {\n" 1771 " 'type': 'file',\n" 1772 " 'name': 'a',\n" 1773 " 'external-contents': '//root/a'\n" 1774 " }\n" 1775 " ]\n" 1776 "}\n" 1777 "]\n" 1778 "}", 1779 Lower); 1780 ASSERT_TRUE(FS.get() != nullptr); 1781 1782 std::error_code EC; 1783 checkContents(FS->dir_begin("//root/", EC), 1784 {"//root/a", "//root/b"}); 1785 } 1786 1787 TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) { 1788 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1789 Lower->addDirectory("//root/"); 1790 Lower->addDirectory("//root/foo"); 1791 Lower->addRegularFile("//root/foo/a"); 1792 Lower->addRegularFile("//root/foo/b"); 1793 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1794 "{ 'use-external-names': false,\n" 1795 " 'roots': [\n" 1796 "{\n" 1797 " 'type': 'directory',\n" 1798 " 'name': '//root/',\n" 1799 " 'contents': [ {\n" 1800 " 'type': 'file',\n" 1801 " 'name': 'bar/a',\n" 1802 " 'external-contents': '//root/foo/a'\n" 1803 " }\n" 1804 " ]\n" 1805 "}\n" 1806 "]\n" 1807 "}", 1808 Lower); 1809 ASSERT_TRUE(FS.get() != nullptr); 1810 1811 std::error_code EC; 1812 checkContents(FS->dir_begin("//root/foo", EC), 1813 {"//root/foo/a", "//root/foo/b"}); 1814 } 1815 1816 TEST_F(VFSFromYAMLTest, GetRealPath) { 1817 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1818 Lower->addDirectory("//dir/"); 1819 Lower->addRegularFile("/foo"); 1820 Lower->addSymlink("/link"); 1821 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1822 "{ 'use-external-names': false,\n" 1823 " 'roots': [\n" 1824 "{\n" 1825 " 'type': 'directory',\n" 1826 " 'name': '//root/',\n" 1827 " 'contents': [ {\n" 1828 " 'type': 'file',\n" 1829 " 'name': 'bar',\n" 1830 " 'external-contents': '/link'\n" 1831 " }\n" 1832 " ]\n" 1833 "},\n" 1834 "{\n" 1835 " 'type': 'directory',\n" 1836 " 'name': '//dir/',\n" 1837 " 'contents': []\n" 1838 "}\n" 1839 "]\n" 1840 "}", 1841 Lower); 1842 ASSERT_TRUE(FS.get() != nullptr); 1843 1844 // Regular file present in underlying file system. 1845 SmallString<16> RealPath; 1846 EXPECT_FALSE(FS->getRealPath("/foo", RealPath)); 1847 EXPECT_EQ(RealPath.str(), "/foo"); 1848 1849 // File present in YAML pointing to symlink in underlying file system. 1850 EXPECT_FALSE(FS->getRealPath("//root/bar", RealPath)); 1851 EXPECT_EQ(RealPath.str(), "/symlink"); 1852 1853 // Directories should fall back to the underlying file system is possible. 1854 EXPECT_FALSE(FS->getRealPath("//dir/", RealPath)); 1855 EXPECT_EQ(RealPath.str(), "//dir/"); 1856 1857 // Try a non-existing file. 1858 EXPECT_EQ(FS->getRealPath("/non_existing", RealPath), 1859 errc::no_such_file_or_directory); 1860 } 1861