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