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 "gtest/gtest.h"
14 #include "llvm/Config/llvm-config.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(new 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(new 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 FakeStatCache *statCache = new 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(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(new 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 FakeStatCache *statCache = new FakeStatCache;
179 statCache->InjectDirectory(".", 41);
180 statCache->InjectFile("foo.cpp", 42);
181 statCache->InjectFile("bar.cpp", 43);
182 manager.addStatCache(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 FakeStatCache *statCache = new FakeStatCache;
196 statCache->InjectDirectory(".", 41);
197 statCache->InjectFile("foo.cpp", 42);
198 manager.addStatCache(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 FakeStatCache *statCache = new FakeStatCache;
215 statCache->InjectDirectory("abc", 41);
216 statCache->InjectFile("abc/foo.cpp", 42);
217 statCache->InjectFile("abc/bar.cpp", 42);
218 manager.addStatCache(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 FakeStatCache *statCache = new FakeStatCache;
228 statCache->InjectDirectory("abc", 41);
229 statCache->InjectFile("abc/foo.cpp", 42);
230 statCache->InjectFile("abc/bar.cpp", 42);
231 manager.addStatCache(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
239 #endif // !LLVM_ON_WIN32
240
241 } // anonymous namespace
242