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