xref: /llvm-project/llvm/unittests/Support/VirtualFileSystemTest.cpp (revision 502f14d6f2eee10d2993ed22d820f12cf52462d6)
1 //===- unittests/Support/VirtualFileSystem.cpp -------------- VFS tests ---===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/Support/VirtualFileSystem.h"
10 #include "llvm/ADT/Triple.h"
11 #include "llvm/Config/llvm-config.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 "llvm/Testing/Support/SupportHelpers.h"
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
20 #include <map>
21 #include <string>
22 
23 using namespace llvm;
24 using llvm::sys::fs::UniqueID;
25 using llvm::unittest::TempDir;
26 using llvm::unittest::TempFile;
27 using llvm::unittest::TempLink;
28 using testing::ElementsAre;
29 using testing::Pair;
30 using testing::UnorderedElementsAre;
31 
32 namespace {
33 struct DummyFile : public vfs::File {
34   vfs::Status S;
35   explicit DummyFile(vfs::Status S) : S(S) {}
36   llvm::ErrorOr<vfs::Status> status() override { return S; }
37   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
38   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
39             bool IsVolatile) override {
40     llvm_unreachable("unimplemented");
41   }
42   std::error_code close() override { return std::error_code(); }
43 };
44 
45 class DummyFileSystem : public vfs::FileSystem {
46   int FSID;   // used to produce UniqueIDs
47   int FileID; // used to produce UniqueIDs
48   std::string WorkingDirectory;
49   std::map<std::string, vfs::Status> FilesAndDirs;
50   typedef std::map<std::string, vfs::Status>::const_iterator const_iterator;
51 
52   static int getNextFSID() {
53     static int Count = 0;
54     return Count++;
55   }
56 
57 public:
58   DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
59 
60   ErrorOr<vfs::Status> status(const Twine &Path) override {
61     auto I = findEntry(Path);
62     if (I == FilesAndDirs.end())
63       return make_error_code(llvm::errc::no_such_file_or_directory);
64     return I->second;
65   }
66   ErrorOr<std::unique_ptr<vfs::File>>
67   openFileForRead(const Twine &Path) override {
68     auto S = status(Path);
69     if (S)
70       return std::unique_ptr<vfs::File>(new DummyFile{*S});
71     return S.getError();
72   }
73   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
74     return WorkingDirectory;
75   }
76   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
77     WorkingDirectory = Path.str();
78     return std::error_code();
79   }
80   // Map any symlink to "/symlink".
81   std::error_code getRealPath(const Twine &Path,
82                               SmallVectorImpl<char> &Output) const override {
83     auto I = findEntry(Path);
84     if (I == FilesAndDirs.end())
85       return make_error_code(llvm::errc::no_such_file_or_directory);
86     if (I->second.isSymlink()) {
87       Output.clear();
88       Twine("/symlink").toVector(Output);
89       return std::error_code();
90     }
91     Output.clear();
92     Path.toVector(Output);
93     return std::error_code();
94   }
95 
96   struct DirIterImpl : public llvm::vfs::detail::DirIterImpl {
97     std::map<std::string, vfs::Status> &FilesAndDirs;
98     std::map<std::string, vfs::Status>::iterator I;
99     std::string Path;
100     bool isInPath(StringRef S) {
101       if (Path.size() < S.size() && S.find(Path) == 0) {
102         auto LastSep = S.find_last_of('/');
103         if (LastSep == Path.size() || LastSep == Path.size() - 1)
104           return true;
105       }
106       return false;
107     }
108     DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
109                 const Twine &_Path)
110         : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
111           Path(_Path.str()) {
112       for (; I != FilesAndDirs.end(); ++I) {
113         if (isInPath(I->first)) {
114           CurrentEntry = vfs::directory_entry(std::string(I->second.getName()),
115                                               I->second.getType());
116           break;
117         }
118       }
119     }
120     std::error_code increment() override {
121       ++I;
122       for (; I != FilesAndDirs.end(); ++I) {
123         if (isInPath(I->first)) {
124           CurrentEntry = vfs::directory_entry(std::string(I->second.getName()),
125                                               I->second.getType());
126           break;
127         }
128       }
129       if (I == FilesAndDirs.end())
130         CurrentEntry = vfs::directory_entry();
131       return std::error_code();
132     }
133   };
134 
135   vfs::directory_iterator dir_begin(const Twine &Dir,
136                                     std::error_code &EC) override {
137     return vfs::directory_iterator(
138         std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
139   }
140 
141   void addEntry(StringRef Path, const vfs::Status &Status) {
142     FilesAndDirs[std::string(Path)] = Status;
143   }
144 
145   const_iterator findEntry(const Twine &Path) const {
146     SmallString<128> P;
147     Path.toVector(P);
148     std::error_code EC = makeAbsolute(P);
149     assert(!EC);
150     (void)EC;
151     return FilesAndDirs.find(std::string(P.str()));
152   }
153 
154   void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
155     vfs::Status S(Path, UniqueID(FSID, FileID++),
156                   std::chrono::system_clock::now(), 0, 0, 1024,
157                   sys::fs::file_type::regular_file, Perms);
158     addEntry(Path, S);
159   }
160 
161   void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
162     vfs::Status S(Path, UniqueID(FSID, FileID++),
163                   std::chrono::system_clock::now(), 0, 0, 0,
164                   sys::fs::file_type::directory_file, Perms);
165     addEntry(Path, S);
166   }
167 
168   void addSymlink(StringRef Path) {
169     vfs::Status S(Path, UniqueID(FSID, FileID++),
170                   std::chrono::system_clock::now(), 0, 0, 0,
171                   sys::fs::file_type::symlink_file, sys::fs::all_all);
172     addEntry(Path, S);
173   }
174 };
175 
176 class ErrorDummyFileSystem : public DummyFileSystem {
177   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
178     return llvm::errc::no_such_file_or_directory;
179   }
180 };
181 
182 /// Replace back-slashes by front-slashes.
183 std::string getPosixPath(std::string S) {
184   SmallString<128> Result;
185   llvm::sys::path::native(S, Result, llvm::sys::path::Style::posix);
186   return std::string(Result.str());
187 }
188 } // end anonymous namespace
189 
190 TEST(VirtualFileSystemTest, StatusQueries) {
191   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
192   ErrorOr<vfs::Status> Status((std::error_code()));
193 
194   D->addRegularFile("/foo");
195   Status = D->status("/foo");
196   ASSERT_FALSE(Status.getError());
197   EXPECT_TRUE(Status->isStatusKnown());
198   EXPECT_FALSE(Status->isDirectory());
199   EXPECT_TRUE(Status->isRegularFile());
200   EXPECT_FALSE(Status->isSymlink());
201   EXPECT_FALSE(Status->isOther());
202   EXPECT_TRUE(Status->exists());
203 
204   D->addDirectory("/bar");
205   Status = D->status("/bar");
206   ASSERT_FALSE(Status.getError());
207   EXPECT_TRUE(Status->isStatusKnown());
208   EXPECT_TRUE(Status->isDirectory());
209   EXPECT_FALSE(Status->isRegularFile());
210   EXPECT_FALSE(Status->isSymlink());
211   EXPECT_FALSE(Status->isOther());
212   EXPECT_TRUE(Status->exists());
213 
214   D->addSymlink("/baz");
215   Status = D->status("/baz");
216   ASSERT_FALSE(Status.getError());
217   EXPECT_TRUE(Status->isStatusKnown());
218   EXPECT_FALSE(Status->isDirectory());
219   EXPECT_FALSE(Status->isRegularFile());
220   EXPECT_TRUE(Status->isSymlink());
221   EXPECT_FALSE(Status->isOther());
222   EXPECT_TRUE(Status->exists());
223 
224   EXPECT_TRUE(Status->equivalent(*Status));
225   ErrorOr<vfs::Status> Status2 = D->status("/foo");
226   ASSERT_FALSE(Status2.getError());
227   EXPECT_FALSE(Status->equivalent(*Status2));
228 }
229 
230 TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
231   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
232   ErrorOr<vfs::Status> Status((std::error_code()));
233   EXPECT_FALSE(Status = D->status("/foo"));
234 
235   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
236   EXPECT_FALSE(Status = O->status("/foo"));
237 
238   D->addRegularFile("/foo");
239   Status = D->status("/foo");
240   EXPECT_FALSE(Status.getError());
241 
242   ErrorOr<vfs::Status> Status2((std::error_code()));
243   Status2 = O->status("/foo");
244   EXPECT_FALSE(Status2.getError());
245   EXPECT_TRUE(Status->equivalent(*Status2));
246 }
247 
248 TEST(VirtualFileSystemTest, GetRealPathInOverlay) {
249   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
250   Lower->addRegularFile("/foo");
251   Lower->addSymlink("/lower_link");
252   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
253 
254   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
255       new vfs::OverlayFileSystem(Lower));
256   O->pushOverlay(Upper);
257 
258   // Regular file.
259   SmallString<16> RealPath;
260   EXPECT_FALSE(O->getRealPath("/foo", RealPath));
261   EXPECT_EQ(RealPath.str(), "/foo");
262 
263   // Expect no error getting real path for symlink in lower overlay.
264   EXPECT_FALSE(O->getRealPath("/lower_link", RealPath));
265   EXPECT_EQ(RealPath.str(), "/symlink");
266 
267   // Try a non-existing link.
268   EXPECT_EQ(O->getRealPath("/upper_link", RealPath),
269             errc::no_such_file_or_directory);
270 
271   // Add a new symlink in upper.
272   Upper->addSymlink("/upper_link");
273   EXPECT_FALSE(O->getRealPath("/upper_link", RealPath));
274   EXPECT_EQ(RealPath.str(), "/symlink");
275 }
276 
277 TEST(VirtualFileSystemTest, OverlayFiles) {
278   IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
279   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
280   IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
281   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
282       new vfs::OverlayFileSystem(Base));
283   O->pushOverlay(Middle);
284   O->pushOverlay(Top);
285 
286   ErrorOr<vfs::Status> Status1((std::error_code())),
287       Status2((std::error_code())), Status3((std::error_code())),
288       StatusB((std::error_code())), StatusM((std::error_code())),
289       StatusT((std::error_code()));
290 
291   Base->addRegularFile("/foo");
292   StatusB = Base->status("/foo");
293   ASSERT_FALSE(StatusB.getError());
294   Status1 = O->status("/foo");
295   ASSERT_FALSE(Status1.getError());
296   Middle->addRegularFile("/foo");
297   StatusM = Middle->status("/foo");
298   ASSERT_FALSE(StatusM.getError());
299   Status2 = O->status("/foo");
300   ASSERT_FALSE(Status2.getError());
301   Top->addRegularFile("/foo");
302   StatusT = Top->status("/foo");
303   ASSERT_FALSE(StatusT.getError());
304   Status3 = O->status("/foo");
305   ASSERT_FALSE(Status3.getError());
306 
307   EXPECT_TRUE(Status1->equivalent(*StatusB));
308   EXPECT_TRUE(Status2->equivalent(*StatusM));
309   EXPECT_TRUE(Status3->equivalent(*StatusT));
310 
311   EXPECT_FALSE(Status1->equivalent(*Status2));
312   EXPECT_FALSE(Status2->equivalent(*Status3));
313   EXPECT_FALSE(Status1->equivalent(*Status3));
314 }
315 
316 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
317   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
318   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
319   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
320       new vfs::OverlayFileSystem(Lower));
321   O->pushOverlay(Upper);
322 
323   Lower->addDirectory("/lower-only");
324   Upper->addDirectory("/upper-only");
325 
326   // non-merged paths should be the same
327   ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
328   ASSERT_FALSE(Status1.getError());
329   ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
330   ASSERT_FALSE(Status2.getError());
331   EXPECT_TRUE(Status1->equivalent(*Status2));
332 
333   Status1 = Upper->status("/upper-only");
334   ASSERT_FALSE(Status1.getError());
335   Status2 = O->status("/upper-only");
336   ASSERT_FALSE(Status2.getError());
337   EXPECT_TRUE(Status1->equivalent(*Status2));
338 }
339 
340 TEST(VirtualFileSystemTest, MergedDirPermissions) {
341   // merged directories get the permissions of the upper dir
342   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
343   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
344   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
345       new vfs::OverlayFileSystem(Lower));
346   O->pushOverlay(Upper);
347 
348   ErrorOr<vfs::Status> Status((std::error_code()));
349   Lower->addDirectory("/both", sys::fs::owner_read);
350   Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
351   Status = O->status("/both");
352   ASSERT_FALSE(Status.getError());
353   EXPECT_EQ(0740, Status->getPermissions());
354 
355   // permissions (as usual) are not recursively applied
356   Lower->addRegularFile("/both/foo", sys::fs::owner_read);
357   Upper->addRegularFile("/both/bar", sys::fs::owner_write);
358   Status = O->status("/both/foo");
359   ASSERT_FALSE(Status.getError());
360   EXPECT_EQ(0400, Status->getPermissions());
361   Status = O->status("/both/bar");
362   ASSERT_FALSE(Status.getError());
363   EXPECT_EQ(0200, Status->getPermissions());
364 }
365 
366 TEST(VirtualFileSystemTest, OverlayIterator) {
367   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
368   Lower->addRegularFile("/foo");
369   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
370 
371   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
372       new vfs::OverlayFileSystem(Lower));
373   O->pushOverlay(Upper);
374 
375   ErrorOr<vfs::Status> Status((std::error_code()));
376   {
377     auto it = O->overlays_begin();
378     auto end = O->overlays_end();
379 
380     EXPECT_NE(it, end);
381 
382     Status = (*it)->status("/foo");
383     ASSERT_TRUE(Status.getError());
384 
385     it++;
386     EXPECT_NE(it, end);
387 
388     Status = (*it)->status("/foo");
389     ASSERT_FALSE(Status.getError());
390     EXPECT_TRUE(Status->exists());
391 
392     it++;
393     EXPECT_EQ(it, end);
394   }
395 
396   {
397     auto it = O->overlays_rbegin();
398     auto end = O->overlays_rend();
399 
400     EXPECT_NE(it, end);
401 
402     Status = (*it)->status("/foo");
403     ASSERT_FALSE(Status.getError());
404     EXPECT_TRUE(Status->exists());
405 
406     it++;
407     EXPECT_NE(it, end);
408 
409     Status = (*it)->status("/foo");
410     ASSERT_TRUE(Status.getError());
411 
412     it++;
413     EXPECT_EQ(it, end);
414   }
415 }
416 
417 TEST(VirtualFileSystemTest, BasicRealFSIteration) {
418   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
419   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
420 
421   std::error_code EC;
422   vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory.path()), EC);
423   ASSERT_FALSE(EC);
424   EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
425 
426   TempDir _a(TestDirectory.path("a"));
427   TempDir _ab(TestDirectory.path("a/b"));
428   TempDir _c(TestDirectory.path("c"));
429   TempDir _cd(TestDirectory.path("c/d"));
430 
431   I = FS->dir_begin(Twine(TestDirectory.path()), EC);
432   ASSERT_FALSE(EC);
433   ASSERT_NE(vfs::directory_iterator(), I);
434   // Check either a or c, since we can't rely on the iteration order.
435   EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));
436   I.increment(EC);
437   ASSERT_FALSE(EC);
438   ASSERT_NE(vfs::directory_iterator(), I);
439   EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));
440   I.increment(EC);
441   EXPECT_EQ(vfs::directory_iterator(), I);
442 }
443 
444 #ifdef LLVM_ON_UNIX
445 TEST(VirtualFileSystemTest, MultipleWorkingDirs) {
446   // Our root contains a/aa, b/bb, c, where c is a link to a/.
447   // Run tests both in root/b/ and root/c/ (to test "normal" and symlink dirs).
448   // Interleave operations to show the working directories are independent.
449   TempDir Root("r", /*Unique*/ true);
450   TempDir ADir(Root.path("a"));
451   TempDir BDir(Root.path("b"));
452   TempLink C(ADir.path(), Root.path("c"));
453   TempFile AA(ADir.path("aa"), "", "aaaa");
454   TempFile BB(BDir.path("bb"), "", "bbbb");
455   std::unique_ptr<vfs::FileSystem> BFS = vfs::createPhysicalFileSystem(),
456                                    CFS = vfs::createPhysicalFileSystem();
457 
458   ASSERT_FALSE(BFS->setCurrentWorkingDirectory(BDir.path()));
459   ASSERT_FALSE(CFS->setCurrentWorkingDirectory(C.path()));
460   EXPECT_EQ(BDir.path(), *BFS->getCurrentWorkingDirectory());
461   EXPECT_EQ(C.path(), *CFS->getCurrentWorkingDirectory());
462 
463   // openFileForRead(), indirectly.
464   auto BBuf = BFS->getBufferForFile("bb");
465   ASSERT_TRUE(BBuf);
466   EXPECT_EQ("bbbb", (*BBuf)->getBuffer());
467 
468   auto ABuf = CFS->getBufferForFile("aa");
469   ASSERT_TRUE(ABuf);
470   EXPECT_EQ("aaaa", (*ABuf)->getBuffer());
471 
472   // status()
473   auto BStat = BFS->status("bb");
474   ASSERT_TRUE(BStat);
475   EXPECT_EQ("bb", BStat->getName());
476 
477   auto AStat = CFS->status("aa");
478   ASSERT_TRUE(AStat);
479   EXPECT_EQ("aa", AStat->getName()); // unresolved name
480 
481   // getRealPath()
482   SmallString<128> BPath;
483   ASSERT_FALSE(BFS->getRealPath("bb", BPath));
484   EXPECT_EQ(BB.path(), BPath);
485 
486   SmallString<128> APath;
487   ASSERT_FALSE(CFS->getRealPath("aa", APath));
488   EXPECT_EQ(AA.path(), APath); // Reports resolved name.
489 
490   // dir_begin
491   std::error_code EC;
492   auto BIt = BFS->dir_begin(".", EC);
493   ASSERT_FALSE(EC);
494   ASSERT_NE(BIt, vfs::directory_iterator());
495   EXPECT_EQ((BDir.path() + "/./bb").str(), BIt->path());
496   BIt.increment(EC);
497   ASSERT_FALSE(EC);
498   ASSERT_EQ(BIt, vfs::directory_iterator());
499 
500   auto CIt = CFS->dir_begin(".", EC);
501   ASSERT_FALSE(EC);
502   ASSERT_NE(CIt, vfs::directory_iterator());
503   EXPECT_EQ((ADir.path() + "/./aa").str(),
504             CIt->path()); // Partly resolved name!
505   CIt.increment(EC); // Because likely to read through this path.
506   ASSERT_FALSE(EC);
507   ASSERT_EQ(CIt, vfs::directory_iterator());
508 }
509 
510 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) {
511   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
512   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
513 
514   TempLink _a("no_such_file", TestDirectory.path("a"));
515   TempDir _b(TestDirectory.path("b"));
516   TempLink _c("no_such_file", TestDirectory.path("c"));
517 
518   // Should get no iteration error, but a stat error for the broken symlinks.
519   std::map<std::string, std::error_code> StatResults;
520   std::error_code EC;
521   for (vfs::directory_iterator
522            I = FS->dir_begin(Twine(TestDirectory.path()), EC),
523            E;
524        I != E; I.increment(EC)) {
525     EXPECT_FALSE(EC);
526     StatResults[std::string(sys::path::filename(I->path()))] =
527         FS->status(I->path()).getError();
528   }
529   EXPECT_THAT(
530       StatResults,
531       ElementsAre(
532           Pair("a", std::make_error_code(std::errc::no_such_file_or_directory)),
533           Pair("b", std::error_code()),
534           Pair("c",
535                std::make_error_code(std::errc::no_such_file_or_directory))));
536 }
537 #endif
538 
539 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
540   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
541   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
542 
543   std::error_code EC;
544   auto I =
545       vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);
546   ASSERT_FALSE(EC);
547   EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
548 
549   TempDir _a(TestDirectory.path("a"));
550   TempDir _ab(TestDirectory.path("a/b"));
551   TempDir _c(TestDirectory.path("c"));
552   TempDir _cd(TestDirectory.path("c/d"));
553 
554   I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);
555   ASSERT_FALSE(EC);
556   ASSERT_NE(vfs::recursive_directory_iterator(), I);
557 
558   std::vector<std::string> Contents;
559   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
560        I.increment(EC)) {
561     Contents.push_back(std::string(I->path()));
562   }
563 
564   // Check contents, which may be in any order
565   EXPECT_EQ(4U, Contents.size());
566   int Counts[4] = {0, 0, 0, 0};
567   for (const std::string &Name : Contents) {
568     ASSERT_FALSE(Name.empty());
569     int Index = Name[Name.size() - 1] - 'a';
570     ASSERT_GE(Index, 0);
571     ASSERT_LT(Index, 4);
572     Counts[Index]++;
573   }
574   EXPECT_EQ(1, Counts[0]); // a
575   EXPECT_EQ(1, Counts[1]); // b
576   EXPECT_EQ(1, Counts[2]); // c
577   EXPECT_EQ(1, Counts[3]); // d
578 }
579 
580 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) {
581   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
582 
583   TempDir _a(TestDirectory.path("a"));
584   TempDir _ab(TestDirectory.path("a/b"));
585   TempDir _c(TestDirectory.path("c"));
586   TempDir _cd(TestDirectory.path("c/d"));
587   TempDir _e(TestDirectory.path("e"));
588   TempDir _ef(TestDirectory.path("e/f"));
589   TempDir _g(TestDirectory.path("g"));
590 
591   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
592 
593   // Test that calling no_push on entries without subdirectories has no effect.
594   {
595     std::error_code EC;
596     auto I =
597         vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);
598     ASSERT_FALSE(EC);
599 
600     std::vector<std::string> Contents;
601     for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
602          I.increment(EC)) {
603       Contents.push_back(std::string(I->path()));
604       char last = I->path().back();
605       switch (last) {
606       case 'b':
607       case 'd':
608       case 'f':
609       case 'g':
610         I.no_push();
611         break;
612       default:
613         break;
614       }
615     }
616     EXPECT_EQ(7U, Contents.size());
617   }
618 
619   // Test that calling no_push skips subdirectories.
620   {
621     std::error_code EC;
622     auto I =
623         vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);
624     ASSERT_FALSE(EC);
625 
626     std::vector<std::string> Contents;
627     for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
628          I.increment(EC)) {
629       Contents.push_back(std::string(I->path()));
630       char last = I->path().back();
631       switch (last) {
632       case 'a':
633       case 'c':
634       case 'e':
635         I.no_push();
636         break;
637       default:
638         break;
639       }
640     }
641 
642     // Check contents, which may be in any order
643     EXPECT_EQ(4U, Contents.size());
644     int Counts[7] = {0, 0, 0, 0, 0, 0, 0};
645     for (const std::string &Name : Contents) {
646       ASSERT_FALSE(Name.empty());
647       int Index = Name[Name.size() - 1] - 'a';
648       ASSERT_GE(Index, 0);
649       ASSERT_LT(Index, 7);
650       Counts[Index]++;
651     }
652     EXPECT_EQ(1, Counts[0]); // a
653     EXPECT_EQ(0, Counts[1]); // b
654     EXPECT_EQ(1, Counts[2]); // c
655     EXPECT_EQ(0, Counts[3]); // d
656     EXPECT_EQ(1, Counts[4]); // e
657     EXPECT_EQ(0, Counts[5]); // f
658     EXPECT_EQ(1, Counts[6]); // g
659   }
660 }
661 
662 #ifdef LLVM_ON_UNIX
663 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {
664   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
665   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
666 
667   TempLink _a("no_such_file", TestDirectory.path("a"));
668   TempDir _b(TestDirectory.path("b"));
669   TempLink _ba("no_such_file", TestDirectory.path("b/a"));
670   TempDir _bb(TestDirectory.path("b/b"));
671   TempLink _bc("no_such_file", TestDirectory.path("b/c"));
672   TempLink _c("no_such_file", TestDirectory.path("c"));
673   TempDir _d(TestDirectory.path("d"));
674   TempDir _dd(TestDirectory.path("d/d"));
675   TempDir _ddd(TestDirectory.path("d/d/d"));
676   TempLink _e("no_such_file", TestDirectory.path("e"));
677 
678   std::vector<std::string> VisitedBrokenSymlinks;
679   std::vector<std::string> VisitedNonBrokenSymlinks;
680   std::error_code EC;
681   for (vfs::recursive_directory_iterator
682            I(*FS, Twine(TestDirectory.path()), EC),
683        E;
684        I != E; I.increment(EC)) {
685     EXPECT_FALSE(EC);
686     (FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks)
687         .push_back(std::string(I->path()));
688   }
689 
690   // Check visited file names.
691   EXPECT_THAT(VisitedBrokenSymlinks,
692               UnorderedElementsAre(_a.path().str(), _ba.path().str(),
693                                    _bc.path().str(), _c.path().str(),
694                                    _e.path().str()));
695   EXPECT_THAT(VisitedNonBrokenSymlinks,
696               UnorderedElementsAre(_b.path().str(), _bb.path().str(),
697                                    _d.path().str(), _dd.path().str(),
698                                    _ddd.path().str()));
699 }
700 #endif
701 
702 template <typename DirIter>
703 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
704   std::error_code EC;
705   SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());
706   SmallVector<std::string, 4> InputToCheck;
707 
708   // Do not rely on iteration order to check for contents, sort both
709   // content vectors before comparison.
710   for (DirIter E; !EC && I != E; I.increment(EC))
711     InputToCheck.push_back(std::string(I->path()));
712 
713   llvm::sort(InputToCheck);
714   llvm::sort(Expected);
715   EXPECT_EQ(InputToCheck.size(), Expected.size());
716 
717   unsigned LastElt = std::min(InputToCheck.size(), Expected.size());
718   for (unsigned Idx = 0; Idx != LastElt; ++Idx)
719     EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);
720 }
721 
722 TEST(VirtualFileSystemTest, OverlayIteration) {
723   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
724   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
725   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
726       new vfs::OverlayFileSystem(Lower));
727   O->pushOverlay(Upper);
728 
729   std::error_code EC;
730   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
731 
732   Lower->addRegularFile("/file1");
733   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
734 
735   Upper->addRegularFile("/file2");
736   checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});
737 
738   Lower->addDirectory("/dir1");
739   Lower->addRegularFile("/dir1/foo");
740   Upper->addDirectory("/dir2");
741   Upper->addRegularFile("/dir2/foo");
742   checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
743   checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});
744 }
745 
746 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
747   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
748   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
749   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
750   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
751       new vfs::OverlayFileSystem(Lower));
752   O->pushOverlay(Middle);
753   O->pushOverlay(Upper);
754 
755   std::error_code EC;
756   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
757                 ArrayRef<StringRef>());
758 
759   Lower->addRegularFile("/file1");
760   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
761                 ArrayRef<StringRef>("/file1"));
762 
763   Upper->addDirectory("/dir");
764   Upper->addRegularFile("/dir/file2");
765   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
766                 {"/dir", "/dir/file2", "/file1"});
767 
768   Lower->addDirectory("/dir1");
769   Lower->addRegularFile("/dir1/foo");
770   Lower->addDirectory("/dir1/a");
771   Lower->addRegularFile("/dir1/a/b");
772   Middle->addDirectory("/a");
773   Middle->addDirectory("/a/b");
774   Middle->addDirectory("/a/b/c");
775   Middle->addRegularFile("/a/b/c/d");
776   Middle->addRegularFile("/hiddenByUp");
777   Upper->addDirectory("/dir2");
778   Upper->addRegularFile("/dir2/foo");
779   Upper->addRegularFile("/hiddenByUp");
780   checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
781                 ArrayRef<StringRef>("/dir2/foo"));
782   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
783                 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
784                  "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
785                  "/dir1/a/b", "/dir1/foo", "/file1"});
786 }
787 
788 TEST(VirtualFileSystemTest, ThreeLevelIteration) {
789   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
790   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
791   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
792   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
793       new vfs::OverlayFileSystem(Lower));
794   O->pushOverlay(Middle);
795   O->pushOverlay(Upper);
796 
797   std::error_code EC;
798   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
799 
800   Middle->addRegularFile("/file2");
801   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
802 
803   Lower->addRegularFile("/file1");
804   Upper->addRegularFile("/file3");
805   checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});
806 }
807 
808 TEST(VirtualFileSystemTest, HiddenInIteration) {
809   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
810   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
811   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
812   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
813       new vfs::OverlayFileSystem(Lower));
814   O->pushOverlay(Middle);
815   O->pushOverlay(Upper);
816 
817   std::error_code EC;
818   Lower->addRegularFile("/onlyInLow");
819   Lower->addDirectory("/hiddenByMid");
820   Lower->addDirectory("/hiddenByUp");
821   Middle->addRegularFile("/onlyInMid");
822   Middle->addRegularFile("/hiddenByMid");
823   Middle->addDirectory("/hiddenByUp");
824   Upper->addRegularFile("/onlyInUp");
825   Upper->addRegularFile("/hiddenByUp");
826   checkContents(
827       O->dir_begin("/", EC),
828       {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
829 
830   // Make sure we get the top-most entry
831   {
832     std::error_code EC;
833     vfs::directory_iterator I = O->dir_begin("/", EC), E;
834     for (; !EC && I != E; I.increment(EC))
835       if (I->path() == "/hiddenByUp")
836         break;
837     ASSERT_NE(E, I);
838     EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
839   }
840   {
841     std::error_code EC;
842     vfs::directory_iterator I = O->dir_begin("/", EC), E;
843     for (; !EC && I != E; I.increment(EC))
844       if (I->path() == "/hiddenByMid")
845         break;
846     ASSERT_NE(E, I);
847     EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
848   }
849 }
850 
851 TEST(ProxyFileSystemTest, Basic) {
852   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> Base(
853       new vfs::InMemoryFileSystem());
854   vfs::ProxyFileSystem PFS(Base);
855 
856   Base->addFile("/a", 0, MemoryBuffer::getMemBuffer("test"));
857 
858   auto Stat = PFS.status("/a");
859   ASSERT_FALSE(Stat.getError());
860 
861   auto File = PFS.openFileForRead("/a");
862   ASSERT_FALSE(File.getError());
863   EXPECT_EQ("test", (*(*File)->getBuffer("ignored"))->getBuffer());
864 
865   std::error_code EC;
866   vfs::directory_iterator I = PFS.dir_begin("/", EC);
867   ASSERT_FALSE(EC);
868   ASSERT_EQ("/a", I->path());
869   I.increment(EC);
870   ASSERT_FALSE(EC);
871   ASSERT_EQ(vfs::directory_iterator(), I);
872 
873   ASSERT_FALSE(PFS.setCurrentWorkingDirectory("/"));
874 
875   auto PWD = PFS.getCurrentWorkingDirectory();
876   ASSERT_FALSE(PWD.getError());
877   ASSERT_EQ("/", *PWD);
878 
879   SmallString<16> Path;
880   ASSERT_FALSE(PFS.getRealPath("a", Path));
881   ASSERT_EQ("/a", Path);
882 
883   bool Local = true;
884   ASSERT_FALSE(PFS.isLocal("/a", Local));
885   EXPECT_FALSE(Local);
886 }
887 
888 class InMemoryFileSystemTest : public ::testing::Test {
889 protected:
890   llvm::vfs::InMemoryFileSystem FS;
891   llvm::vfs::InMemoryFileSystem NormalizedFS;
892 
893   InMemoryFileSystemTest()
894       : FS(/*UseNormalizedPaths=*/false),
895         NormalizedFS(/*UseNormalizedPaths=*/true) {}
896 };
897 
898 MATCHER_P2(IsHardLinkTo, FS, Target, "") {
899   StringRef From = arg;
900   StringRef To = Target;
901   auto OpenedFrom = FS->openFileForRead(From);
902   auto OpenedTo = FS->openFileForRead(To);
903   return !OpenedFrom.getError() && !OpenedTo.getError() &&
904          (*OpenedFrom)->status()->getUniqueID() ==
905              (*OpenedTo)->status()->getUniqueID();
906 }
907 
908 TEST_F(InMemoryFileSystemTest, IsEmpty) {
909   auto Stat = FS.status("/a");
910   ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
911   Stat = FS.status("/");
912   ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
913 }
914 
915 TEST_F(InMemoryFileSystemTest, WindowsPath) {
916   FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
917   auto Stat = FS.status("c:");
918 #if !defined(_WIN32)
919   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
920 #endif
921   Stat = FS.status("c:/windows/system128/foo.cpp");
922   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
923   FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
924   Stat = FS.status("d:/windows/foo.cpp");
925   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
926 }
927 
928 TEST_F(InMemoryFileSystemTest, OverlayFile) {
929   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
930   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
931   auto Stat = FS.status("/");
932   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
933   Stat = FS.status("/.");
934   ASSERT_FALSE(Stat);
935   Stat = NormalizedFS.status("/.");
936   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
937   Stat = FS.status("/a");
938   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
939   ASSERT_EQ("/a", Stat->getName());
940 }
941 
942 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {
943   auto Buf = MemoryBuffer::getMemBuffer("a");
944   FS.addFileNoOwn("/a", 0, *Buf);
945   auto Stat = FS.status("/a");
946   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
947   ASSERT_EQ("/a", Stat->getName());
948 }
949 
950 TEST_F(InMemoryFileSystemTest, OpenFileForRead) {
951   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
952   FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
953   FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
954   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
955   NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
956   NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
957   auto File = FS.openFileForRead("/a");
958   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
959   File = FS.openFileForRead("/a"); // Open again.
960   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
961   File = NormalizedFS.openFileForRead("/././a"); // Open again.
962   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
963   File = FS.openFileForRead("/");
964   ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();
965   File = FS.openFileForRead("/b");
966   ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();
967   File = FS.openFileForRead("./c");
968   ASSERT_FALSE(File);
969   File = FS.openFileForRead("e/../d");
970   ASSERT_FALSE(File);
971   File = NormalizedFS.openFileForRead("./c");
972   ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());
973   File = NormalizedFS.openFileForRead("e/../d");
974   ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());
975 }
976 
977 TEST_F(InMemoryFileSystemTest, DuplicatedFile) {
978   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
979   ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
980   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
981   ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
982 }
983 
984 TEST_F(InMemoryFileSystemTest, DirectoryIteration) {
985   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
986   FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
987 
988   std::error_code EC;
989   vfs::directory_iterator I = FS.dir_begin("/", EC);
990   ASSERT_FALSE(EC);
991   ASSERT_EQ("/a", I->path());
992   I.increment(EC);
993   ASSERT_FALSE(EC);
994   ASSERT_EQ("/b", I->path());
995   I.increment(EC);
996   ASSERT_FALSE(EC);
997   ASSERT_EQ(vfs::directory_iterator(), I);
998 
999   I = FS.dir_begin("/b", EC);
1000   ASSERT_FALSE(EC);
1001   // When on Windows, we end up with "/b\\c" as the name.  Convert to Posix
1002   // path for the sake of the comparison.
1003   ASSERT_EQ("/b/c", getPosixPath(std::string(I->path())));
1004   I.increment(EC);
1005   ASSERT_FALSE(EC);
1006   ASSERT_EQ(vfs::directory_iterator(), I);
1007 }
1008 
1009 TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
1010   FS.setCurrentWorkingDirectory("/b");
1011   FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1012 
1013   auto Stat = FS.status("/b/c");
1014   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1015   ASSERT_EQ("/b/c", Stat->getName());
1016   ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());
1017 
1018   Stat = FS.status("c");
1019   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1020 
1021   NormalizedFS.setCurrentWorkingDirectory("/b/c");
1022   NormalizedFS.setCurrentWorkingDirectory(".");
1023   ASSERT_EQ("/b/c",
1024             getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
1025   NormalizedFS.setCurrentWorkingDirectory("..");
1026   ASSERT_EQ("/b",
1027             getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
1028 }
1029 
1030 TEST_F(InMemoryFileSystemTest, IsLocal) {
1031   FS.setCurrentWorkingDirectory("/b");
1032   FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1033 
1034   std::error_code EC;
1035   bool IsLocal = true;
1036   EC = FS.isLocal("c", IsLocal);
1037   ASSERT_FALSE(EC);
1038   ASSERT_FALSE(IsLocal);
1039 }
1040 
1041 #if !defined(_WIN32)
1042 TEST_F(InMemoryFileSystemTest, GetRealPath) {
1043   SmallString<16> Path;
1044   EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted);
1045 
1046   auto GetRealPath = [this](StringRef P) {
1047     SmallString<16> Output;
1048     auto EC = FS.getRealPath(P, Output);
1049     EXPECT_FALSE(EC);
1050     return std::string(Output);
1051   };
1052 
1053   FS.setCurrentWorkingDirectory("a");
1054   EXPECT_EQ(GetRealPath("b"), "a/b");
1055   EXPECT_EQ(GetRealPath("../b"), "b");
1056   EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
1057 
1058   FS.setCurrentWorkingDirectory("/a");
1059   EXPECT_EQ(GetRealPath("b"), "/a/b");
1060   EXPECT_EQ(GetRealPath("../b"), "/b");
1061   EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
1062 }
1063 #endif // _WIN32
1064 
1065 TEST_F(InMemoryFileSystemTest, AddFileWithUser) {
1066   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
1067   auto Stat = FS.status("/a");
1068   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1069   ASSERT_TRUE(Stat->isDirectory());
1070   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
1071   Stat = FS.status("/a/b");
1072   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1073   ASSERT_TRUE(Stat->isDirectory());
1074   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
1075   Stat = FS.status("/a/b/c");
1076   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1077   ASSERT_TRUE(Stat->isRegularFile());
1078   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
1079   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
1080 }
1081 
1082 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {
1083   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00);
1084   auto Stat = FS.status("/a");
1085   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1086   ASSERT_TRUE(Stat->isDirectory());
1087   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
1088   Stat = FS.status("/a/b");
1089   ASSERT_TRUE(Stat->isDirectory());
1090   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1091   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
1092   Stat = FS.status("/a/b/c");
1093   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1094   ASSERT_TRUE(Stat->isRegularFile());
1095   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
1096   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
1097 }
1098 
1099 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {
1100   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
1101              sys::fs::file_type::socket_file);
1102   auto Stat = FS.status("/a");
1103   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1104   ASSERT_TRUE(Stat->isDirectory());
1105   Stat = FS.status("/a/b");
1106   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1107   ASSERT_TRUE(Stat->isDirectory());
1108   Stat = FS.status("/a/b/c");
1109   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1110   ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());
1111   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
1112 }
1113 
1114 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {
1115   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, None,
1116              sys::fs::perms::owner_read | sys::fs::perms::owner_write);
1117   auto Stat = FS.status("/a");
1118   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1119   ASSERT_TRUE(Stat->isDirectory());
1120   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
1121                 sys::fs::perms::owner_exe,
1122             Stat->getPermissions());
1123   Stat = FS.status("/a/b");
1124   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1125   ASSERT_TRUE(Stat->isDirectory());
1126   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
1127                 sys::fs::perms::owner_exe,
1128             Stat->getPermissions());
1129   Stat = FS.status("/a/b/c");
1130   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1131   ASSERT_TRUE(Stat->isRegularFile());
1132   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,
1133             Stat->getPermissions());
1134 }
1135 
1136 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) {
1137   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None,
1138              /*Group=*/None, sys::fs::file_type::directory_file);
1139   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None,
1140              /*Group=*/None, sys::fs::file_type::regular_file);
1141   auto Stat = FS.status("/a");
1142   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1143   ASSERT_TRUE(Stat->isDirectory());
1144   Stat = FS.status("/a/b");
1145   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1146   ASSERT_TRUE(Stat->isRegularFile());
1147 }
1148 
1149 // Test that the name returned by status() is in the same form as the path that
1150 // was requested (to match the behavior of RealFileSystem).
1151 TEST_F(InMemoryFileSystemTest, StatusName) {
1152   NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
1153                        /*User=*/None,
1154                        /*Group=*/None, sys::fs::file_type::regular_file);
1155   NormalizedFS.setCurrentWorkingDirectory("/a/b");
1156 
1157   // Access using InMemoryFileSystem::status.
1158   auto Stat = NormalizedFS.status("../b/c");
1159   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
1160                                 << NormalizedFS.toString();
1161   ASSERT_TRUE(Stat->isRegularFile());
1162   ASSERT_EQ("../b/c", Stat->getName());
1163 
1164   // Access using InMemoryFileAdaptor::status.
1165   auto File = NormalizedFS.openFileForRead("../b/c");
1166   ASSERT_FALSE(File.getError()) << File.getError() << "\n"
1167                                 << NormalizedFS.toString();
1168   Stat = (*File)->status();
1169   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
1170                                 << NormalizedFS.toString();
1171   ASSERT_TRUE(Stat->isRegularFile());
1172   ASSERT_EQ("../b/c", Stat->getName());
1173 
1174   // Access using a directory iterator.
1175   std::error_code EC;
1176   llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC);
1177   // When on Windows, we end up with "../b\\c" as the name.  Convert to Posix
1178   // path for the sake of the comparison.
1179   ASSERT_EQ("../b/c", getPosixPath(std::string(It->path())));
1180 }
1181 
1182 TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) {
1183   StringRef FromLink = "/path/to/FROM/link";
1184   StringRef Target = "/path/to/TO/file";
1185   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1186   EXPECT_TRUE(FS.addHardLink(FromLink, Target));
1187   EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target));
1188   EXPECT_EQ(FS.status(FromLink)->getSize(), FS.status(Target)->getSize());
1189   EXPECT_EQ(FS.getBufferForFile(FromLink)->get()->getBuffer(),
1190             FS.getBufferForFile(Target)->get()->getBuffer());
1191 }
1192 
1193 TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) {
1194   StringRef Link0 = "/path/to/0/link";
1195   StringRef Link1 = "/path/to/1/link";
1196   StringRef Link2 = "/path/to/2/link";
1197   StringRef Target = "/path/to/target";
1198   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file"));
1199   EXPECT_TRUE(FS.addHardLink(Link2, Target));
1200   EXPECT_TRUE(FS.addHardLink(Link1, Link2));
1201   EXPECT_TRUE(FS.addHardLink(Link0, Link1));
1202   EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target));
1203   EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target));
1204   EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target));
1205 }
1206 
1207 TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) {
1208   EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target"));
1209 }
1210 
1211 TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) {
1212   StringRef Link = "/path/to/link";
1213   StringRef Target = "/path/to/target";
1214   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1215   FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link"));
1216   EXPECT_FALSE(FS.addHardLink(Link, Target));
1217 }
1218 
1219 TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) {
1220   StringRef Link = "/path/to/link";
1221   StringRef Target = "/path/to/target";
1222   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1223   EXPECT_TRUE(FS.addHardLink(Link, Target));
1224   EXPECT_FALSE(FS.addHardLink(Link, Target));
1225 }
1226 
1227 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) {
1228   StringRef Link = "/path/to/link";
1229   StringRef Target = "/path/to/target";
1230   StringRef Content = "content of target";
1231   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1232   EXPECT_TRUE(FS.addHardLink(Link, Target));
1233   EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content)));
1234 }
1235 
1236 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) {
1237   StringRef Link = "/path/to/link";
1238   StringRef Target = "/path/to/target";
1239   StringRef Content = "content of target";
1240   StringRef LinkContent = "different content of link";
1241   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1242   EXPECT_TRUE(FS.addHardLink(Link, Target));
1243   EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent)));
1244 }
1245 
1246 TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) {
1247   StringRef Dir = "path/to/dummy/dir";
1248   StringRef Link = "/path/to/link";
1249   StringRef File = "path/to/dummy/dir/target";
1250   StringRef Content = "content of target";
1251   EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content)));
1252   EXPECT_FALSE(FS.addHardLink(Link, Dir));
1253 }
1254 
1255 TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) {
1256   StringRef Dir = "path/to/dummy/dir";
1257   StringRef Target = "path/to/dummy/dir/target";
1258   StringRef Content = "content of target";
1259   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1260   EXPECT_FALSE(FS.addHardLink(Dir, Target));
1261 }
1262 
1263 TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) {
1264   StringRef CommonContent = "content string";
1265   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent));
1266   FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent));
1267   EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b"));
1268 }
1269 
1270 TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) {
1271   std::error_code EC;
1272   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1273   EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b"));
1274   auto I = vfs::recursive_directory_iterator(FS, "/", EC);
1275   ASSERT_FALSE(EC);
1276   std::vector<std::string> Nodes;
1277   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
1278        I.increment(EC)) {
1279     Nodes.push_back(getPosixPath(std::string(I->path())));
1280   }
1281   EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1282 }
1283 
1284 TEST_F(InMemoryFileSystemTest, UniqueID) {
1285   ASSERT_TRUE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));
1286   ASSERT_TRUE(FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer("text")));
1287   ASSERT_TRUE(FS.addHardLink("/e/f", "/a/b"));
1288 
1289   EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/a/b")->getUniqueID());
1290   EXPECT_NE(FS.status("/a/b")->getUniqueID(), FS.status("/c/d")->getUniqueID());
1291   EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/e/f")->getUniqueID());
1292   EXPECT_EQ(FS.status("/a")->getUniqueID(), FS.status("/a")->getUniqueID());
1293   EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/c")->getUniqueID());
1294   EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/e")->getUniqueID());
1295 
1296   // Recreating the "same" FS yields the same UniqueIDs.
1297   vfs::InMemoryFileSystem FS2;
1298   ASSERT_TRUE(FS2.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));
1299   EXPECT_EQ(FS.status("/a/b")->getUniqueID(),
1300             FS2.status("/a/b")->getUniqueID());
1301   EXPECT_EQ(FS.status("/a")->getUniqueID(), FS2.status("/a")->getUniqueID());
1302 }
1303 
1304 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1305 // a legal *absolute* path on Windows as well as *nix.
1306 class VFSFromYAMLTest : public ::testing::Test {
1307 public:
1308   int NumDiagnostics;
1309 
1310   void SetUp() override { NumDiagnostics = 0; }
1311 
1312   static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
1313     VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
1314     ++Test->NumDiagnostics;
1315   }
1316 
1317   std::unique_ptr<vfs::FileSystem>
1318   getFromYAMLRawString(StringRef Content,
1319                        IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
1320     std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
1321     return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,
1322                           ExternalFS);
1323   }
1324 
1325   std::unique_ptr<vfs::FileSystem> getFromYAMLString(
1326       StringRef Content,
1327       IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
1328     std::string VersionPlusContent("{\n  'version':0,\n");
1329     VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
1330     return getFromYAMLRawString(VersionPlusContent, ExternalFS);
1331   }
1332 
1333   // This is intended as a "XFAIL" for windows hosts.
1334   bool supportsSameDirMultipleYAMLEntries() {
1335     Triple Host(Triple::normalize(sys::getProcessTriple()));
1336     return !Host.isOSWindows();
1337   }
1338 };
1339 
1340 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
1341   IntrusiveRefCntPtr<vfs::FileSystem> FS;
1342   FS = getFromYAMLString("");
1343   EXPECT_EQ(nullptr, FS.get());
1344   FS = getFromYAMLString("[]");
1345   EXPECT_EQ(nullptr, FS.get());
1346   FS = getFromYAMLString("'string'");
1347   EXPECT_EQ(nullptr, FS.get());
1348   EXPECT_EQ(3, NumDiagnostics);
1349 }
1350 
1351 TEST_F(VFSFromYAMLTest, MappedFiles) {
1352   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1353   Lower->addDirectory("//root/foo/bar");
1354   Lower->addRegularFile("//root/foo/bar/a");
1355   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1356       "{ 'roots': [\n"
1357       "{\n"
1358       "  'type': 'directory',\n"
1359       "  'name': '//root/',\n"
1360       "  'contents': [ {\n"
1361       "                  'type': 'file',\n"
1362       "                  'name': 'file1',\n"
1363       "                  'external-contents': '//root/foo/bar/a'\n"
1364       "                },\n"
1365       "                {\n"
1366       "                  'type': 'file',\n"
1367       "                  'name': 'file2',\n"
1368       "                  'external-contents': '//root/foo/b'\n"
1369       "                },\n"
1370       "                {\n"
1371       "                  'type': 'directory-remap',\n"
1372       "                  'name': 'mappeddir',\n"
1373       "                  'external-contents': '//root/foo/bar'\n"
1374       "                },\n"
1375       "                {\n"
1376       "                  'type': 'directory-remap',\n"
1377       "                  'name': 'mappeddir2',\n"
1378       "                  'use-external-name': false,\n"
1379       "                  'external-contents': '//root/foo/bar'\n"
1380       "                }\n"
1381       "              ]\n"
1382       "}\n"
1383       "]\n"
1384       "}",
1385       Lower);
1386   ASSERT_NE(FS.get(), nullptr);
1387 
1388   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1389       new vfs::OverlayFileSystem(Lower));
1390   O->pushOverlay(FS);
1391 
1392   // file
1393   ErrorOr<vfs::Status> S = O->status("//root/file1");
1394   ASSERT_FALSE(S.getError());
1395   EXPECT_EQ("//root/foo/bar/a", S->getName());
1396   EXPECT_TRUE(S->IsVFSMapped);
1397 
1398   ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
1399   EXPECT_EQ("//root/foo/bar/a", SLower->getName());
1400   EXPECT_TRUE(S->equivalent(*SLower));
1401   EXPECT_FALSE(SLower->IsVFSMapped);
1402 
1403   // file after opening
1404   auto OpenedF = O->openFileForRead("//root/file1");
1405   ASSERT_FALSE(OpenedF.getError());
1406   auto OpenedS = (*OpenedF)->status();
1407   ASSERT_FALSE(OpenedS.getError());
1408   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1409   EXPECT_TRUE(OpenedS->IsVFSMapped);
1410 
1411   // directory
1412   S = O->status("//root/");
1413   ASSERT_FALSE(S.getError());
1414   EXPECT_TRUE(S->isDirectory());
1415   EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
1416 
1417   // remapped directory
1418   S = O->status("//root/mappeddir");
1419   ASSERT_FALSE(S.getError());
1420   EXPECT_TRUE(S->isDirectory());
1421   EXPECT_TRUE(S->IsVFSMapped);
1422   EXPECT_TRUE(S->equivalent(*O->status("//root/foo/bar")));
1423 
1424   SLower = O->status("//root/foo/bar");
1425   EXPECT_EQ("//root/foo/bar", SLower->getName());
1426   EXPECT_TRUE(S->equivalent(*SLower));
1427   EXPECT_FALSE(SLower->IsVFSMapped);
1428 
1429   // file in remapped directory
1430   S = O->status("//root/mappeddir/a");
1431   ASSERT_FALSE(S.getError());
1432   ASSERT_FALSE(S->isDirectory());
1433   ASSERT_TRUE(S->IsVFSMapped);
1434   ASSERT_EQ("//root/foo/bar/a", S->getName());
1435 
1436   // file in remapped directory, with use-external-name=false
1437   S = O->status("//root/mappeddir2/a");
1438   ASSERT_FALSE(S.getError());
1439   ASSERT_FALSE(S->isDirectory());
1440   ASSERT_TRUE(S->IsVFSMapped);
1441   ASSERT_EQ("//root/mappeddir2/a", S->getName());
1442 
1443   // file contents in remapped directory
1444   OpenedF = O->openFileForRead("//root/mappeddir/a");
1445   ASSERT_FALSE(OpenedF.getError());
1446   OpenedS = (*OpenedF)->status();
1447   ASSERT_FALSE(OpenedS.getError());
1448   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1449   EXPECT_TRUE(OpenedS->IsVFSMapped);
1450 
1451   // file contents in remapped directory, with use-external-name=false
1452   OpenedF = O->openFileForRead("//root/mappeddir2/a");
1453   ASSERT_FALSE(OpenedF.getError());
1454   OpenedS = (*OpenedF)->status();
1455   ASSERT_FALSE(OpenedS.getError());
1456   EXPECT_EQ("//root/mappeddir2/a", OpenedS->getName());
1457   EXPECT_TRUE(OpenedS->IsVFSMapped);
1458 
1459   // broken mapping
1460   EXPECT_EQ(O->status("//root/file2").getError(),
1461             llvm::errc::no_such_file_or_directory);
1462   EXPECT_EQ(0, NumDiagnostics);
1463 }
1464 
1465 TEST_F(VFSFromYAMLTest, MappedRoot) {
1466   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1467   Lower->addDirectory("//root/foo/bar");
1468   Lower->addRegularFile("//root/foo/bar/a");
1469   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1470       getFromYAMLString("{ 'roots': [\n"
1471                         "{\n"
1472                         "  'type': 'directory-remap',\n"
1473                         "  'name': '//mappedroot/',\n"
1474                         "  'external-contents': '//root/foo/bar'\n"
1475                         "}\n"
1476                         "]\n"
1477                         "}",
1478                         Lower);
1479   ASSERT_NE(FS.get(), nullptr);
1480 
1481   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1482       new vfs::OverlayFileSystem(Lower));
1483   O->pushOverlay(FS);
1484 
1485   // file
1486   ErrorOr<vfs::Status> S = O->status("//mappedroot/a");
1487   ASSERT_FALSE(S.getError());
1488   EXPECT_EQ("//root/foo/bar/a", S->getName());
1489   EXPECT_TRUE(S->IsVFSMapped);
1490 
1491   ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
1492   EXPECT_EQ("//root/foo/bar/a", SLower->getName());
1493   EXPECT_TRUE(S->equivalent(*SLower));
1494   EXPECT_FALSE(SLower->IsVFSMapped);
1495 
1496   // file after opening
1497   auto OpenedF = O->openFileForRead("//mappedroot/a");
1498   ASSERT_FALSE(OpenedF.getError());
1499   auto OpenedS = (*OpenedF)->status();
1500   ASSERT_FALSE(OpenedS.getError());
1501   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1502   EXPECT_TRUE(OpenedS->IsVFSMapped);
1503 
1504   EXPECT_EQ(0, NumDiagnostics);
1505 }
1506 
1507 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlay) {
1508   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1509   Lower->addDirectory("//root/foo");
1510   Lower->addRegularFile("//root/foo/a");
1511   Lower->addDirectory("//root/bar");
1512   Lower->addRegularFile("//root/bar/b");
1513   Lower->addRegularFile("//root/bar/c");
1514   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1515       getFromYAMLString("{ 'roots': [\n"
1516                         "{\n"
1517                         "  'type': 'directory',\n"
1518                         "  'name': '//root/',\n"
1519                         "  'contents': [ {\n"
1520                         "                  'type': 'directory-remap',\n"
1521                         "                  'name': 'bar',\n"
1522                         "                  'external-contents': '//root/foo'\n"
1523                         "                }\n"
1524                         "              ]\n"
1525                         "}]}",
1526                         Lower);
1527   ASSERT_NE(FS.get(), nullptr);
1528 
1529   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1530       new vfs::OverlayFileSystem(Lower));
1531   O->pushOverlay(FS);
1532 
1533   ErrorOr<vfs::Status> S = O->status("//root/foo");
1534   ASSERT_FALSE(S.getError());
1535 
1536   ErrorOr<vfs::Status> SS = O->status("//root/bar");
1537   ASSERT_FALSE(SS.getError());
1538   EXPECT_TRUE(S->equivalent(*SS));
1539 
1540   std::error_code EC;
1541   checkContents(O->dir_begin("//root/bar", EC),
1542                 {"//root/foo/a", "//root/bar/b", "//root/bar/c"});
1543 
1544   Lower->addRegularFile("//root/foo/b");
1545   checkContents(O->dir_begin("//root/bar", EC),
1546                 {"//root/foo/a", "//root/foo/b", "//root/bar/c"});
1547 
1548   EXPECT_EQ(0, NumDiagnostics);
1549 }
1550 
1551 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoExternalNames) {
1552   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1553   Lower->addDirectory("//root/foo");
1554   Lower->addRegularFile("//root/foo/a");
1555   Lower->addDirectory("//root/bar");
1556   Lower->addRegularFile("//root/bar/b");
1557   Lower->addRegularFile("//root/bar/c");
1558   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1559       getFromYAMLString("{ 'use-external-names': false,\n"
1560                         "  'roots': [\n"
1561                         "{\n"
1562                         "  'type': 'directory',\n"
1563                         "  'name': '//root/',\n"
1564                         "  'contents': [ {\n"
1565                         "                  'type': 'directory-remap',\n"
1566                         "                  'name': 'bar',\n"
1567                         "                  'external-contents': '//root/foo'\n"
1568                         "                }\n"
1569                         "              ]\n"
1570                         "}]}",
1571                         Lower);
1572   ASSERT_NE(FS.get(), nullptr);
1573 
1574   ErrorOr<vfs::Status> S = FS->status("//root/foo");
1575   ASSERT_FALSE(S.getError());
1576 
1577   ErrorOr<vfs::Status> SS = FS->status("//root/bar");
1578   ASSERT_FALSE(SS.getError());
1579   EXPECT_TRUE(S->equivalent(*SS));
1580 
1581   std::error_code EC;
1582   checkContents(FS->dir_begin("//root/bar", EC),
1583                 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});
1584 
1585   Lower->addRegularFile("//root/foo/b");
1586   checkContents(FS->dir_begin("//root/bar", EC),
1587                 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});
1588 
1589   EXPECT_EQ(0, NumDiagnostics);
1590 }
1591 
1592 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoFallthrough) {
1593   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1594   Lower->addDirectory("//root/foo");
1595   Lower->addRegularFile("//root/foo/a");
1596   Lower->addDirectory("//root/bar");
1597   Lower->addRegularFile("//root/bar/b");
1598   Lower->addRegularFile("//root/bar/c");
1599   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1600       getFromYAMLString("{ 'fallthrough': false,\n"
1601                         "  'roots': [\n"
1602                         "{\n"
1603                         "  'type': 'directory',\n"
1604                         "  'name': '//root/',\n"
1605                         "  'contents': [ {\n"
1606                         "                  'type': 'directory-remap',\n"
1607                         "                  'name': 'bar',\n"
1608                         "                  'external-contents': '//root/foo'\n"
1609                         "                }\n"
1610                         "              ]\n"
1611                         "}]}",
1612                         Lower);
1613   ASSERT_NE(FS.get(), nullptr);
1614 
1615   ErrorOr<vfs::Status> S = Lower->status("//root/foo");
1616   ASSERT_FALSE(S.getError());
1617 
1618   ErrorOr<vfs::Status> SS = FS->status("//root/bar");
1619   ASSERT_FALSE(SS.getError());
1620   EXPECT_TRUE(S->equivalent(*SS));
1621 
1622   std::error_code EC;
1623   checkContents(FS->dir_begin("//root/bar", EC), {"//root/foo/a"});
1624 
1625   Lower->addRegularFile("//root/foo/b");
1626   checkContents(FS->dir_begin("//root/bar", EC),
1627                 {"//root/foo/a", "//root/foo/b"});
1628 
1629   EXPECT_EQ(0, NumDiagnostics);
1630 }
1631 
1632 TEST_F(VFSFromYAMLTest, ReturnsRequestedPathVFSMiss) {
1633   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(
1634       new vfs::InMemoryFileSystem);
1635   BaseFS->addFile("//root/foo/a", 0,
1636                   MemoryBuffer::getMemBuffer("contents of a"));
1637   ASSERT_FALSE(BaseFS->setCurrentWorkingDirectory("//root/foo"));
1638   auto RemappedFS = vfs::RedirectingFileSystem::create(
1639       {}, /*UseExternalNames=*/false, *BaseFS);
1640 
1641   auto OpenedF = RemappedFS->openFileForRead("a");
1642   ASSERT_FALSE(OpenedF.getError());
1643   llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();
1644   ASSERT_FALSE(Name.getError());
1645   EXPECT_EQ("a", Name.get());
1646 
1647   auto OpenedS = (*OpenedF)->status();
1648   ASSERT_FALSE(OpenedS.getError());
1649   EXPECT_EQ("a", OpenedS->getName());
1650   EXPECT_FALSE(OpenedS->IsVFSMapped);
1651 
1652   auto DirectS = RemappedFS->status("a");
1653   ASSERT_FALSE(DirectS.getError());
1654   EXPECT_EQ("a", DirectS->getName());
1655   EXPECT_FALSE(DirectS->IsVFSMapped);
1656 
1657   EXPECT_EQ(0, NumDiagnostics);
1658 }
1659 
1660 TEST_F(VFSFromYAMLTest, ReturnsExternalPathVFSHit) {
1661   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(
1662       new vfs::InMemoryFileSystem);
1663   BaseFS->addFile("//root/foo/realname", 0,
1664                   MemoryBuffer::getMemBuffer("contents of a"));
1665   auto FS =
1666       getFromYAMLString("{ 'use-external-names': true,\n"
1667                         "  'roots': [\n"
1668                         "{\n"
1669                         "  'type': 'directory',\n"
1670                         "  'name': '//root/foo',\n"
1671                         "  'contents': [ {\n"
1672                         "                  'type': 'file',\n"
1673                         "                  'name': 'vfsname',\n"
1674                         "                  'external-contents': 'realname'\n"
1675                         "                }\n"
1676                         "              ]\n"
1677                         "}]}",
1678                         BaseFS);
1679   ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo"));
1680 
1681   auto OpenedF = FS->openFileForRead("vfsname");
1682   ASSERT_FALSE(OpenedF.getError());
1683   llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();
1684   ASSERT_FALSE(Name.getError());
1685   EXPECT_EQ("realname", Name.get());
1686 
1687   auto OpenedS = (*OpenedF)->status();
1688   ASSERT_FALSE(OpenedS.getError());
1689   EXPECT_EQ("realname", OpenedS->getName());
1690   EXPECT_TRUE(OpenedS->IsVFSMapped);
1691 
1692   auto DirectS = FS->status("vfsname");
1693   ASSERT_FALSE(DirectS.getError());
1694   EXPECT_EQ("realname", DirectS->getName());
1695   EXPECT_TRUE(DirectS->IsVFSMapped);
1696 
1697   EXPECT_EQ(0, NumDiagnostics);
1698 }
1699 
1700 TEST_F(VFSFromYAMLTest, ReturnsInternalPathVFSHit) {
1701   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(
1702       new vfs::InMemoryFileSystem);
1703   BaseFS->addFile("//root/foo/realname", 0,
1704                   MemoryBuffer::getMemBuffer("contents of a"));
1705   auto FS =
1706       getFromYAMLString("{ 'use-external-names': false,\n"
1707                         "  'roots': [\n"
1708                         "{\n"
1709                         "  'type': 'directory',\n"
1710                         "  'name': '//root/foo',\n"
1711                         "  'contents': [ {\n"
1712                         "                  'type': 'file',\n"
1713                         "                  'name': 'vfsname',\n"
1714                         "                  'external-contents': 'realname'\n"
1715                         "                }\n"
1716                         "              ]\n"
1717                         "}]}",
1718                         BaseFS);
1719   ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo"));
1720 
1721   auto OpenedF = FS->openFileForRead("vfsname");
1722   ASSERT_FALSE(OpenedF.getError());
1723   llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();
1724   ASSERT_FALSE(Name.getError());
1725   EXPECT_EQ("vfsname", Name.get());
1726 
1727   auto OpenedS = (*OpenedF)->status();
1728   ASSERT_FALSE(OpenedS.getError());
1729   EXPECT_EQ("vfsname", OpenedS->getName());
1730   EXPECT_TRUE(OpenedS->IsVFSMapped);
1731 
1732   auto DirectS = FS->status("vfsname");
1733   ASSERT_FALSE(DirectS.getError());
1734   EXPECT_EQ("vfsname", DirectS->getName());
1735   EXPECT_TRUE(DirectS->IsVFSMapped);
1736 
1737   EXPECT_EQ(0, NumDiagnostics);
1738 }
1739 
1740 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
1741   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1742   Lower->addRegularFile("//root/foo/bar/a");
1743   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1744       "{ 'case-sensitive': 'false',\n"
1745       "  'roots': [\n"
1746       "{\n"
1747       "  'type': 'directory',\n"
1748       "  'name': '//root/',\n"
1749       "  'contents': [ {\n"
1750       "                  'type': 'file',\n"
1751       "                  'name': 'XX',\n"
1752       "                  'external-contents': '//root/foo/bar/a'\n"
1753       "                }\n"
1754       "              ]\n"
1755       "}]}",
1756       Lower);
1757   ASSERT_NE(FS.get(), nullptr);
1758 
1759   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1760       new vfs::OverlayFileSystem(Lower));
1761   O->pushOverlay(FS);
1762 
1763   ErrorOr<vfs::Status> S = O->status("//root/XX");
1764   ASSERT_FALSE(S.getError());
1765 
1766   ErrorOr<vfs::Status> SS = O->status("//root/xx");
1767   ASSERT_FALSE(SS.getError());
1768   EXPECT_TRUE(S->equivalent(*SS));
1769   SS = O->status("//root/xX");
1770   EXPECT_TRUE(S->equivalent(*SS));
1771   SS = O->status("//root/Xx");
1772   EXPECT_TRUE(S->equivalent(*SS));
1773   EXPECT_EQ(0, NumDiagnostics);
1774 }
1775 
1776 TEST_F(VFSFromYAMLTest, CaseSensitive) {
1777   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1778   Lower->addRegularFile("//root/foo/bar/a");
1779   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1780       "{ 'case-sensitive': 'true',\n"
1781       "  'roots': [\n"
1782       "{\n"
1783       "  'type': 'directory',\n"
1784       "  'name': '//root/',\n"
1785       "  'contents': [ {\n"
1786       "                  'type': 'file',\n"
1787       "                  'name': 'XX',\n"
1788       "                  'external-contents': '//root/foo/bar/a'\n"
1789       "                }\n"
1790       "              ]\n"
1791       "}]}",
1792       Lower);
1793   ASSERT_NE(FS.get(), nullptr);
1794 
1795   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1796       new vfs::OverlayFileSystem(Lower));
1797   O->pushOverlay(FS);
1798 
1799   ErrorOr<vfs::Status> SS = O->status("//root/xx");
1800   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1801   SS = O->status("//root/xX");
1802   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1803   SS = O->status("//root/Xx");
1804   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1805   EXPECT_EQ(0, NumDiagnostics);
1806 }
1807 
1808 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
1809   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1810 
1811   // invalid YAML at top-level
1812   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
1813   EXPECT_EQ(nullptr, FS.get());
1814   // invalid YAML in roots
1815   FS = getFromYAMLString("{ 'roots':[}", Lower);
1816   // invalid YAML in directory
1817   FS = getFromYAMLString(
1818       "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
1819       Lower);
1820   EXPECT_EQ(nullptr, FS.get());
1821 
1822   // invalid configuration
1823   FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
1824   EXPECT_EQ(nullptr, FS.get());
1825   FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
1826   EXPECT_EQ(nullptr, FS.get());
1827 
1828   // invalid roots
1829   FS = getFromYAMLString("{ 'roots':'' }", Lower);
1830   EXPECT_EQ(nullptr, FS.get());
1831   FS = getFromYAMLString("{ 'roots':{} }", Lower);
1832   EXPECT_EQ(nullptr, FS.get());
1833 
1834   // invalid entries
1835   FS = getFromYAMLString(
1836       "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
1837   EXPECT_EQ(nullptr, FS.get());
1838   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
1839                          "'external-contents': 'other' }",
1840                          Lower);
1841   EXPECT_EQ(nullptr, FS.get());
1842   FS = getFromYAMLString(
1843       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
1844       Lower);
1845   EXPECT_EQ(nullptr, FS.get());
1846   FS = getFromYAMLString(
1847       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
1848       Lower);
1849   EXPECT_EQ(nullptr, FS.get());
1850   FS = getFromYAMLString(
1851       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
1852       Lower);
1853   EXPECT_EQ(nullptr, FS.get());
1854   FS = getFromYAMLString(
1855       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
1856       Lower);
1857   EXPECT_EQ(nullptr, FS.get());
1858   FS = getFromYAMLString(
1859       "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
1860       Lower);
1861   EXPECT_EQ(nullptr, FS.get());
1862 
1863   // missing mandatory fields
1864   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
1865   EXPECT_EQ(nullptr, FS.get());
1866   FS = getFromYAMLString(
1867       "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
1868   EXPECT_EQ(nullptr, FS.get());
1869   FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
1870   EXPECT_EQ(nullptr, FS.get());
1871 
1872   // duplicate keys
1873   FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
1874   EXPECT_EQ(nullptr, FS.get());
1875   FS = getFromYAMLString(
1876       "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
1877       Lower);
1878   EXPECT_EQ(nullptr, FS.get());
1879   FS =
1880       getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
1881                         "'external-contents':'blah' } ] }",
1882                         Lower);
1883   EXPECT_EQ(nullptr, FS.get());
1884 
1885   // missing version
1886   FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
1887   EXPECT_EQ(nullptr, FS.get());
1888 
1889   // bad version number
1890   FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
1891   EXPECT_EQ(nullptr, FS.get());
1892   FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
1893   EXPECT_EQ(nullptr, FS.get());
1894   FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
1895   EXPECT_EQ(nullptr, FS.get());
1896 
1897   // both 'external-contents' and 'contents' specified
1898   Lower->addDirectory("//root/external/dir");
1899   FS = getFromYAMLString(
1900       "{ 'roots':[ \n"
1901       "{ 'type': 'directory', 'name': '//root/A', 'contents': [],\n"
1902       "  'external-contents': '//root/external/dir'}]}",
1903       Lower);
1904   EXPECT_EQ(nullptr, FS.get());
1905 
1906   // 'directory-remap' with 'contents'
1907   FS = getFromYAMLString(
1908       "{ 'roots':[ \n"
1909       "{ 'type': 'directory-remap', 'name': '//root/A', 'contents': [] }]}",
1910       Lower);
1911   EXPECT_EQ(nullptr, FS.get());
1912 
1913   // invalid redirect kind
1914   FS = getFromYAMLString("{ 'redirecting-with': 'none', 'roots': [{\n"
1915                          "  'type': 'directory-remap',\n"
1916                          "  'name': '//root/A',\n"
1917                          "  'external-contents': '//root/B' }]}",
1918                          Lower);
1919   EXPECT_EQ(nullptr, FS.get());
1920 
1921   // redirect and fallthrough passed
1922   FS = getFromYAMLString("{ 'redirecting-with': 'fallthrough',\n"
1923                          "  'fallthrough': true,\n"
1924                          "  'roots': [{\n"
1925                          "    'type': 'directory-remap',\n"
1926                          "    'name': '//root/A',\n"
1927                          "    'external-contents': '//root/B' }]}",
1928                          Lower);
1929   EXPECT_EQ(nullptr, FS.get());
1930 
1931   EXPECT_EQ(28, NumDiagnostics);
1932 }
1933 
1934 TEST_F(VFSFromYAMLTest, UseExternalName) {
1935   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1936   Lower->addRegularFile("//root/external/file");
1937 
1938   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1939       getFromYAMLString("{ 'roots': [\n"
1940                         "  { 'type': 'file', 'name': '//root/A',\n"
1941                         "    'external-contents': '//root/external/file'\n"
1942                         "  },\n"
1943                         "  { 'type': 'file', 'name': '//root/B',\n"
1944                         "    'use-external-name': true,\n"
1945                         "    'external-contents': '//root/external/file'\n"
1946                         "  },\n"
1947                         "  { 'type': 'file', 'name': '//root/C',\n"
1948                         "    'use-external-name': false,\n"
1949                         "    'external-contents': '//root/external/file'\n"
1950                         "  }\n"
1951                         "] }",
1952                         Lower);
1953   ASSERT_NE(nullptr, FS.get());
1954 
1955   // default true
1956   EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
1957   // explicit
1958   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1959   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1960 
1961   // global configuration
1962   FS = getFromYAMLString("{ 'use-external-names': false,\n"
1963                          "  'roots': [\n"
1964                          "  { 'type': 'file', 'name': '//root/A',\n"
1965                          "    'external-contents': '//root/external/file'\n"
1966                          "  },\n"
1967                          "  { 'type': 'file', 'name': '//root/B',\n"
1968                          "    'use-external-name': true,\n"
1969                          "    'external-contents': '//root/external/file'\n"
1970                          "  },\n"
1971                          "  { 'type': 'file', 'name': '//root/C',\n"
1972                          "    'use-external-name': false,\n"
1973                          "    'external-contents': '//root/external/file'\n"
1974                          "  }\n"
1975                          "] }",
1976                          Lower);
1977   ASSERT_NE(nullptr, FS.get());
1978 
1979   // default
1980   EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
1981   // explicit
1982   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1983   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1984 }
1985 
1986 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
1987   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1988   Lower->addRegularFile("//root/other");
1989 
1990   // file in roots
1991   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1992       getFromYAMLString("{ 'roots': [\n"
1993                         "  { 'type': 'file', 'name': '//root/path/to/file',\n"
1994                         "    'external-contents': '//root/other' }]\n"
1995                         "}",
1996                         Lower);
1997   ASSERT_NE(nullptr, FS.get());
1998   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1999   EXPECT_FALSE(FS->status("//root/path/to").getError());
2000   EXPECT_FALSE(FS->status("//root/path").getError());
2001   EXPECT_FALSE(FS->status("//root/").getError());
2002 
2003   // at the start
2004   FS = getFromYAMLString(
2005       "{ 'roots': [\n"
2006       "  { 'type': 'directory', 'name': '//root/path/to',\n"
2007       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
2008       "                    'external-contents': '//root/other' }]}]\n"
2009       "}",
2010       Lower);
2011   ASSERT_NE(nullptr, FS.get());
2012   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
2013   EXPECT_FALSE(FS->status("//root/path/to").getError());
2014   EXPECT_FALSE(FS->status("//root/path").getError());
2015   EXPECT_FALSE(FS->status("//root/").getError());
2016 
2017   // at the end
2018   FS = getFromYAMLString(
2019       "{ 'roots': [\n"
2020       "  { 'type': 'directory', 'name': '//root/',\n"
2021       "    'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
2022       "                    'external-contents': '//root/other' }]}]\n"
2023       "}",
2024       Lower);
2025   ASSERT_NE(nullptr, FS.get());
2026   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
2027   EXPECT_FALSE(FS->status("//root/path/to").getError());
2028   EXPECT_FALSE(FS->status("//root/path").getError());
2029   EXPECT_FALSE(FS->status("//root/").getError());
2030 }
2031 
2032 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
2033   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2034   Lower->addRegularFile("//root/other");
2035 
2036   // file in roots
2037   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2038       "{ 'roots': [\n"
2039       "  { 'type': 'directory', 'name': '//root/path/to////',\n"
2040       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
2041       "                    'external-contents': '//root/other' }]}]\n"
2042       "}",
2043       Lower);
2044   ASSERT_NE(nullptr, FS.get());
2045   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
2046   EXPECT_FALSE(FS->status("//root/path/to").getError());
2047   EXPECT_FALSE(FS->status("//root/path").getError());
2048   EXPECT_FALSE(FS->status("//root/").getError());
2049 }
2050 
2051 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
2052   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2053   Lower->addDirectory("//root/");
2054   Lower->addDirectory("//root/foo");
2055   Lower->addDirectory("//root/foo/bar");
2056   Lower->addRegularFile("//root/foo/bar/a");
2057   Lower->addRegularFile("//root/foo/bar/b");
2058   Lower->addRegularFile("//root/file3");
2059   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2060       "{ 'use-external-names': false,\n"
2061       "  'roots': [\n"
2062       "{\n"
2063       "  'type': 'directory',\n"
2064       "  'name': '//root/',\n"
2065       "  'contents': [ {\n"
2066       "                  'type': 'file',\n"
2067       "                  'name': 'file1',\n"
2068       "                  'external-contents': '//root/foo/bar/a'\n"
2069       "                },\n"
2070       "                {\n"
2071       "                  'type': 'file',\n"
2072       "                  'name': 'file2',\n"
2073       "                  'external-contents': '//root/foo/bar/b'\n"
2074       "                }\n"
2075       "              ]\n"
2076       "}\n"
2077       "]\n"
2078       "}",
2079       Lower);
2080   ASSERT_NE(FS.get(), nullptr);
2081 
2082   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
2083       new vfs::OverlayFileSystem(Lower));
2084   O->pushOverlay(FS);
2085 
2086   std::error_code EC;
2087   checkContents(O->dir_begin("//root/", EC),
2088                 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
2089 
2090   checkContents(O->dir_begin("//root/foo/bar", EC),
2091                 {"//root/foo/bar/a", "//root/foo/bar/b"});
2092 }
2093 
2094 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
2095   // https://llvm.org/bugs/show_bug.cgi?id=27725
2096   if (!supportsSameDirMultipleYAMLEntries())
2097     return;
2098 
2099   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2100   Lower->addDirectory("//root/zab");
2101   Lower->addDirectory("//root/baz");
2102   Lower->addRegularFile("//root/zab/a");
2103   Lower->addRegularFile("//root/zab/b");
2104   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2105       "{ 'use-external-names': false,\n"
2106       "  'roots': [\n"
2107       "{\n"
2108       "  'type': 'directory',\n"
2109       "  'name': '//root/baz/',\n"
2110       "  'contents': [ {\n"
2111       "                  'type': 'file',\n"
2112       "                  'name': 'x',\n"
2113       "                  'external-contents': '//root/zab/a'\n"
2114       "                }\n"
2115       "              ]\n"
2116       "},\n"
2117       "{\n"
2118       "  'type': 'directory',\n"
2119       "  'name': '//root/baz/',\n"
2120       "  'contents': [ {\n"
2121       "                  'type': 'file',\n"
2122       "                  'name': 'y',\n"
2123       "                  'external-contents': '//root/zab/b'\n"
2124       "                }\n"
2125       "              ]\n"
2126       "}\n"
2127       "]\n"
2128       "}",
2129       Lower);
2130   ASSERT_NE(FS.get(), nullptr);
2131 
2132   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
2133       new vfs::OverlayFileSystem(Lower));
2134   O->pushOverlay(FS);
2135 
2136   std::error_code EC;
2137 
2138   checkContents(O->dir_begin("//root/baz/", EC),
2139                 {"//root/baz/x", "//root/baz/y"});
2140 }
2141 
2142 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
2143 
2144   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2145   Lower->addDirectory("//root/a");
2146   Lower->addDirectory("//root/a/b");
2147   Lower->addDirectory("//root/a/b/c");
2148   Lower->addRegularFile("//root/a/b/c/file");
2149   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2150       "{ 'use-external-names': false,\n"
2151       "  'roots': [\n"
2152       "{\n"
2153       "  'type': 'directory',\n"
2154       "  'name': '//root/a/b/c/',\n"
2155       "  'contents': [ {\n"
2156       "                  'type': 'file',\n"
2157       "                  'name': 'file',\n"
2158       "                  'external-contents': '//root/a/b/c/file'\n"
2159       "                }\n"
2160       "              ]\n"
2161       "},\n"
2162       "]\n"
2163       "}",
2164       Lower);
2165   ASSERT_NE(FS.get(), nullptr);
2166 
2167   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
2168       new vfs::OverlayFileSystem(Lower));
2169   O->pushOverlay(FS);
2170 
2171   std::error_code EC;
2172 
2173   // Test recursive_directory_iterator level()
2174   vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
2175                                         *O, "//root", EC),
2176                                     E;
2177   ASSERT_FALSE(EC);
2178   for (int l = 0; I != E; I.increment(EC), ++l) {
2179     ASSERT_FALSE(EC);
2180     EXPECT_EQ(I.level(), l);
2181   }
2182   EXPECT_EQ(I, E);
2183 }
2184 
2185 TEST_F(VFSFromYAMLTest, RelativePaths) {
2186   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2187   std::error_code EC;
2188   SmallString<128> CWD;
2189   EC = llvm::sys::fs::current_path(CWD);
2190   ASSERT_FALSE(EC);
2191 
2192   // Filename at root level without a parent directory.
2193   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2194       "{ 'roots': [\n"
2195       "  { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
2196       "    'external-contents': '//root/external/file'\n"
2197       "  }\n"
2198       "] }",
2199       Lower);
2200   ASSERT_TRUE(FS.get() != nullptr);
2201   SmallString<128> ExpectedPathNotInDir("file-not-in-directory.h");
2202   llvm::sys::fs::make_absolute(ExpectedPathNotInDir);
2203   checkContents(FS->dir_begin(CWD, EC), {ExpectedPathNotInDir});
2204 
2205   // Relative file path.
2206   FS = getFromYAMLString("{ 'roots': [\n"
2207                          "  { 'type': 'file', 'name': 'relative/path.h',\n"
2208                          "    'external-contents': '//root/external/file'\n"
2209                          "  }\n"
2210                          "] }",
2211                          Lower);
2212   ASSERT_TRUE(FS.get() != nullptr);
2213   SmallString<128> Parent("relative");
2214   llvm::sys::fs::make_absolute(Parent);
2215   auto I = FS->dir_begin(Parent, EC);
2216   ASSERT_FALSE(EC);
2217   // Convert to POSIX path for comparison of windows paths
2218   ASSERT_EQ("relative/path.h",
2219             getPosixPath(std::string(I->path().substr(CWD.size() + 1))));
2220 
2221   // Relative directory path.
2222   FS = getFromYAMLString(
2223       "{ 'roots': [\n"
2224       "  { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
2225       "    'contents': []\n"
2226       "  }\n"
2227       "] }",
2228       Lower);
2229   ASSERT_TRUE(FS.get() != nullptr);
2230   SmallString<128> Root("relative/directory");
2231   llvm::sys::fs::make_absolute(Root);
2232   I = FS->dir_begin(Root, EC);
2233   ASSERT_FALSE(EC);
2234   ASSERT_EQ("path.h", std::string(I->path().substr(Root.size() + 1)));
2235 
2236   EXPECT_EQ(0, NumDiagnostics);
2237 }
2238 
2239 TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) {
2240   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2241   Lower->addDirectory("//root/");
2242   Lower->addRegularFile("//root/a");
2243   Lower->addRegularFile("//root/b");
2244   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2245       "{ 'use-external-names': false,\n"
2246       "  'fallthrough': false,\n"
2247       "  'roots': [\n"
2248       "{\n"
2249       "  'type': 'directory',\n"
2250       "  'name': '//root/',\n"
2251       "  'contents': [ {\n"
2252       "                  'type': 'file',\n"
2253       "                  'name': 'c',\n"
2254       "                  'external-contents': '//root/a'\n"
2255       "                }\n"
2256       "              ]\n"
2257       "}\n"
2258       "]\n"
2259       "}",
2260       Lower);
2261   ASSERT_NE(FS.get(), nullptr);
2262 
2263   std::error_code EC;
2264   checkContents(FS->dir_begin("//root/", EC),
2265                 {"//root/c"});
2266 }
2267 
2268 TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) {
2269   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2270   Lower->addDirectory("//root/");
2271   Lower->addRegularFile("//root/a");
2272   Lower->addRegularFile("//root/b");
2273   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2274       "{ 'use-external-names': false,\n"
2275       "  'roots': [\n"
2276       "{\n"
2277       "  'type': 'directory',\n"
2278       "  'name': '//root/',\n"
2279       "  'contents': [ {\n"
2280       "                  'type': 'file',\n"
2281       "                  'name': 'a',\n"
2282       "                  'external-contents': '//root/a'\n"
2283       "                }\n"
2284       "              ]\n"
2285       "}\n"
2286       "]\n"
2287       "}",
2288 	  Lower);
2289   ASSERT_NE(FS.get(), nullptr);
2290 
2291   std::error_code EC;
2292   checkContents(FS->dir_begin("//root/", EC),
2293                 {"//root/a", "//root/b"});
2294 }
2295 
2296 TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) {
2297   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2298   Lower->addDirectory("//root/");
2299   Lower->addDirectory("//root/foo");
2300   Lower->addRegularFile("//root/foo/a");
2301   Lower->addRegularFile("//root/foo/b");
2302   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2303       "{ 'use-external-names': false,\n"
2304       "  'roots': [\n"
2305       "{\n"
2306       "  'type': 'directory',\n"
2307       "  'name': '//root/',\n"
2308       "  'contents': [ {\n"
2309       "                  'type': 'file',\n"
2310       "                  'name': 'bar/a',\n"
2311       "                  'external-contents': '//root/foo/a'\n"
2312       "                }\n"
2313       "              ]\n"
2314       "}\n"
2315       "]\n"
2316       "}",
2317       Lower);
2318   ASSERT_NE(FS.get(), nullptr);
2319 
2320   std::error_code EC;
2321   checkContents(FS->dir_begin("//root/foo", EC),
2322                 {"//root/foo/a", "//root/foo/b"});
2323 }
2324 
2325 TEST_F(VFSFromYAMLTest, GetRealPath) {
2326   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2327   Lower->addDirectory("//dir/");
2328   Lower->addRegularFile("/foo");
2329   Lower->addSymlink("/link");
2330   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2331       "{ 'use-external-names': false,\n"
2332       "  'roots': [\n"
2333       "{\n"
2334       "  'type': 'directory',\n"
2335       "  'name': '//root/',\n"
2336       "  'contents': [ {\n"
2337       "                  'type': 'file',\n"
2338       "                  'name': 'bar',\n"
2339       "                  'external-contents': '/link'\n"
2340       "                }\n"
2341       "              ]\n"
2342       "},\n"
2343       "{\n"
2344       "  'type': 'directory',\n"
2345       "  'name': '//dir/',\n"
2346       "  'contents': []\n"
2347       "}\n"
2348       "]\n"
2349       "}",
2350       Lower);
2351   ASSERT_NE(FS.get(), nullptr);
2352 
2353   // Regular file present in underlying file system.
2354   SmallString<16> RealPath;
2355   EXPECT_FALSE(FS->getRealPath("/foo", RealPath));
2356   EXPECT_EQ(RealPath.str(), "/foo");
2357 
2358   // File present in YAML pointing to symlink in underlying file system.
2359   EXPECT_FALSE(FS->getRealPath("//root/bar", RealPath));
2360   EXPECT_EQ(RealPath.str(), "/symlink");
2361 
2362   // Directories should fall back to the underlying file system is possible.
2363   EXPECT_FALSE(FS->getRealPath("//dir/", RealPath));
2364   EXPECT_EQ(RealPath.str(), "//dir/");
2365 
2366   // Try a non-existing file.
2367   EXPECT_EQ(FS->getRealPath("/non_existing", RealPath),
2368             errc::no_such_file_or_directory);
2369 }
2370 
2371 TEST_F(VFSFromYAMLTest, WorkingDirectory) {
2372   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2373   Lower->addDirectory("//root/");
2374   Lower->addDirectory("//root/foo");
2375   Lower->addRegularFile("//root/foo/a");
2376   Lower->addRegularFile("//root/foo/b");
2377   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2378       "{ 'use-external-names': false,\n"
2379       "  'roots': [\n"
2380       "{\n"
2381       "  'type': 'directory',\n"
2382       "  'name': '//root/bar',\n"
2383       "  'contents': [ {\n"
2384       "                  'type': 'file',\n"
2385       "                  'name': 'a',\n"
2386       "                  'external-contents': '//root/foo/a'\n"
2387       "                }\n"
2388       "              ]\n"
2389       "}\n"
2390       "]\n"
2391       "}",
2392       Lower);
2393   ASSERT_NE(FS.get(), nullptr);
2394   std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar");
2395   ASSERT_FALSE(EC);
2396 
2397   llvm::ErrorOr<std::string> WorkingDir = FS->getCurrentWorkingDirectory();
2398   ASSERT_TRUE(WorkingDir);
2399   EXPECT_EQ(*WorkingDir, "//root/bar");
2400 
2401   llvm::ErrorOr<vfs::Status> Status = FS->status("./a");
2402   ASSERT_FALSE(Status.getError());
2403   EXPECT_TRUE(Status->isStatusKnown());
2404   EXPECT_FALSE(Status->isDirectory());
2405   EXPECT_TRUE(Status->isRegularFile());
2406   EXPECT_FALSE(Status->isSymlink());
2407   EXPECT_FALSE(Status->isOther());
2408   EXPECT_TRUE(Status->exists());
2409 
2410   EC = FS->setCurrentWorkingDirectory("bogus");
2411   ASSERT_TRUE(EC);
2412   WorkingDir = FS->getCurrentWorkingDirectory();
2413   ASSERT_TRUE(WorkingDir);
2414   EXPECT_EQ(*WorkingDir, "//root/bar");
2415 
2416   EC = FS->setCurrentWorkingDirectory("//root/");
2417   ASSERT_FALSE(EC);
2418   WorkingDir = FS->getCurrentWorkingDirectory();
2419   ASSERT_TRUE(WorkingDir);
2420   EXPECT_EQ(*WorkingDir, "//root/");
2421 
2422   EC = FS->setCurrentWorkingDirectory("bar");
2423   ASSERT_FALSE(EC);
2424   WorkingDir = FS->getCurrentWorkingDirectory();
2425   ASSERT_TRUE(WorkingDir);
2426   EXPECT_EQ(*WorkingDir, "//root/bar");
2427 }
2428 
2429 TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthrough) {
2430   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2431   Lower->addDirectory("//root/");
2432   Lower->addDirectory("//root/foo");
2433   Lower->addRegularFile("//root/foo/a");
2434   Lower->addRegularFile("//root/foo/b");
2435   Lower->addRegularFile("//root/c");
2436   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2437       "{ 'use-external-names': false,\n"
2438       "  'roots': [\n"
2439       "{\n"
2440       "  'type': 'directory',\n"
2441       "  'name': '//root/bar',\n"
2442       "  'contents': [ {\n"
2443       "                  'type': 'file',\n"
2444       "                  'name': 'a',\n"
2445       "                  'external-contents': '//root/foo/a'\n"
2446       "                }\n"
2447       "              ]\n"
2448       "},\n"
2449       "{\n"
2450       "  'type': 'directory',\n"
2451       "  'name': '//root/bar/baz',\n"
2452       "  'contents': [ {\n"
2453       "                  'type': 'file',\n"
2454       "                  'name': 'a',\n"
2455       "                  'external-contents': '//root/foo/a'\n"
2456       "                }\n"
2457       "              ]\n"
2458       "}\n"
2459       "]\n"
2460       "}",
2461       Lower);
2462   ASSERT_NE(FS.get(), nullptr);
2463   std::error_code EC = FS->setCurrentWorkingDirectory("//root/");
2464   ASSERT_FALSE(EC);
2465   ASSERT_NE(FS.get(), nullptr);
2466 
2467   llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a");
2468   ASSERT_FALSE(Status.getError());
2469   EXPECT_TRUE(Status->exists());
2470 
2471   Status = FS->status("foo/a");
2472   ASSERT_FALSE(Status.getError());
2473   EXPECT_TRUE(Status->exists());
2474 
2475   EC = FS->setCurrentWorkingDirectory("//root/bar");
2476   ASSERT_FALSE(EC);
2477 
2478   Status = FS->status("./a");
2479   ASSERT_FALSE(Status.getError());
2480   EXPECT_TRUE(Status->exists());
2481 
2482   Status = FS->status("./b");
2483   ASSERT_TRUE(Status.getError());
2484 
2485   Status = FS->status("./c");
2486   ASSERT_TRUE(Status.getError());
2487 
2488   EC = FS->setCurrentWorkingDirectory("//root/");
2489   ASSERT_FALSE(EC);
2490 
2491   Status = FS->status("c");
2492   ASSERT_FALSE(Status.getError());
2493   EXPECT_TRUE(Status->exists());
2494 
2495   Status = FS->status("./bar/baz/a");
2496   ASSERT_FALSE(Status.getError());
2497   EXPECT_TRUE(Status->exists());
2498 
2499   EC = FS->setCurrentWorkingDirectory("//root/bar");
2500   ASSERT_FALSE(EC);
2501 
2502   Status = FS->status("./baz/a");
2503   ASSERT_FALSE(Status.getError());
2504   EXPECT_TRUE(Status->exists());
2505 
2506   Status = FS->status("../bar/baz/a");
2507   ASSERT_FALSE(Status.getError());
2508   EXPECT_TRUE(Status->exists());
2509 }
2510 
2511 TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthroughInvalid) {
2512   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2513   Lower->addDirectory("//root/");
2514   Lower->addDirectory("//root/foo");
2515   Lower->addRegularFile("//root/foo/a");
2516   Lower->addRegularFile("//root/foo/b");
2517   Lower->addRegularFile("//root/c");
2518   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2519       "{ 'use-external-names': false,\n"
2520       "  'roots': [\n"
2521       "{\n"
2522       "  'type': 'directory',\n"
2523       "  'name': '//root/bar',\n"
2524       "  'contents': [ {\n"
2525       "                  'type': 'file',\n"
2526       "                  'name': 'a',\n"
2527       "                  'external-contents': '//root/foo/a'\n"
2528       "                }\n"
2529       "              ]\n"
2530       "}\n"
2531       "]\n"
2532       "}",
2533       Lower);
2534   ASSERT_NE(FS.get(), nullptr);
2535   std::error_code EC = FS->setCurrentWorkingDirectory("//root/");
2536   ASSERT_FALSE(EC);
2537   ASSERT_NE(FS.get(), nullptr);
2538 
2539   llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a");
2540   ASSERT_FALSE(Status.getError());
2541   EXPECT_TRUE(Status->exists());
2542 
2543   Status = FS->status("foo/a");
2544   ASSERT_FALSE(Status.getError());
2545   EXPECT_TRUE(Status->exists());
2546 }
2547 
2548 TEST_F(VFSFromYAMLTest, VirtualWorkingDirectory) {
2549   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2550   Lower->addDirectory("//root/");
2551   Lower->addDirectory("//root/foo");
2552   Lower->addRegularFile("//root/foo/a");
2553   Lower->addRegularFile("//root/foo/b");
2554   Lower->addRegularFile("//root/c");
2555   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2556       "{ 'use-external-names': false,\n"
2557       "  'roots': [\n"
2558       "{\n"
2559       "  'type': 'directory',\n"
2560       "  'name': '//root/bar',\n"
2561       "  'contents': [ {\n"
2562       "                  'type': 'file',\n"
2563       "                  'name': 'a',\n"
2564       "                  'external-contents': '//root/foo/a'\n"
2565       "                }\n"
2566       "              ]\n"
2567       "}\n"
2568       "]\n"
2569       "}",
2570       Lower);
2571   ASSERT_NE(FS.get(), nullptr);
2572   std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar");
2573   ASSERT_FALSE(EC);
2574   ASSERT_NE(FS.get(), nullptr);
2575 
2576   llvm::ErrorOr<vfs::Status> Status = FS->status("a");
2577   ASSERT_FALSE(Status.getError());
2578   EXPECT_TRUE(Status->exists());
2579 }
2580 
2581 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest) {
2582   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
2583   TempDir _a(TestDirectory.path("a"));
2584   TempFile _ab(TestDirectory.path("a, b"));
2585   TempDir _c(TestDirectory.path("c"));
2586   TempFile _cd(TestDirectory.path("c/d"));
2587   TempDir _e(TestDirectory.path("e"));
2588   TempDir _ef(TestDirectory.path("e/f"));
2589   TempFile _g(TestDirectory.path("g"));
2590   TempDir _h(TestDirectory.path("h"));
2591 
2592   vfs::YAMLVFSWriter VFSWriter;
2593   VFSWriter.addDirectoryMapping(_a.path(), "//root/a");
2594   VFSWriter.addFileMapping(_ab.path(), "//root/a/b");
2595   VFSWriter.addFileMapping(_cd.path(), "//root/c/d");
2596   VFSWriter.addDirectoryMapping(_e.path(), "//root/e");
2597   VFSWriter.addDirectoryMapping(_ef.path(), "//root/e/f");
2598   VFSWriter.addFileMapping(_g.path(), "//root/g");
2599   VFSWriter.addDirectoryMapping(_h.path(), "//root/h");
2600 
2601   std::string Buffer;
2602   raw_string_ostream OS(Buffer);
2603   VFSWriter.write(OS);
2604   OS.flush();
2605 
2606   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2607   Lower->addDirectory("//root/");
2608   Lower->addDirectory("//root/a");
2609   Lower->addRegularFile("//root/a/b");
2610   Lower->addDirectory("//root/b");
2611   Lower->addDirectory("//root/c");
2612   Lower->addRegularFile("//root/c/d");
2613   Lower->addDirectory("//root/e");
2614   Lower->addDirectory("//root/e/f");
2615   Lower->addRegularFile("//root/g");
2616   Lower->addDirectory("//root/h");
2617 
2618   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);
2619   ASSERT_NE(FS.get(), nullptr);
2620 
2621   EXPECT_TRUE(FS->exists(_a.path()));
2622   EXPECT_TRUE(FS->exists(_ab.path()));
2623   EXPECT_TRUE(FS->exists(_c.path()));
2624   EXPECT_TRUE(FS->exists(_cd.path()));
2625   EXPECT_TRUE(FS->exists(_e.path()));
2626   EXPECT_TRUE(FS->exists(_ef.path()));
2627   EXPECT_TRUE(FS->exists(_g.path()));
2628   EXPECT_TRUE(FS->exists(_h.path()));
2629 }
2630 
2631 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest2) {
2632   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
2633   TempDir _a(TestDirectory.path("a"));
2634   TempFile _ab(TestDirectory.path("a/b"));
2635   TempDir _ac(TestDirectory.path("a/c"));
2636   TempFile _acd(TestDirectory.path("a/c/d"));
2637   TempFile _ace(TestDirectory.path("a/c/e"));
2638   TempFile _acf(TestDirectory.path("a/c/f"));
2639   TempDir _ag(TestDirectory.path("a/g"));
2640   TempFile _agh(TestDirectory.path("a/g/h"));
2641 
2642   vfs::YAMLVFSWriter VFSWriter;
2643   VFSWriter.addDirectoryMapping(_a.path(), "//root/a");
2644   VFSWriter.addFileMapping(_ab.path(), "//root/a/b");
2645   VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c");
2646   VFSWriter.addFileMapping(_acd.path(), "//root/a/c/d");
2647   VFSWriter.addFileMapping(_ace.path(), "//root/a/c/e");
2648   VFSWriter.addFileMapping(_acf.path(), "//root/a/c/f");
2649   VFSWriter.addDirectoryMapping(_ag.path(), "//root/a/g");
2650   VFSWriter.addFileMapping(_agh.path(), "//root/a/g/h");
2651 
2652   std::string Buffer;
2653   raw_string_ostream OS(Buffer);
2654   VFSWriter.write(OS);
2655   OS.flush();
2656 
2657   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2658   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);
2659   EXPECT_NE(FS.get(), nullptr);
2660 }
2661 
2662 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest3) {
2663   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
2664   TempDir _a(TestDirectory.path("a"));
2665   TempFile _ab(TestDirectory.path("a/b"));
2666   TempDir _ac(TestDirectory.path("a/c"));
2667   TempDir _acd(TestDirectory.path("a/c/d"));
2668   TempDir _acde(TestDirectory.path("a/c/d/e"));
2669   TempFile _acdef(TestDirectory.path("a/c/d/e/f"));
2670   TempFile _acdeg(TestDirectory.path("a/c/d/e/g"));
2671   TempDir _ah(TestDirectory.path("a/h"));
2672   TempFile _ahi(TestDirectory.path("a/h/i"));
2673 
2674   vfs::YAMLVFSWriter VFSWriter;
2675   VFSWriter.addDirectoryMapping(_a.path(), "//root/a");
2676   VFSWriter.addFileMapping(_ab.path(), "//root/a/b");
2677   VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c");
2678   VFSWriter.addDirectoryMapping(_acd.path(), "//root/a/c/d");
2679   VFSWriter.addDirectoryMapping(_acde.path(), "//root/a/c/d/e");
2680   VFSWriter.addFileMapping(_acdef.path(), "//root/a/c/d/e/f");
2681   VFSWriter.addFileMapping(_acdeg.path(), "//root/a/c/d/e/g");
2682   VFSWriter.addDirectoryMapping(_ahi.path(), "//root/a/h");
2683   VFSWriter.addFileMapping(_ahi.path(), "//root/a/h/i");
2684 
2685   std::string Buffer;
2686   raw_string_ostream OS(Buffer);
2687   VFSWriter.write(OS);
2688   OS.flush();
2689 
2690   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2691   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);
2692   EXPECT_NE(FS.get(), nullptr);
2693 }
2694 
2695 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTestHandleDirs) {
2696   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
2697   TempDir _a(TestDirectory.path("a"));
2698   TempDir _b(TestDirectory.path("b"));
2699   TempDir _c(TestDirectory.path("c"));
2700 
2701   vfs::YAMLVFSWriter VFSWriter;
2702   VFSWriter.addDirectoryMapping(_a.path(), "//root/a");
2703   VFSWriter.addDirectoryMapping(_b.path(), "//root/b");
2704   VFSWriter.addDirectoryMapping(_c.path(), "//root/c");
2705 
2706   std::string Buffer;
2707   raw_string_ostream OS(Buffer);
2708   VFSWriter.write(OS);
2709   OS.flush();
2710 
2711   // We didn't add a single file - only directories.
2712   EXPECT_EQ(Buffer.find("'type': 'file'"), std::string::npos);
2713 
2714   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2715   Lower->addDirectory("//root/a");
2716   Lower->addDirectory("//root/b");
2717   Lower->addDirectory("//root/c");
2718   // canaries
2719   Lower->addRegularFile("//root/a/a");
2720   Lower->addRegularFile("//root/b/b");
2721   Lower->addRegularFile("//root/c/c");
2722 
2723   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);
2724   ASSERT_NE(FS.get(), nullptr);
2725 
2726   EXPECT_FALSE(FS->exists(_a.path("a")));
2727   EXPECT_FALSE(FS->exists(_b.path("b")));
2728   EXPECT_FALSE(FS->exists(_c.path("c")));
2729 }
2730 
2731 TEST_F(VFSFromYAMLTest, RedirectingWith) {
2732   IntrusiveRefCntPtr<DummyFileSystem> Both(new DummyFileSystem());
2733   Both->addDirectory("//root/a");
2734   Both->addRegularFile("//root/a/f");
2735   Both->addDirectory("//root/b");
2736   Both->addRegularFile("//root/b/f");
2737 
2738   IntrusiveRefCntPtr<DummyFileSystem> AOnly(new DummyFileSystem());
2739   AOnly->addDirectory("//root/a");
2740   AOnly->addRegularFile("//root/a/f");
2741 
2742   IntrusiveRefCntPtr<DummyFileSystem> BOnly(new DummyFileSystem());
2743   BOnly->addDirectory("//root/b");
2744   BOnly->addRegularFile("//root/b/f");
2745 
2746   auto BaseStr = std::string("  'roots': [\n"
2747                              "    {\n"
2748                              "      'type': 'directory-remap',\n"
2749                              "      'name': '//root/a',\n"
2750                              "      'external-contents': '//root/b'\n"
2751                              "    }\n"
2752                              "  ]\n"
2753                              "}");
2754   auto FallthroughStr = "{ 'redirecting-with': 'fallthrough',\n" + BaseStr;
2755   auto FallbackStr = "{ 'redirecting-with': 'fallback',\n" + BaseStr;
2756   auto RedirectOnlyStr = "{ 'redirecting-with': 'redirect-only',\n" + BaseStr;
2757 
2758   auto ExpectPath = [&](vfs::FileSystem &FS, StringRef Expected,
2759                         StringRef Message) {
2760     auto AF = FS.openFileForRead("//root/a/f");
2761     ASSERT_FALSE(AF.getError()) << Message;
2762     auto AFName = (*AF)->getName();
2763     ASSERT_FALSE(AFName.getError()) << Message;
2764     EXPECT_EQ(Expected.str(), AFName.get()) << Message;
2765 
2766     auto AS = FS.status("//root/a/f");
2767     ASSERT_FALSE(AS.getError()) << Message;
2768     EXPECT_EQ(Expected.str(), AS->getName()) << Message;
2769   };
2770 
2771   auto ExpectFailure = [&](vfs::FileSystem &FS, StringRef Message) {
2772     EXPECT_TRUE(FS.openFileForRead("//root/a/f").getError()) << Message;
2773     EXPECT_TRUE(FS.status("//root/a/f").getError()) << Message;
2774   };
2775 
2776   {
2777     // `f` in both `a` and `b`
2778 
2779     // `fallthrough` tries `external-name` first, so should be `b`
2780     IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =
2781         getFromYAMLString(FallthroughStr, Both);
2782     ASSERT_TRUE(Fallthrough.get() != nullptr);
2783     ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, both exist");
2784 
2785     // `fallback` tries the original name first, so should be `a`
2786     IntrusiveRefCntPtr<vfs::FileSystem> Fallback =
2787         getFromYAMLString(FallbackStr, Both);
2788     ASSERT_TRUE(Fallback.get() != nullptr);
2789     ExpectPath(*Fallback, "//root/a/f", "fallback, both exist");
2790 
2791     // `redirect-only` is the same as `fallthrough` but doesn't try the
2792     // original on failure, so no change here (ie. `b`)
2793     IntrusiveRefCntPtr<vfs::FileSystem> Redirect =
2794         getFromYAMLString(RedirectOnlyStr, Both);
2795     ASSERT_TRUE(Redirect.get() != nullptr);
2796     ExpectPath(*Redirect, "//root/b/f", "redirect-only, both exist");
2797   }
2798 
2799   {
2800     // `f` in `a` only
2801 
2802     // Fallthrough to the original path, `a`
2803     IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =
2804         getFromYAMLString(FallthroughStr, AOnly);
2805     ASSERT_TRUE(Fallthrough.get() != nullptr);
2806     ExpectPath(*Fallthrough, "//root/a/f", "fallthrough, a only");
2807 
2808     // Original first, so still `a`
2809     IntrusiveRefCntPtr<vfs::FileSystem> Fallback =
2810         getFromYAMLString(FallbackStr, AOnly);
2811     ASSERT_TRUE(Fallback.get() != nullptr);
2812     ExpectPath(*Fallback, "//root/a/f", "fallback, a only");
2813 
2814     // Fails since no fallthrough
2815     IntrusiveRefCntPtr<vfs::FileSystem> Redirect =
2816         getFromYAMLString(RedirectOnlyStr, AOnly);
2817     ASSERT_TRUE(Redirect.get() != nullptr);
2818     ExpectFailure(*Redirect, "redirect-only, a only");
2819   }
2820 
2821   {
2822     // `f` in `b` only
2823 
2824     // Tries `b` first (no fallthrough)
2825     IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =
2826         getFromYAMLString(FallthroughStr, BOnly);
2827     ASSERT_TRUE(Fallthrough.get() != nullptr);
2828     ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, b only");
2829 
2830     // Tries original first but then fallsback to `b`
2831     IntrusiveRefCntPtr<vfs::FileSystem> Fallback =
2832         getFromYAMLString(FallbackStr, BOnly);
2833     ASSERT_TRUE(Fallback.get() != nullptr);
2834     ExpectPath(*Fallback, "//root/b/f", "fallback, b only");
2835 
2836     // Redirect exists, so uses it (`b`)
2837     IntrusiveRefCntPtr<vfs::FileSystem> Redirect =
2838         getFromYAMLString(RedirectOnlyStr, BOnly);
2839     ASSERT_TRUE(Redirect.get() != nullptr);
2840     ExpectPath(*Redirect, "//root/b/f", "redirect-only, b only");
2841   }
2842 
2843   EXPECT_EQ(0, NumDiagnostics);
2844 }
2845 
2846 TEST(VFSFromRemappedFilesTest, Basic) {
2847   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS =
2848       new vfs::InMemoryFileSystem;
2849   BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
2850   BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
2851 
2852   std::vector<std::pair<std::string, std::string>> RemappedFiles = {
2853       {"//root/a/a", "//root/b"},
2854       {"//root/a/b/c", "//root/c"},
2855   };
2856   auto RemappedFS = vfs::RedirectingFileSystem::create(
2857       RemappedFiles, /*UseExternalNames=*/false, *BaseFS);
2858 
2859   auto StatA = RemappedFS->status("//root/a/a");
2860   auto StatB = RemappedFS->status("//root/a/b/c");
2861   ASSERT_TRUE(StatA);
2862   ASSERT_TRUE(StatB);
2863   EXPECT_EQ("//root/a/a", StatA->getName());
2864   EXPECT_EQ("//root/a/b/c", StatB->getName());
2865 
2866   auto BufferA = RemappedFS->getBufferForFile("//root/a/a");
2867   auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c");
2868   ASSERT_TRUE(BufferA);
2869   ASSERT_TRUE(BufferB);
2870   EXPECT_EQ("contents of b", (*BufferA)->getBuffer());
2871   EXPECT_EQ("contents of c", (*BufferB)->getBuffer());
2872 }
2873 
2874 TEST(VFSFromRemappedFilesTest, UseExternalNames) {
2875   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS =
2876       new vfs::InMemoryFileSystem;
2877   BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
2878   BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
2879 
2880   std::vector<std::pair<std::string, std::string>> RemappedFiles = {
2881       {"//root/a/a", "//root/b"},
2882       {"//root/a/b/c", "//root/c"},
2883   };
2884   auto RemappedFS = vfs::RedirectingFileSystem::create(
2885       RemappedFiles, /*UseExternalNames=*/true, *BaseFS);
2886 
2887   auto StatA = RemappedFS->status("//root/a/a");
2888   auto StatB = RemappedFS->status("//root/a/b/c");
2889   ASSERT_TRUE(StatA);
2890   ASSERT_TRUE(StatB);
2891   EXPECT_EQ("//root/b", StatA->getName());
2892   EXPECT_EQ("//root/c", StatB->getName());
2893 
2894   auto BufferA = RemappedFS->getBufferForFile("//root/a/a");
2895   auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c");
2896   ASSERT_TRUE(BufferA);
2897   ASSERT_TRUE(BufferB);
2898   EXPECT_EQ("contents of b", (*BufferA)->getBuffer());
2899   EXPECT_EQ("contents of c", (*BufferB)->getBuffer());
2900 }
2901 
2902 TEST(VFSFromRemappedFilesTest, LastMappingWins) {
2903   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS =
2904       new vfs::InMemoryFileSystem;
2905   BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
2906   BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
2907 
2908   std::vector<std::pair<std::string, std::string>> RemappedFiles = {
2909       {"//root/a", "//root/b"},
2910       {"//root/a", "//root/c"},
2911   };
2912   auto RemappedFSKeepName = vfs::RedirectingFileSystem::create(
2913       RemappedFiles, /*UseExternalNames=*/false, *BaseFS);
2914   auto RemappedFSExternalName = vfs::RedirectingFileSystem::create(
2915       RemappedFiles, /*UseExternalNames=*/true, *BaseFS);
2916 
2917   auto StatKeepA = RemappedFSKeepName->status("//root/a");
2918   auto StatExternalA = RemappedFSExternalName->status("//root/a");
2919   ASSERT_TRUE(StatKeepA);
2920   ASSERT_TRUE(StatExternalA);
2921   EXPECT_EQ("//root/a", StatKeepA->getName());
2922   EXPECT_EQ("//root/c", StatExternalA->getName());
2923 
2924   auto BufferKeepA = RemappedFSKeepName->getBufferForFile("//root/a");
2925   auto BufferExternalA = RemappedFSExternalName->getBufferForFile("//root/a");
2926   ASSERT_TRUE(BufferKeepA);
2927   ASSERT_TRUE(BufferExternalA);
2928   EXPECT_EQ("contents of c", (*BufferKeepA)->getBuffer());
2929   EXPECT_EQ("contents of c", (*BufferExternalA)->getBuffer());
2930 }
2931