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 | |
26 | using namespace llvm; |
27 | using llvm::sys::fs::UniqueID; |
28 | using llvm::unittest::TempDir; |
29 | using llvm::unittest::TempFile; |
30 | using llvm::unittest::TempLink; |
31 | using testing::ElementsAre; |
32 | using testing::Pair; |
33 | using testing::UnorderedElementsAre; |
34 | |
35 | namespace { |
36 | struct 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 | |
48 | class 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 | |
60 | public: |
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 | |
178 | protected: |
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 | |
198 | class 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. |
206 | class NoStatusDummyFileSystem : public DummyFileSystem { |
207 | public: |
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. |
220 | std::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 | |
227 | TEST(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 | |
267 | TEST(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 | |
285 | TEST(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 | |
314 | TEST(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 | |
353 | TEST(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 | |
377 | TEST(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 | |
403 | TEST(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 | |
454 | TEST(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 |
482 | TEST(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 | |
547 | TEST(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 | |
574 | TEST(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 | |
603 | TEST(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 | |
644 | TEST(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 |
727 | TEST(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 | |
766 | template <typename DirIter> |
767 | static 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 | |
786 | TEST(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 | |
810 | TEST(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 | |
852 | TEST(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 | |
872 | TEST(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 | |
915 | TEST(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 | |
956 | TEST(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 | |
986 | TEST(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 | |
1010 | TEST(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 | |
1047 | class InMemoryFileSystemTest : public ::testing::Test { |
1048 | protected: |
1049 | llvm::vfs::InMemoryFileSystem FS; |
1050 | llvm::vfs::InMemoryFileSystem NormalizedFS; |
1051 | |
1052 | InMemoryFileSystemTest() |
1053 | : FS(/*UseNormalizedPaths=*/false), |
1054 | NormalizedFS(/*UseNormalizedPaths=*/true) {} |
1055 | }; |
1056 | |
1057 | MATCHER_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 | |
1067 | TEST_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 | |
1074 | TEST_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 | |
1087 | TEST_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 | |
1101 | TEST_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 | |
1109 | TEST_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 | |
1136 | TEST_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 | |
1143 | TEST_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 | |
1168 | TEST_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 | |
1189 | TEST_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) |
1201 | TEST_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 | |
1224 | TEST_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 | |
1241 | TEST_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 | |
1259 | TEST_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 | |
1274 | TEST_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 | |
1297 | TEST_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). |
1313 | TEST_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 | |
1345 | TEST_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 | |
1356 | TEST_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 | |
1370 | TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) { |
1371 | EXPECT_FALSE(FS.addHardLink("/path/to/link" , "/path/to/target" )); |
1372 | } |
1373 | |
1374 | TEST_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 | |
1382 | TEST_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 | |
1390 | TEST_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 | |
1399 | TEST_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 | |
1409 | TEST_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 | |
1418 | TEST_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 | |
1425 | TEST_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 | |
1433 | TEST_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 | |
1440 | TEST_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 | |
1454 | TEST_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 | |
1475 | TEST_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 | |
1483 | TEST_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 | |
1493 | TEST_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 | |
1513 | TEST_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 | |
1521 | TEST_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 | |
1537 | TEST_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. |
1556 | class VFSFromYAMLTest : public ::testing::Test { |
1557 | public: |
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 | |
1592 | TEST_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 | |
1603 | TEST_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 | |
1717 | TEST_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 | |
1759 | TEST_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 | |
1803 | TEST_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 | |
1844 | TEST_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 | |
1884 | TEST_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 | |
1912 | TEST_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 | |
1952 | TEST_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 | |
2015 | TEST_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 | |
2055 | TEST_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 | |
2091 | TEST_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 | |
2123 | TEST_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 | |
2249 | TEST_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 | |
2301 | TEST_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 | |
2347 | TEST_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 | |
2366 | TEST_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 | |
2409 | TEST_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 | |
2457 | TEST_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 | |
2500 | TEST_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 | |
2554 | TEST_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 | |
2583 | TEST_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 | |
2611 | TEST_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 | |
2640 | TEST_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 | |
2692 | TEST_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 | |
2750 | TEST_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 | |
2832 | TEST_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 | |
2869 | TEST_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 | |
2902 | TEST_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 | |
2952 | TEST_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 | |
2983 | TEST_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 | |
3016 | TEST_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 | |
3052 | TEST_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 | |
3167 | TEST(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 | |
3195 | TEST(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 | |
3223 | TEST(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 | |
3253 | TEST(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 | |
3330 | TEST(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. |
3378 | TEST(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 | |
3443 | TEST(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 | |
3514 | TEST(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 | |
3538 | TEST(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 | |