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