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