blob: 50d11019f30501dde01573d2f5cb2a717c1748d7 [file] [log] [blame]
Ben Langmuirc8130a72014-02-20 21:59:23 +00001//===- unittests/Basic/VirtualFileSystem.cpp ---------------- VFS tests ---===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "clang/Basic/VirtualFileSystem.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000011#include "llvm/Support/MemoryBuffer.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000012#include "llvm/Support/Path.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000013#include "llvm/Support/SourceMgr.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000014#include "gtest/gtest.h"
15#include <map>
16using namespace clang;
17using namespace llvm;
18using llvm::sys::fs::UniqueID;
19
20namespace {
21class DummyFileSystem : public vfs::FileSystem {
22 int FSID; // used to produce UniqueIDs
23 int FileID; // used to produce UniqueIDs
24 std::map<std::string, vfs::Status> FilesAndDirs;
25
26 static int getNextFSID() {
27 static int Count = 0;
28 return Count++;
29 }
30
31public:
32 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
33
34 ErrorOr<vfs::Status> status(const Twine &Path) {
35 std::map<std::string, vfs::Status>::iterator I =
Ben Langmuird51ba0b2014-02-21 23:39:37 +000036 FilesAndDirs.find(Path.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +000037 if (I == FilesAndDirs.end())
38 return error_code(errc::no_such_file_or_directory, posix_category());
39 return I->second;
40 }
41 error_code openFileForRead(const Twine &Path, OwningPtr<vfs::File> &Result) {
42 llvm_unreachable("unimplemented");
43 }
44 error_code getBufferForFile(const Twine &Name,
45 OwningPtr<MemoryBuffer> &Result,
46 int64_t FileSize = -1,
47 bool RequiresNullTerminator = true) {
48 llvm_unreachable("unimplemented");
49 }
50
51 void addEntry(StringRef Path, const vfs::Status &Status) {
52 FilesAndDirs[Path] = Status;
53 }
54
Ben Langmuird51ba0b2014-02-21 23:39:37 +000055 void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
Ben Langmuirc8130a72014-02-20 21:59:23 +000056 vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(),
57 0, 0, 1024, sys::fs::file_type::regular_file, Perms);
58 addEntry(Path, S);
59 }
60
Ben Langmuird51ba0b2014-02-21 23:39:37 +000061 void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
Ben Langmuirc8130a72014-02-20 21:59:23 +000062 vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(),
63 0, 0, 0, sys::fs::file_type::directory_file, Perms);
64 addEntry(Path, S);
65 }
66
67 void addSymlink(StringRef Path) {
68 vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(),
69 0, 0, 0, sys::fs::file_type::symlink_file, sys::fs::all_all);
70 addEntry(Path, S);
71 }
72};
73} // end anonymous namespace
74
Ben Langmuird51ba0b2014-02-21 23:39:37 +000075TEST(VirtualFileSystemTest, StatusQueries) {
Ben Langmuirc8130a72014-02-20 21:59:23 +000076 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
77 ErrorOr<vfs::Status> Status((error_code()));
78
79 D->addRegularFile("/foo");
80 Status = D->status("/foo");
81 ASSERT_EQ(errc::success, Status.getError());
82 EXPECT_TRUE(Status->isStatusKnown());
83 EXPECT_FALSE(Status->isDirectory());
84 EXPECT_TRUE(Status->isRegularFile());
85 EXPECT_FALSE(Status->isSymlink());
86 EXPECT_FALSE(Status->isOther());
87 EXPECT_TRUE(Status->exists());
88
89 D->addDirectory("/bar");
90 Status = D->status("/bar");
91 ASSERT_EQ(errc::success, Status.getError());
92 EXPECT_TRUE(Status->isStatusKnown());
93 EXPECT_TRUE(Status->isDirectory());
94 EXPECT_FALSE(Status->isRegularFile());
95 EXPECT_FALSE(Status->isSymlink());
96 EXPECT_FALSE(Status->isOther());
97 EXPECT_TRUE(Status->exists());
98
99 D->addSymlink("/baz");
100 Status = D->status("/baz");
101 ASSERT_EQ(errc::success, Status.getError());
102 EXPECT_TRUE(Status->isStatusKnown());
103 EXPECT_FALSE(Status->isDirectory());
104 EXPECT_FALSE(Status->isRegularFile());
105 EXPECT_TRUE(Status->isSymlink());
106 EXPECT_FALSE(Status->isOther());
107 EXPECT_TRUE(Status->exists());
108
109 EXPECT_TRUE(Status->equivalent(*Status));
110 ErrorOr<vfs::Status> Status2 = D->status("/foo");
111 ASSERT_EQ(errc::success, Status2.getError());
112 EXPECT_FALSE(Status->equivalent(*Status2));
113}
114
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000115TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000116 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
117 ErrorOr<vfs::Status> Status((error_code()));
118 EXPECT_FALSE(Status = D->status("/foo"));
119
120 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
121 EXPECT_FALSE(Status = O->status("/foo"));
122
123 D->addRegularFile("/foo");
124 Status = D->status("/foo");
125 EXPECT_EQ(errc::success, Status.getError());
126
127 ErrorOr<vfs::Status> Status2((error_code()));
128 Status2 = O->status("/foo");
129 EXPECT_EQ(errc::success, Status2.getError());
130 EXPECT_TRUE(Status->equivalent(*Status2));
131}
132
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000133TEST(VirtualFileSystemTest, OverlayFiles) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000134 IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
135 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
136 IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000137 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
138 new vfs::OverlayFileSystem(Base));
Ben Langmuirc8130a72014-02-20 21:59:23 +0000139 O->pushOverlay(Middle);
140 O->pushOverlay(Top);
141
142 ErrorOr<vfs::Status> Status1((error_code())), Status2((error_code())),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000143 Status3((error_code())), StatusB((error_code())), StatusM((error_code())),
144 StatusT((error_code()));
Ben Langmuirc8130a72014-02-20 21:59:23 +0000145
146 Base->addRegularFile("/foo");
147 StatusB = Base->status("/foo");
148 ASSERT_EQ(errc::success, StatusB.getError());
149 Status1 = O->status("/foo");
150 ASSERT_EQ(errc::success, Status1.getError());
151 Middle->addRegularFile("/foo");
152 StatusM = Middle->status("/foo");
153 ASSERT_EQ(errc::success, StatusM.getError());
154 Status2 = O->status("/foo");
155 ASSERT_EQ(errc::success, Status2.getError());
156 Top->addRegularFile("/foo");
157 StatusT = Top->status("/foo");
158 ASSERT_EQ(errc::success, StatusT.getError());
159 Status3 = O->status("/foo");
160 ASSERT_EQ(errc::success, Status3.getError());
161
162 EXPECT_TRUE(Status1->equivalent(*StatusB));
163 EXPECT_TRUE(Status2->equivalent(*StatusM));
164 EXPECT_TRUE(Status3->equivalent(*StatusT));
165
166 EXPECT_FALSE(Status1->equivalent(*Status2));
167 EXPECT_FALSE(Status2->equivalent(*Status3));
168 EXPECT_FALSE(Status1->equivalent(*Status3));
169}
170
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000171TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000172 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
173 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000174 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
175 new vfs::OverlayFileSystem(Lower));
Ben Langmuirc8130a72014-02-20 21:59:23 +0000176 O->pushOverlay(Upper);
177
178 Lower->addDirectory("/lower-only");
179 Upper->addDirectory("/upper-only");
180
181 // non-merged paths should be the same
182 ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
183 ASSERT_EQ(errc::success, Status1.getError());
184 ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
185 ASSERT_EQ(errc::success, Status2.getError());
186 EXPECT_TRUE(Status1->equivalent(*Status2));
187
188 Status1 = Upper->status("/upper-only");
189 ASSERT_EQ(errc::success, Status1.getError());
190 Status2 = O->status("/upper-only");
191 ASSERT_EQ(errc::success, Status2.getError());
192 EXPECT_TRUE(Status1->equivalent(*Status2));
193}
194
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000195TEST(VirtualFileSystemTest, MergedDirPermissions) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000196 // merged directories get the permissions of the upper dir
197 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
198 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000199 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
200 new vfs::OverlayFileSystem(Lower));
Ben Langmuirc8130a72014-02-20 21:59:23 +0000201 O->pushOverlay(Upper);
202
203 ErrorOr<vfs::Status> Status((error_code()));
204 Lower->addDirectory("/both", sys::fs::owner_read);
205 Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
206 Status = O->status("/both");
207 ASSERT_EQ(errc::success, Status.getError());
208 EXPECT_EQ(0740, Status->getPermissions());
209
210 // permissions (as usual) are not recursively applied
211 Lower->addRegularFile("/both/foo", sys::fs::owner_read);
212 Upper->addRegularFile("/both/bar", sys::fs::owner_write);
213 Status = O->status("/both/foo");
214 ASSERT_EQ(errc::success, Status.getError());
215 EXPECT_EQ(0400, Status->getPermissions());
216 Status = O->status("/both/bar");
217 ASSERT_EQ(errc::success, Status.getError());
218 EXPECT_EQ(0200, Status->getPermissions());
219}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000220
221static int NumDiagnostics = 0;
222static void CountingDiagHandler(const SMDiagnostic &, void *) {
223 ++NumDiagnostics;
224}
225
226static IntrusiveRefCntPtr<vfs::FileSystem>
227getFromYAMLRawString(StringRef Content,
228 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
229 MemoryBuffer *Buffer = MemoryBuffer::getMemBuffer(Content);
230 return getVFSFromYAML(Buffer, CountingDiagHandler, ExternalFS);
231}
232
233static IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
234 StringRef Content,
235 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
236 std::string VersionPlusContent("{\n 'version':0,\n");
237 VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
238 return getFromYAMLRawString(VersionPlusContent, ExternalFS);
239}
240
241TEST(VirtualFileSystemTest, BasicVFSFromYAML) {
242 NumDiagnostics = 0;
243 IntrusiveRefCntPtr<vfs::FileSystem> FS;
244 FS = getFromYAMLString("");
245 EXPECT_EQ(NULL, FS.getPtr());
246 FS = getFromYAMLString("[]");
247 EXPECT_EQ(NULL, FS.getPtr());
248 FS = getFromYAMLString("'string'");
249 EXPECT_EQ(NULL, FS.getPtr());
250 EXPECT_EQ(3, NumDiagnostics);
251}
252
253TEST(VirtualFileSystemTest, MappedFiles) {
254 NumDiagnostics = 0;
255 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
256 Lower->addRegularFile("/foo/bar/a");
257 IntrusiveRefCntPtr<vfs::FileSystem> FS =
258 getFromYAMLString("{ 'roots': [\n"
259 "{\n"
260 " 'type': 'directory',\n"
261 " 'name': '/',\n"
262 " 'contents': [ {\n"
263 " 'type': 'file',\n"
264 " 'name': 'file1',\n"
265 " 'external-contents': '/foo/bar/a'\n"
266 " },\n"
267 " {\n"
268 " 'type': 'file',\n"
269 " 'name': 'file2',\n"
270 " 'external-contents': '/foo/b'\n"
271 " }\n"
272 " ]\n"
273 "}\n"
274 "]\n"
275 "}",
276 Lower);
277 ASSERT_TRUE(FS.getPtr());
278
279 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
280 new vfs::OverlayFileSystem(Lower));
281 O->pushOverlay(FS);
282
283 // file
284 ErrorOr<vfs::Status> S = O->status("/file1");
285 ASSERT_EQ(errc::success, S.getError());
286 EXPECT_EQ("/file1", S->getName());
287 EXPECT_EQ("/foo/bar/a", S->getExternalName());
288
289 ErrorOr<vfs::Status> SLower = O->status("/foo/bar/a");
290 EXPECT_EQ("/foo/bar/a", SLower->getName());
291 EXPECT_TRUE(S->equivalent(*SLower));
292
293 // directory
294 S = O->status("/");
295 ASSERT_EQ(errc::success, S.getError());
296 EXPECT_TRUE(S->isDirectory());
297 EXPECT_TRUE(S->equivalent(*O->status("/"))); // non-volatile UniqueID
298
299 // broken mapping
300 EXPECT_EQ(errc::no_such_file_or_directory, O->status("/file2").getError());
301 EXPECT_EQ(0, NumDiagnostics);
302}
303
304TEST(VirtualFileSystemTest, CaseInsensitive) {
305 NumDiagnostics = 0;
306 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
307 Lower->addRegularFile("/foo/bar/a");
308 IntrusiveRefCntPtr<vfs::FileSystem> FS =
309 getFromYAMLString("{ 'case-sensitive': 'false',\n"
310 " 'roots': [\n"
311 "{\n"
312 " 'type': 'directory',\n"
313 " 'name': '/',\n"
314 " 'contents': [ {\n"
315 " 'type': 'file',\n"
316 " 'name': 'XX',\n"
317 " 'external-contents': '/foo/bar/a'\n"
318 " }\n"
319 " ]\n"
320 "}]}",
321 Lower);
322 ASSERT_TRUE(FS.getPtr());
323
324 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
325 new vfs::OverlayFileSystem(Lower));
326 O->pushOverlay(FS);
327
328 ErrorOr<vfs::Status> S = O->status("/XX");
329 ASSERT_EQ(errc::success, S.getError());
330
331 ErrorOr<vfs::Status> SS = O->status("/xx");
332 ASSERT_EQ(errc::success, SS.getError());
333 EXPECT_TRUE(S->equivalent(*SS));
334 SS = O->status("/xX");
335 EXPECT_TRUE(S->equivalent(*SS));
336 SS = O->status("/Xx");
337 EXPECT_TRUE(S->equivalent(*SS));
338 EXPECT_EQ(0, NumDiagnostics);
339}
340
341TEST(VirtualFileSystemTest, CaseSensitive) {
342 NumDiagnostics = 0;
343 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
344 Lower->addRegularFile("/foo/bar/a");
345 IntrusiveRefCntPtr<vfs::FileSystem> FS =
346 getFromYAMLString("{ 'case-sensitive': 'true',\n"
347 " 'roots': [\n"
348 "{\n"
349 " 'type': 'directory',\n"
350 " 'name': '/',\n"
351 " 'contents': [ {\n"
352 " 'type': 'file',\n"
353 " 'name': 'XX',\n"
354 " 'external-contents': '/foo/bar/a'\n"
355 " }\n"
356 " ]\n"
357 "}]}",
358 Lower);
359 ASSERT_TRUE(FS.getPtr());
360
361 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
362 new vfs::OverlayFileSystem(Lower));
363 O->pushOverlay(FS);
364
365 ErrorOr<vfs::Status> SS = O->status("/xx");
366 EXPECT_EQ(errc::no_such_file_or_directory, SS.getError());
367 SS = O->status("/xX");
368 EXPECT_EQ(errc::no_such_file_or_directory, SS.getError());
369 SS = O->status("/Xx");
370 EXPECT_EQ(errc::no_such_file_or_directory, SS.getError());
371 EXPECT_EQ(0, NumDiagnostics);
372}
373
374TEST(VirtualFileSystemTest, IllegalVFSFile) {
375 NumDiagnostics = 0;
376 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
377
378 // invalid YAML at top-level
379 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
380 EXPECT_FALSE(FS.getPtr());
381 // invalid YAML in roots
382 FS = getFromYAMLString("{ 'roots':[}", Lower);
383 // invalid YAML in directory
384 FS = getFromYAMLString(
385 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
386 Lower);
387 EXPECT_FALSE(FS.getPtr());
388
389 // invalid configuration
390 FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
391 EXPECT_FALSE(FS.getPtr());
392 FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
393 EXPECT_FALSE(FS.getPtr());
394
395 // invalid roots
396 FS = getFromYAMLString("{ 'roots':'' }", Lower);
397 EXPECT_FALSE(FS.getPtr());
398 FS = getFromYAMLString("{ 'roots':{} }", Lower);
399 EXPECT_FALSE(FS.getPtr());
400
401 // invalid entries
402 FS = getFromYAMLString(
403 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
404 EXPECT_FALSE(FS.getPtr());
405 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
406 "'external-contents': 'other' }",
407 Lower);
408 EXPECT_FALSE(FS.getPtr());
409 FS = getFromYAMLString(
410 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
411 Lower);
412 EXPECT_FALSE(FS.getPtr());
413 FS = getFromYAMLString(
414 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
415 Lower);
416 EXPECT_FALSE(FS.getPtr());
417 FS = getFromYAMLString(
418 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
419 Lower);
420 EXPECT_FALSE(FS.getPtr());
421 FS = getFromYAMLString(
422 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
423 Lower);
424 EXPECT_FALSE(FS.getPtr());
425 FS = getFromYAMLString(
426 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
427 Lower);
428 EXPECT_FALSE(FS.getPtr());
429
430 // missing mandatory fields
431 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
432 EXPECT_FALSE(FS.getPtr());
433 FS = getFromYAMLString(
434 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
435 EXPECT_FALSE(FS.getPtr());
436 FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
437 EXPECT_FALSE(FS.getPtr());
438
439 // duplicate keys
440 FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
441 EXPECT_FALSE(FS.getPtr());
442 FS = getFromYAMLString(
443 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
444 Lower);
445 EXPECT_FALSE(FS.getPtr());
446 FS =
447 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
448 "'external-contents':'blah' } ] }",
449 Lower);
450 EXPECT_FALSE(FS.getPtr());
451
452 // missing version
453 FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
454 EXPECT_FALSE(FS.getPtr());
455
456 // bad version number
457 FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
458 EXPECT_FALSE(FS.getPtr());
459 FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
460 EXPECT_FALSE(FS.getPtr());
461 FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
462 EXPECT_FALSE(FS.getPtr());
463 EXPECT_EQ(24, NumDiagnostics);
464}