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