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 386 struct ScopedFile { 387 SmallString<128> Path; 388 ScopedFile(const Twine &Path, StringRef Contents) { 389 Path.toVector(this->Path); 390 std::error_code EC; 391 raw_fd_ostream OS(this->Path, EC); 392 EXPECT_FALSE(EC); 393 OS << Contents; 394 OS.flush(); 395 EXPECT_FALSE(OS.error()); 396 if (EC || OS.error()) 397 this->Path = ""; 398 } 399 ~ScopedFile() { 400 if (Path != "") 401 EXPECT_FALSE(llvm::sys::fs::remove(Path.str())); 402 } 403 }; 404 } // end anonymous namespace 405 406 TEST(VirtualFileSystemTest, BasicRealFSIteration) { 407 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 408 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 409 410 std::error_code EC; 411 vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC); 412 ASSERT_FALSE(EC); 413 EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty 414 415 ScopedDir _a(TestDirectory + "/a"); 416 ScopedDir _ab(TestDirectory + "/a/b"); 417 ScopedDir _c(TestDirectory + "/c"); 418 ScopedDir _cd(TestDirectory + "/c/d"); 419 420 I = FS->dir_begin(Twine(TestDirectory), EC); 421 ASSERT_FALSE(EC); 422 ASSERT_NE(vfs::directory_iterator(), I); 423 // Check either a or c, since we can't rely on the iteration order. 424 EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c")); 425 I.increment(EC); 426 ASSERT_FALSE(EC); 427 ASSERT_NE(vfs::directory_iterator(), I); 428 EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c")); 429 I.increment(EC); 430 EXPECT_EQ(vfs::directory_iterator(), I); 431 } 432 433 TEST(VirtualFileSystemTest, MultipleWorkingDirs) { 434 // Our root contains a/aa, b/bb, c, where c is a link to a/. 435 // Run tests both in root/b/ and root/c/ (to test "normal" and symlink dirs). 436 // Interleave operations to show the working directories are independent. 437 ScopedDir Root("r", true), ADir(Root.Path + "/a"), BDir(Root.Path + "/b"); 438 ScopedLink C(ADir.Path, Root.Path + "/c"); 439 ScopedFile AA(ADir.Path + "/aa", "aaaa"), BB(BDir.Path + "/bb", "bbbb"); 440 std::unique_ptr<vfs::FileSystem> BFS = vfs::createPhysicalFileSystem(), 441 CFS = vfs::createPhysicalFileSystem(); 442 443 ASSERT_FALSE(BFS->setCurrentWorkingDirectory(BDir.Path)); 444 ASSERT_FALSE(CFS->setCurrentWorkingDirectory(C.Path)); 445 EXPECT_EQ(BDir.Path, *BFS->getCurrentWorkingDirectory()); 446 EXPECT_EQ(C.Path, *CFS->getCurrentWorkingDirectory()); 447 448 // openFileForRead(), indirectly. 449 auto BBuf = BFS->getBufferForFile("bb"); 450 ASSERT_TRUE(BBuf); 451 EXPECT_EQ("bbbb", (*BBuf)->getBuffer()); 452 453 auto ABuf = CFS->getBufferForFile("aa"); 454 ASSERT_TRUE(ABuf); 455 EXPECT_EQ("aaaa", (*ABuf)->getBuffer()); 456 457 // status() 458 auto BStat = BFS->status("bb"); 459 ASSERT_TRUE(BStat); 460 EXPECT_EQ("bb", BStat->getName()); 461 462 auto AStat = CFS->status("aa"); 463 ASSERT_TRUE(AStat); 464 EXPECT_EQ("aa", AStat->getName()); // unresolved name 465 466 // getRealPath() 467 SmallString<128> BPath; 468 ASSERT_FALSE(BFS->getRealPath("bb", BPath)); 469 EXPECT_EQ(BB.Path, BPath); 470 471 SmallString<128> APath; 472 ASSERT_FALSE(CFS->getRealPath("aa", APath)); 473 EXPECT_EQ(AA.Path, APath); // Reports resolved name. 474 475 // dir_begin 476 std::error_code EC; 477 auto BIt = BFS->dir_begin(".", EC); 478 ASSERT_FALSE(EC); 479 ASSERT_NE(BIt, vfs::directory_iterator()); 480 EXPECT_EQ((BDir.Path + "/./bb").str(), BIt->path()); 481 BIt.increment(EC); 482 ASSERT_FALSE(EC); 483 ASSERT_EQ(BIt, vfs::directory_iterator()); 484 485 auto CIt = CFS->dir_begin(".", EC); 486 ASSERT_FALSE(EC); 487 ASSERT_NE(CIt, vfs::directory_iterator()); 488 EXPECT_EQ((ADir.Path + "/./aa").str(), CIt->path()); // Partly resolved name! 489 CIt.increment(EC); // Because likely to read through this path. 490 ASSERT_FALSE(EC); 491 ASSERT_EQ(CIt, vfs::directory_iterator()); 492 } 493 494 #ifdef LLVM_ON_UNIX 495 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) { 496 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 497 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 498 499 ScopedLink _a("no_such_file", TestDirectory + "/a"); 500 ScopedDir _b(TestDirectory + "/b"); 501 ScopedLink _c("no_such_file", TestDirectory + "/c"); 502 503 // Should get no iteration error, but a stat error for the broken symlinks. 504 std::map<std::string, std::error_code> StatResults; 505 std::error_code EC; 506 for (vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC), E; 507 I != E; I.increment(EC)) { 508 EXPECT_FALSE(EC); 509 StatResults[sys::path::filename(I->path())] = 510 FS->status(I->path()).getError(); 511 } 512 EXPECT_THAT( 513 StatResults, 514 ElementsAre( 515 Pair("a", std::make_error_code(std::errc::no_such_file_or_directory)), 516 Pair("b", std::error_code()), 517 Pair("c", 518 std::make_error_code(std::errc::no_such_file_or_directory)))); 519 } 520 #endif 521 522 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) { 523 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 524 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 525 526 std::error_code EC; 527 auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); 528 ASSERT_FALSE(EC); 529 EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty 530 531 ScopedDir _a(TestDirectory + "/a"); 532 ScopedDir _ab(TestDirectory + "/a/b"); 533 ScopedDir _c(TestDirectory + "/c"); 534 ScopedDir _cd(TestDirectory + "/c/d"); 535 536 I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); 537 ASSERT_FALSE(EC); 538 ASSERT_NE(vfs::recursive_directory_iterator(), I); 539 540 std::vector<std::string> Contents; 541 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 542 I.increment(EC)) { 543 Contents.push_back(I->path()); 544 } 545 546 // Check contents, which may be in any order 547 EXPECT_EQ(4U, Contents.size()); 548 int Counts[4] = {0, 0, 0, 0}; 549 for (const std::string &Name : Contents) { 550 ASSERT_FALSE(Name.empty()); 551 int Index = Name[Name.size() - 1] - 'a'; 552 ASSERT_TRUE(Index >= 0 && Index < 4); 553 Counts[Index]++; 554 } 555 EXPECT_EQ(1, Counts[0]); // a 556 EXPECT_EQ(1, Counts[1]); // b 557 EXPECT_EQ(1, Counts[2]); // c 558 EXPECT_EQ(1, Counts[3]); // d 559 } 560 561 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) { 562 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 563 564 ScopedDir _a(TestDirectory + "/a"); 565 ScopedDir _ab(TestDirectory + "/a/b"); 566 ScopedDir _c(TestDirectory + "/c"); 567 ScopedDir _cd(TestDirectory + "/c/d"); 568 ScopedDir _e(TestDirectory + "/e"); 569 ScopedDir _ef(TestDirectory + "/e/f"); 570 ScopedDir _g(TestDirectory + "/g"); 571 572 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 573 574 // Test that calling no_push on entries without subdirectories has no effect. 575 { 576 std::error_code EC; 577 auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); 578 ASSERT_FALSE(EC); 579 580 std::vector<std::string> Contents; 581 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 582 I.increment(EC)) { 583 Contents.push_back(I->path()); 584 char last = I->path().back(); 585 switch (last) { 586 case 'b': 587 case 'd': 588 case 'f': 589 case 'g': 590 I.no_push(); 591 break; 592 default: 593 break; 594 } 595 } 596 EXPECT_EQ(7U, Contents.size()); 597 } 598 599 // Test that calling no_push skips subdirectories. 600 { 601 std::error_code EC; 602 auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); 603 ASSERT_FALSE(EC); 604 605 std::vector<std::string> Contents; 606 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 607 I.increment(EC)) { 608 Contents.push_back(I->path()); 609 char last = I->path().back(); 610 switch (last) { 611 case 'a': 612 case 'c': 613 case 'e': 614 I.no_push(); 615 break; 616 default: 617 break; 618 } 619 } 620 621 // Check contents, which may be in any order 622 EXPECT_EQ(4U, Contents.size()); 623 int Counts[7] = {0, 0, 0, 0, 0, 0, 0}; 624 for (const std::string &Name : Contents) { 625 ASSERT_FALSE(Name.empty()); 626 int Index = Name[Name.size() - 1] - 'a'; 627 ASSERT_TRUE(Index >= 0 && Index < 7); 628 Counts[Index]++; 629 } 630 EXPECT_EQ(1, Counts[0]); // a 631 EXPECT_EQ(0, Counts[1]); // b 632 EXPECT_EQ(1, Counts[2]); // c 633 EXPECT_EQ(0, Counts[3]); // d 634 EXPECT_EQ(1, Counts[4]); // e 635 EXPECT_EQ(0, Counts[5]); // f 636 EXPECT_EQ(1, Counts[6]); // g 637 } 638 } 639 640 #ifdef LLVM_ON_UNIX 641 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) { 642 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); 643 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 644 645 ScopedLink _a("no_such_file", TestDirectory + "/a"); 646 ScopedDir _b(TestDirectory + "/b"); 647 ScopedLink _ba("no_such_file", TestDirectory + "/b/a"); 648 ScopedDir _bb(TestDirectory + "/b/b"); 649 ScopedLink _bc("no_such_file", TestDirectory + "/b/c"); 650 ScopedLink _c("no_such_file", TestDirectory + "/c"); 651 ScopedDir _d(TestDirectory + "/d"); 652 ScopedDir _dd(TestDirectory + "/d/d"); 653 ScopedDir _ddd(TestDirectory + "/d/d/d"); 654 ScopedLink _e("no_such_file", TestDirectory + "/e"); 655 656 std::vector<std::string> VisitedBrokenSymlinks; 657 std::vector<std::string> VisitedNonBrokenSymlinks; 658 std::error_code EC; 659 for (vfs::recursive_directory_iterator I(*FS, Twine(TestDirectory), EC), E; 660 I != E; I.increment(EC)) { 661 EXPECT_FALSE(EC); 662 (FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks) 663 .push_back(I->path()); 664 } 665 666 // Check visited file names. 667 EXPECT_THAT(VisitedBrokenSymlinks, 668 UnorderedElementsAre(StringRef(_a), StringRef(_ba), 669 StringRef(_bc), StringRef(_c), 670 StringRef(_e))); 671 EXPECT_THAT(VisitedNonBrokenSymlinks, 672 UnorderedElementsAre(StringRef(_b), StringRef(_bb), StringRef(_d), 673 StringRef(_dd), StringRef(_ddd))); 674 } 675 #endif 676 677 template <typename DirIter> 678 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) { 679 std::error_code EC; 680 SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end()); 681 SmallVector<std::string, 4> InputToCheck; 682 683 // Do not rely on iteration order to check for contents, sort both 684 // content vectors before comparison. 685 for (DirIter E; !EC && I != E; I.increment(EC)) 686 InputToCheck.push_back(I->path()); 687 688 llvm::sort(InputToCheck); 689 llvm::sort(Expected); 690 EXPECT_EQ(InputToCheck.size(), Expected.size()); 691 692 unsigned LastElt = std::min(InputToCheck.size(), Expected.size()); 693 for (unsigned Idx = 0; Idx != LastElt; ++Idx) 694 EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]); 695 } 696 697 TEST(VirtualFileSystemTest, OverlayIteration) { 698 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 699 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 700 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 701 new vfs::OverlayFileSystem(Lower)); 702 O->pushOverlay(Upper); 703 704 std::error_code EC; 705 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); 706 707 Lower->addRegularFile("/file1"); 708 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1")); 709 710 Upper->addRegularFile("/file2"); 711 checkContents(O->dir_begin("/", EC), {"/file2", "/file1"}); 712 713 Lower->addDirectory("/dir1"); 714 Lower->addRegularFile("/dir1/foo"); 715 Upper->addDirectory("/dir2"); 716 Upper->addRegularFile("/dir2/foo"); 717 checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo")); 718 checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"}); 719 } 720 721 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) { 722 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 723 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 724 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 725 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 726 new vfs::OverlayFileSystem(Lower)); 727 O->pushOverlay(Middle); 728 O->pushOverlay(Upper); 729 730 std::error_code EC; 731 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 732 ArrayRef<StringRef>()); 733 734 Lower->addRegularFile("/file1"); 735 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 736 ArrayRef<StringRef>("/file1")); 737 738 Upper->addDirectory("/dir"); 739 Upper->addRegularFile("/dir/file2"); 740 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 741 {"/dir", "/dir/file2", "/file1"}); 742 743 Lower->addDirectory("/dir1"); 744 Lower->addRegularFile("/dir1/foo"); 745 Lower->addDirectory("/dir1/a"); 746 Lower->addRegularFile("/dir1/a/b"); 747 Middle->addDirectory("/a"); 748 Middle->addDirectory("/a/b"); 749 Middle->addDirectory("/a/b/c"); 750 Middle->addRegularFile("/a/b/c/d"); 751 Middle->addRegularFile("/hiddenByUp"); 752 Upper->addDirectory("/dir2"); 753 Upper->addRegularFile("/dir2/foo"); 754 Upper->addRegularFile("/hiddenByUp"); 755 checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC), 756 ArrayRef<StringRef>("/dir2/foo")); 757 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 758 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp", 759 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a", 760 "/dir1/a/b", "/dir1/foo", "/file1"}); 761 } 762 763 TEST(VirtualFileSystemTest, ThreeLevelIteration) { 764 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 765 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 766 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 767 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 768 new vfs::OverlayFileSystem(Lower)); 769 O->pushOverlay(Middle); 770 O->pushOverlay(Upper); 771 772 std::error_code EC; 773 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); 774 775 Middle->addRegularFile("/file2"); 776 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2")); 777 778 Lower->addRegularFile("/file1"); 779 Upper->addRegularFile("/file3"); 780 checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"}); 781 } 782 783 TEST(VirtualFileSystemTest, HiddenInIteration) { 784 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 785 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 786 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 787 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 788 new vfs::OverlayFileSystem(Lower)); 789 O->pushOverlay(Middle); 790 O->pushOverlay(Upper); 791 792 std::error_code EC; 793 Lower->addRegularFile("/onlyInLow"); 794 Lower->addDirectory("/hiddenByMid"); 795 Lower->addDirectory("/hiddenByUp"); 796 Middle->addRegularFile("/onlyInMid"); 797 Middle->addRegularFile("/hiddenByMid"); 798 Middle->addDirectory("/hiddenByUp"); 799 Upper->addRegularFile("/onlyInUp"); 800 Upper->addRegularFile("/hiddenByUp"); 801 checkContents( 802 O->dir_begin("/", EC), 803 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"}); 804 805 // Make sure we get the top-most entry 806 { 807 std::error_code EC; 808 vfs::directory_iterator I = O->dir_begin("/", EC), E; 809 for (; !EC && I != E; I.increment(EC)) 810 if (I->path() == "/hiddenByUp") 811 break; 812 ASSERT_NE(E, I); 813 EXPECT_EQ(sys::fs::file_type::regular_file, I->type()); 814 } 815 { 816 std::error_code EC; 817 vfs::directory_iterator I = O->dir_begin("/", EC), E; 818 for (; !EC && I != E; I.increment(EC)) 819 if (I->path() == "/hiddenByMid") 820 break; 821 ASSERT_NE(E, I); 822 EXPECT_EQ(sys::fs::file_type::regular_file, I->type()); 823 } 824 } 825 826 TEST(ProxyFileSystemTest, Basic) { 827 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> Base( 828 new vfs::InMemoryFileSystem()); 829 vfs::ProxyFileSystem PFS(Base); 830 831 Base->addFile("/a", 0, MemoryBuffer::getMemBuffer("test")); 832 833 auto Stat = PFS.status("/a"); 834 ASSERT_FALSE(Stat.getError()); 835 836 auto File = PFS.openFileForRead("/a"); 837 ASSERT_FALSE(File.getError()); 838 EXPECT_EQ("test", (*(*File)->getBuffer("ignored"))->getBuffer()); 839 840 std::error_code EC; 841 vfs::directory_iterator I = PFS.dir_begin("/", EC); 842 ASSERT_FALSE(EC); 843 ASSERT_EQ("/a", I->path()); 844 I.increment(EC); 845 ASSERT_FALSE(EC); 846 ASSERT_EQ(vfs::directory_iterator(), I); 847 848 ASSERT_FALSE(PFS.setCurrentWorkingDirectory("/")); 849 850 auto PWD = PFS.getCurrentWorkingDirectory(); 851 ASSERT_FALSE(PWD.getError()); 852 ASSERT_EQ("/", *PWD); 853 854 SmallString<16> Path; 855 ASSERT_FALSE(PFS.getRealPath("a", Path)); 856 ASSERT_EQ("/a", Path); 857 858 bool Local = true; 859 ASSERT_FALSE(PFS.isLocal("/a", Local)); 860 ASSERT_EQ(false, Local); 861 } 862 863 class InMemoryFileSystemTest : public ::testing::Test { 864 protected: 865 llvm::vfs::InMemoryFileSystem FS; 866 llvm::vfs::InMemoryFileSystem NormalizedFS; 867 868 InMemoryFileSystemTest() 869 : FS(/*UseNormalizedPaths=*/false), 870 NormalizedFS(/*UseNormalizedPaths=*/true) {} 871 }; 872 873 MATCHER_P2(IsHardLinkTo, FS, Target, "") { 874 StringRef From = arg; 875 StringRef To = Target; 876 auto OpenedFrom = FS->openFileForRead(From); 877 auto OpenedTo = FS->openFileForRead(To); 878 return !OpenedFrom.getError() && !OpenedTo.getError() && 879 (*OpenedFrom)->status()->getUniqueID() == 880 (*OpenedTo)->status()->getUniqueID(); 881 } 882 883 TEST_F(InMemoryFileSystemTest, IsEmpty) { 884 auto Stat = FS.status("/a"); 885 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString(); 886 Stat = FS.status("/"); 887 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString(); 888 } 889 890 TEST_F(InMemoryFileSystemTest, WindowsPath) { 891 FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); 892 auto Stat = FS.status("c:"); 893 #if !defined(_WIN32) 894 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 895 #endif 896 Stat = FS.status("c:/windows/system128/foo.cpp"); 897 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 898 FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); 899 Stat = FS.status("d:/windows/foo.cpp"); 900 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 901 } 902 903 TEST_F(InMemoryFileSystemTest, OverlayFile) { 904 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 905 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 906 auto Stat = FS.status("/"); 907 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 908 Stat = FS.status("/."); 909 ASSERT_FALSE(Stat); 910 Stat = NormalizedFS.status("/."); 911 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 912 Stat = FS.status("/a"); 913 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 914 ASSERT_EQ("/a", Stat->getName()); 915 } 916 917 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) { 918 auto Buf = MemoryBuffer::getMemBuffer("a"); 919 FS.addFileNoOwn("/a", 0, Buf.get()); 920 auto Stat = FS.status("/a"); 921 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 922 ASSERT_EQ("/a", Stat->getName()); 923 } 924 925 TEST_F(InMemoryFileSystemTest, OpenFileForRead) { 926 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 927 FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); 928 FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); 929 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 930 NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); 931 NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); 932 auto File = FS.openFileForRead("/a"); 933 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 934 File = FS.openFileForRead("/a"); // Open again. 935 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 936 File = NormalizedFS.openFileForRead("/././a"); // Open again. 937 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 938 File = FS.openFileForRead("/"); 939 ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString(); 940 File = FS.openFileForRead("/b"); 941 ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString(); 942 File = FS.openFileForRead("./c"); 943 ASSERT_FALSE(File); 944 File = FS.openFileForRead("e/../d"); 945 ASSERT_FALSE(File); 946 File = NormalizedFS.openFileForRead("./c"); 947 ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer()); 948 File = NormalizedFS.openFileForRead("e/../d"); 949 ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer()); 950 } 951 952 TEST_F(InMemoryFileSystemTest, DuplicatedFile) { 953 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); 954 ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a"))); 955 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); 956 ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b"))); 957 } 958 959 TEST_F(InMemoryFileSystemTest, DirectoryIteration) { 960 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("")); 961 FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer("")); 962 963 std::error_code EC; 964 vfs::directory_iterator I = FS.dir_begin("/", EC); 965 ASSERT_FALSE(EC); 966 ASSERT_EQ("/a", I->path()); 967 I.increment(EC); 968 ASSERT_FALSE(EC); 969 ASSERT_EQ("/b", I->path()); 970 I.increment(EC); 971 ASSERT_FALSE(EC); 972 ASSERT_EQ(vfs::directory_iterator(), I); 973 974 I = FS.dir_begin("/b", EC); 975 ASSERT_FALSE(EC); 976 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix 977 // path for the sake of the comparison. 978 ASSERT_EQ("/b/c", getPosixPath(I->path())); 979 I.increment(EC); 980 ASSERT_FALSE(EC); 981 ASSERT_EQ(vfs::directory_iterator(), I); 982 } 983 984 TEST_F(InMemoryFileSystemTest, WorkingDirectory) { 985 FS.setCurrentWorkingDirectory("/b"); 986 FS.addFile("c", 0, MemoryBuffer::getMemBuffer("")); 987 988 auto Stat = FS.status("/b/c"); 989 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 990 ASSERT_EQ("/b/c", Stat->getName()); 991 ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory()); 992 993 Stat = FS.status("c"); 994 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 995 996 NormalizedFS.setCurrentWorkingDirectory("/b/c"); 997 NormalizedFS.setCurrentWorkingDirectory("."); 998 ASSERT_EQ("/b/c", 999 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get())); 1000 NormalizedFS.setCurrentWorkingDirectory(".."); 1001 ASSERT_EQ("/b", 1002 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get())); 1003 } 1004 1005 TEST_F(InMemoryFileSystemTest, IsLocal) { 1006 FS.setCurrentWorkingDirectory("/b"); 1007 FS.addFile("c", 0, MemoryBuffer::getMemBuffer("")); 1008 1009 std::error_code EC; 1010 bool IsLocal = true; 1011 EC = FS.isLocal("c", IsLocal); 1012 ASSERT_FALSE(EC); 1013 ASSERT_FALSE(IsLocal); 1014 } 1015 1016 #if !defined(_WIN32) 1017 TEST_F(InMemoryFileSystemTest, GetRealPath) { 1018 SmallString<16> Path; 1019 EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted); 1020 1021 auto GetRealPath = [this](StringRef P) { 1022 SmallString<16> Output; 1023 auto EC = FS.getRealPath(P, Output); 1024 EXPECT_FALSE(EC); 1025 return Output.str().str(); 1026 }; 1027 1028 FS.setCurrentWorkingDirectory("a"); 1029 EXPECT_EQ(GetRealPath("b"), "a/b"); 1030 EXPECT_EQ(GetRealPath("../b"), "b"); 1031 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c"); 1032 1033 FS.setCurrentWorkingDirectory("/a"); 1034 EXPECT_EQ(GetRealPath("b"), "/a/b"); 1035 EXPECT_EQ(GetRealPath("../b"), "/b"); 1036 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c"); 1037 } 1038 #endif // _WIN32 1039 1040 TEST_F(InMemoryFileSystemTest, AddFileWithUser) { 1041 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE); 1042 auto Stat = FS.status("/a"); 1043 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1044 ASSERT_TRUE(Stat->isDirectory()); 1045 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 1046 Stat = FS.status("/a/b"); 1047 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1048 ASSERT_TRUE(Stat->isDirectory()); 1049 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 1050 Stat = FS.status("/a/b/c"); 1051 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1052 ASSERT_TRUE(Stat->isRegularFile()); 1053 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 1054 ASSERT_EQ(0xFEEDFACE, Stat->getUser()); 1055 } 1056 1057 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) { 1058 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00); 1059 auto Stat = FS.status("/a"); 1060 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1061 ASSERT_TRUE(Stat->isDirectory()); 1062 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 1063 Stat = FS.status("/a/b"); 1064 ASSERT_TRUE(Stat->isDirectory()); 1065 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1066 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 1067 Stat = FS.status("/a/b/c"); 1068 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1069 ASSERT_TRUE(Stat->isRegularFile()); 1070 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 1071 ASSERT_EQ(0xDABBAD00, Stat->getGroup()); 1072 } 1073 1074 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) { 1075 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, 1076 sys::fs::file_type::socket_file); 1077 auto Stat = FS.status("/a"); 1078 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1079 ASSERT_TRUE(Stat->isDirectory()); 1080 Stat = FS.status("/a/b"); 1081 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1082 ASSERT_TRUE(Stat->isDirectory()); 1083 Stat = FS.status("/a/b/c"); 1084 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1085 ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType()); 1086 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); 1087 } 1088 1089 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) { 1090 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, None, 1091 sys::fs::perms::owner_read | sys::fs::perms::owner_write); 1092 auto Stat = FS.status("/a"); 1093 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1094 ASSERT_TRUE(Stat->isDirectory()); 1095 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write | 1096 sys::fs::perms::owner_exe, 1097 Stat->getPermissions()); 1098 Stat = FS.status("/a/b"); 1099 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1100 ASSERT_TRUE(Stat->isDirectory()); 1101 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write | 1102 sys::fs::perms::owner_exe, 1103 Stat->getPermissions()); 1104 Stat = FS.status("/a/b/c"); 1105 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1106 ASSERT_TRUE(Stat->isRegularFile()); 1107 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write, 1108 Stat->getPermissions()); 1109 } 1110 1111 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) { 1112 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None, 1113 /*Group=*/None, sys::fs::file_type::directory_file); 1114 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None, 1115 /*Group=*/None, sys::fs::file_type::regular_file); 1116 auto Stat = FS.status("/a"); 1117 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1118 ASSERT_TRUE(Stat->isDirectory()); 1119 Stat = FS.status("/a/b"); 1120 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 1121 ASSERT_TRUE(Stat->isRegularFile()); 1122 } 1123 1124 // Test that the name returned by status() is in the same form as the path that 1125 // was requested (to match the behavior of RealFileSystem). 1126 TEST_F(InMemoryFileSystemTest, StatusName) { 1127 NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 1128 /*User=*/None, 1129 /*Group=*/None, sys::fs::file_type::regular_file); 1130 NormalizedFS.setCurrentWorkingDirectory("/a/b"); 1131 1132 // Access using InMemoryFileSystem::status. 1133 auto Stat = NormalizedFS.status("../b/c"); 1134 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" 1135 << NormalizedFS.toString(); 1136 ASSERT_TRUE(Stat->isRegularFile()); 1137 ASSERT_EQ("../b/c", Stat->getName()); 1138 1139 // Access using InMemoryFileAdaptor::status. 1140 auto File = NormalizedFS.openFileForRead("../b/c"); 1141 ASSERT_FALSE(File.getError()) << File.getError() << "\n" 1142 << NormalizedFS.toString(); 1143 Stat = (*File)->status(); 1144 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" 1145 << NormalizedFS.toString(); 1146 ASSERT_TRUE(Stat->isRegularFile()); 1147 ASSERT_EQ("../b/c", Stat->getName()); 1148 1149 // Access using a directory iterator. 1150 std::error_code EC; 1151 llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC); 1152 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix 1153 // path for the sake of the comparison. 1154 ASSERT_EQ("../b/c", getPosixPath(It->path())); 1155 } 1156 1157 TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) { 1158 StringRef FromLink = "/path/to/FROM/link"; 1159 StringRef Target = "/path/to/TO/file"; 1160 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1161 EXPECT_TRUE(FS.addHardLink(FromLink, Target)); 1162 EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target)); 1163 EXPECT_TRUE(FS.status(FromLink)->getSize() == FS.status(Target)->getSize()); 1164 EXPECT_TRUE(FS.getBufferForFile(FromLink)->get()->getBuffer() == 1165 FS.getBufferForFile(Target)->get()->getBuffer()); 1166 } 1167 1168 TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) { 1169 StringRef Link0 = "/path/to/0/link"; 1170 StringRef Link1 = "/path/to/1/link"; 1171 StringRef Link2 = "/path/to/2/link"; 1172 StringRef Target = "/path/to/target"; 1173 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file")); 1174 EXPECT_TRUE(FS.addHardLink(Link2, Target)); 1175 EXPECT_TRUE(FS.addHardLink(Link1, Link2)); 1176 EXPECT_TRUE(FS.addHardLink(Link0, Link1)); 1177 EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target)); 1178 EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target)); 1179 EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target)); 1180 } 1181 1182 TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) { 1183 EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target")); 1184 } 1185 1186 TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) { 1187 StringRef Link = "/path/to/link"; 1188 StringRef Target = "/path/to/target"; 1189 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1190 FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link")); 1191 EXPECT_FALSE(FS.addHardLink(Link, Target)); 1192 } 1193 1194 TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) { 1195 StringRef Link = "/path/to/link"; 1196 StringRef Target = "/path/to/target"; 1197 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); 1198 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1199 EXPECT_FALSE(FS.addHardLink(Link, Target)); 1200 } 1201 1202 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) { 1203 StringRef Link = "/path/to/link"; 1204 StringRef Target = "/path/to/target"; 1205 StringRef Content = "content of target"; 1206 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1207 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1208 EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content))); 1209 } 1210 1211 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) { 1212 StringRef Link = "/path/to/link"; 1213 StringRef Target = "/path/to/target"; 1214 StringRef Content = "content of target"; 1215 StringRef LinkContent = "different content of link"; 1216 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1217 EXPECT_TRUE(FS.addHardLink(Link, Target)); 1218 EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent))); 1219 } 1220 1221 TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) { 1222 StringRef Dir = "path/to/dummy/dir"; 1223 StringRef Link = "/path/to/link"; 1224 StringRef File = "path/to/dummy/dir/target"; 1225 StringRef Content = "content of target"; 1226 EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content))); 1227 EXPECT_FALSE(FS.addHardLink(Link, Dir)); 1228 } 1229 1230 TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) { 1231 StringRef Dir = "path/to/dummy/dir"; 1232 StringRef Target = "path/to/dummy/dir/target"; 1233 StringRef Content = "content of target"; 1234 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); 1235 EXPECT_FALSE(FS.addHardLink(Dir, Target)); 1236 } 1237 1238 TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) { 1239 StringRef CommonContent = "content string"; 1240 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent)); 1241 FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent)); 1242 EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b")); 1243 } 1244 1245 TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) { 1246 std::error_code EC; 1247 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string")); 1248 EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b")); 1249 auto I = vfs::recursive_directory_iterator(FS, "/", EC); 1250 ASSERT_FALSE(EC); 1251 std::vector<std::string> Nodes; 1252 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 1253 I.increment(EC)) { 1254 Nodes.push_back(getPosixPath(I->path())); 1255 } 1256 EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d")); 1257 } 1258 1259 // NOTE: in the tests below, we use '//root/' as our root directory, since it is 1260 // a legal *absolute* path on Windows as well as *nix. 1261 class VFSFromYAMLTest : public ::testing::Test { 1262 public: 1263 int NumDiagnostics; 1264 1265 void SetUp() override { NumDiagnostics = 0; } 1266 1267 static void CountingDiagHandler(const SMDiagnostic &, void *Context) { 1268 VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context); 1269 ++Test->NumDiagnostics; 1270 } 1271 1272 IntrusiveRefCntPtr<vfs::FileSystem> 1273 getFromYAMLRawString(StringRef Content, 1274 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) { 1275 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content); 1276 return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this, 1277 ExternalFS); 1278 } 1279 1280 IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString( 1281 StringRef Content, 1282 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) { 1283 std::string VersionPlusContent("{\n 'version':0,\n"); 1284 VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos); 1285 return getFromYAMLRawString(VersionPlusContent, ExternalFS); 1286 } 1287 1288 // This is intended as a "XFAIL" for windows hosts. 1289 bool supportsSameDirMultipleYAMLEntries() { 1290 Triple Host(Triple::normalize(sys::getProcessTriple())); 1291 return !Host.isOSWindows(); 1292 } 1293 }; 1294 1295 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) { 1296 IntrusiveRefCntPtr<vfs::FileSystem> FS; 1297 FS = getFromYAMLString(""); 1298 EXPECT_EQ(nullptr, FS.get()); 1299 FS = getFromYAMLString("[]"); 1300 EXPECT_EQ(nullptr, FS.get()); 1301 FS = getFromYAMLString("'string'"); 1302 EXPECT_EQ(nullptr, FS.get()); 1303 EXPECT_EQ(3, NumDiagnostics); 1304 } 1305 1306 TEST_F(VFSFromYAMLTest, MappedFiles) { 1307 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1308 Lower->addRegularFile("//root/foo/bar/a"); 1309 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1310 "{ 'roots': [\n" 1311 "{\n" 1312 " 'type': 'directory',\n" 1313 " 'name': '//root/',\n" 1314 " 'contents': [ {\n" 1315 " 'type': 'file',\n" 1316 " 'name': 'file1',\n" 1317 " 'external-contents': '//root/foo/bar/a'\n" 1318 " },\n" 1319 " {\n" 1320 " 'type': 'file',\n" 1321 " 'name': 'file2',\n" 1322 " 'external-contents': '//root/foo/b'\n" 1323 " }\n" 1324 " ]\n" 1325 "}\n" 1326 "]\n" 1327 "}", 1328 Lower); 1329 ASSERT_TRUE(FS.get() != nullptr); 1330 1331 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1332 new vfs::OverlayFileSystem(Lower)); 1333 O->pushOverlay(FS); 1334 1335 // file 1336 ErrorOr<vfs::Status> S = O->status("//root/file1"); 1337 ASSERT_FALSE(S.getError()); 1338 EXPECT_EQ("//root/foo/bar/a", S->getName()); 1339 EXPECT_TRUE(S->IsVFSMapped); 1340 1341 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); 1342 EXPECT_EQ("//root/foo/bar/a", SLower->getName()); 1343 EXPECT_TRUE(S->equivalent(*SLower)); 1344 EXPECT_FALSE(SLower->IsVFSMapped); 1345 1346 // file after opening 1347 auto OpenedF = O->openFileForRead("//root/file1"); 1348 ASSERT_FALSE(OpenedF.getError()); 1349 auto OpenedS = (*OpenedF)->status(); 1350 ASSERT_FALSE(OpenedS.getError()); 1351 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 1352 EXPECT_TRUE(OpenedS->IsVFSMapped); 1353 1354 // directory 1355 S = O->status("//root/"); 1356 ASSERT_FALSE(S.getError()); 1357 EXPECT_TRUE(S->isDirectory()); 1358 EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID 1359 1360 // broken mapping 1361 EXPECT_EQ(O->status("//root/file2").getError(), 1362 llvm::errc::no_such_file_or_directory); 1363 EXPECT_EQ(0, NumDiagnostics); 1364 } 1365 1366 TEST_F(VFSFromYAMLTest, CaseInsensitive) { 1367 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1368 Lower->addRegularFile("//root/foo/bar/a"); 1369 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1370 "{ 'case-sensitive': 'false',\n" 1371 " 'roots': [\n" 1372 "{\n" 1373 " 'type': 'directory',\n" 1374 " 'name': '//root/',\n" 1375 " 'contents': [ {\n" 1376 " 'type': 'file',\n" 1377 " 'name': 'XX',\n" 1378 " 'external-contents': '//root/foo/bar/a'\n" 1379 " }\n" 1380 " ]\n" 1381 "}]}", 1382 Lower); 1383 ASSERT_TRUE(FS.get() != nullptr); 1384 1385 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1386 new vfs::OverlayFileSystem(Lower)); 1387 O->pushOverlay(FS); 1388 1389 ErrorOr<vfs::Status> S = O->status("//root/XX"); 1390 ASSERT_FALSE(S.getError()); 1391 1392 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 1393 ASSERT_FALSE(SS.getError()); 1394 EXPECT_TRUE(S->equivalent(*SS)); 1395 SS = O->status("//root/xX"); 1396 EXPECT_TRUE(S->equivalent(*SS)); 1397 SS = O->status("//root/Xx"); 1398 EXPECT_TRUE(S->equivalent(*SS)); 1399 EXPECT_EQ(0, NumDiagnostics); 1400 } 1401 1402 TEST_F(VFSFromYAMLTest, CaseSensitive) { 1403 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1404 Lower->addRegularFile("//root/foo/bar/a"); 1405 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1406 "{ 'case-sensitive': 'true',\n" 1407 " 'roots': [\n" 1408 "{\n" 1409 " 'type': 'directory',\n" 1410 " 'name': '//root/',\n" 1411 " 'contents': [ {\n" 1412 " 'type': 'file',\n" 1413 " 'name': 'XX',\n" 1414 " 'external-contents': '//root/foo/bar/a'\n" 1415 " }\n" 1416 " ]\n" 1417 "}]}", 1418 Lower); 1419 ASSERT_TRUE(FS.get() != nullptr); 1420 1421 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1422 new vfs::OverlayFileSystem(Lower)); 1423 O->pushOverlay(FS); 1424 1425 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 1426 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 1427 SS = O->status("//root/xX"); 1428 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 1429 SS = O->status("//root/Xx"); 1430 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 1431 EXPECT_EQ(0, NumDiagnostics); 1432 } 1433 1434 TEST_F(VFSFromYAMLTest, IllegalVFSFile) { 1435 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1436 1437 // invalid YAML at top-level 1438 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower); 1439 EXPECT_EQ(nullptr, FS.get()); 1440 // invalid YAML in roots 1441 FS = getFromYAMLString("{ 'roots':[}", Lower); 1442 // invalid YAML in directory 1443 FS = getFromYAMLString( 1444 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}", 1445 Lower); 1446 EXPECT_EQ(nullptr, FS.get()); 1447 1448 // invalid configuration 1449 FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower); 1450 EXPECT_EQ(nullptr, FS.get()); 1451 FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower); 1452 EXPECT_EQ(nullptr, FS.get()); 1453 1454 // invalid roots 1455 FS = getFromYAMLString("{ 'roots':'' }", Lower); 1456 EXPECT_EQ(nullptr, FS.get()); 1457 FS = getFromYAMLString("{ 'roots':{} }", Lower); 1458 EXPECT_EQ(nullptr, FS.get()); 1459 1460 // invalid entries 1461 FS = getFromYAMLString( 1462 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower); 1463 EXPECT_EQ(nullptr, FS.get()); 1464 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], " 1465 "'external-contents': 'other' }", 1466 Lower); 1467 EXPECT_EQ(nullptr, FS.get()); 1468 FS = getFromYAMLString( 1469 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }", 1470 Lower); 1471 EXPECT_EQ(nullptr, FS.get()); 1472 FS = getFromYAMLString( 1473 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }", 1474 Lower); 1475 EXPECT_EQ(nullptr, FS.get()); 1476 FS = getFromYAMLString( 1477 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }", 1478 Lower); 1479 EXPECT_EQ(nullptr, FS.get()); 1480 FS = getFromYAMLString( 1481 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }", 1482 Lower); 1483 EXPECT_EQ(nullptr, FS.get()); 1484 FS = getFromYAMLString( 1485 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }", 1486 Lower); 1487 EXPECT_EQ(nullptr, FS.get()); 1488 1489 // missing mandatory fields 1490 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower); 1491 EXPECT_EQ(nullptr, FS.get()); 1492 FS = getFromYAMLString( 1493 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower); 1494 EXPECT_EQ(nullptr, FS.get()); 1495 FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower); 1496 EXPECT_EQ(nullptr, FS.get()); 1497 1498 // duplicate keys 1499 FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower); 1500 EXPECT_EQ(nullptr, FS.get()); 1501 FS = getFromYAMLString( 1502 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }", 1503 Lower); 1504 EXPECT_EQ(nullptr, FS.get()); 1505 FS = 1506 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', " 1507 "'external-contents':'blah' } ] }", 1508 Lower); 1509 EXPECT_EQ(nullptr, FS.get()); 1510 1511 // missing version 1512 FS = getFromYAMLRawString("{ 'roots':[] }", Lower); 1513 EXPECT_EQ(nullptr, FS.get()); 1514 1515 // bad version number 1516 FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower); 1517 EXPECT_EQ(nullptr, FS.get()); 1518 FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower); 1519 EXPECT_EQ(nullptr, FS.get()); 1520 FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower); 1521 EXPECT_EQ(nullptr, FS.get()); 1522 EXPECT_EQ(24, NumDiagnostics); 1523 } 1524 1525 TEST_F(VFSFromYAMLTest, UseExternalName) { 1526 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1527 Lower->addRegularFile("//root/external/file"); 1528 1529 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1530 getFromYAMLString("{ 'roots': [\n" 1531 " { 'type': 'file', 'name': '//root/A',\n" 1532 " 'external-contents': '//root/external/file'\n" 1533 " },\n" 1534 " { 'type': 'file', 'name': '//root/B',\n" 1535 " 'use-external-name': true,\n" 1536 " 'external-contents': '//root/external/file'\n" 1537 " },\n" 1538 " { 'type': 'file', 'name': '//root/C',\n" 1539 " 'use-external-name': false,\n" 1540 " 'external-contents': '//root/external/file'\n" 1541 " }\n" 1542 "] }", 1543 Lower); 1544 ASSERT_TRUE(nullptr != FS.get()); 1545 1546 // default true 1547 EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName()); 1548 // explicit 1549 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 1550 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 1551 1552 // global configuration 1553 FS = getFromYAMLString("{ 'use-external-names': false,\n" 1554 " 'roots': [\n" 1555 " { 'type': 'file', 'name': '//root/A',\n" 1556 " 'external-contents': '//root/external/file'\n" 1557 " },\n" 1558 " { 'type': 'file', 'name': '//root/B',\n" 1559 " 'use-external-name': true,\n" 1560 " 'external-contents': '//root/external/file'\n" 1561 " },\n" 1562 " { 'type': 'file', 'name': '//root/C',\n" 1563 " 'use-external-name': false,\n" 1564 " 'external-contents': '//root/external/file'\n" 1565 " }\n" 1566 "] }", 1567 Lower); 1568 ASSERT_TRUE(nullptr != FS.get()); 1569 1570 // default 1571 EXPECT_EQ("//root/A", FS->status("//root/A")->getName()); 1572 // explicit 1573 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 1574 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 1575 } 1576 1577 TEST_F(VFSFromYAMLTest, MultiComponentPath) { 1578 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1579 Lower->addRegularFile("//root/other"); 1580 1581 // file in roots 1582 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1583 getFromYAMLString("{ 'roots': [\n" 1584 " { 'type': 'file', 'name': '//root/path/to/file',\n" 1585 " 'external-contents': '//root/other' }]\n" 1586 "}", 1587 Lower); 1588 ASSERT_TRUE(nullptr != FS.get()); 1589 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1590 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1591 EXPECT_FALSE(FS->status("//root/path").getError()); 1592 EXPECT_FALSE(FS->status("//root/").getError()); 1593 1594 // at the start 1595 FS = getFromYAMLString( 1596 "{ 'roots': [\n" 1597 " { 'type': 'directory', 'name': '//root/path/to',\n" 1598 " 'contents': [ { 'type': 'file', 'name': 'file',\n" 1599 " 'external-contents': '//root/other' }]}]\n" 1600 "}", 1601 Lower); 1602 ASSERT_TRUE(nullptr != FS.get()); 1603 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1604 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1605 EXPECT_FALSE(FS->status("//root/path").getError()); 1606 EXPECT_FALSE(FS->status("//root/").getError()); 1607 1608 // at the end 1609 FS = getFromYAMLString( 1610 "{ 'roots': [\n" 1611 " { 'type': 'directory', 'name': '//root/',\n" 1612 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n" 1613 " 'external-contents': '//root/other' }]}]\n" 1614 "}", 1615 Lower); 1616 ASSERT_TRUE(nullptr != FS.get()); 1617 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1618 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1619 EXPECT_FALSE(FS->status("//root/path").getError()); 1620 EXPECT_FALSE(FS->status("//root/").getError()); 1621 } 1622 1623 TEST_F(VFSFromYAMLTest, TrailingSlashes) { 1624 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1625 Lower->addRegularFile("//root/other"); 1626 1627 // file in roots 1628 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1629 "{ 'roots': [\n" 1630 " { 'type': 'directory', 'name': '//root/path/to////',\n" 1631 " 'contents': [ { 'type': 'file', 'name': 'file',\n" 1632 " 'external-contents': '//root/other' }]}]\n" 1633 "}", 1634 Lower); 1635 ASSERT_TRUE(nullptr != FS.get()); 1636 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1637 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1638 EXPECT_FALSE(FS->status("//root/path").getError()); 1639 EXPECT_FALSE(FS->status("//root/").getError()); 1640 } 1641 1642 TEST_F(VFSFromYAMLTest, DirectoryIteration) { 1643 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1644 Lower->addDirectory("//root/"); 1645 Lower->addDirectory("//root/foo"); 1646 Lower->addDirectory("//root/foo/bar"); 1647 Lower->addRegularFile("//root/foo/bar/a"); 1648 Lower->addRegularFile("//root/foo/bar/b"); 1649 Lower->addRegularFile("//root/file3"); 1650 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1651 "{ 'use-external-names': false,\n" 1652 " 'roots': [\n" 1653 "{\n" 1654 " 'type': 'directory',\n" 1655 " 'name': '//root/',\n" 1656 " 'contents': [ {\n" 1657 " 'type': 'file',\n" 1658 " 'name': 'file1',\n" 1659 " 'external-contents': '//root/foo/bar/a'\n" 1660 " },\n" 1661 " {\n" 1662 " 'type': 'file',\n" 1663 " 'name': 'file2',\n" 1664 " 'external-contents': '//root/foo/bar/b'\n" 1665 " }\n" 1666 " ]\n" 1667 "}\n" 1668 "]\n" 1669 "}", 1670 Lower); 1671 ASSERT_TRUE(FS.get() != nullptr); 1672 1673 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1674 new vfs::OverlayFileSystem(Lower)); 1675 O->pushOverlay(FS); 1676 1677 std::error_code EC; 1678 checkContents(O->dir_begin("//root/", EC), 1679 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"}); 1680 1681 checkContents(O->dir_begin("//root/foo/bar", EC), 1682 {"//root/foo/bar/a", "//root/foo/bar/b"}); 1683 } 1684 1685 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) { 1686 // https://llvm.org/bugs/show_bug.cgi?id=27725 1687 if (!supportsSameDirMultipleYAMLEntries()) 1688 return; 1689 1690 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1691 Lower->addDirectory("//root/zab"); 1692 Lower->addDirectory("//root/baz"); 1693 Lower->addRegularFile("//root/zab/a"); 1694 Lower->addRegularFile("//root/zab/b"); 1695 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1696 "{ 'use-external-names': false,\n" 1697 " 'roots': [\n" 1698 "{\n" 1699 " 'type': 'directory',\n" 1700 " 'name': '//root/baz/',\n" 1701 " 'contents': [ {\n" 1702 " 'type': 'file',\n" 1703 " 'name': 'x',\n" 1704 " 'external-contents': '//root/zab/a'\n" 1705 " }\n" 1706 " ]\n" 1707 "},\n" 1708 "{\n" 1709 " 'type': 'directory',\n" 1710 " 'name': '//root/baz/',\n" 1711 " 'contents': [ {\n" 1712 " 'type': 'file',\n" 1713 " 'name': 'y',\n" 1714 " 'external-contents': '//root/zab/b'\n" 1715 " }\n" 1716 " ]\n" 1717 "}\n" 1718 "]\n" 1719 "}", 1720 Lower); 1721 ASSERT_TRUE(FS.get() != nullptr); 1722 1723 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1724 new vfs::OverlayFileSystem(Lower)); 1725 O->pushOverlay(FS); 1726 1727 std::error_code EC; 1728 1729 checkContents(O->dir_begin("//root/baz/", EC), 1730 {"//root/baz/x", "//root/baz/y"}); 1731 } 1732 1733 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) { 1734 1735 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1736 Lower->addDirectory("//root/a"); 1737 Lower->addDirectory("//root/a/b"); 1738 Lower->addDirectory("//root/a/b/c"); 1739 Lower->addRegularFile("//root/a/b/c/file"); 1740 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1741 "{ 'use-external-names': false,\n" 1742 " 'roots': [\n" 1743 "{\n" 1744 " 'type': 'directory',\n" 1745 " 'name': '//root/a/b/c/',\n" 1746 " 'contents': [ {\n" 1747 " 'type': 'file',\n" 1748 " 'name': 'file',\n" 1749 " 'external-contents': '//root/a/b/c/file'\n" 1750 " }\n" 1751 " ]\n" 1752 "},\n" 1753 "]\n" 1754 "}", 1755 Lower); 1756 ASSERT_TRUE(FS.get() != nullptr); 1757 1758 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1759 new vfs::OverlayFileSystem(Lower)); 1760 O->pushOverlay(FS); 1761 1762 std::error_code EC; 1763 1764 // Test recursive_directory_iterator level() 1765 vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator( 1766 *O, "//root", EC), 1767 E; 1768 ASSERT_FALSE(EC); 1769 for (int l = 0; I != E; I.increment(EC), ++l) { 1770 ASSERT_FALSE(EC); 1771 EXPECT_EQ(I.level(), l); 1772 } 1773 EXPECT_EQ(I, E); 1774 } 1775 1776 TEST_F(VFSFromYAMLTest, RelativePaths) { 1777 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1778 // Filename at root level without a parent directory. 1779 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1780 "{ 'roots': [\n" 1781 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n" 1782 " 'external-contents': '//root/external/file'\n" 1783 " }\n" 1784 "] }", 1785 Lower); 1786 EXPECT_EQ(nullptr, FS.get()); 1787 1788 // Relative file path. 1789 FS = getFromYAMLString("{ 'roots': [\n" 1790 " { 'type': 'file', 'name': 'relative/file/path.h',\n" 1791 " 'external-contents': '//root/external/file'\n" 1792 " }\n" 1793 "] }", 1794 Lower); 1795 EXPECT_EQ(nullptr, FS.get()); 1796 1797 // Relative directory path. 1798 FS = getFromYAMLString( 1799 "{ 'roots': [\n" 1800 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n" 1801 " 'contents': []\n" 1802 " }\n" 1803 "] }", 1804 Lower); 1805 EXPECT_EQ(nullptr, FS.get()); 1806 1807 EXPECT_EQ(3, NumDiagnostics); 1808 } 1809 1810 TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) { 1811 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1812 Lower->addDirectory("//root/"); 1813 Lower->addRegularFile("//root/a"); 1814 Lower->addRegularFile("//root/b"); 1815 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1816 "{ 'use-external-names': false,\n" 1817 " 'fallthrough': false,\n" 1818 " 'roots': [\n" 1819 "{\n" 1820 " 'type': 'directory',\n" 1821 " 'name': '//root/',\n" 1822 " 'contents': [ {\n" 1823 " 'type': 'file',\n" 1824 " 'name': 'c',\n" 1825 " 'external-contents': '//root/a'\n" 1826 " }\n" 1827 " ]\n" 1828 "}\n" 1829 "]\n" 1830 "}", 1831 Lower); 1832 ASSERT_TRUE(FS.get() != nullptr); 1833 1834 std::error_code EC; 1835 checkContents(FS->dir_begin("//root/", EC), 1836 {"//root/c"}); 1837 } 1838 1839 TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) { 1840 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1841 Lower->addDirectory("//root/"); 1842 Lower->addRegularFile("//root/a"); 1843 Lower->addRegularFile("//root/b"); 1844 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1845 "{ 'use-external-names': false,\n" 1846 " 'roots': [\n" 1847 "{\n" 1848 " 'type': 'directory',\n" 1849 " 'name': '//root/',\n" 1850 " 'contents': [ {\n" 1851 " 'type': 'file',\n" 1852 " 'name': 'a',\n" 1853 " 'external-contents': '//root/a'\n" 1854 " }\n" 1855 " ]\n" 1856 "}\n" 1857 "]\n" 1858 "}", 1859 Lower); 1860 ASSERT_TRUE(FS.get() != nullptr); 1861 1862 std::error_code EC; 1863 checkContents(FS->dir_begin("//root/", EC), 1864 {"//root/a", "//root/b"}); 1865 } 1866 1867 TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) { 1868 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1869 Lower->addDirectory("//root/"); 1870 Lower->addDirectory("//root/foo"); 1871 Lower->addRegularFile("//root/foo/a"); 1872 Lower->addRegularFile("//root/foo/b"); 1873 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1874 "{ 'use-external-names': false,\n" 1875 " 'roots': [\n" 1876 "{\n" 1877 " 'type': 'directory',\n" 1878 " 'name': '//root/',\n" 1879 " 'contents': [ {\n" 1880 " 'type': 'file',\n" 1881 " 'name': 'bar/a',\n" 1882 " 'external-contents': '//root/foo/a'\n" 1883 " }\n" 1884 " ]\n" 1885 "}\n" 1886 "]\n" 1887 "}", 1888 Lower); 1889 ASSERT_TRUE(FS.get() != nullptr); 1890 1891 std::error_code EC; 1892 checkContents(FS->dir_begin("//root/foo", EC), 1893 {"//root/foo/a", "//root/foo/b"}); 1894 } 1895 1896 TEST_F(VFSFromYAMLTest, GetRealPath) { 1897 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1898 Lower->addDirectory("//dir/"); 1899 Lower->addRegularFile("/foo"); 1900 Lower->addSymlink("/link"); 1901 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1902 "{ 'use-external-names': false,\n" 1903 " 'roots': [\n" 1904 "{\n" 1905 " 'type': 'directory',\n" 1906 " 'name': '//root/',\n" 1907 " 'contents': [ {\n" 1908 " 'type': 'file',\n" 1909 " 'name': 'bar',\n" 1910 " 'external-contents': '/link'\n" 1911 " }\n" 1912 " ]\n" 1913 "},\n" 1914 "{\n" 1915 " 'type': 'directory',\n" 1916 " 'name': '//dir/',\n" 1917 " 'contents': []\n" 1918 "}\n" 1919 "]\n" 1920 "}", 1921 Lower); 1922 ASSERT_TRUE(FS.get() != nullptr); 1923 1924 // Regular file present in underlying file system. 1925 SmallString<16> RealPath; 1926 EXPECT_FALSE(FS->getRealPath("/foo", RealPath)); 1927 EXPECT_EQ(RealPath.str(), "/foo"); 1928 1929 // File present in YAML pointing to symlink in underlying file system. 1930 EXPECT_FALSE(FS->getRealPath("//root/bar", RealPath)); 1931 EXPECT_EQ(RealPath.str(), "/symlink"); 1932 1933 // Directories should fall back to the underlying file system is possible. 1934 EXPECT_FALSE(FS->getRealPath("//dir/", RealPath)); 1935 EXPECT_EQ(RealPath.str(), "//dir/"); 1936 1937 // Try a non-existing file. 1938 EXPECT_EQ(FS->getRealPath("/non_existing", RealPath), 1939 errc::no_such_file_or_directory); 1940 } 1941