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;
DummyFile__anon73ed79de0111::DummyFile27 explicit DummyFile(vfs::Status S) : S(S) {}
status__anon73ed79de0111::DummyFile28 llvm::ErrorOr<vfs::Status> status() override { return S; }
29 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBuffer__anon73ed79de0111::DummyFile30 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
31 bool IsVolatile) override {
32 llvm_unreachable("unimplemented");
33 }
close__anon73ed79de0111::DummyFile34 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
getNextFSID()42 static int getNextFSID() {
43 static int Count = 0;
44 return Count++;
45 }
46
47 public:
DummyFileSystem()48 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
49
status(const Twine & Path)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>>
openFileForRead(const Twine & Path)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 }
getCurrentWorkingDirectory() const64 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
65 return std::string();
66 }
setCurrentWorkingDirectory(const Twine & Path)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;
isInPath__anon73ed79de0111::DummyFileSystem::DirIterImpl75 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 }
DirIterImpl__anon73ed79de0111::DummyFileSystem::DirIterImpl83 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 }
increment__anon73ed79de0111::DummyFileSystem::DirIterImpl94 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
dir_begin(const Twine & Dir,std::error_code & EC)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
addEntry(StringRef Path,const vfs::Status & Status)114 void addEntry(StringRef Path, const vfs::Status &Status) {
115 FilesAndDirs[Path] = Status;
116 }
117
addRegularFile(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)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
addDirectory(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)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
addSymlink(StringRef Path)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
TEST(VirtualFileSystemTest,StatusQueries)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
TEST(VirtualFileSystemTest,BaseOnlyOverlay)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
TEST(VirtualFileSystemTest,OverlayFiles)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
TEST(VirtualFileSystemTest,OverlayDirsNonMerged)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
TEST(VirtualFileSystemTest,MergedDirPermissions)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;
ScopedDir__anon73ed79de0211::ScopedDir288 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 }
~ScopedDir__anon73ed79de0211::ScopedDir300 ~ScopedDir() {
301 if (Path != "")
302 EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
303 }
operator StringRef__anon73ed79de0211::ScopedDir304 operator StringRef() { return Path.str(); }
305 };
306 } // end anonymous namespace
307
TEST(VirtualFileSystemTest,BasicRealFSIteration)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
TEST(VirtualFileSystemTest,BasicRealFSRecursiveIteration)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>
checkContents(DirIter I,ArrayRef<StringRef> ExpectedOut)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
TEST(VirtualFileSystemTest,OverlayIteration)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
TEST(VirtualFileSystemTest,OverlayRecursiveIteration)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
TEST(VirtualFileSystemTest,ThreeLevelIteration)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
TEST(VirtualFileSystemTest,HiddenInIteration)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
InMemoryFileSystemTest()528 InMemoryFileSystemTest()
529 : FS(/*UseNormalizedPaths=*/false),
530 NormalizedFS(/*UseNormalizedPaths=*/true) {}
531 };
532
TEST_F(InMemoryFileSystemTest,IsEmpty)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
TEST_F(InMemoryFileSystemTest,WindowsPath)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
TEST_F(InMemoryFileSystemTest,OverlayFile)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
TEST_F(InMemoryFileSystemTest,OverlayFileNoOwn)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
TEST_F(InMemoryFileSystemTest,OpenFileForRead)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
TEST_F(InMemoryFileSystemTest,DuplicatedFile)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
TEST_F(InMemoryFileSystemTest,DirectoryIteration)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
TEST_F(InMemoryFileSystemTest,WorkingDirectory)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
SetUp()663 void SetUp() override { NumDiagnostics = 0; }
664
CountingDiagHandler(const SMDiagnostic &,void * Context)665 static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
666 VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
667 ++Test->NumDiagnostics;
668 }
669
670 IntrusiveRefCntPtr<vfs::FileSystem>
getFromYAMLRawString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS)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
getFromYAMLString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS=new DummyFileSystem ())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.
supportsSameDirMultipleYAMLEntries()687 bool supportsSameDirMultipleYAMLEntries() {
688 Triple Host(Triple::normalize(sys::getProcessTriple()));
689 return !Host.isOSWindows();
690 }
691 };
692
TEST_F(VFSFromYAMLTest,BasicVFSFromYAML)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
TEST_F(VFSFromYAMLTest,MappedFiles)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
TEST_F(VFSFromYAMLTest,CaseInsensitive)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
TEST_F(VFSFromYAMLTest,CaseSensitive)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
TEST_F(VFSFromYAMLTest,IllegalVFSFile)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
TEST_F(VFSFromYAMLTest,UseExternalName)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
TEST_F(VFSFromYAMLTest,MultiComponentPath)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
TEST_F(VFSFromYAMLTest,TrailingSlashes)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
TEST_F(VFSFromYAMLTest,DirectoryIteration)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
TEST_F(VFSFromYAMLTest,DirectoryIterationSameDirMultipleEntries)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
TEST_F(VFSFromYAMLTest,RecursiveDirectoryIterationLevel)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