1 //===- unittests/Basic/VirtualFileSystem.cpp ---------------- VFS tests ---===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "clang/Basic/VirtualFileSystem.h" 11 #include "llvm/ADT/Triple.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 "gtest/gtest.h" 18 #include <map> 19 20 using namespace clang; 21 using namespace llvm; 22 using llvm::sys::fs::UniqueID; 23 24 namespace { 25 struct DummyFile : public vfs::File { 26 vfs::Status S; 27 explicit DummyFile(vfs::Status S) : S(S) {} 28 llvm::ErrorOr<vfs::Status> status() override { return S; } 29 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 30 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 31 bool IsVolatile) override { 32 llvm_unreachable("unimplemented"); 33 } 34 std::error_code close() override { return std::error_code(); } 35 }; 36 37 class DummyFileSystem : public vfs::FileSystem { 38 int FSID; // used to produce UniqueIDs 39 int FileID; // used to produce UniqueIDs 40 std::map<std::string, vfs::Status> FilesAndDirs; 41 42 static int getNextFSID() { 43 static int Count = 0; 44 return Count++; 45 } 46 47 public: 48 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {} 49 50 ErrorOr<vfs::Status> status(const Twine &Path) override { 51 std::map<std::string, vfs::Status>::iterator I = 52 FilesAndDirs.find(Path.str()); 53 if (I == FilesAndDirs.end()) 54 return make_error_code(llvm::errc::no_such_file_or_directory); 55 return I->second; 56 } 57 ErrorOr<std::unique_ptr<vfs::File>> 58 openFileForRead(const Twine &Path) override { 59 auto S = status(Path); 60 if (S) 61 return std::unique_ptr<vfs::File>(new DummyFile{*S}); 62 return S.getError(); 63 } 64 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 65 return std::string(); 66 } 67 std::error_code setCurrentWorkingDirectory(const Twine &Path) override { 68 return std::error_code(); 69 } 70 71 struct DirIterImpl : public clang::vfs::detail::DirIterImpl { 72 std::map<std::string, vfs::Status> &FilesAndDirs; 73 std::map<std::string, vfs::Status>::iterator I; 74 std::string Path; 75 bool isInPath(StringRef S) { 76 if (Path.size() < S.size() && S.find(Path) == 0) { 77 auto LastSep = S.find_last_of('/'); 78 if (LastSep == Path.size() || LastSep == Path.size()-1) 79 return true; 80 } 81 return false; 82 } 83 DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs, 84 const Twine &_Path) 85 : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()), 86 Path(_Path.str()) { 87 for ( ; I != FilesAndDirs.end(); ++I) { 88 if (isInPath(I->first)) { 89 CurrentEntry = I->second; 90 break; 91 } 92 } 93 } 94 std::error_code increment() override { 95 ++I; 96 for ( ; I != FilesAndDirs.end(); ++I) { 97 if (isInPath(I->first)) { 98 CurrentEntry = I->second; 99 break; 100 } 101 } 102 if (I == FilesAndDirs.end()) 103 CurrentEntry = vfs::Status(); 104 return std::error_code(); 105 } 106 }; 107 108 vfs::directory_iterator dir_begin(const Twine &Dir, 109 std::error_code &EC) override { 110 return vfs::directory_iterator( 111 std::make_shared<DirIterImpl>(FilesAndDirs, Dir)); 112 } 113 114 void addEntry(StringRef Path, const vfs::Status &Status) { 115 FilesAndDirs[Path] = Status; 116 } 117 118 void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { 119 vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0, 120 1024, sys::fs::file_type::regular_file, Perms); 121 addEntry(Path, S); 122 } 123 124 void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { 125 vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0, 126 0, sys::fs::file_type::directory_file, Perms); 127 addEntry(Path, S); 128 } 129 130 void addSymlink(StringRef Path) { 131 vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0, 132 0, sys::fs::file_type::symlink_file, sys::fs::all_all); 133 addEntry(Path, S); 134 } 135 }; 136 } // end anonymous namespace 137 138 TEST(VirtualFileSystemTest, StatusQueries) { 139 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); 140 ErrorOr<vfs::Status> Status((std::error_code())); 141 142 D->addRegularFile("/foo"); 143 Status = D->status("/foo"); 144 ASSERT_FALSE(Status.getError()); 145 EXPECT_TRUE(Status->isStatusKnown()); 146 EXPECT_FALSE(Status->isDirectory()); 147 EXPECT_TRUE(Status->isRegularFile()); 148 EXPECT_FALSE(Status->isSymlink()); 149 EXPECT_FALSE(Status->isOther()); 150 EXPECT_TRUE(Status->exists()); 151 152 D->addDirectory("/bar"); 153 Status = D->status("/bar"); 154 ASSERT_FALSE(Status.getError()); 155 EXPECT_TRUE(Status->isStatusKnown()); 156 EXPECT_TRUE(Status->isDirectory()); 157 EXPECT_FALSE(Status->isRegularFile()); 158 EXPECT_FALSE(Status->isSymlink()); 159 EXPECT_FALSE(Status->isOther()); 160 EXPECT_TRUE(Status->exists()); 161 162 D->addSymlink("/baz"); 163 Status = D->status("/baz"); 164 ASSERT_FALSE(Status.getError()); 165 EXPECT_TRUE(Status->isStatusKnown()); 166 EXPECT_FALSE(Status->isDirectory()); 167 EXPECT_FALSE(Status->isRegularFile()); 168 EXPECT_TRUE(Status->isSymlink()); 169 EXPECT_FALSE(Status->isOther()); 170 EXPECT_TRUE(Status->exists()); 171 172 EXPECT_TRUE(Status->equivalent(*Status)); 173 ErrorOr<vfs::Status> Status2 = D->status("/foo"); 174 ASSERT_FALSE(Status2.getError()); 175 EXPECT_FALSE(Status->equivalent(*Status2)); 176 } 177 178 TEST(VirtualFileSystemTest, BaseOnlyOverlay) { 179 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); 180 ErrorOr<vfs::Status> Status((std::error_code())); 181 EXPECT_FALSE(Status = D->status("/foo")); 182 183 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D)); 184 EXPECT_FALSE(Status = O->status("/foo")); 185 186 D->addRegularFile("/foo"); 187 Status = D->status("/foo"); 188 EXPECT_FALSE(Status.getError()); 189 190 ErrorOr<vfs::Status> Status2((std::error_code())); 191 Status2 = O->status("/foo"); 192 EXPECT_FALSE(Status2.getError()); 193 EXPECT_TRUE(Status->equivalent(*Status2)); 194 } 195 196 TEST(VirtualFileSystemTest, OverlayFiles) { 197 IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem()); 198 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 199 IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem()); 200 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 201 new vfs::OverlayFileSystem(Base)); 202 O->pushOverlay(Middle); 203 O->pushOverlay(Top); 204 205 ErrorOr<vfs::Status> Status1((std::error_code())), 206 Status2((std::error_code())), Status3((std::error_code())), 207 StatusB((std::error_code())), StatusM((std::error_code())), 208 StatusT((std::error_code())); 209 210 Base->addRegularFile("/foo"); 211 StatusB = Base->status("/foo"); 212 ASSERT_FALSE(StatusB.getError()); 213 Status1 = O->status("/foo"); 214 ASSERT_FALSE(Status1.getError()); 215 Middle->addRegularFile("/foo"); 216 StatusM = Middle->status("/foo"); 217 ASSERT_FALSE(StatusM.getError()); 218 Status2 = O->status("/foo"); 219 ASSERT_FALSE(Status2.getError()); 220 Top->addRegularFile("/foo"); 221 StatusT = Top->status("/foo"); 222 ASSERT_FALSE(StatusT.getError()); 223 Status3 = O->status("/foo"); 224 ASSERT_FALSE(Status3.getError()); 225 226 EXPECT_TRUE(Status1->equivalent(*StatusB)); 227 EXPECT_TRUE(Status2->equivalent(*StatusM)); 228 EXPECT_TRUE(Status3->equivalent(*StatusT)); 229 230 EXPECT_FALSE(Status1->equivalent(*Status2)); 231 EXPECT_FALSE(Status2->equivalent(*Status3)); 232 EXPECT_FALSE(Status1->equivalent(*Status3)); 233 } 234 235 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) { 236 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 237 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 238 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 239 new vfs::OverlayFileSystem(Lower)); 240 O->pushOverlay(Upper); 241 242 Lower->addDirectory("/lower-only"); 243 Upper->addDirectory("/upper-only"); 244 245 // non-merged paths should be the same 246 ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only"); 247 ASSERT_FALSE(Status1.getError()); 248 ErrorOr<vfs::Status> Status2 = O->status("/lower-only"); 249 ASSERT_FALSE(Status2.getError()); 250 EXPECT_TRUE(Status1->equivalent(*Status2)); 251 252 Status1 = Upper->status("/upper-only"); 253 ASSERT_FALSE(Status1.getError()); 254 Status2 = O->status("/upper-only"); 255 ASSERT_FALSE(Status2.getError()); 256 EXPECT_TRUE(Status1->equivalent(*Status2)); 257 } 258 259 TEST(VirtualFileSystemTest, MergedDirPermissions) { 260 // merged directories get the permissions of the upper dir 261 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 262 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 263 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 264 new vfs::OverlayFileSystem(Lower)); 265 O->pushOverlay(Upper); 266 267 ErrorOr<vfs::Status> Status((std::error_code())); 268 Lower->addDirectory("/both", sys::fs::owner_read); 269 Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read); 270 Status = O->status("/both"); 271 ASSERT_FALSE(Status.getError()); 272 EXPECT_EQ(0740, Status->getPermissions()); 273 274 // permissions (as usual) are not recursively applied 275 Lower->addRegularFile("/both/foo", sys::fs::owner_read); 276 Upper->addRegularFile("/both/bar", sys::fs::owner_write); 277 Status = O->status("/both/foo"); 278 ASSERT_FALSE( Status.getError()); 279 EXPECT_EQ(0400, Status->getPermissions()); 280 Status = O->status("/both/bar"); 281 ASSERT_FALSE(Status.getError()); 282 EXPECT_EQ(0200, Status->getPermissions()); 283 } 284 285 namespace { 286 struct ScopedDir { 287 SmallString<128> Path; 288 ScopedDir(const Twine &Name, bool Unique=false) { 289 std::error_code EC; 290 if (Unique) { 291 EC = llvm::sys::fs::createUniqueDirectory(Name, Path); 292 } else { 293 Path = Name.str(); 294 EC = llvm::sys::fs::create_directory(Twine(Path)); 295 } 296 if (EC) 297 Path = ""; 298 EXPECT_FALSE(EC); 299 } 300 ~ScopedDir() { 301 if (Path != "") 302 EXPECT_FALSE(llvm::sys::fs::remove(Path.str())); 303 } 304 operator StringRef() { return Path.str(); } 305 }; 306 } // end anonymous namespace 307 308 TEST(VirtualFileSystemTest, BasicRealFSIteration) { 309 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true); 310 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 311 312 std::error_code EC; 313 vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC); 314 ASSERT_FALSE(EC); 315 EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty 316 317 ScopedDir _a(TestDirectory+"/a"); 318 ScopedDir _ab(TestDirectory+"/a/b"); 319 ScopedDir _c(TestDirectory+"/c"); 320 ScopedDir _cd(TestDirectory+"/c/d"); 321 322 I = FS->dir_begin(Twine(TestDirectory), EC); 323 ASSERT_FALSE(EC); 324 ASSERT_NE(vfs::directory_iterator(), I); 325 // Check either a or c, since we can't rely on the iteration order. 326 EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c")); 327 I.increment(EC); 328 ASSERT_FALSE(EC); 329 ASSERT_NE(vfs::directory_iterator(), I); 330 EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c")); 331 I.increment(EC); 332 EXPECT_EQ(vfs::directory_iterator(), I); 333 } 334 335 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) { 336 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true); 337 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); 338 339 std::error_code EC; 340 auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); 341 ASSERT_FALSE(EC); 342 EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty 343 344 ScopedDir _a(TestDirectory+"/a"); 345 ScopedDir _ab(TestDirectory+"/a/b"); 346 ScopedDir _c(TestDirectory+"/c"); 347 ScopedDir _cd(TestDirectory+"/c/d"); 348 349 I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); 350 ASSERT_FALSE(EC); 351 ASSERT_NE(vfs::recursive_directory_iterator(), I); 352 353 std::vector<std::string> Contents; 354 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; 355 I.increment(EC)) { 356 Contents.push_back(I->getName()); 357 } 358 359 // Check contents, which may be in any order 360 EXPECT_EQ(4U, Contents.size()); 361 int Counts[4] = { 0, 0, 0, 0 }; 362 for (const std::string &Name : Contents) { 363 ASSERT_FALSE(Name.empty()); 364 int Index = Name[Name.size()-1] - 'a'; 365 ASSERT_TRUE(Index >= 0 && Index < 4); 366 Counts[Index]++; 367 } 368 EXPECT_EQ(1, Counts[0]); // a 369 EXPECT_EQ(1, Counts[1]); // b 370 EXPECT_EQ(1, Counts[2]); // c 371 EXPECT_EQ(1, Counts[3]); // d 372 } 373 374 template <typename DirIter> 375 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) { 376 std::error_code EC; 377 SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end()); 378 SmallVector<std::string, 4> InputToCheck; 379 380 // Do not rely on iteration order to check for contents, sort both 381 // content vectors before comparison. 382 for (DirIter E; !EC && I != E; I.increment(EC)) 383 InputToCheck.push_back(I->getName()); 384 385 std::sort(InputToCheck.begin(), InputToCheck.end()); 386 std::sort(Expected.begin(), Expected.end()); 387 EXPECT_EQ(InputToCheck.size(), Expected.size()); 388 389 unsigned LastElt = std::min(InputToCheck.size(), Expected.size()); 390 for (unsigned Idx = 0; Idx != LastElt; ++Idx) 391 EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]); 392 } 393 394 TEST(VirtualFileSystemTest, OverlayIteration) { 395 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 396 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 397 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 398 new vfs::OverlayFileSystem(Lower)); 399 O->pushOverlay(Upper); 400 401 std::error_code EC; 402 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); 403 404 Lower->addRegularFile("/file1"); 405 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1")); 406 407 Upper->addRegularFile("/file2"); 408 checkContents(O->dir_begin("/", EC), {"/file2", "/file1"}); 409 410 Lower->addDirectory("/dir1"); 411 Lower->addRegularFile("/dir1/foo"); 412 Upper->addDirectory("/dir2"); 413 Upper->addRegularFile("/dir2/foo"); 414 checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo")); 415 checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"}); 416 } 417 418 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) { 419 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 420 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 421 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 422 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 423 new vfs::OverlayFileSystem(Lower)); 424 O->pushOverlay(Middle); 425 O->pushOverlay(Upper); 426 427 std::error_code EC; 428 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 429 ArrayRef<StringRef>()); 430 431 Lower->addRegularFile("/file1"); 432 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 433 ArrayRef<StringRef>("/file1")); 434 435 Upper->addDirectory("/dir"); 436 Upper->addRegularFile("/dir/file2"); 437 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 438 {"/dir", "/dir/file2", "/file1"}); 439 440 Lower->addDirectory("/dir1"); 441 Lower->addRegularFile("/dir1/foo"); 442 Lower->addDirectory("/dir1/a"); 443 Lower->addRegularFile("/dir1/a/b"); 444 Middle->addDirectory("/a"); 445 Middle->addDirectory("/a/b"); 446 Middle->addDirectory("/a/b/c"); 447 Middle->addRegularFile("/a/b/c/d"); 448 Middle->addRegularFile("/hiddenByUp"); 449 Upper->addDirectory("/dir2"); 450 Upper->addRegularFile("/dir2/foo"); 451 Upper->addRegularFile("/hiddenByUp"); 452 checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC), 453 ArrayRef<StringRef>("/dir2/foo")); 454 checkContents(vfs::recursive_directory_iterator(*O, "/", EC), 455 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp", 456 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a", 457 "/dir1/a/b", "/dir1/foo", "/file1"}); 458 } 459 460 TEST(VirtualFileSystemTest, ThreeLevelIteration) { 461 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 462 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 463 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 464 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 465 new vfs::OverlayFileSystem(Lower)); 466 O->pushOverlay(Middle); 467 O->pushOverlay(Upper); 468 469 std::error_code EC; 470 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); 471 472 Middle->addRegularFile("/file2"); 473 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2")); 474 475 Lower->addRegularFile("/file1"); 476 Upper->addRegularFile("/file3"); 477 checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"}); 478 } 479 480 TEST(VirtualFileSystemTest, HiddenInIteration) { 481 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 482 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); 483 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); 484 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 485 new vfs::OverlayFileSystem(Lower)); 486 O->pushOverlay(Middle); 487 O->pushOverlay(Upper); 488 489 std::error_code EC; 490 Lower->addRegularFile("/onlyInLow", sys::fs::owner_read); 491 Lower->addRegularFile("/hiddenByMid", sys::fs::owner_read); 492 Lower->addRegularFile("/hiddenByUp", sys::fs::owner_read); 493 Middle->addRegularFile("/onlyInMid", sys::fs::owner_write); 494 Middle->addRegularFile("/hiddenByMid", sys::fs::owner_write); 495 Middle->addRegularFile("/hiddenByUp", sys::fs::owner_write); 496 Upper->addRegularFile("/onlyInUp", sys::fs::owner_all); 497 Upper->addRegularFile("/hiddenByUp", sys::fs::owner_all); 498 checkContents( 499 O->dir_begin("/", EC), 500 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"}); 501 502 // Make sure we get the top-most entry 503 { 504 std::error_code EC; 505 vfs::directory_iterator I = O->dir_begin("/", EC), E; 506 for ( ; !EC && I != E; I.increment(EC)) 507 if (I->getName() == "/hiddenByUp") 508 break; 509 ASSERT_NE(E, I); 510 EXPECT_EQ(sys::fs::owner_all, I->getPermissions()); 511 } 512 { 513 std::error_code EC; 514 vfs::directory_iterator I = O->dir_begin("/", EC), E; 515 for ( ; !EC && I != E; I.increment(EC)) 516 if (I->getName() == "/hiddenByMid") 517 break; 518 ASSERT_NE(E, I); 519 EXPECT_EQ(sys::fs::owner_write, I->getPermissions()); 520 } 521 } 522 523 class InMemoryFileSystemTest : public ::testing::Test { 524 protected: 525 clang::vfs::InMemoryFileSystem FS; 526 clang::vfs::InMemoryFileSystem NormalizedFS; 527 528 InMemoryFileSystemTest() 529 : FS(/*UseNormalizedPaths=*/false), 530 NormalizedFS(/*UseNormalizedPaths=*/true) {} 531 }; 532 533 TEST_F(InMemoryFileSystemTest, IsEmpty) { 534 auto Stat = FS.status("/a"); 535 ASSERT_EQ(Stat.getError(),errc::no_such_file_or_directory) << FS.toString(); 536 Stat = FS.status("/"); 537 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString(); 538 } 539 540 TEST_F(InMemoryFileSystemTest, WindowsPath) { 541 FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); 542 auto Stat = FS.status("c:"); 543 #if !defined(_WIN32) 544 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 545 #endif 546 Stat = FS.status("c:/windows/system128/foo.cpp"); 547 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 548 FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); 549 Stat = FS.status("d:/windows/foo.cpp"); 550 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 551 } 552 553 TEST_F(InMemoryFileSystemTest, OverlayFile) { 554 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 555 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 556 auto Stat = FS.status("/"); 557 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 558 Stat = FS.status("/."); 559 ASSERT_FALSE(Stat); 560 Stat = NormalizedFS.status("/."); 561 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); 562 Stat = FS.status("/a"); 563 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 564 ASSERT_EQ("/a", Stat->getName()); 565 } 566 567 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) { 568 auto Buf = MemoryBuffer::getMemBuffer("a"); 569 FS.addFileNoOwn("/a", 0, Buf.get()); 570 auto Stat = FS.status("/a"); 571 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 572 ASSERT_EQ("/a", Stat->getName()); 573 } 574 575 TEST_F(InMemoryFileSystemTest, OpenFileForRead) { 576 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 577 FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); 578 FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); 579 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); 580 NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); 581 NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); 582 auto File = FS.openFileForRead("/a"); 583 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 584 File = FS.openFileForRead("/a"); // Open again. 585 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 586 File = NormalizedFS.openFileForRead("/././a"); // Open again. 587 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); 588 File = FS.openFileForRead("/"); 589 ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString(); 590 File = FS.openFileForRead("/b"); 591 ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString(); 592 File = FS.openFileForRead("./c"); 593 ASSERT_FALSE(File); 594 File = FS.openFileForRead("e/../d"); 595 ASSERT_FALSE(File); 596 File = NormalizedFS.openFileForRead("./c"); 597 ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer()); 598 File = NormalizedFS.openFileForRead("e/../d"); 599 ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer()); 600 } 601 602 TEST_F(InMemoryFileSystemTest, DuplicatedFile) { 603 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); 604 ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a"))); 605 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); 606 ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b"))); 607 } 608 609 TEST_F(InMemoryFileSystemTest, DirectoryIteration) { 610 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("")); 611 FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer("")); 612 613 std::error_code EC; 614 vfs::directory_iterator I = FS.dir_begin("/", EC); 615 ASSERT_FALSE(EC); 616 ASSERT_EQ("/a", I->getName()); 617 I.increment(EC); 618 ASSERT_FALSE(EC); 619 ASSERT_EQ("/b", I->getName()); 620 I.increment(EC); 621 ASSERT_FALSE(EC); 622 ASSERT_EQ(vfs::directory_iterator(), I); 623 624 I = FS.dir_begin("/b", EC); 625 ASSERT_FALSE(EC); 626 ASSERT_EQ("/b/c", I->getName()); 627 I.increment(EC); 628 ASSERT_FALSE(EC); 629 ASSERT_EQ(vfs::directory_iterator(), I); 630 } 631 632 TEST_F(InMemoryFileSystemTest, WorkingDirectory) { 633 FS.setCurrentWorkingDirectory("/b"); 634 FS.addFile("c", 0, MemoryBuffer::getMemBuffer("")); 635 636 auto Stat = FS.status("/b/c"); 637 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 638 ASSERT_EQ("c", Stat->getName()); 639 ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory()); 640 641 Stat = FS.status("c"); 642 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); 643 644 auto ReplaceBackslashes = [](std::string S) { 645 std::replace(S.begin(), S.end(), '\\', '/'); 646 return S; 647 }; 648 NormalizedFS.setCurrentWorkingDirectory("/b/c"); 649 NormalizedFS.setCurrentWorkingDirectory("."); 650 ASSERT_EQ("/b/c", ReplaceBackslashes( 651 NormalizedFS.getCurrentWorkingDirectory().get())); 652 NormalizedFS.setCurrentWorkingDirectory(".."); 653 ASSERT_EQ("/b", ReplaceBackslashes( 654 NormalizedFS.getCurrentWorkingDirectory().get())); 655 } 656 657 // NOTE: in the tests below, we use '//root/' as our root directory, since it is 658 // a legal *absolute* path on Windows as well as *nix. 659 class VFSFromYAMLTest : public ::testing::Test { 660 public: 661 int NumDiagnostics; 662 663 void SetUp() override { NumDiagnostics = 0; } 664 665 static void CountingDiagHandler(const SMDiagnostic &, void *Context) { 666 VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context); 667 ++Test->NumDiagnostics; 668 } 669 670 IntrusiveRefCntPtr<vfs::FileSystem> 671 getFromYAMLRawString(StringRef Content, 672 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) { 673 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content); 674 return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this, 675 ExternalFS); 676 } 677 678 IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString( 679 StringRef Content, 680 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) { 681 std::string VersionPlusContent("{\n 'version':0,\n"); 682 VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos); 683 return getFromYAMLRawString(VersionPlusContent, ExternalFS); 684 } 685 686 // This is intended as a "XFAIL" for windows hosts. 687 bool supportsSameDirMultipleYAMLEntries() { 688 Triple Host(Triple::normalize(sys::getProcessTriple())); 689 return !Host.isOSWindows(); 690 } 691 }; 692 693 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) { 694 IntrusiveRefCntPtr<vfs::FileSystem> FS; 695 FS = getFromYAMLString(""); 696 EXPECT_EQ(nullptr, FS.get()); 697 FS = getFromYAMLString("[]"); 698 EXPECT_EQ(nullptr, FS.get()); 699 FS = getFromYAMLString("'string'"); 700 EXPECT_EQ(nullptr, FS.get()); 701 EXPECT_EQ(3, NumDiagnostics); 702 } 703 704 TEST_F(VFSFromYAMLTest, MappedFiles) { 705 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 706 Lower->addRegularFile("//root/foo/bar/a"); 707 IntrusiveRefCntPtr<vfs::FileSystem> FS = 708 getFromYAMLString("{ 'roots': [\n" 709 "{\n" 710 " 'type': 'directory',\n" 711 " 'name': '//root/',\n" 712 " 'contents': [ {\n" 713 " 'type': 'file',\n" 714 " 'name': 'file1',\n" 715 " 'external-contents': '//root/foo/bar/a'\n" 716 " },\n" 717 " {\n" 718 " 'type': 'file',\n" 719 " 'name': 'file2',\n" 720 " 'external-contents': '//root/foo/b'\n" 721 " }\n" 722 " ]\n" 723 "}\n" 724 "]\n" 725 "}", 726 Lower); 727 ASSERT_TRUE(FS.get() != nullptr); 728 729 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 730 new vfs::OverlayFileSystem(Lower)); 731 O->pushOverlay(FS); 732 733 // file 734 ErrorOr<vfs::Status> S = O->status("//root/file1"); 735 ASSERT_FALSE(S.getError()); 736 EXPECT_EQ("//root/foo/bar/a", S->getName()); 737 EXPECT_TRUE(S->IsVFSMapped); 738 739 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); 740 EXPECT_EQ("//root/foo/bar/a", SLower->getName()); 741 EXPECT_TRUE(S->equivalent(*SLower)); 742 EXPECT_FALSE(SLower->IsVFSMapped); 743 744 // file after opening 745 auto OpenedF = O->openFileForRead("//root/file1"); 746 ASSERT_FALSE(OpenedF.getError()); 747 auto OpenedS = (*OpenedF)->status(); 748 ASSERT_FALSE(OpenedS.getError()); 749 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); 750 EXPECT_TRUE(OpenedS->IsVFSMapped); 751 752 // directory 753 S = O->status("//root/"); 754 ASSERT_FALSE(S.getError()); 755 EXPECT_TRUE(S->isDirectory()); 756 EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID 757 758 // broken mapping 759 EXPECT_EQ(O->status("//root/file2").getError(), 760 llvm::errc::no_such_file_or_directory); 761 EXPECT_EQ(0, NumDiagnostics); 762 } 763 764 TEST_F(VFSFromYAMLTest, CaseInsensitive) { 765 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 766 Lower->addRegularFile("//root/foo/bar/a"); 767 IntrusiveRefCntPtr<vfs::FileSystem> FS = 768 getFromYAMLString("{ 'case-sensitive': 'false',\n" 769 " 'roots': [\n" 770 "{\n" 771 " 'type': 'directory',\n" 772 " 'name': '//root/',\n" 773 " 'contents': [ {\n" 774 " 'type': 'file',\n" 775 " 'name': 'XX',\n" 776 " 'external-contents': '//root/foo/bar/a'\n" 777 " }\n" 778 " ]\n" 779 "}]}", 780 Lower); 781 ASSERT_TRUE(FS.get() != nullptr); 782 783 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 784 new vfs::OverlayFileSystem(Lower)); 785 O->pushOverlay(FS); 786 787 ErrorOr<vfs::Status> S = O->status("//root/XX"); 788 ASSERT_FALSE(S.getError()); 789 790 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 791 ASSERT_FALSE(SS.getError()); 792 EXPECT_TRUE(S->equivalent(*SS)); 793 SS = O->status("//root/xX"); 794 EXPECT_TRUE(S->equivalent(*SS)); 795 SS = O->status("//root/Xx"); 796 EXPECT_TRUE(S->equivalent(*SS)); 797 EXPECT_EQ(0, NumDiagnostics); 798 } 799 800 TEST_F(VFSFromYAMLTest, CaseSensitive) { 801 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 802 Lower->addRegularFile("//root/foo/bar/a"); 803 IntrusiveRefCntPtr<vfs::FileSystem> FS = 804 getFromYAMLString("{ 'case-sensitive': 'true',\n" 805 " 'roots': [\n" 806 "{\n" 807 " 'type': 'directory',\n" 808 " 'name': '//root/',\n" 809 " 'contents': [ {\n" 810 " 'type': 'file',\n" 811 " 'name': 'XX',\n" 812 " 'external-contents': '//root/foo/bar/a'\n" 813 " }\n" 814 " ]\n" 815 "}]}", 816 Lower); 817 ASSERT_TRUE(FS.get() != nullptr); 818 819 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 820 new vfs::OverlayFileSystem(Lower)); 821 O->pushOverlay(FS); 822 823 ErrorOr<vfs::Status> SS = O->status("//root/xx"); 824 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 825 SS = O->status("//root/xX"); 826 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 827 SS = O->status("//root/Xx"); 828 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); 829 EXPECT_EQ(0, NumDiagnostics); 830 } 831 832 TEST_F(VFSFromYAMLTest, IllegalVFSFile) { 833 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 834 835 // invalid YAML at top-level 836 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower); 837 EXPECT_EQ(nullptr, FS.get()); 838 // invalid YAML in roots 839 FS = getFromYAMLString("{ 'roots':[}", Lower); 840 // invalid YAML in directory 841 FS = getFromYAMLString( 842 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}", 843 Lower); 844 EXPECT_EQ(nullptr, FS.get()); 845 846 // invalid configuration 847 FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower); 848 EXPECT_EQ(nullptr, FS.get()); 849 FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower); 850 EXPECT_EQ(nullptr, FS.get()); 851 852 // invalid roots 853 FS = getFromYAMLString("{ 'roots':'' }", Lower); 854 EXPECT_EQ(nullptr, FS.get()); 855 FS = getFromYAMLString("{ 'roots':{} }", Lower); 856 EXPECT_EQ(nullptr, FS.get()); 857 858 // invalid entries 859 FS = getFromYAMLString( 860 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower); 861 EXPECT_EQ(nullptr, FS.get()); 862 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], " 863 "'external-contents': 'other' }", 864 Lower); 865 EXPECT_EQ(nullptr, FS.get()); 866 FS = getFromYAMLString( 867 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }", 868 Lower); 869 EXPECT_EQ(nullptr, FS.get()); 870 FS = getFromYAMLString( 871 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }", 872 Lower); 873 EXPECT_EQ(nullptr, FS.get()); 874 FS = getFromYAMLString( 875 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }", 876 Lower); 877 EXPECT_EQ(nullptr, FS.get()); 878 FS = getFromYAMLString( 879 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }", 880 Lower); 881 EXPECT_EQ(nullptr, FS.get()); 882 FS = getFromYAMLString( 883 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }", 884 Lower); 885 EXPECT_EQ(nullptr, FS.get()); 886 887 // missing mandatory fields 888 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower); 889 EXPECT_EQ(nullptr, FS.get()); 890 FS = getFromYAMLString( 891 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower); 892 EXPECT_EQ(nullptr, FS.get()); 893 FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower); 894 EXPECT_EQ(nullptr, FS.get()); 895 896 // duplicate keys 897 FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower); 898 EXPECT_EQ(nullptr, FS.get()); 899 FS = getFromYAMLString( 900 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }", 901 Lower); 902 EXPECT_EQ(nullptr, FS.get()); 903 FS = 904 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', " 905 "'external-contents':'blah' } ] }", 906 Lower); 907 EXPECT_EQ(nullptr, FS.get()); 908 909 // missing version 910 FS = getFromYAMLRawString("{ 'roots':[] }", Lower); 911 EXPECT_EQ(nullptr, FS.get()); 912 913 // bad version number 914 FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower); 915 EXPECT_EQ(nullptr, FS.get()); 916 FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower); 917 EXPECT_EQ(nullptr, FS.get()); 918 FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower); 919 EXPECT_EQ(nullptr, FS.get()); 920 EXPECT_EQ(24, NumDiagnostics); 921 } 922 923 TEST_F(VFSFromYAMLTest, UseExternalName) { 924 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 925 Lower->addRegularFile("//root/external/file"); 926 927 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 928 "{ 'roots': [\n" 929 " { 'type': 'file', 'name': '//root/A',\n" 930 " 'external-contents': '//root/external/file'\n" 931 " },\n" 932 " { 'type': 'file', 'name': '//root/B',\n" 933 " 'use-external-name': true,\n" 934 " 'external-contents': '//root/external/file'\n" 935 " },\n" 936 " { 'type': 'file', 'name': '//root/C',\n" 937 " 'use-external-name': false,\n" 938 " 'external-contents': '//root/external/file'\n" 939 " }\n" 940 "] }", Lower); 941 ASSERT_TRUE(nullptr != FS.get()); 942 943 // default true 944 EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName()); 945 // explicit 946 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 947 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 948 949 // global configuration 950 FS = getFromYAMLString( 951 "{ 'use-external-names': false,\n" 952 " 'roots': [\n" 953 " { 'type': 'file', 'name': '//root/A',\n" 954 " 'external-contents': '//root/external/file'\n" 955 " },\n" 956 " { 'type': 'file', 'name': '//root/B',\n" 957 " 'use-external-name': true,\n" 958 " 'external-contents': '//root/external/file'\n" 959 " },\n" 960 " { 'type': 'file', 'name': '//root/C',\n" 961 " 'use-external-name': false,\n" 962 " 'external-contents': '//root/external/file'\n" 963 " }\n" 964 "] }", Lower); 965 ASSERT_TRUE(nullptr != FS.get()); 966 967 // default 968 EXPECT_EQ("//root/A", FS->status("//root/A")->getName()); 969 // explicit 970 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); 971 EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); 972 } 973 974 TEST_F(VFSFromYAMLTest, MultiComponentPath) { 975 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 976 Lower->addRegularFile("//root/other"); 977 978 // file in roots 979 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 980 "{ 'roots': [\n" 981 " { 'type': 'file', 'name': '//root/path/to/file',\n" 982 " 'external-contents': '//root/other' }]\n" 983 "}", Lower); 984 ASSERT_TRUE(nullptr != FS.get()); 985 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 986 EXPECT_FALSE(FS->status("//root/path/to").getError()); 987 EXPECT_FALSE(FS->status("//root/path").getError()); 988 EXPECT_FALSE(FS->status("//root/").getError()); 989 990 // at the start 991 FS = getFromYAMLString( 992 "{ 'roots': [\n" 993 " { 'type': 'directory', 'name': '//root/path/to',\n" 994 " 'contents': [ { 'type': 'file', 'name': 'file',\n" 995 " 'external-contents': '//root/other' }]}]\n" 996 "}", Lower); 997 ASSERT_TRUE(nullptr != FS.get()); 998 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 999 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1000 EXPECT_FALSE(FS->status("//root/path").getError()); 1001 EXPECT_FALSE(FS->status("//root/").getError()); 1002 1003 // at the end 1004 FS = getFromYAMLString( 1005 "{ 'roots': [\n" 1006 " { 'type': 'directory', 'name': '//root/',\n" 1007 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n" 1008 " 'external-contents': '//root/other' }]}]\n" 1009 "}", Lower); 1010 ASSERT_TRUE(nullptr != FS.get()); 1011 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1012 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1013 EXPECT_FALSE(FS->status("//root/path").getError()); 1014 EXPECT_FALSE(FS->status("//root/").getError()); 1015 } 1016 1017 TEST_F(VFSFromYAMLTest, TrailingSlashes) { 1018 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1019 Lower->addRegularFile("//root/other"); 1020 1021 // file in roots 1022 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1023 "{ 'roots': [\n" 1024 " { 'type': 'directory', 'name': '//root/path/to////',\n" 1025 " 'contents': [ { 'type': 'file', 'name': 'file',\n" 1026 " 'external-contents': '//root/other' }]}]\n" 1027 "}", Lower); 1028 ASSERT_TRUE(nullptr != FS.get()); 1029 EXPECT_FALSE(FS->status("//root/path/to/file").getError()); 1030 EXPECT_FALSE(FS->status("//root/path/to").getError()); 1031 EXPECT_FALSE(FS->status("//root/path").getError()); 1032 EXPECT_FALSE(FS->status("//root/").getError()); 1033 } 1034 1035 TEST_F(VFSFromYAMLTest, DirectoryIteration) { 1036 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1037 Lower->addDirectory("//root/"); 1038 Lower->addDirectory("//root/foo"); 1039 Lower->addDirectory("//root/foo/bar"); 1040 Lower->addRegularFile("//root/foo/bar/a"); 1041 Lower->addRegularFile("//root/foo/bar/b"); 1042 Lower->addRegularFile("//root/file3"); 1043 IntrusiveRefCntPtr<vfs::FileSystem> FS = 1044 getFromYAMLString("{ 'use-external-names': false,\n" 1045 " 'roots': [\n" 1046 "{\n" 1047 " 'type': 'directory',\n" 1048 " 'name': '//root/',\n" 1049 " 'contents': [ {\n" 1050 " 'type': 'file',\n" 1051 " 'name': 'file1',\n" 1052 " 'external-contents': '//root/foo/bar/a'\n" 1053 " },\n" 1054 " {\n" 1055 " 'type': 'file',\n" 1056 " 'name': 'file2',\n" 1057 " 'external-contents': '//root/foo/bar/b'\n" 1058 " }\n" 1059 " ]\n" 1060 "}\n" 1061 "]\n" 1062 "}", 1063 Lower); 1064 ASSERT_TRUE(FS.get() != nullptr); 1065 1066 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1067 new vfs::OverlayFileSystem(Lower)); 1068 O->pushOverlay(FS); 1069 1070 std::error_code EC; 1071 checkContents(O->dir_begin("//root/", EC), 1072 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"}); 1073 1074 checkContents(O->dir_begin("//root/foo/bar", EC), 1075 {"//root/foo/bar/a", "//root/foo/bar/b"}); 1076 } 1077 1078 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) { 1079 // https://llvm.org/bugs/show_bug.cgi?id=27725 1080 if (!supportsSameDirMultipleYAMLEntries()) 1081 return; 1082 1083 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1084 Lower->addDirectory("//root/zab"); 1085 Lower->addDirectory("//root/baz"); 1086 Lower->addRegularFile("//root/zab/a"); 1087 Lower->addRegularFile("//root/zab/b"); 1088 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1089 "{ 'use-external-names': false,\n" 1090 " 'roots': [\n" 1091 "{\n" 1092 " 'type': 'directory',\n" 1093 " 'name': '//root/baz/',\n" 1094 " 'contents': [ {\n" 1095 " 'type': 'file',\n" 1096 " 'name': 'x',\n" 1097 " 'external-contents': '//root/zab/a'\n" 1098 " }\n" 1099 " ]\n" 1100 "},\n" 1101 "{\n" 1102 " 'type': 'directory',\n" 1103 " 'name': '//root/baz/',\n" 1104 " 'contents': [ {\n" 1105 " 'type': 'file',\n" 1106 " 'name': 'y',\n" 1107 " 'external-contents': '//root/zab/b'\n" 1108 " }\n" 1109 " ]\n" 1110 "}\n" 1111 "]\n" 1112 "}", 1113 Lower); 1114 ASSERT_TRUE(FS.get() != nullptr); 1115 1116 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1117 new vfs::OverlayFileSystem(Lower)); 1118 O->pushOverlay(FS); 1119 1120 std::error_code EC; 1121 1122 checkContents(O->dir_begin("//root/baz/", EC), 1123 {"//root/baz/x", "//root/baz/y"}); 1124 } 1125 1126 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) { 1127 1128 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); 1129 Lower->addDirectory("//root/a"); 1130 Lower->addDirectory("//root/a/b"); 1131 Lower->addDirectory("//root/a/b/c"); 1132 Lower->addRegularFile("//root/a/b/c/file"); 1133 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( 1134 "{ 'use-external-names': false,\n" 1135 " 'roots': [\n" 1136 "{\n" 1137 " 'type': 'directory',\n" 1138 " 'name': '//root/a/b/c/',\n" 1139 " 'contents': [ {\n" 1140 " 'type': 'file',\n" 1141 " 'name': 'file',\n" 1142 " 'external-contents': '//root/a/b/c/file'\n" 1143 " }\n" 1144 " ]\n" 1145 "},\n" 1146 "]\n" 1147 "}", 1148 Lower); 1149 ASSERT_TRUE(FS.get() != nullptr); 1150 1151 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( 1152 new vfs::OverlayFileSystem(Lower)); 1153 O->pushOverlay(FS); 1154 1155 std::error_code EC; 1156 1157 // Test recursive_directory_iterator level() 1158 vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator( 1159 *O, "//root", EC), E; 1160 ASSERT_FALSE(EC); 1161 for (int l = 0; I != E; I.increment(EC), ++l) { 1162 ASSERT_FALSE(EC); 1163 EXPECT_EQ(I.level(), l); 1164 } 1165 EXPECT_EQ(I, E); 1166 } 1167