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
16using namespace llvm;
17using namespace clang;
18
19namespace {
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.
24class FakeStatCache : public FileSystemStatCache {
25private:
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
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
42public:
43  // Inject a file with the given inode value to the fake file system.
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.
49  void InjectDirectory(const char *Path, ino_t INode) {
50    InjectFileOrDirectory(Path, INode, /*IsFile=*/false);
51  }
52
53  // Implement FileSystemStatCache::getStat().
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.
67class FileManagerTest : public ::testing::Test {
68 protected:
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).
78TEST_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.
95TEST_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.
108TEST_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.
125TEST_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.
159TEST_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.
175TEST_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.
193TEST_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.
212TEST_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.
225TEST_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