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