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