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