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/FileSystemOptions.h"
11#include "clang/Basic/FileSystemStatCache.h"
12#include "clang/Basic/FileManager.h"
13
14#include "gtest/gtest.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<struct stat, llvm::BumpPtrAllocator> StatCalls;
29
30  void InjectFileOrDirectory(const char *Path, ino_t INode, bool IsFile) {
31    struct stat statBuf;
32    memset(&statBuf, 0, sizeof(statBuf));
33    statBuf.st_dev = 1;
34#ifndef _WIN32  // struct stat has no st_ino field on Windows.
35    statBuf.st_ino = INode;
36#endif
37    statBuf.st_mode = IsFile ? (0777 | S_IFREG)  // a regular file
38        : (0777 | S_IFDIR);  // a directory
39    StatCalls[Path] = statBuf;
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  virtual LookupResult getStat(const char *Path, struct stat &StatBuf,
55                               int *FileDescriptor) {
56    if (StatCalls.count(Path) != 0) {
57      StatBuf = StatCalls[Path];
58      return CacheExists;
59    }
60
61    return CacheMissing;  // This means the file/directory doesn't exist.
62  }
63};
64
65// The test fixture.
66class FileManagerTest : public ::testing::Test {
67 protected:
68  FileManagerTest() : manager(options) {
69  }
70
71  FileSystemOptions options;
72  FileManager manager;
73};
74
75// When a virtual file is added, its getDir() field is set correctly
76// (not NULL, correct name).
77TEST_F(FileManagerTest, getVirtualFileSetsTheDirFieldCorrectly) {
78  const FileEntry *file = manager.getVirtualFile("foo.cpp", 42, 0);
79  ASSERT_TRUE(file != NULL);
80
81  const DirectoryEntry *dir = file->getDir();
82  ASSERT_TRUE(dir != NULL);
83  EXPECT_STREQ(".", dir->getName());
84
85  file = manager.getVirtualFile("x/y/z.cpp", 42, 0);
86  ASSERT_TRUE(file != NULL);
87
88  dir = file->getDir();
89  ASSERT_TRUE(dir != NULL);
90  EXPECT_STREQ("x/y", dir->getName());
91}
92
93// Before any virtual file is added, no virtual directory exists.
94TEST_F(FileManagerTest, NoVirtualDirectoryExistsBeforeAVirtualFileIsAdded) {
95  // An empty FakeStatCache causes all stat calls made by the
96  // FileManager to report "file/directory doesn't exist".  This
97  // avoids the possibility of the result of this test being affected
98  // by what's in the real file system.
99  manager.addStatCache(new FakeStatCache);
100
101  EXPECT_EQ(NULL, manager.getDirectory("virtual/dir/foo"));
102  EXPECT_EQ(NULL, manager.getDirectory("virtual/dir"));
103  EXPECT_EQ(NULL, manager.getDirectory("virtual"));
104}
105
106// When a virtual file is added, all of its ancestors should be created.
107TEST_F(FileManagerTest, getVirtualFileCreatesDirectoryEntriesForAncestors) {
108  // Fake an empty real file system.
109  manager.addStatCache(new FakeStatCache);
110
111  manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
112  EXPECT_EQ(NULL, manager.getDirectory("virtual/dir/foo"));
113
114  const DirectoryEntry *dir = manager.getDirectory("virtual/dir");
115  ASSERT_TRUE(dir != NULL);
116  EXPECT_STREQ("virtual/dir", dir->getName());
117
118  dir = manager.getDirectory("virtual");
119  ASSERT_TRUE(dir != NULL);
120  EXPECT_STREQ("virtual", dir->getName());
121}
122
123// getFile() returns non-NULL if a real file exists at the given path.
124TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) {
125  // Inject fake files into the file system.
126  FakeStatCache *statCache = new FakeStatCache;
127  statCache->InjectDirectory("/tmp", 42);
128  statCache->InjectFile("/tmp/test", 43);
129  manager.addStatCache(statCache);
130
131  const FileEntry *file = manager.getFile("/tmp/test");
132  ASSERT_TRUE(file != NULL);
133  EXPECT_STREQ("/tmp/test", file->getName());
134
135  const DirectoryEntry *dir = file->getDir();
136  ASSERT_TRUE(dir != NULL);
137  EXPECT_STREQ("/tmp", dir->getName());
138}
139
140// getFile() returns non-NULL if a virtual file exists at the given path.
141TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingVirtualFile) {
142  // Fake an empty real file system.
143  manager.addStatCache(new FakeStatCache);
144
145  manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
146  const FileEntry *file = manager.getFile("virtual/dir/bar.h");
147  ASSERT_TRUE(file != NULL);
148  EXPECT_STREQ("virtual/dir/bar.h", file->getName());
149
150  const DirectoryEntry *dir = file->getDir();
151  ASSERT_TRUE(dir != NULL);
152  EXPECT_STREQ("virtual/dir", dir->getName());
153}
154
155// getFile() returns different FileEntries for different paths when
156// there's no aliasing.
157TEST_F(FileManagerTest, getFileReturnsDifferentFileEntriesForDifferentFiles) {
158  // Inject two fake files into the file system.  Different inodes
159  // mean the files are not symlinked together.
160  FakeStatCache *statCache = new FakeStatCache;
161  statCache->InjectDirectory(".", 41);
162  statCache->InjectFile("foo.cpp", 42);
163  statCache->InjectFile("bar.cpp", 43);
164  manager.addStatCache(statCache);
165
166  const FileEntry *fileFoo = manager.getFile("foo.cpp");
167  const FileEntry *fileBar = manager.getFile("bar.cpp");
168  ASSERT_TRUE(fileFoo != NULL);
169  ASSERT_TRUE(fileBar != NULL);
170  EXPECT_NE(fileFoo, fileBar);
171}
172
173// getFile() returns NULL if neither a real file nor a virtual file
174// exists at the given path.
175TEST_F(FileManagerTest, getFileReturnsNULLForNonexistentFile) {
176  // Inject a fake foo.cpp into the file system.
177  FakeStatCache *statCache = new FakeStatCache;
178  statCache->InjectDirectory(".", 41);
179  statCache->InjectFile("foo.cpp", 42);
180  manager.addStatCache(statCache);
181
182  // Create a virtual bar.cpp file.
183  manager.getVirtualFile("bar.cpp", 200, 0);
184
185  const FileEntry *file = manager.getFile("xyz.txt");
186  EXPECT_EQ(NULL, file);
187}
188
189// The following tests apply to Unix-like system only.
190
191#ifndef _WIN32
192
193// getFile() returns the same FileEntry for real files that are aliases.
194TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedRealFiles) {
195  // Inject two real files with the same inode.
196  FakeStatCache *statCache = new FakeStatCache;
197  statCache->InjectDirectory("abc", 41);
198  statCache->InjectFile("abc/foo.cpp", 42);
199  statCache->InjectFile("abc/bar.cpp", 42);
200  manager.addStatCache(statCache);
201
202  EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp"));
203}
204
205// getFile() returns the same FileEntry for virtual files that have
206// corresponding real files that are aliases.
207TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedVirtualFiles) {
208  // Inject two real files with the same inode.
209  FakeStatCache *statCache = new FakeStatCache;
210  statCache->InjectDirectory("abc", 41);
211  statCache->InjectFile("abc/foo.cpp", 42);
212  statCache->InjectFile("abc/bar.cpp", 42);
213  manager.addStatCache(statCache);
214
215  manager.getVirtualFile("abc/foo.cpp", 100, 0);
216  manager.getVirtualFile("abc/bar.cpp", 200, 0);
217
218  EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp"));
219}
220
221#endif  // !_WIN32
222
223} // anonymous namespace
224