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

source code of llvm/unittests/Support/VirtualFileSystemTest.cpp