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