1 //===- unittests/Basic/FileMangerTest.cpp ------------ FileManger 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/FileManager.h"
11 #include "clang/Basic/FileSystemOptions.h"
12 #include "clang/Basic/FileSystemStatCache.h"
13 #include "clang/Basic/VirtualFileSystem.h"
14 #include "llvm/ADT/STLExtras.h"
15 #include "llvm/Config/llvm-config.h"
16 #include "llvm/Support/Path.h"
17 #include "gtest/gtest.h"
18 
19 using namespace llvm;
20 using namespace clang;
21 
22 namespace {
23 
24 // Used to create a fake file system for running the tests with such
25 // that the tests are not affected by the structure/contents of the
26 // file system on the machine running the tests.
27 class FakeStatCache : public FileSystemStatCache {
28 private:
29   // Maps a file/directory path to its desired stat result.  Anything
30   // not in this map is considered to not exist in the file system.
31   llvm::StringMap<FileData, llvm::BumpPtrAllocator> StatCalls;
32 
33   void InjectFileOrDirectory(const char *Path, ino_t INode, bool IsFile) {
34 #ifndef LLVM_ON_WIN32
35     SmallString<128> NormalizedPath(Path);
36     llvm::sys::path::native(NormalizedPath);
37     Path = NormalizedPath.c_str();
38 #endif
39 
40     FileData Data;
41     Data.Name = Path;
42     Data.Size = 0;
43     Data.ModTime = 0;
44     Data.UniqueID = llvm::sys::fs::UniqueID(1, INode);
45     Data.IsDirectory = !IsFile;
46     Data.IsNamedPipe = false;
47     Data.InPCH = false;
48     StatCalls[Path] = Data;
49   }
50 
51 public:
52   // Inject a file with the given inode value to the fake file system.
53   void InjectFile(const char *Path, ino_t INode) {
54     InjectFileOrDirectory(Path, INode, /*IsFile=*/true);
55   }
56 
57   // Inject a directory with the given inode value to the fake file system.
58   void InjectDirectory(const char *Path, ino_t INode) {
59     InjectFileOrDirectory(Path, INode, /*IsFile=*/false);
60   }
61 
62   // Implement FileSystemStatCache::getStat().
63   LookupResult getStat(StringRef Path, FileData &Data, bool isFile,
64                        std::unique_ptr<vfs::File> *F,
65                        vfs::FileSystem &FS) override {
66 #ifndef LLVM_ON_WIN32
67     SmallString<128> NormalizedPath(Path);
68     llvm::sys::path::native(NormalizedPath);
69     Path = NormalizedPath.c_str();
70 #endif
71 
72     if (StatCalls.count(Path) != 0) {
73       Data = StatCalls[Path];
74       return CacheExists;
75     }
76 
77     return CacheMissing;  // This means the file/directory doesn't exist.
78   }
79 };
80 
81 // The test fixture.
82 class FileManagerTest : public ::testing::Test {
83  protected:
84   FileManagerTest() : manager(options) {
85   }
86 
87   FileSystemOptions options;
88   FileManager manager;
89 };
90 
91 // When a virtual file is added, its getDir() field is set correctly
92 // (not NULL, correct name).
93 TEST_F(FileManagerTest, getVirtualFileSetsTheDirFieldCorrectly) {
94   const FileEntry *file = manager.getVirtualFile("foo.cpp", 42, 0);
95   ASSERT_TRUE(file != nullptr);
96 
97   const DirectoryEntry *dir = file->getDir();
98   ASSERT_TRUE(dir != nullptr);
99   EXPECT_EQ(".", dir->getName());
100 
101   file = manager.getVirtualFile("x/y/z.cpp", 42, 0);
102   ASSERT_TRUE(file != nullptr);
103 
104   dir = file->getDir();
105   ASSERT_TRUE(dir != nullptr);
106   EXPECT_EQ("x/y", dir->getName());
107 }
108 
109 // Before any virtual file is added, no virtual directory exists.
110 TEST_F(FileManagerTest, NoVirtualDirectoryExistsBeforeAVirtualFileIsAdded) {
111   // An empty FakeStatCache causes all stat calls made by the
112   // FileManager to report "file/directory doesn't exist".  This
113   // avoids the possibility of the result of this test being affected
114   // by what's in the real file system.
115   manager.addStatCache(llvm::make_unique<FakeStatCache>());
116 
117   EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir/foo"));
118   EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir"));
119   EXPECT_EQ(nullptr, manager.getDirectory("virtual"));
120 }
121 
122 // When a virtual file is added, all of its ancestors should be created.
123 TEST_F(FileManagerTest, getVirtualFileCreatesDirectoryEntriesForAncestors) {
124   // Fake an empty real file system.
125   manager.addStatCache(llvm::make_unique<FakeStatCache>());
126 
127   manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
128   EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir/foo"));
129 
130   const DirectoryEntry *dir = manager.getDirectory("virtual/dir");
131   ASSERT_TRUE(dir != nullptr);
132   EXPECT_EQ("virtual/dir", dir->getName());
133 
134   dir = manager.getDirectory("virtual");
135   ASSERT_TRUE(dir != nullptr);
136   EXPECT_EQ("virtual", dir->getName());
137 }
138 
139 // getFile() returns non-NULL if a real file exists at the given path.
140 TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) {
141   // Inject fake files into the file system.
142   auto statCache = llvm::make_unique<FakeStatCache>();
143   statCache->InjectDirectory("/tmp", 42);
144   statCache->InjectFile("/tmp/test", 43);
145 
146 #ifdef LLVM_ON_WIN32
147   const char *DirName = "C:.";
148   const char *FileName = "C:test";
149   statCache->InjectDirectory(DirName, 44);
150   statCache->InjectFile(FileName, 45);
151 #endif
152 
153   manager.addStatCache(std::move(statCache));
154 
155   const FileEntry *file = manager.getFile("/tmp/test");
156   ASSERT_TRUE(file != nullptr);
157   ASSERT_TRUE(file->isValid());
158   EXPECT_EQ("/tmp/test", file->getName());
159 
160   const DirectoryEntry *dir = file->getDir();
161   ASSERT_TRUE(dir != nullptr);
162   EXPECT_EQ("/tmp", dir->getName());
163 
164 #ifdef LLVM_ON_WIN32
165   file = manager.getFile(FileName);
166   ASSERT_TRUE(file != NULL);
167 
168   dir = file->getDir();
169   ASSERT_TRUE(dir != NULL);
170   EXPECT_EQ(DirName, dir->getName());
171 #endif
172 }
173 
174 // getFile() returns non-NULL if a virtual file exists at the given path.
175 TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingVirtualFile) {
176   // Fake an empty real file system.
177   manager.addStatCache(llvm::make_unique<FakeStatCache>());
178 
179   manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
180   const FileEntry *file = manager.getFile("virtual/dir/bar.h");
181   ASSERT_TRUE(file != nullptr);
182   ASSERT_TRUE(file->isValid());
183   EXPECT_EQ("virtual/dir/bar.h", file->getName());
184 
185   const DirectoryEntry *dir = file->getDir();
186   ASSERT_TRUE(dir != nullptr);
187   EXPECT_EQ("virtual/dir", dir->getName());
188 }
189 
190 // getFile() returns different FileEntries for different paths when
191 // there's no aliasing.
192 TEST_F(FileManagerTest, getFileReturnsDifferentFileEntriesForDifferentFiles) {
193   // Inject two fake files into the file system.  Different inodes
194   // mean the files are not symlinked together.
195   auto statCache = llvm::make_unique<FakeStatCache>();
196   statCache->InjectDirectory(".", 41);
197   statCache->InjectFile("foo.cpp", 42);
198   statCache->InjectFile("bar.cpp", 43);
199   manager.addStatCache(std::move(statCache));
200 
201   const FileEntry *fileFoo = manager.getFile("foo.cpp");
202   const FileEntry *fileBar = manager.getFile("bar.cpp");
203   ASSERT_TRUE(fileFoo != nullptr);
204   ASSERT_TRUE(fileFoo->isValid());
205   ASSERT_TRUE(fileBar != nullptr);
206   ASSERT_TRUE(fileBar->isValid());
207   EXPECT_NE(fileFoo, fileBar);
208 }
209 
210 // getFile() returns NULL if neither a real file nor a virtual file
211 // exists at the given path.
212 TEST_F(FileManagerTest, getFileReturnsNULLForNonexistentFile) {
213   // Inject a fake foo.cpp into the file system.
214   auto statCache = llvm::make_unique<FakeStatCache>();
215   statCache->InjectDirectory(".", 41);
216   statCache->InjectFile("foo.cpp", 42);
217   manager.addStatCache(std::move(statCache));
218 
219   // Create a virtual bar.cpp file.
220   manager.getVirtualFile("bar.cpp", 200, 0);
221 
222   const FileEntry *file = manager.getFile("xyz.txt");
223   EXPECT_EQ(nullptr, file);
224 }
225 
226 // The following tests apply to Unix-like system only.
227 
228 #ifndef LLVM_ON_WIN32
229 
230 // getFile() returns the same FileEntry for real files that are aliases.
231 TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedRealFiles) {
232   // Inject two real files with the same inode.
233   auto statCache = llvm::make_unique<FakeStatCache>();
234   statCache->InjectDirectory("abc", 41);
235   statCache->InjectFile("abc/foo.cpp", 42);
236   statCache->InjectFile("abc/bar.cpp", 42);
237   manager.addStatCache(std::move(statCache));
238 
239   EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp"));
240 }
241 
242 // getFile() returns the same FileEntry for virtual files that have
243 // corresponding real files that are aliases.
244 TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedVirtualFiles) {
245   // Inject two real files with the same inode.
246   auto statCache = llvm::make_unique<FakeStatCache>();
247   statCache->InjectDirectory("abc", 41);
248   statCache->InjectFile("abc/foo.cpp", 42);
249   statCache->InjectFile("abc/bar.cpp", 42);
250   manager.addStatCache(std::move(statCache));
251 
252   ASSERT_TRUE(manager.getVirtualFile("abc/foo.cpp", 100, 0)->isValid());
253   ASSERT_TRUE(manager.getVirtualFile("abc/bar.cpp", 200, 0)->isValid());
254 
255   EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp"));
256 }
257 
258 TEST_F(FileManagerTest, addRemoveStatCache) {
259   manager.addStatCache(llvm::make_unique<FakeStatCache>());
260   auto statCacheOwner = llvm::make_unique<FakeStatCache>();
261   auto *statCache = statCacheOwner.get();
262   manager.addStatCache(std::move(statCacheOwner));
263   manager.addStatCache(llvm::make_unique<FakeStatCache>());
264   manager.removeStatCache(statCache);
265 }
266 
267 // getFile() Should return the same entry as getVirtualFile if the file actually
268 // is a virtual file, even if the name is not exactly the same (but is after
269 // normalisation done by the file system, like on Windows). This can be checked
270 // here by checkng the size.
271 TEST_F(FileManagerTest, getVirtualFileWithDifferentName) {
272   // Inject fake files into the file system.
273   auto statCache = llvm::make_unique<FakeStatCache>();
274   statCache->InjectDirectory("c:\\tmp", 42);
275   statCache->InjectFile("c:\\tmp\\test", 43);
276 
277   manager.addStatCache(std::move(statCache));
278 
279   // Inject the virtual file:
280   const FileEntry *file1 = manager.getVirtualFile("c:\\tmp\\test", 123, 1);
281   ASSERT_TRUE(file1 != nullptr);
282   ASSERT_TRUE(file1->isValid());
283   EXPECT_EQ(43U, file1->getUniqueID().getFile());
284   EXPECT_EQ(123, file1->getSize());
285 
286   // Lookup the virtual file with a different name:
287   const FileEntry *file2 = manager.getFile("c:/tmp/test", 100, 1);
288   ASSERT_TRUE(file2 != nullptr);
289   ASSERT_TRUE(file2->isValid());
290   // Check that it's the same UFE:
291   EXPECT_EQ(file1, file2);
292   EXPECT_EQ(43U, file2->getUniqueID().getFile());
293   // Check that the contents of the UFE are not overwritten by the entry in the
294   // filesystem:
295   EXPECT_EQ(123, file2->getSize());
296 }
297 
298 #endif  // !LLVM_ON_WIN32
299 
300 TEST_F(FileManagerTest, makeAbsoluteUsesVFS) {
301   SmallString<64> CustomWorkingDir;
302 #ifdef LLVM_ON_WIN32
303   CustomWorkingDir = "C:";
304 #else
305   CustomWorkingDir = "/";
306 #endif
307   llvm::sys::path::append(CustomWorkingDir, "some", "weird", "path");
308 
309   auto FS =
310       IntrusiveRefCntPtr<vfs::InMemoryFileSystem>(new vfs::InMemoryFileSystem);
311   // setCurrentworkingdirectory must finish without error.
312   ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir));
313 
314   FileSystemOptions Opts;
315   FileManager Manager(Opts, FS);
316 
317   SmallString<64> Path("a/foo.cpp");
318 
319   SmallString<64> ExpectedResult(CustomWorkingDir);
320   llvm::sys::path::append(ExpectedResult, Path);
321 
322   ASSERT_TRUE(Manager.makeAbsolutePath(Path));
323   EXPECT_EQ(Path, ExpectedResult);
324 }
325 
326 } // anonymous namespace
327