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/Config/llvm-config.h"
13 #include "llvm/Support/Errc.h"
14 #include "llvm/Support/Host.h"
15 #include "llvm/Support/MemoryBuffer.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__anon43ce3cdf0111::DummyFile27 explicit DummyFile(vfs::Status S) : S(S) {}
status__anon43ce3cdf0111::DummyFile28 llvm::ErrorOr<vfs::Status> status() override { return S; }
29 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBuffer__anon43ce3cdf0111::DummyFile30 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
31 bool IsVolatile) override {
32 llvm_unreachable("unimplemented");
33 }
close__anon43ce3cdf0111::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 // Map any symlink to "/symlink".
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const71 std::error_code getRealPath(const Twine &Path,
72 SmallVectorImpl<char> &Output) const override {
73 auto I = FilesAndDirs.find(Path.str());
74 if (I == FilesAndDirs.end())
75 return make_error_code(llvm::errc::no_such_file_or_directory);
76 if (I->second.isSymlink()) {
77 Output.clear();
78 Twine("/symlink").toVector(Output);
79 return std::error_code();
80 }
81 Output.clear();
82 Path.toVector(Output);
83 return std::error_code();
84 }
85
86 struct DirIterImpl : public clang::vfs::detail::DirIterImpl {
87 std::map<std::string, vfs::Status> &FilesAndDirs;
88 std::map<std::string, vfs::Status>::iterator I;
89 std::string Path;
isInPath__anon43ce3cdf0111::DummyFileSystem::DirIterImpl90 bool isInPath(StringRef S) {
91 if (Path.size() < S.size() && S.find(Path) == 0) {
92 auto LastSep = S.find_last_of('/');
93 if (LastSep == Path.size() || LastSep == Path.size()-1)
94 return true;
95 }
96 return false;
97 }
DirIterImpl__anon43ce3cdf0111::DummyFileSystem::DirIterImpl98 DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
99 const Twine &_Path)
100 : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
101 Path(_Path.str()) {
102 for ( ; I != FilesAndDirs.end(); ++I) {
103 if (isInPath(I->first)) {
104 CurrentEntry = I->second;
105 break;
106 }
107 }
108 }
increment__anon43ce3cdf0111::DummyFileSystem::DirIterImpl109 std::error_code increment() override {
110 ++I;
111 for ( ; I != FilesAndDirs.end(); ++I) {
112 if (isInPath(I->first)) {
113 CurrentEntry = I->second;
114 break;
115 }
116 }
117 if (I == FilesAndDirs.end())
118 CurrentEntry = vfs::Status();
119 return std::error_code();
120 }
121 };
122
dir_begin(const Twine & Dir,std::error_code & EC)123 vfs::directory_iterator dir_begin(const Twine &Dir,
124 std::error_code &EC) override {
125 return vfs::directory_iterator(
126 std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
127 }
128
addEntry(StringRef Path,const vfs::Status & Status)129 void addEntry(StringRef Path, const vfs::Status &Status) {
130 FilesAndDirs[Path] = Status;
131 }
132
addRegularFile(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)133 void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
134 vfs::Status S(Path, UniqueID(FSID, FileID++),
135 std::chrono::system_clock::now(), 0, 0, 1024,
136 sys::fs::file_type::regular_file, Perms);
137 addEntry(Path, S);
138 }
139
addDirectory(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)140 void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
141 vfs::Status S(Path, UniqueID(FSID, FileID++),
142 std::chrono::system_clock::now(), 0, 0, 0,
143 sys::fs::file_type::directory_file, Perms);
144 addEntry(Path, S);
145 }
146
addSymlink(StringRef Path)147 void addSymlink(StringRef Path) {
148 vfs::Status S(Path, UniqueID(FSID, FileID++),
149 std::chrono::system_clock::now(), 0, 0, 0,
150 sys::fs::file_type::symlink_file, sys::fs::all_all);
151 addEntry(Path, S);
152 }
153 };
154 } // end anonymous namespace
155
TEST(VirtualFileSystemTest,StatusQueries)156 TEST(VirtualFileSystemTest, StatusQueries) {
157 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
158 ErrorOr<vfs::Status> Status((std::error_code()));
159
160 D->addRegularFile("/foo");
161 Status = D->status("/foo");
162 ASSERT_FALSE(Status.getError());
163 EXPECT_TRUE(Status->isStatusKnown());
164 EXPECT_FALSE(Status->isDirectory());
165 EXPECT_TRUE(Status->isRegularFile());
166 EXPECT_FALSE(Status->isSymlink());
167 EXPECT_FALSE(Status->isOther());
168 EXPECT_TRUE(Status->exists());
169
170 D->addDirectory("/bar");
171 Status = D->status("/bar");
172 ASSERT_FALSE(Status.getError());
173 EXPECT_TRUE(Status->isStatusKnown());
174 EXPECT_TRUE(Status->isDirectory());
175 EXPECT_FALSE(Status->isRegularFile());
176 EXPECT_FALSE(Status->isSymlink());
177 EXPECT_FALSE(Status->isOther());
178 EXPECT_TRUE(Status->exists());
179
180 D->addSymlink("/baz");
181 Status = D->status("/baz");
182 ASSERT_FALSE(Status.getError());
183 EXPECT_TRUE(Status->isStatusKnown());
184 EXPECT_FALSE(Status->isDirectory());
185 EXPECT_FALSE(Status->isRegularFile());
186 EXPECT_TRUE(Status->isSymlink());
187 EXPECT_FALSE(Status->isOther());
188 EXPECT_TRUE(Status->exists());
189
190 EXPECT_TRUE(Status->equivalent(*Status));
191 ErrorOr<vfs::Status> Status2 = D->status("/foo");
192 ASSERT_FALSE(Status2.getError());
193 EXPECT_FALSE(Status->equivalent(*Status2));
194 }
195
TEST(VirtualFileSystemTest,BaseOnlyOverlay)196 TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
197 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
198 ErrorOr<vfs::Status> Status((std::error_code()));
199 EXPECT_FALSE(Status = D->status("/foo"));
200
201 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
202 EXPECT_FALSE(Status = O->status("/foo"));
203
204 D->addRegularFile("/foo");
205 Status = D->status("/foo");
206 EXPECT_FALSE(Status.getError());
207
208 ErrorOr<vfs::Status> Status2((std::error_code()));
209 Status2 = O->status("/foo");
210 EXPECT_FALSE(Status2.getError());
211 EXPECT_TRUE(Status->equivalent(*Status2));
212 }
213
TEST(VirtualFileSystemTest,GetRealPathInOverlay)214 TEST(VirtualFileSystemTest, GetRealPathInOverlay) {
215 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
216 Lower->addRegularFile("/foo");
217 Lower->addSymlink("/lower_link");
218 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
219
220 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
221 new vfs::OverlayFileSystem(Lower));
222 O->pushOverlay(Upper);
223
224 // Regular file.
225 SmallString<16> RealPath;
226 EXPECT_FALSE(O->getRealPath("/foo", RealPath));
227 EXPECT_EQ(RealPath.str(), "/foo");
228
229 // Expect no error getting real path for symlink in lower overlay.
230 EXPECT_FALSE(O->getRealPath("/lower_link", RealPath));
231 EXPECT_EQ(RealPath.str(), "/symlink");
232
233 // Try a non-existing link.
234 EXPECT_EQ(O->getRealPath("/upper_link", RealPath),
235 errc::no_such_file_or_directory);
236
237 // Add a new symlink in upper.
238 Upper->addSymlink("/upper_link");
239 EXPECT_FALSE(O->getRealPath("/upper_link", RealPath));
240 EXPECT_EQ(RealPath.str(), "/symlink");
241 }
242
TEST(VirtualFileSystemTest,OverlayFiles)243 TEST(VirtualFileSystemTest, OverlayFiles) {
244 IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
245 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
246 IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
247 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
248 new vfs::OverlayFileSystem(Base));
249 O->pushOverlay(Middle);
250 O->pushOverlay(Top);
251
252 ErrorOr<vfs::Status> Status1((std::error_code())),
253 Status2((std::error_code())), Status3((std::error_code())),
254 StatusB((std::error_code())), StatusM((std::error_code())),
255 StatusT((std::error_code()));
256
257 Base->addRegularFile("/foo");
258 StatusB = Base->status("/foo");
259 ASSERT_FALSE(StatusB.getError());
260 Status1 = O->status("/foo");
261 ASSERT_FALSE(Status1.getError());
262 Middle->addRegularFile("/foo");
263 StatusM = Middle->status("/foo");
264 ASSERT_FALSE(StatusM.getError());
265 Status2 = O->status("/foo");
266 ASSERT_FALSE(Status2.getError());
267 Top->addRegularFile("/foo");
268 StatusT = Top->status("/foo");
269 ASSERT_FALSE(StatusT.getError());
270 Status3 = O->status("/foo");
271 ASSERT_FALSE(Status3.getError());
272
273 EXPECT_TRUE(Status1->equivalent(*StatusB));
274 EXPECT_TRUE(Status2->equivalent(*StatusM));
275 EXPECT_TRUE(Status3->equivalent(*StatusT));
276
277 EXPECT_FALSE(Status1->equivalent(*Status2));
278 EXPECT_FALSE(Status2->equivalent(*Status3));
279 EXPECT_FALSE(Status1->equivalent(*Status3));
280 }
281
TEST(VirtualFileSystemTest,OverlayDirsNonMerged)282 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
283 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
284 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
285 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
286 new vfs::OverlayFileSystem(Lower));
287 O->pushOverlay(Upper);
288
289 Lower->addDirectory("/lower-only");
290 Upper->addDirectory("/upper-only");
291
292 // non-merged paths should be the same
293 ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
294 ASSERT_FALSE(Status1.getError());
295 ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
296 ASSERT_FALSE(Status2.getError());
297 EXPECT_TRUE(Status1->equivalent(*Status2));
298
299 Status1 = Upper->status("/upper-only");
300 ASSERT_FALSE(Status1.getError());
301 Status2 = O->status("/upper-only");
302 ASSERT_FALSE(Status2.getError());
303 EXPECT_TRUE(Status1->equivalent(*Status2));
304 }
305
TEST(VirtualFileSystemTest,MergedDirPermissions)306 TEST(VirtualFileSystemTest, MergedDirPermissions) {
307 // merged directories get the permissions of the upper dir
308 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
309 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
310 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
311 new vfs::OverlayFileSystem(Lower));
312 O->pushOverlay(Upper);
313
314 ErrorOr<vfs::Status> Status((std::error_code()));
315 Lower->addDirectory("/both", sys::fs::owner_read);
316 Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
317 Status = O->status("/both");
318 ASSERT_FALSE(Status.getError());
319 EXPECT_EQ(0740, Status->getPermissions());
320
321 // permissions (as usual) are not recursively applied
322 Lower->addRegularFile("/both/foo", sys::fs::owner_read);
323 Upper->addRegularFile("/both/bar", sys::fs::owner_write);
324 Status = O->status("/both/foo");
325 ASSERT_FALSE( Status.getError());
326 EXPECT_EQ(0400, Status->getPermissions());
327 Status = O->status("/both/bar");
328 ASSERT_FALSE(Status.getError());
329 EXPECT_EQ(0200, Status->getPermissions());
330 }
331
332 namespace {
333 struct ScopedDir {
334 SmallString<128> Path;
ScopedDir__anon43ce3cdf0211::ScopedDir335 ScopedDir(const Twine &Name, bool Unique=false) {
336 std::error_code EC;
337 if (Unique) {
338 EC = llvm::sys::fs::createUniqueDirectory(Name, Path);
339 } else {
340 Path = Name.str();
341 EC = llvm::sys::fs::create_directory(Twine(Path));
342 }
343 if (EC)
344 Path = "";
345 EXPECT_FALSE(EC);
346 }
~ScopedDir__anon43ce3cdf0211::ScopedDir347 ~ScopedDir() {
348 if (Path != "") {
349 EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
350 }
351 }
operator StringRef__anon43ce3cdf0211::ScopedDir352 operator StringRef() { return Path.str(); }
353 };
354
355 struct ScopedLink {
356 SmallString<128> Path;
ScopedLink__anon43ce3cdf0211::ScopedLink357 ScopedLink(const Twine &To, const Twine &From) {
358 Path = From.str();
359 std::error_code EC = sys::fs::create_link(To, From);
360 if (EC)
361 Path = "";
362 EXPECT_FALSE(EC);
363 }
~ScopedLink__anon43ce3cdf0211::ScopedLink364 ~ScopedLink() {
365 if (Path != "") {
366 EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
367 }
368 }
operator StringRef__anon43ce3cdf0211::ScopedLink369 operator StringRef() { return Path.str(); }
370 };
371 } // end anonymous namespace
372
TEST(VirtualFileSystemTest,BasicRealFSIteration)373 TEST(VirtualFileSystemTest, BasicRealFSIteration) {
374 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
375 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
376
377 std::error_code EC;
378 vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC);
379 ASSERT_FALSE(EC);
380 EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
381
382 ScopedDir _a(TestDirectory+"/a");
383 ScopedDir _ab(TestDirectory+"/a/b");
384 ScopedDir _c(TestDirectory+"/c");
385 ScopedDir _cd(TestDirectory+"/c/d");
386
387 I = FS->dir_begin(Twine(TestDirectory), EC);
388 ASSERT_FALSE(EC);
389 ASSERT_NE(vfs::directory_iterator(), I);
390 // Check either a or c, since we can't rely on the iteration order.
391 EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
392 I.increment(EC);
393 ASSERT_FALSE(EC);
394 ASSERT_NE(vfs::directory_iterator(), I);
395 EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
396 I.increment(EC);
397 EXPECT_EQ(vfs::directory_iterator(), I);
398 }
399
400 #ifdef LLVM_ON_UNIX
TEST(VirtualFileSystemTest,BrokenSymlinkRealFSIteration)401 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) {
402 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
403 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
404
405 ScopedLink _a("no_such_file", TestDirectory + "/a");
406 ScopedDir _b(TestDirectory + "/b");
407 ScopedLink _c("no_such_file", TestDirectory + "/c");
408
409 std::error_code EC;
410 for (vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC), E;
411 I != E; I.increment(EC)) {
412 // Skip broken symlinks.
413 auto EC2 = std::make_error_code(std::errc::no_such_file_or_directory);
414 if (EC == EC2) {
415 EC.clear();
416 continue;
417 }
418 // For bot debugging.
419 if (EC) {
420 outs() << "Error code found:\n"
421 << "EC value: " << EC.value() << "\n"
422 << "EC category: " << EC.category().name()
423 << "EC message: " << EC.message() << "\n";
424
425 outs() << "Error code tested for:\n"
426 << "EC value: " << EC2.value() << "\n"
427 << "EC category: " << EC2.category().name()
428 << "EC message: " << EC2.message() << "\n";
429 }
430 ASSERT_FALSE(EC);
431 EXPECT_TRUE(I->getName() == _b);
432 }
433 }
434 #endif
435
TEST(VirtualFileSystemTest,BasicRealFSRecursiveIteration)436 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
437 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
438 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
439
440 std::error_code EC;
441 auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
442 ASSERT_FALSE(EC);
443 EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
444
445 ScopedDir _a(TestDirectory+"/a");
446 ScopedDir _ab(TestDirectory+"/a/b");
447 ScopedDir _c(TestDirectory+"/c");
448 ScopedDir _cd(TestDirectory+"/c/d");
449
450 I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
451 ASSERT_FALSE(EC);
452 ASSERT_NE(vfs::recursive_directory_iterator(), I);
453
454 std::vector<std::string> Contents;
455 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
456 I.increment(EC)) {
457 Contents.push_back(I->getName());
458 }
459
460 // Check contents, which may be in any order
461 EXPECT_EQ(4U, Contents.size());
462 int Counts[4] = { 0, 0, 0, 0 };
463 for (const std::string &Name : Contents) {
464 ASSERT_FALSE(Name.empty());
465 int Index = Name[Name.size()-1] - 'a';
466 ASSERT_TRUE(Index >= 0 && Index < 4);
467 Counts[Index]++;
468 }
469 EXPECT_EQ(1, Counts[0]); // a
470 EXPECT_EQ(1, Counts[1]); // b
471 EXPECT_EQ(1, Counts[2]); // c
472 EXPECT_EQ(1, Counts[3]); // d
473 }
474
475 #ifdef LLVM_ON_UNIX
TEST(VirtualFileSystemTest,BrokenSymlinkRealFSRecursiveIteration)476 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {
477 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
478 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
479
480 ScopedLink _a("no_such_file", TestDirectory + "/a");
481 ScopedDir _b(TestDirectory + "/b");
482 ScopedLink _ba("no_such_file", TestDirectory + "/b/a");
483 ScopedDir _bb(TestDirectory + "/b/b");
484 ScopedLink _bc("no_such_file", TestDirectory + "/b/c");
485 ScopedLink _c("no_such_file", TestDirectory + "/c");
486 ScopedDir _d(TestDirectory + "/d");
487 ScopedDir _dd(TestDirectory + "/d/d");
488 ScopedDir _ddd(TestDirectory + "/d/d/d");
489 ScopedLink _e("no_such_file", TestDirectory + "/e");
490
491 std::vector<StringRef> ExpectedBrokenSymlinks = {_a, _ba, _bc, _c, _e};
492 std::vector<StringRef> ExpectedNonBrokenSymlinks = {_b, _bb, _d, _dd, _ddd};
493 std::vector<std::string> VisitedBrokenSymlinks;
494 std::vector<std::string> VisitedNonBrokenSymlinks;
495 std::error_code EC;
496 for (vfs::recursive_directory_iterator I(*FS, Twine(TestDirectory), EC), E;
497 I != E; I.increment(EC)) {
498 auto EC2 = std::make_error_code(std::errc::no_such_file_or_directory);
499 if (EC == EC2) {
500 VisitedBrokenSymlinks.push_back(I->getName());
501 continue;
502 }
503 // For bot debugging.
504 if (EC) {
505 outs() << "Error code found:\n"
506 << "EC value: " << EC.value() << "\n"
507 << "EC category: " << EC.category().name()
508 << "EC message: " << EC.message() << "\n";
509
510 outs() << "Error code tested for:\n"
511 << "EC value: " << EC2.value() << "\n"
512 << "EC category: " << EC2.category().name()
513 << "EC message: " << EC2.message() << "\n";
514 }
515 ASSERT_FALSE(EC);
516 VisitedNonBrokenSymlinks.push_back(I->getName());
517 }
518
519 // Check visited file names.
520 std::sort(VisitedBrokenSymlinks.begin(), VisitedBrokenSymlinks.end());
521 std::sort(VisitedNonBrokenSymlinks.begin(), VisitedNonBrokenSymlinks.end());
522 EXPECT_EQ(ExpectedBrokenSymlinks.size(), VisitedBrokenSymlinks.size());
523 EXPECT_TRUE(std::equal(VisitedBrokenSymlinks.begin(),
524 VisitedBrokenSymlinks.end(),
525 ExpectedBrokenSymlinks.begin()));
526 EXPECT_EQ(ExpectedNonBrokenSymlinks.size(), VisitedNonBrokenSymlinks.size());
527 EXPECT_TRUE(std::equal(VisitedNonBrokenSymlinks.begin(),
528 VisitedNonBrokenSymlinks.end(),
529 ExpectedNonBrokenSymlinks.begin()));
530 }
531 #endif
532
533 template <typename DirIter>
checkContents(DirIter I,ArrayRef<StringRef> ExpectedOut)534 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
535 std::error_code EC;
536 SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());
537 SmallVector<std::string, 4> InputToCheck;
538
539 // Do not rely on iteration order to check for contents, sort both
540 // content vectors before comparison.
541 for (DirIter E; !EC && I != E; I.increment(EC))
542 InputToCheck.push_back(I->getName());
543
544 llvm::sort(InputToCheck.begin(), InputToCheck.end());
545 llvm::sort(Expected.begin(), Expected.end());
546 EXPECT_EQ(InputToCheck.size(), Expected.size());
547
548 unsigned LastElt = std::min(InputToCheck.size(), Expected.size());
549 for (unsigned Idx = 0; Idx != LastElt; ++Idx)
550 EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);
551 }
552
TEST(VirtualFileSystemTest,OverlayIteration)553 TEST(VirtualFileSystemTest, OverlayIteration) {
554 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
555 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
556 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
557 new vfs::OverlayFileSystem(Lower));
558 O->pushOverlay(Upper);
559
560 std::error_code EC;
561 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
562
563 Lower->addRegularFile("/file1");
564 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
565
566 Upper->addRegularFile("/file2");
567 checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});
568
569 Lower->addDirectory("/dir1");
570 Lower->addRegularFile("/dir1/foo");
571 Upper->addDirectory("/dir2");
572 Upper->addRegularFile("/dir2/foo");
573 checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
574 checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});
575 }
576
TEST(VirtualFileSystemTest,OverlayRecursiveIteration)577 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
578 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
579 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
580 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
581 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
582 new vfs::OverlayFileSystem(Lower));
583 O->pushOverlay(Middle);
584 O->pushOverlay(Upper);
585
586 std::error_code EC;
587 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
588 ArrayRef<StringRef>());
589
590 Lower->addRegularFile("/file1");
591 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
592 ArrayRef<StringRef>("/file1"));
593
594 Upper->addDirectory("/dir");
595 Upper->addRegularFile("/dir/file2");
596 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
597 {"/dir", "/dir/file2", "/file1"});
598
599 Lower->addDirectory("/dir1");
600 Lower->addRegularFile("/dir1/foo");
601 Lower->addDirectory("/dir1/a");
602 Lower->addRegularFile("/dir1/a/b");
603 Middle->addDirectory("/a");
604 Middle->addDirectory("/a/b");
605 Middle->addDirectory("/a/b/c");
606 Middle->addRegularFile("/a/b/c/d");
607 Middle->addRegularFile("/hiddenByUp");
608 Upper->addDirectory("/dir2");
609 Upper->addRegularFile("/dir2/foo");
610 Upper->addRegularFile("/hiddenByUp");
611 checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
612 ArrayRef<StringRef>("/dir2/foo"));
613 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
614 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
615 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
616 "/dir1/a/b", "/dir1/foo", "/file1"});
617 }
618
TEST(VirtualFileSystemTest,ThreeLevelIteration)619 TEST(VirtualFileSystemTest, ThreeLevelIteration) {
620 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
621 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
622 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
623 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
624 new vfs::OverlayFileSystem(Lower));
625 O->pushOverlay(Middle);
626 O->pushOverlay(Upper);
627
628 std::error_code EC;
629 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
630
631 Middle->addRegularFile("/file2");
632 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
633
634 Lower->addRegularFile("/file1");
635 Upper->addRegularFile("/file3");
636 checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});
637 }
638
TEST(VirtualFileSystemTest,HiddenInIteration)639 TEST(VirtualFileSystemTest, HiddenInIteration) {
640 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
641 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
642 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
643 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
644 new vfs::OverlayFileSystem(Lower));
645 O->pushOverlay(Middle);
646 O->pushOverlay(Upper);
647
648 std::error_code EC;
649 Lower->addRegularFile("/onlyInLow", sys::fs::owner_read);
650 Lower->addRegularFile("/hiddenByMid", sys::fs::owner_read);
651 Lower->addRegularFile("/hiddenByUp", sys::fs::owner_read);
652 Middle->addRegularFile("/onlyInMid", sys::fs::owner_write);
653 Middle->addRegularFile("/hiddenByMid", sys::fs::owner_write);
654 Middle->addRegularFile("/hiddenByUp", sys::fs::owner_write);
655 Upper->addRegularFile("/onlyInUp", sys::fs::owner_all);
656 Upper->addRegularFile("/hiddenByUp", sys::fs::owner_all);
657 checkContents(
658 O->dir_begin("/", EC),
659 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
660
661 // Make sure we get the top-most entry
662 {
663 std::error_code EC;
664 vfs::directory_iterator I = O->dir_begin("/", EC), E;
665 for ( ; !EC && I != E; I.increment(EC))
666 if (I->getName() == "/hiddenByUp")
667 break;
668 ASSERT_NE(E, I);
669 EXPECT_EQ(sys::fs::owner_all, I->getPermissions());
670 }
671 {
672 std::error_code EC;
673 vfs::directory_iterator I = O->dir_begin("/", EC), E;
674 for ( ; !EC && I != E; I.increment(EC))
675 if (I->getName() == "/hiddenByMid")
676 break;
677 ASSERT_NE(E, I);
678 EXPECT_EQ(sys::fs::owner_write, I->getPermissions());
679 }
680 }
681
682 class InMemoryFileSystemTest : public ::testing::Test {
683 protected:
684 clang::vfs::InMemoryFileSystem FS;
685 clang::vfs::InMemoryFileSystem NormalizedFS;
686
InMemoryFileSystemTest()687 InMemoryFileSystemTest()
688 : FS(/*UseNormalizedPaths=*/false),
689 NormalizedFS(/*UseNormalizedPaths=*/true) {}
690 };
691
TEST_F(InMemoryFileSystemTest,IsEmpty)692 TEST_F(InMemoryFileSystemTest, IsEmpty) {
693 auto Stat = FS.status("/a");
694 ASSERT_EQ(Stat.getError(),errc::no_such_file_or_directory) << FS.toString();
695 Stat = FS.status("/");
696 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
697 }
698
TEST_F(InMemoryFileSystemTest,WindowsPath)699 TEST_F(InMemoryFileSystemTest, WindowsPath) {
700 FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
701 auto Stat = FS.status("c:");
702 #if !defined(_WIN32)
703 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
704 #endif
705 Stat = FS.status("c:/windows/system128/foo.cpp");
706 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
707 FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
708 Stat = FS.status("d:/windows/foo.cpp");
709 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
710 }
711
TEST_F(InMemoryFileSystemTest,OverlayFile)712 TEST_F(InMemoryFileSystemTest, OverlayFile) {
713 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
714 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
715 auto Stat = FS.status("/");
716 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
717 Stat = FS.status("/.");
718 ASSERT_FALSE(Stat);
719 Stat = NormalizedFS.status("/.");
720 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
721 Stat = FS.status("/a");
722 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
723 ASSERT_EQ("/a", Stat->getName());
724 }
725
TEST_F(InMemoryFileSystemTest,OverlayFileNoOwn)726 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {
727 auto Buf = MemoryBuffer::getMemBuffer("a");
728 FS.addFileNoOwn("/a", 0, Buf.get());
729 auto Stat = FS.status("/a");
730 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
731 ASSERT_EQ("/a", Stat->getName());
732 }
733
TEST_F(InMemoryFileSystemTest,OpenFileForRead)734 TEST_F(InMemoryFileSystemTest, OpenFileForRead) {
735 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
736 FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
737 FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
738 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
739 NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
740 NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
741 auto File = FS.openFileForRead("/a");
742 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
743 File = FS.openFileForRead("/a"); // Open again.
744 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
745 File = NormalizedFS.openFileForRead("/././a"); // Open again.
746 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
747 File = FS.openFileForRead("/");
748 ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();
749 File = FS.openFileForRead("/b");
750 ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();
751 File = FS.openFileForRead("./c");
752 ASSERT_FALSE(File);
753 File = FS.openFileForRead("e/../d");
754 ASSERT_FALSE(File);
755 File = NormalizedFS.openFileForRead("./c");
756 ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());
757 File = NormalizedFS.openFileForRead("e/../d");
758 ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());
759 }
760
TEST_F(InMemoryFileSystemTest,DuplicatedFile)761 TEST_F(InMemoryFileSystemTest, DuplicatedFile) {
762 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
763 ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
764 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
765 ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
766 }
767
TEST_F(InMemoryFileSystemTest,DirectoryIteration)768 TEST_F(InMemoryFileSystemTest, DirectoryIteration) {
769 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
770 FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
771
772 std::error_code EC;
773 vfs::directory_iterator I = FS.dir_begin("/", EC);
774 ASSERT_FALSE(EC);
775 ASSERT_EQ("/a", I->getName());
776 I.increment(EC);
777 ASSERT_FALSE(EC);
778 ASSERT_EQ("/b", I->getName());
779 I.increment(EC);
780 ASSERT_FALSE(EC);
781 ASSERT_EQ(vfs::directory_iterator(), I);
782
783 I = FS.dir_begin("/b", EC);
784 ASSERT_FALSE(EC);
785 ASSERT_EQ("/b/c", I->getName());
786 I.increment(EC);
787 ASSERT_FALSE(EC);
788 ASSERT_EQ(vfs::directory_iterator(), I);
789 }
790
TEST_F(InMemoryFileSystemTest,WorkingDirectory)791 TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
792 FS.setCurrentWorkingDirectory("/b");
793 FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
794
795 auto Stat = FS.status("/b/c");
796 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
797 ASSERT_EQ("c", Stat->getName());
798 ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());
799
800 Stat = FS.status("c");
801 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
802
803 auto ReplaceBackslashes = [](std::string S) {
804 std::replace(S.begin(), S.end(), '\\', '/');
805 return S;
806 };
807 NormalizedFS.setCurrentWorkingDirectory("/b/c");
808 NormalizedFS.setCurrentWorkingDirectory(".");
809 ASSERT_EQ("/b/c", ReplaceBackslashes(
810 NormalizedFS.getCurrentWorkingDirectory().get()));
811 NormalizedFS.setCurrentWorkingDirectory("..");
812 ASSERT_EQ("/b", ReplaceBackslashes(
813 NormalizedFS.getCurrentWorkingDirectory().get()));
814 }
815
816 #if !defined(_WIN32)
TEST_F(InMemoryFileSystemTest,GetRealPath)817 TEST_F(InMemoryFileSystemTest, GetRealPath) {
818 SmallString<16> Path;
819 EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted);
820
821 auto GetRealPath = [this](StringRef P) {
822 SmallString<16> Output;
823 auto EC = FS.getRealPath(P, Output);
824 EXPECT_FALSE(EC);
825 return Output.str().str();
826 };
827
828 FS.setCurrentWorkingDirectory("a");
829 EXPECT_EQ(GetRealPath("b"), "a/b");
830 EXPECT_EQ(GetRealPath("../b"), "b");
831 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
832
833 FS.setCurrentWorkingDirectory("/a");
834 EXPECT_EQ(GetRealPath("b"), "/a/b");
835 EXPECT_EQ(GetRealPath("../b"), "/b");
836 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
837 }
838 #endif // _WIN32
839
TEST_F(InMemoryFileSystemTest,AddFileWithUser)840 TEST_F(InMemoryFileSystemTest, AddFileWithUser) {
841 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
842 auto Stat = FS.status("/a");
843 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
844 ASSERT_TRUE(Stat->isDirectory());
845 ASSERT_EQ(0xFEEDFACE, Stat->getUser());
846 Stat = FS.status("/a/b");
847 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
848 ASSERT_TRUE(Stat->isDirectory());
849 ASSERT_EQ(0xFEEDFACE, Stat->getUser());
850 Stat = FS.status("/a/b/c");
851 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
852 ASSERT_TRUE(Stat->isRegularFile());
853 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
854 ASSERT_EQ(0xFEEDFACE, Stat->getUser());
855 }
856
TEST_F(InMemoryFileSystemTest,AddFileWithGroup)857 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {
858 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00);
859 auto Stat = FS.status("/a");
860 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
861 ASSERT_TRUE(Stat->isDirectory());
862 ASSERT_EQ(0xDABBAD00, Stat->getGroup());
863 Stat = FS.status("/a/b");
864 ASSERT_TRUE(Stat->isDirectory());
865 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
866 ASSERT_EQ(0xDABBAD00, Stat->getGroup());
867 Stat = FS.status("/a/b/c");
868 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
869 ASSERT_TRUE(Stat->isRegularFile());
870 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
871 ASSERT_EQ(0xDABBAD00, Stat->getGroup());
872 }
873
TEST_F(InMemoryFileSystemTest,AddFileWithFileType)874 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {
875 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
876 sys::fs::file_type::socket_file);
877 auto Stat = FS.status("/a");
878 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
879 ASSERT_TRUE(Stat->isDirectory());
880 Stat = FS.status("/a/b");
881 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
882 ASSERT_TRUE(Stat->isDirectory());
883 Stat = FS.status("/a/b/c");
884 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
885 ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());
886 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
887 }
888
TEST_F(InMemoryFileSystemTest,AddFileWithPerms)889 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {
890 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
891 None, sys::fs::perms::owner_read | sys::fs::perms::owner_write);
892 auto Stat = FS.status("/a");
893 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
894 ASSERT_TRUE(Stat->isDirectory());
895 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
896 sys::fs::perms::owner_exe, Stat->getPermissions());
897 Stat = FS.status("/a/b");
898 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
899 ASSERT_TRUE(Stat->isDirectory());
900 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
901 sys::fs::perms::owner_exe, Stat->getPermissions());
902 Stat = FS.status("/a/b/c");
903 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
904 ASSERT_TRUE(Stat->isRegularFile());
905 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,
906 Stat->getPermissions());
907 }
908
TEST_F(InMemoryFileSystemTest,AddDirectoryThenAddChild)909 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) {
910 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None,
911 /*Group=*/None, sys::fs::file_type::directory_file);
912 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None,
913 /*Group=*/None, sys::fs::file_type::regular_file);
914 auto Stat = FS.status("/a");
915 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
916 ASSERT_TRUE(Stat->isDirectory());
917 Stat = FS.status("/a/b");
918 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
919 ASSERT_TRUE(Stat->isRegularFile());
920 }
921
922 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
923 // a legal *absolute* path on Windows as well as *nix.
924 class VFSFromYAMLTest : public ::testing::Test {
925 public:
926 int NumDiagnostics;
927
SetUp()928 void SetUp() override { NumDiagnostics = 0; }
929
CountingDiagHandler(const SMDiagnostic &,void * Context)930 static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
931 VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
932 ++Test->NumDiagnostics;
933 }
934
935 IntrusiveRefCntPtr<vfs::FileSystem>
getFromYAMLRawString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS)936 getFromYAMLRawString(StringRef Content,
937 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
938 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
939 return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,
940 ExternalFS);
941 }
942
getFromYAMLString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS=new DummyFileSystem ())943 IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
944 StringRef Content,
945 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
946 std::string VersionPlusContent("{\n 'version':0,\n");
947 VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
948 return getFromYAMLRawString(VersionPlusContent, ExternalFS);
949 }
950
951 // This is intended as a "XFAIL" for windows hosts.
supportsSameDirMultipleYAMLEntries()952 bool supportsSameDirMultipleYAMLEntries() {
953 Triple Host(Triple::normalize(sys::getProcessTriple()));
954 return !Host.isOSWindows();
955 }
956 };
957
TEST_F(VFSFromYAMLTest,BasicVFSFromYAML)958 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
959 IntrusiveRefCntPtr<vfs::FileSystem> FS;
960 FS = getFromYAMLString("");
961 EXPECT_EQ(nullptr, FS.get());
962 FS = getFromYAMLString("[]");
963 EXPECT_EQ(nullptr, FS.get());
964 FS = getFromYAMLString("'string'");
965 EXPECT_EQ(nullptr, FS.get());
966 EXPECT_EQ(3, NumDiagnostics);
967 }
968
TEST_F(VFSFromYAMLTest,MappedFiles)969 TEST_F(VFSFromYAMLTest, MappedFiles) {
970 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
971 Lower->addRegularFile("//root/foo/bar/a");
972 IntrusiveRefCntPtr<vfs::FileSystem> FS =
973 getFromYAMLString("{ 'roots': [\n"
974 "{\n"
975 " 'type': 'directory',\n"
976 " 'name': '//root/',\n"
977 " 'contents': [ {\n"
978 " 'type': 'file',\n"
979 " 'name': 'file1',\n"
980 " 'external-contents': '//root/foo/bar/a'\n"
981 " },\n"
982 " {\n"
983 " 'type': 'file',\n"
984 " 'name': 'file2',\n"
985 " 'external-contents': '//root/foo/b'\n"
986 " }\n"
987 " ]\n"
988 "}\n"
989 "]\n"
990 "}",
991 Lower);
992 ASSERT_TRUE(FS.get() != nullptr);
993
994 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
995 new vfs::OverlayFileSystem(Lower));
996 O->pushOverlay(FS);
997
998 // file
999 ErrorOr<vfs::Status> S = O->status("//root/file1");
1000 ASSERT_FALSE(S.getError());
1001 EXPECT_EQ("//root/foo/bar/a", S->getName());
1002 EXPECT_TRUE(S->IsVFSMapped);
1003
1004 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
1005 EXPECT_EQ("//root/foo/bar/a", SLower->getName());
1006 EXPECT_TRUE(S->equivalent(*SLower));
1007 EXPECT_FALSE(SLower->IsVFSMapped);
1008
1009 // file after opening
1010 auto OpenedF = O->openFileForRead("//root/file1");
1011 ASSERT_FALSE(OpenedF.getError());
1012 auto OpenedS = (*OpenedF)->status();
1013 ASSERT_FALSE(OpenedS.getError());
1014 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1015 EXPECT_TRUE(OpenedS->IsVFSMapped);
1016
1017 // directory
1018 S = O->status("//root/");
1019 ASSERT_FALSE(S.getError());
1020 EXPECT_TRUE(S->isDirectory());
1021 EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
1022
1023 // broken mapping
1024 EXPECT_EQ(O->status("//root/file2").getError(),
1025 llvm::errc::no_such_file_or_directory);
1026 EXPECT_EQ(0, NumDiagnostics);
1027 }
1028
TEST_F(VFSFromYAMLTest,CaseInsensitive)1029 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
1030 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1031 Lower->addRegularFile("//root/foo/bar/a");
1032 IntrusiveRefCntPtr<vfs::FileSystem> FS =
1033 getFromYAMLString("{ 'case-sensitive': 'false',\n"
1034 " 'roots': [\n"
1035 "{\n"
1036 " 'type': 'directory',\n"
1037 " 'name': '//root/',\n"
1038 " 'contents': [ {\n"
1039 " 'type': 'file',\n"
1040 " 'name': 'XX',\n"
1041 " 'external-contents': '//root/foo/bar/a'\n"
1042 " }\n"
1043 " ]\n"
1044 "}]}",
1045 Lower);
1046 ASSERT_TRUE(FS.get() != nullptr);
1047
1048 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1049 new vfs::OverlayFileSystem(Lower));
1050 O->pushOverlay(FS);
1051
1052 ErrorOr<vfs::Status> S = O->status("//root/XX");
1053 ASSERT_FALSE(S.getError());
1054
1055 ErrorOr<vfs::Status> SS = O->status("//root/xx");
1056 ASSERT_FALSE(SS.getError());
1057 EXPECT_TRUE(S->equivalent(*SS));
1058 SS = O->status("//root/xX");
1059 EXPECT_TRUE(S->equivalent(*SS));
1060 SS = O->status("//root/Xx");
1061 EXPECT_TRUE(S->equivalent(*SS));
1062 EXPECT_EQ(0, NumDiagnostics);
1063 }
1064
TEST_F(VFSFromYAMLTest,CaseSensitive)1065 TEST_F(VFSFromYAMLTest, CaseSensitive) {
1066 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1067 Lower->addRegularFile("//root/foo/bar/a");
1068 IntrusiveRefCntPtr<vfs::FileSystem> FS =
1069 getFromYAMLString("{ 'case-sensitive': 'true',\n"
1070 " 'roots': [\n"
1071 "{\n"
1072 " 'type': 'directory',\n"
1073 " 'name': '//root/',\n"
1074 " 'contents': [ {\n"
1075 " 'type': 'file',\n"
1076 " 'name': 'XX',\n"
1077 " 'external-contents': '//root/foo/bar/a'\n"
1078 " }\n"
1079 " ]\n"
1080 "}]}",
1081 Lower);
1082 ASSERT_TRUE(FS.get() != nullptr);
1083
1084 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1085 new vfs::OverlayFileSystem(Lower));
1086 O->pushOverlay(FS);
1087
1088 ErrorOr<vfs::Status> SS = O->status("//root/xx");
1089 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1090 SS = O->status("//root/xX");
1091 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1092 SS = O->status("//root/Xx");
1093 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1094 EXPECT_EQ(0, NumDiagnostics);
1095 }
1096
TEST_F(VFSFromYAMLTest,IllegalVFSFile)1097 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
1098 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1099
1100 // invalid YAML at top-level
1101 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
1102 EXPECT_EQ(nullptr, FS.get());
1103 // invalid YAML in roots
1104 FS = getFromYAMLString("{ 'roots':[}", Lower);
1105 // invalid YAML in directory
1106 FS = getFromYAMLString(
1107 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
1108 Lower);
1109 EXPECT_EQ(nullptr, FS.get());
1110
1111 // invalid configuration
1112 FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
1113 EXPECT_EQ(nullptr, FS.get());
1114 FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
1115 EXPECT_EQ(nullptr, FS.get());
1116
1117 // invalid roots
1118 FS = getFromYAMLString("{ 'roots':'' }", Lower);
1119 EXPECT_EQ(nullptr, FS.get());
1120 FS = getFromYAMLString("{ 'roots':{} }", Lower);
1121 EXPECT_EQ(nullptr, FS.get());
1122
1123 // invalid entries
1124 FS = getFromYAMLString(
1125 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
1126 EXPECT_EQ(nullptr, FS.get());
1127 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
1128 "'external-contents': 'other' }",
1129 Lower);
1130 EXPECT_EQ(nullptr, FS.get());
1131 FS = getFromYAMLString(
1132 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
1133 Lower);
1134 EXPECT_EQ(nullptr, FS.get());
1135 FS = getFromYAMLString(
1136 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
1137 Lower);
1138 EXPECT_EQ(nullptr, FS.get());
1139 FS = getFromYAMLString(
1140 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
1141 Lower);
1142 EXPECT_EQ(nullptr, FS.get());
1143 FS = getFromYAMLString(
1144 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
1145 Lower);
1146 EXPECT_EQ(nullptr, FS.get());
1147 FS = getFromYAMLString(
1148 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
1149 Lower);
1150 EXPECT_EQ(nullptr, FS.get());
1151
1152 // missing mandatory fields
1153 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
1154 EXPECT_EQ(nullptr, FS.get());
1155 FS = getFromYAMLString(
1156 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
1157 EXPECT_EQ(nullptr, FS.get());
1158 FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
1159 EXPECT_EQ(nullptr, FS.get());
1160
1161 // duplicate keys
1162 FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
1163 EXPECT_EQ(nullptr, FS.get());
1164 FS = getFromYAMLString(
1165 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
1166 Lower);
1167 EXPECT_EQ(nullptr, FS.get());
1168 FS =
1169 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
1170 "'external-contents':'blah' } ] }",
1171 Lower);
1172 EXPECT_EQ(nullptr, FS.get());
1173
1174 // missing version
1175 FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
1176 EXPECT_EQ(nullptr, FS.get());
1177
1178 // bad version number
1179 FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
1180 EXPECT_EQ(nullptr, FS.get());
1181 FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
1182 EXPECT_EQ(nullptr, FS.get());
1183 FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
1184 EXPECT_EQ(nullptr, FS.get());
1185 EXPECT_EQ(24, NumDiagnostics);
1186 }
1187
TEST_F(VFSFromYAMLTest,UseExternalName)1188 TEST_F(VFSFromYAMLTest, UseExternalName) {
1189 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1190 Lower->addRegularFile("//root/external/file");
1191
1192 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1193 "{ 'roots': [\n"
1194 " { 'type': 'file', 'name': '//root/A',\n"
1195 " 'external-contents': '//root/external/file'\n"
1196 " },\n"
1197 " { 'type': 'file', 'name': '//root/B',\n"
1198 " 'use-external-name': true,\n"
1199 " 'external-contents': '//root/external/file'\n"
1200 " },\n"
1201 " { 'type': 'file', 'name': '//root/C',\n"
1202 " 'use-external-name': false,\n"
1203 " 'external-contents': '//root/external/file'\n"
1204 " }\n"
1205 "] }", Lower);
1206 ASSERT_TRUE(nullptr != FS.get());
1207
1208 // default true
1209 EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
1210 // explicit
1211 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1212 EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1213
1214 // global configuration
1215 FS = getFromYAMLString(
1216 "{ 'use-external-names': false,\n"
1217 " 'roots': [\n"
1218 " { 'type': 'file', 'name': '//root/A',\n"
1219 " 'external-contents': '//root/external/file'\n"
1220 " },\n"
1221 " { 'type': 'file', 'name': '//root/B',\n"
1222 " 'use-external-name': true,\n"
1223 " 'external-contents': '//root/external/file'\n"
1224 " },\n"
1225 " { 'type': 'file', 'name': '//root/C',\n"
1226 " 'use-external-name': false,\n"
1227 " 'external-contents': '//root/external/file'\n"
1228 " }\n"
1229 "] }", Lower);
1230 ASSERT_TRUE(nullptr != FS.get());
1231
1232 // default
1233 EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
1234 // explicit
1235 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1236 EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1237 }
1238
TEST_F(VFSFromYAMLTest,MultiComponentPath)1239 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
1240 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1241 Lower->addRegularFile("//root/other");
1242
1243 // file in roots
1244 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1245 "{ 'roots': [\n"
1246 " { 'type': 'file', 'name': '//root/path/to/file',\n"
1247 " 'external-contents': '//root/other' }]\n"
1248 "}", Lower);
1249 ASSERT_TRUE(nullptr != FS.get());
1250 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1251 EXPECT_FALSE(FS->status("//root/path/to").getError());
1252 EXPECT_FALSE(FS->status("//root/path").getError());
1253 EXPECT_FALSE(FS->status("//root/").getError());
1254
1255 // at the start
1256 FS = getFromYAMLString(
1257 "{ 'roots': [\n"
1258 " { 'type': 'directory', 'name': '//root/path/to',\n"
1259 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
1260 " 'external-contents': '//root/other' }]}]\n"
1261 "}", Lower);
1262 ASSERT_TRUE(nullptr != FS.get());
1263 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1264 EXPECT_FALSE(FS->status("//root/path/to").getError());
1265 EXPECT_FALSE(FS->status("//root/path").getError());
1266 EXPECT_FALSE(FS->status("//root/").getError());
1267
1268 // at the end
1269 FS = getFromYAMLString(
1270 "{ 'roots': [\n"
1271 " { 'type': 'directory', 'name': '//root/',\n"
1272 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
1273 " 'external-contents': '//root/other' }]}]\n"
1274 "}", Lower);
1275 ASSERT_TRUE(nullptr != FS.get());
1276 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1277 EXPECT_FALSE(FS->status("//root/path/to").getError());
1278 EXPECT_FALSE(FS->status("//root/path").getError());
1279 EXPECT_FALSE(FS->status("//root/").getError());
1280 }
1281
TEST_F(VFSFromYAMLTest,TrailingSlashes)1282 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
1283 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1284 Lower->addRegularFile("//root/other");
1285
1286 // file in roots
1287 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1288 "{ 'roots': [\n"
1289 " { 'type': 'directory', 'name': '//root/path/to////',\n"
1290 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
1291 " 'external-contents': '//root/other' }]}]\n"
1292 "}", Lower);
1293 ASSERT_TRUE(nullptr != FS.get());
1294 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1295 EXPECT_FALSE(FS->status("//root/path/to").getError());
1296 EXPECT_FALSE(FS->status("//root/path").getError());
1297 EXPECT_FALSE(FS->status("//root/").getError());
1298 }
1299
TEST_F(VFSFromYAMLTest,DirectoryIteration)1300 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
1301 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1302 Lower->addDirectory("//root/");
1303 Lower->addDirectory("//root/foo");
1304 Lower->addDirectory("//root/foo/bar");
1305 Lower->addRegularFile("//root/foo/bar/a");
1306 Lower->addRegularFile("//root/foo/bar/b");
1307 Lower->addRegularFile("//root/file3");
1308 IntrusiveRefCntPtr<vfs::FileSystem> FS =
1309 getFromYAMLString("{ 'use-external-names': false,\n"
1310 " 'roots': [\n"
1311 "{\n"
1312 " 'type': 'directory',\n"
1313 " 'name': '//root/',\n"
1314 " 'contents': [ {\n"
1315 " 'type': 'file',\n"
1316 " 'name': 'file1',\n"
1317 " 'external-contents': '//root/foo/bar/a'\n"
1318 " },\n"
1319 " {\n"
1320 " 'type': 'file',\n"
1321 " 'name': 'file2',\n"
1322 " 'external-contents': '//root/foo/bar/b'\n"
1323 " }\n"
1324 " ]\n"
1325 "}\n"
1326 "]\n"
1327 "}",
1328 Lower);
1329 ASSERT_TRUE(FS.get() != nullptr);
1330
1331 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1332 new vfs::OverlayFileSystem(Lower));
1333 O->pushOverlay(FS);
1334
1335 std::error_code EC;
1336 checkContents(O->dir_begin("//root/", EC),
1337 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1338
1339 checkContents(O->dir_begin("//root/foo/bar", EC),
1340 {"//root/foo/bar/a", "//root/foo/bar/b"});
1341 }
1342
TEST_F(VFSFromYAMLTest,DirectoryIterationSameDirMultipleEntries)1343 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
1344 // https://llvm.org/bugs/show_bug.cgi?id=27725
1345 if (!supportsSameDirMultipleYAMLEntries())
1346 return;
1347
1348 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1349 Lower->addDirectory("//root/zab");
1350 Lower->addDirectory("//root/baz");
1351 Lower->addRegularFile("//root/zab/a");
1352 Lower->addRegularFile("//root/zab/b");
1353 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1354 "{ 'use-external-names': false,\n"
1355 " 'roots': [\n"
1356 "{\n"
1357 " 'type': 'directory',\n"
1358 " 'name': '//root/baz/',\n"
1359 " 'contents': [ {\n"
1360 " 'type': 'file',\n"
1361 " 'name': 'x',\n"
1362 " 'external-contents': '//root/zab/a'\n"
1363 " }\n"
1364 " ]\n"
1365 "},\n"
1366 "{\n"
1367 " 'type': 'directory',\n"
1368 " 'name': '//root/baz/',\n"
1369 " 'contents': [ {\n"
1370 " 'type': 'file',\n"
1371 " 'name': 'y',\n"
1372 " 'external-contents': '//root/zab/b'\n"
1373 " }\n"
1374 " ]\n"
1375 "}\n"
1376 "]\n"
1377 "}",
1378 Lower);
1379 ASSERT_TRUE(FS.get() != nullptr);
1380
1381 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1382 new vfs::OverlayFileSystem(Lower));
1383 O->pushOverlay(FS);
1384
1385 std::error_code EC;
1386
1387 checkContents(O->dir_begin("//root/baz/", EC),
1388 {"//root/baz/x", "//root/baz/y"});
1389 }
1390
TEST_F(VFSFromYAMLTest,RecursiveDirectoryIterationLevel)1391 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
1392
1393 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1394 Lower->addDirectory("//root/a");
1395 Lower->addDirectory("//root/a/b");
1396 Lower->addDirectory("//root/a/b/c");
1397 Lower->addRegularFile("//root/a/b/c/file");
1398 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1399 "{ 'use-external-names': false,\n"
1400 " 'roots': [\n"
1401 "{\n"
1402 " 'type': 'directory',\n"
1403 " 'name': '//root/a/b/c/',\n"
1404 " 'contents': [ {\n"
1405 " 'type': 'file',\n"
1406 " 'name': 'file',\n"
1407 " 'external-contents': '//root/a/b/c/file'\n"
1408 " }\n"
1409 " ]\n"
1410 "},\n"
1411 "]\n"
1412 "}",
1413 Lower);
1414 ASSERT_TRUE(FS.get() != nullptr);
1415
1416 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1417 new vfs::OverlayFileSystem(Lower));
1418 O->pushOverlay(FS);
1419
1420 std::error_code EC;
1421
1422 // Test recursive_directory_iterator level()
1423 vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
1424 *O, "//root", EC), E;
1425 ASSERT_FALSE(EC);
1426 for (int l = 0; I != E; I.increment(EC), ++l) {
1427 ASSERT_FALSE(EC);
1428 EXPECT_EQ(I.level(), l);
1429 }
1430 EXPECT_EQ(I, E);
1431 }
1432