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 "llvm/Config/llvm-config.h"
14 #include "gtest/gtest.h"
15 
16 using namespace llvm;
17 using namespace clang;
18 
19 namespace {
20 
21 // Used to create a fake file system for running the tests with such
22 // that the tests are not affected by the structure/contents of the
23 // file system on the machine running the tests.
24 class FakeStatCache : public FileSystemStatCache {
25 private:
26   // Maps a file/directory path to its desired stat result.  Anything
27   // not in this map is considered to not exist in the file system.
28   llvm::StringMap<FileData, llvm::BumpPtrAllocator> StatCalls;
29 
InjectFileOrDirectory(const char * Path,ino_t INode,bool IsFile)30   void InjectFileOrDirectory(const char *Path, ino_t INode, bool IsFile) {
31     FileData Data;
32     Data.Name = Path;
33     Data.Size = 0;
34     Data.ModTime = 0;
35     Data.UniqueID = llvm::sys::fs::UniqueID(1, INode);
36     Data.IsDirectory = !IsFile;
37     Data.IsNamedPipe = false;
38     Data.InPCH = false;
39     StatCalls[Path] = Data;
40   }
41 
42 public:
43   // Inject a file with the given inode value to the fake file system.
InjectFile(const char * Path,ino_t INode)44   void InjectFile(const char *Path, ino_t INode) {
45     InjectFileOrDirectory(Path, INode, /*IsFile=*/true);
46   }
47 
48   // Inject a directory with the given inode value to the fake file system.
InjectDirectory(const char * Path,ino_t INode)49   void InjectDirectory(const char *Path, ino_t INode) {
50     InjectFileOrDirectory(Path, INode, /*IsFile=*/false);
51   }
52 
53   // Implement FileSystemStatCache::getStat().
getStat(const char * Path,FileData & Data,bool isFile,std::unique_ptr<vfs::File> * F,vfs::FileSystem & FS)54   LookupResult getStat(const char *Path, FileData &Data, bool isFile,
55                        std::unique_ptr<vfs::File> *F,
56                        vfs::FileSystem &FS) override {
57     if (StatCalls.count(Path) != 0) {
58       Data = StatCalls[Path];
59       return CacheExists;
60     }
61 
62     return CacheMissing;  // This means the file/directory doesn't exist.
63   }
64 };
65 
66 // The test fixture.
67 class FileManagerTest : public ::testing::Test {
68  protected:
FileManagerTest()69   FileManagerTest() : manager(options) {
70   }
71 
72   FileSystemOptions options;
73   FileManager manager;
74 };
75 
76 // When a virtual file is added, its getDir() field is set correctly
77 // (not NULL, correct name).
TEST_F(FileManagerTest,getVirtualFileSetsTheDirFieldCorrectly)78 TEST_F(FileManagerTest, getVirtualFileSetsTheDirFieldCorrectly) {
79   const FileEntry *file = manager.getVirtualFile("foo.cpp", 42, 0);
80   ASSERT_TRUE(file != nullptr);
81 
82   const DirectoryEntry *dir = file->getDir();
83   ASSERT_TRUE(dir != nullptr);
84   EXPECT_STREQ(".", dir->getName());
85 
86   file = manager.getVirtualFile("x/y/z.cpp", 42, 0);
87   ASSERT_TRUE(file != nullptr);
88 
89   dir = file->getDir();
90   ASSERT_TRUE(dir != nullptr);
91   EXPECT_STREQ("x/y", dir->getName());
92 }
93 
94 // Before any virtual file is added, no virtual directory exists.
TEST_F(FileManagerTest,NoVirtualDirectoryExistsBeforeAVirtualFileIsAdded)95 TEST_F(FileManagerTest, NoVirtualDirectoryExistsBeforeAVirtualFileIsAdded) {
96   // An empty FakeStatCache causes all stat calls made by the
97   // FileManager to report "file/directory doesn't exist".  This
98   // avoids the possibility of the result of this test being affected
99   // by what's in the real file system.
100   manager.addStatCache(llvm::make_unique<FakeStatCache>());
101 
102   EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir/foo"));
103   EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir"));
104   EXPECT_EQ(nullptr, manager.getDirectory("virtual"));
105 }
106 
107 // When a virtual file is added, all of its ancestors should be created.
TEST_F(FileManagerTest,getVirtualFileCreatesDirectoryEntriesForAncestors)108 TEST_F(FileManagerTest, getVirtualFileCreatesDirectoryEntriesForAncestors) {
109   // Fake an empty real file system.
110   manager.addStatCache(llvm::make_unique<FakeStatCache>());
111 
112   manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
113   EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir/foo"));
114 
115   const DirectoryEntry *dir = manager.getDirectory("virtual/dir");
116   ASSERT_TRUE(dir != nullptr);
117   EXPECT_STREQ("virtual/dir", dir->getName());
118 
119   dir = manager.getDirectory("virtual");
120   ASSERT_TRUE(dir != nullptr);
121   EXPECT_STREQ("virtual", dir->getName());
122 }
123 
124 // getFile() returns non-NULL if a real file exists at the given path.
TEST_F(FileManagerTest,getFileReturnsValidFileEntryForExistingRealFile)125 TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) {
126   // Inject fake files into the file system.
127   auto statCache = llvm::make_unique<FakeStatCache>();
128   statCache->InjectDirectory("/tmp", 42);
129   statCache->InjectFile("/tmp/test", 43);
130 
131 #ifdef LLVM_ON_WIN32
132   const char *DirName = "C:.";
133   const char *FileName = "C:test";
134   statCache->InjectDirectory(DirName, 44);
135   statCache->InjectFile(FileName, 45);
136 #endif
137 
138   manager.addStatCache(std::move(statCache));
139 
140   const FileEntry *file = manager.getFile("/tmp/test");
141   ASSERT_TRUE(file != nullptr);
142   EXPECT_STREQ("/tmp/test", file->getName());
143 
144   const DirectoryEntry *dir = file->getDir();
145   ASSERT_TRUE(dir != nullptr);
146   EXPECT_STREQ("/tmp", dir->getName());
147 
148 #ifdef LLVM_ON_WIN32
149   file = manager.getFile(FileName);
150   ASSERT_TRUE(file != NULL);
151 
152   dir = file->getDir();
153   ASSERT_TRUE(dir != NULL);
154   EXPECT_STREQ(DirName, dir->getName());
155 #endif
156 }
157 
158 // getFile() returns non-NULL if a virtual file exists at the given path.
TEST_F(FileManagerTest,getFileReturnsValidFileEntryForExistingVirtualFile)159 TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingVirtualFile) {
160   // Fake an empty real file system.
161   manager.addStatCache(llvm::make_unique<FakeStatCache>());
162 
163   manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
164   const FileEntry *file = manager.getFile("virtual/dir/bar.h");
165   ASSERT_TRUE(file != nullptr);
166   EXPECT_STREQ("virtual/dir/bar.h", file->getName());
167 
168   const DirectoryEntry *dir = file->getDir();
169   ASSERT_TRUE(dir != nullptr);
170   EXPECT_STREQ("virtual/dir", dir->getName());
171 }
172 
173 // getFile() returns different FileEntries for different paths when
174 // there's no aliasing.
TEST_F(FileManagerTest,getFileReturnsDifferentFileEntriesForDifferentFiles)175 TEST_F(FileManagerTest, getFileReturnsDifferentFileEntriesForDifferentFiles) {
176   // Inject two fake files into the file system.  Different inodes
177   // mean the files are not symlinked together.
178   auto statCache = llvm::make_unique<FakeStatCache>();
179   statCache->InjectDirectory(".", 41);
180   statCache->InjectFile("foo.cpp", 42);
181   statCache->InjectFile("bar.cpp", 43);
182   manager.addStatCache(std::move(statCache));
183 
184   const FileEntry *fileFoo = manager.getFile("foo.cpp");
185   const FileEntry *fileBar = manager.getFile("bar.cpp");
186   ASSERT_TRUE(fileFoo != nullptr);
187   ASSERT_TRUE(fileBar != nullptr);
188   EXPECT_NE(fileFoo, fileBar);
189 }
190 
191 // getFile() returns NULL if neither a real file nor a virtual file
192 // exists at the given path.
TEST_F(FileManagerTest,getFileReturnsNULLForNonexistentFile)193 TEST_F(FileManagerTest, getFileReturnsNULLForNonexistentFile) {
194   // Inject a fake foo.cpp into the file system.
195   auto statCache = llvm::make_unique<FakeStatCache>();
196   statCache->InjectDirectory(".", 41);
197   statCache->InjectFile("foo.cpp", 42);
198   manager.addStatCache(std::move(statCache));
199 
200   // Create a virtual bar.cpp file.
201   manager.getVirtualFile("bar.cpp", 200, 0);
202 
203   const FileEntry *file = manager.getFile("xyz.txt");
204   EXPECT_EQ(nullptr, file);
205 }
206 
207 // The following tests apply to Unix-like system only.
208 
209 #ifndef LLVM_ON_WIN32
210 
211 // getFile() returns the same FileEntry for real files that are aliases.
TEST_F(FileManagerTest,getFileReturnsSameFileEntryForAliasedRealFiles)212 TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedRealFiles) {
213   // Inject two real files with the same inode.
214   auto statCache = llvm::make_unique<FakeStatCache>();
215   statCache->InjectDirectory("abc", 41);
216   statCache->InjectFile("abc/foo.cpp", 42);
217   statCache->InjectFile("abc/bar.cpp", 42);
218   manager.addStatCache(std::move(statCache));
219 
220   EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp"));
221 }
222 
223 // getFile() returns the same FileEntry for virtual files that have
224 // corresponding real files that are aliases.
TEST_F(FileManagerTest,getFileReturnsSameFileEntryForAliasedVirtualFiles)225 TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedVirtualFiles) {
226   // Inject two real files with the same inode.
227   auto statCache = llvm::make_unique<FakeStatCache>();
228   statCache->InjectDirectory("abc", 41);
229   statCache->InjectFile("abc/foo.cpp", 42);
230   statCache->InjectFile("abc/bar.cpp", 42);
231   manager.addStatCache(std::move(statCache));
232 
233   manager.getVirtualFile("abc/foo.cpp", 100, 0);
234   manager.getVirtualFile("abc/bar.cpp", 200, 0);
235 
236   EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp"));
237 }
238 
TEST_F(FileManagerTest,addRemoveStatCache)239 TEST_F(FileManagerTest, addRemoveStatCache) {
240   manager.addStatCache(llvm::make_unique<FakeStatCache>());
241   auto statCacheOwner = llvm::make_unique<FakeStatCache>();
242   auto *statCache = statCacheOwner.get();
243   manager.addStatCache(std::move(statCacheOwner));
244   manager.addStatCache(llvm::make_unique<FakeStatCache>());
245   manager.removeStatCache(statCache);
246 }
247 
248 #endif  // !LLVM_ON_WIN32
249 
250 } // anonymous namespace
251