1 //===- unittests/Basic/FileMangerTest.cpp ------------ FileManger tests ---===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/Basic/FileManager.h"
10 #include "clang/Basic/FileSystemOptions.h"
11 #include "clang/Basic/FileSystemStatCache.h"
12 #include "llvm/ADT/STLExtras.h"
13 #include "llvm/Support/Path.h"
14 #include "llvm/Support/VirtualFileSystem.h"
15 #include "llvm/Testing/Support/Error.h"
16 #include "gtest/gtest.h"
17 
18 using namespace llvm;
19 using namespace clang;
20 
21 namespace {
22 
23 // Used to create a fake file system for running the tests with such
24 // that the tests are not affected by the structure/contents of the
25 // file system on the machine running the tests.
26 class FakeStatCache : public FileSystemStatCache {
27 private:
28   // Maps a file/directory path to its desired stat result.  Anything
29   // not in this map is considered to not exist in the file system.
30   llvm::StringMap<llvm::vfs::Status, llvm::BumpPtrAllocator> StatCalls;
31 
32   void InjectFileOrDirectory(const char *Path, ino_t INode, bool IsFile,
33                              const char *StatPath) {
34     SmallString<128> NormalizedPath(Path);
35     SmallString<128> NormalizedStatPath;
36     if (is_style_posix(llvm::sys::path::Style::native)) {
37       llvm::sys::path::native(NormalizedPath);
38       Path = NormalizedPath.c_str();
39 
40       if (StatPath) {
41         NormalizedStatPath = StatPath;
42         llvm::sys::path::native(NormalizedStatPath);
43         StatPath = NormalizedStatPath.c_str();
44       }
45     }
46 
47     if (!StatPath)
48       StatPath = Path;
49 
50     auto fileType = IsFile ?
51       llvm::sys::fs::file_type::regular_file :
52       llvm::sys::fs::file_type::directory_file;
53     llvm::vfs::Status Status(StatPath, llvm::sys::fs::UniqueID(1, INode),
54                              /*MTime*/{}, /*User*/0, /*Group*/0,
55                              /*Size*/0, fileType,
56                              llvm::sys::fs::perms::all_all);
57     StatCalls[Path] = Status;
58   }
59 
60 public:
61   // Inject a file with the given inode value to the fake file system.
62   void InjectFile(const char *Path, ino_t INode,
63                   const char *StatPath = nullptr) {
64     InjectFileOrDirectory(Path, INode, /*IsFile=*/true, StatPath);
65   }
66 
67   // Inject a directory with the given inode value to the fake file system.
68   void InjectDirectory(const char *Path, ino_t INode) {
69     InjectFileOrDirectory(Path, INode, /*IsFile=*/false, nullptr);
70   }
71 
72   // Implement FileSystemStatCache::getStat().
73   std::error_code getStat(StringRef Path, llvm::vfs::Status &Status,
74                           bool isFile,
75                           std::unique_ptr<llvm::vfs::File> *F,
76                           llvm::vfs::FileSystem &FS) override {
77     SmallString<128> NormalizedPath(Path);
78     if (is_style_posix(llvm::sys::path::Style::native)) {
79       llvm::sys::path::native(NormalizedPath);
80       Path = NormalizedPath.c_str();
81     }
82 
83     if (StatCalls.count(Path) != 0) {
84       Status = StatCalls[Path];
85       return std::error_code();
86     }
87 
88     return std::make_error_code(std::errc::no_such_file_or_directory);
89   }
90 };
91 
92 // The test fixture.
93 class FileManagerTest : public ::testing::Test {
94  protected:
95   FileManagerTest() : manager(options) {
96   }
97 
98   FileSystemOptions options;
99   FileManager manager;
100 };
101 
102 // When a virtual file is added, its getDir() field has correct name.
103 TEST_F(FileManagerTest, getVirtualFileSetsTheDirFieldCorrectly) {
104   FileEntryRef file = manager.getVirtualFileRef("foo.cpp", 42, 0);
105   EXPECT_EQ(".", file.getDir().getName());
106 
107   file = manager.getVirtualFileRef("x/y/z.cpp", 42, 0);
108   EXPECT_EQ("x/y", file.getDir().getName());
109 }
110 
111 // Before any virtual file is added, no virtual directory exists.
112 TEST_F(FileManagerTest, NoVirtualDirectoryExistsBeforeAVirtualFileIsAdded) {
113   // An empty FakeStatCache causes all stat calls made by the
114   // FileManager to report "file/directory doesn't exist".  This
115   // avoids the possibility of the result of this test being affected
116   // by what's in the real file system.
117   manager.setStatCache(std::make_unique<FakeStatCache>());
118 
119   ASSERT_FALSE(manager.getDirectory("virtual/dir/foo"));
120   ASSERT_FALSE(manager.getDirectory("virtual/dir"));
121   ASSERT_FALSE(manager.getDirectory("virtual"));
122 }
123 
124 // When a virtual file is added, all of its ancestors should be created.
125 TEST_F(FileManagerTest, getVirtualFileCreatesDirectoryEntriesForAncestors) {
126   // Fake an empty real file system.
127   manager.setStatCache(std::make_unique<FakeStatCache>());
128 
129   manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
130   ASSERT_FALSE(manager.getDirectory("virtual/dir/foo"));
131 
132   auto dir = manager.getDirectoryRef("virtual/dir");
133   ASSERT_THAT_EXPECTED(dir, llvm::Succeeded());
134   EXPECT_EQ("virtual/dir", dir->getName());
135 
136   dir = manager.getDirectoryRef("virtual");
137   ASSERT_THAT_EXPECTED(dir, llvm::Succeeded());
138   EXPECT_EQ("virtual", dir->getName());
139 }
140 
141 // getFileRef() succeeds if a real file exists at the given path.
142 TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) {
143   // Inject fake files into the file system.
144   auto statCache = std::make_unique<FakeStatCache>();
145   statCache->InjectDirectory("/tmp", 42);
146   statCache->InjectFile("/tmp/test", 43);
147 
148 #ifdef _WIN32
149   const char *DirName = "C:.";
150   const char *FileName = "C:test";
151   statCache->InjectDirectory(DirName, 44);
152   statCache->InjectFile(FileName, 45);
153 #endif
154 
155   manager.setStatCache(std::move(statCache));
156 
157   auto file = manager.getFileRef("/tmp/test");
158   ASSERT_THAT_EXPECTED(file, llvm::Succeeded());
159   EXPECT_EQ("/tmp/test", file->getName());
160 
161   EXPECT_EQ("/tmp", file->getDir().getName());
162 
163 #ifdef _WIN32
164   file = manager.getFileRef(FileName);
165   ASSERT_THAT_EXPECTED(file, llvm::Succeeded());
166   EXPECT_EQ(DirName, file->getDir().getName());
167 #endif
168 }
169 
170 // getFileRef() succeeds if a virtual file exists at the given path.
171 TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingVirtualFile) {
172   // Fake an empty real file system.
173   manager.setStatCache(std::make_unique<FakeStatCache>());
174 
175   manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
176   auto file = manager.getFileRef("virtual/dir/bar.h");
177   ASSERT_THAT_EXPECTED(file, llvm::Succeeded());
178   EXPECT_EQ("virtual/dir/bar.h", file->getName());
179   EXPECT_EQ("virtual/dir", file->getDir().getName());
180 }
181 
182 // getFile() returns different FileEntries for different paths when
183 // there's no aliasing.
184 TEST_F(FileManagerTest, getFileReturnsDifferentFileEntriesForDifferentFiles) {
185   // Inject two fake files into the file system.  Different inodes
186   // mean the files are not symlinked together.
187   auto statCache = std::make_unique<FakeStatCache>();
188   statCache->InjectDirectory(".", 41);
189   statCache->InjectFile("foo.cpp", 42);
190   statCache->InjectFile("bar.cpp", 43);
191   manager.setStatCache(std::move(statCache));
192 
193   auto fileFoo = manager.getFile("foo.cpp");
194   auto fileBar = manager.getFile("bar.cpp");
195   ASSERT_TRUE(fileFoo);
196   ASSERT_TRUE(fileBar);
197   EXPECT_NE(*fileFoo, *fileBar);
198 }
199 
200 // getFile() returns an error if neither a real file nor a virtual file
201 // exists at the given path.
202 TEST_F(FileManagerTest, getFileReturnsErrorForNonexistentFile) {
203   // Inject a fake foo.cpp into the file system.
204   auto statCache = std::make_unique<FakeStatCache>();
205   statCache->InjectDirectory(".", 41);
206   statCache->InjectFile("foo.cpp", 42);
207   statCache->InjectDirectory("MyDirectory", 49);
208   manager.setStatCache(std::move(statCache));
209 
210   // Create a virtual bar.cpp file.
211   manager.getVirtualFile("bar.cpp", 200, 0);
212 
213   auto file = manager.getFile("xyz.txt");
214   ASSERT_FALSE(file);
215   ASSERT_EQ(file.getError(), std::errc::no_such_file_or_directory);
216 
217   auto readingDirAsFile = manager.getFile("MyDirectory");
218   ASSERT_FALSE(readingDirAsFile);
219   ASSERT_EQ(readingDirAsFile.getError(), std::errc::is_a_directory);
220 
221   auto readingFileAsDir = manager.getDirectory("foo.cpp");
222   ASSERT_FALSE(readingFileAsDir);
223   ASSERT_EQ(readingFileAsDir.getError(), std::errc::not_a_directory);
224 }
225 
226 // The following tests apply to Unix-like system only.
227 
228 #ifndef _WIN32
229 
230 // getFile() returns the same FileEntry for real files that are aliases.
231 TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedRealFiles) {
232   // Inject two real files with the same inode.
233   auto statCache = std::make_unique<FakeStatCache>();
234   statCache->InjectDirectory("abc", 41);
235   statCache->InjectFile("abc/foo.cpp", 42);
236   statCache->InjectFile("abc/bar.cpp", 42);
237   manager.setStatCache(std::move(statCache));
238 
239   auto f1 = manager.getFile("abc/foo.cpp");
240   auto f2 = manager.getFile("abc/bar.cpp");
241 
242   EXPECT_EQ(f1 ? *f1 : nullptr,
243             f2 ? *f2 : nullptr);
244 
245   // Check that getFileRef also does the right thing.
246   auto r1 = manager.getFileRef("abc/foo.cpp");
247   auto r2 = manager.getFileRef("abc/bar.cpp");
248   ASSERT_FALSE(!r1);
249   ASSERT_FALSE(!r2);
250 
251   EXPECT_EQ("abc/foo.cpp", r1->getName());
252   EXPECT_EQ("abc/bar.cpp", r2->getName());
253   EXPECT_EQ((f1 ? *f1 : nullptr), &r1->getFileEntry());
254   EXPECT_EQ((f2 ? *f2 : nullptr), &r2->getFileEntry());
255 }
256 
257 TEST_F(FileManagerTest, getFileRefReturnsCorrectNameForDifferentStatPath) {
258   // Inject files with the same inode, but where some files have a stat that
259   // gives a different name. This is adding coverage for stat behaviour
260   // triggered by the RedirectingFileSystem for 'use-external-name' that
261   // FileManager::getFileRef has special logic for.
262   auto StatCache = std::make_unique<FakeStatCache>();
263   StatCache->InjectDirectory("dir", 40);
264   StatCache->InjectFile("dir/f1.cpp", 41);
265   StatCache->InjectFile("dir/f1-alias.cpp", 41, "dir/f1.cpp");
266   StatCache->InjectFile("dir/f2.cpp", 42);
267   StatCache->InjectFile("dir/f2-alias.cpp", 42, "dir/f2.cpp");
268 
269   // This unintuitive rename-the-file-on-stat behaviour supports how the
270   // RedirectingFileSystem VFS layer responds to stats. However, even if you
271   // have two layers, you should only get a single filename back. As such the
272   // following stat cache behaviour is not supported (the correct stat entry
273   // for a double-redirection would be "dir/f1.cpp") and the getFileRef below
274   // should assert.
275   StatCache->InjectFile("dir/f1-alias-alias.cpp", 41, "dir/f1-alias.cpp");
276 
277   manager.setStatCache(std::move(StatCache));
278 
279   // With F1, test accessing the non-redirected name first.
280   auto F1 = manager.getFileRef("dir/f1.cpp");
281   auto F1Alias = manager.getFileRef("dir/f1-alias.cpp");
282   auto F1Alias2 = manager.getFileRef("dir/f1-alias.cpp");
283   ASSERT_FALSE(!F1);
284   ASSERT_FALSE(!F1Alias);
285   ASSERT_FALSE(!F1Alias2);
286   EXPECT_EQ("dir/f1.cpp", F1->getName());
287   EXPECT_EQ("dir/f1.cpp", F1->getFileEntry().getName());
288   EXPECT_EQ("dir/f1.cpp", F1Alias->getName());
289   EXPECT_EQ("dir/f1.cpp", F1Alias2->getName());
290   EXPECT_EQ(&F1->getFileEntry(), &F1Alias->getFileEntry());
291   EXPECT_EQ(&F1->getFileEntry(), &F1Alias2->getFileEntry());
292 
293 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
294   EXPECT_DEATH((void)manager.getFileRef("dir/f1-alias-alias.cpp"),
295                "filename redirected to a non-canonical filename?");
296 #endif
297 
298   // With F2, test accessing the redirected name first.
299   auto F2Alias = manager.getFileRef("dir/f2-alias.cpp");
300   auto F2 = manager.getFileRef("dir/f2.cpp");
301   auto F2Alias2 = manager.getFileRef("dir/f2-alias.cpp");
302   ASSERT_FALSE(!F2);
303   ASSERT_FALSE(!F2Alias);
304   ASSERT_FALSE(!F2Alias2);
305   EXPECT_EQ("dir/f2.cpp", F2->getName());
306   EXPECT_EQ("dir/f2.cpp", F2->getFileEntry().getName());
307   EXPECT_EQ("dir/f2.cpp", F2Alias->getName());
308   EXPECT_EQ("dir/f2.cpp", F2Alias2->getName());
309   EXPECT_EQ(&F2->getFileEntry(), &F2Alias->getFileEntry());
310   EXPECT_EQ(&F2->getFileEntry(), &F2Alias2->getFileEntry());
311 }
312 
313 // getFile() returns the same FileEntry for virtual files that have
314 // corresponding real files that are aliases.
315 TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedVirtualFiles) {
316   // Inject two real files with the same inode.
317   auto statCache = std::make_unique<FakeStatCache>();
318   statCache->InjectDirectory("abc", 41);
319   statCache->InjectFile("abc/foo.cpp", 42);
320   statCache->InjectFile("abc/bar.cpp", 42);
321   manager.setStatCache(std::move(statCache));
322 
323   auto f1 = manager.getFile("abc/foo.cpp");
324   auto f2 = manager.getFile("abc/bar.cpp");
325 
326   EXPECT_EQ(f1 ? *f1 : nullptr,
327             f2 ? *f2 : nullptr);
328 }
329 
330 TEST_F(FileManagerTest, getFileRefEquality) {
331   auto StatCache = std::make_unique<FakeStatCache>();
332   StatCache->InjectDirectory("dir", 40);
333   StatCache->InjectFile("dir/f1.cpp", 41);
334   StatCache->InjectFile("dir/f1-also.cpp", 41);
335   StatCache->InjectFile("dir/f1-redirect.cpp", 41, "dir/f1.cpp");
336   StatCache->InjectFile("dir/f2.cpp", 42);
337   manager.setStatCache(std::move(StatCache));
338 
339   auto F1 = manager.getFileRef("dir/f1.cpp");
340   auto F1Again = manager.getFileRef("dir/f1.cpp");
341   auto F1Also = manager.getFileRef("dir/f1-also.cpp");
342   auto F1Redirect = manager.getFileRef("dir/f1-redirect.cpp");
343   auto F2 = manager.getFileRef("dir/f2.cpp");
344 
345   // Check Expected<FileEntryRef> for error.
346   ASSERT_FALSE(!F1);
347   ASSERT_FALSE(!F1Also);
348   ASSERT_FALSE(!F1Again);
349   ASSERT_FALSE(!F1Redirect);
350   ASSERT_FALSE(!F2);
351 
352   // Check names.
353   EXPECT_EQ("dir/f1.cpp", F1->getName());
354   EXPECT_EQ("dir/f1.cpp", F1Again->getName());
355   EXPECT_EQ("dir/f1-also.cpp", F1Also->getName());
356   EXPECT_EQ("dir/f1.cpp", F1Redirect->getName());
357   EXPECT_EQ("dir/f2.cpp", F2->getName());
358 
359   // Compare against FileEntry*.
360   EXPECT_EQ(&F1->getFileEntry(), *F1);
361   EXPECT_EQ(*F1, &F1->getFileEntry());
362   EXPECT_NE(&F2->getFileEntry(), *F1);
363   EXPECT_NE(*F1, &F2->getFileEntry());
364 
365   // Compare using ==.
366   EXPECT_EQ(*F1, *F1Also);
367   EXPECT_EQ(*F1, *F1Again);
368   EXPECT_EQ(*F1, *F1Redirect);
369   EXPECT_EQ(*F1Also, *F1Redirect);
370   EXPECT_NE(*F2, *F1);
371   EXPECT_NE(*F2, *F1Also);
372   EXPECT_NE(*F2, *F1Again);
373   EXPECT_NE(*F2, *F1Redirect);
374 
375   // Compare using isSameRef.
376   EXPECT_TRUE(F1->isSameRef(*F1Again));
377   EXPECT_TRUE(F1->isSameRef(*F1Redirect));
378   EXPECT_FALSE(F1->isSameRef(*F1Also));
379   EXPECT_FALSE(F1->isSameRef(*F2));
380 }
381 
382 // getFile() Should return the same entry as getVirtualFile if the file actually
383 // is a virtual file, even if the name is not exactly the same (but is after
384 // normalisation done by the file system, like on Windows). This can be checked
385 // here by checking the size.
386 TEST_F(FileManagerTest, getVirtualFileWithDifferentName) {
387   // Inject fake files into the file system.
388   auto statCache = std::make_unique<FakeStatCache>();
389   statCache->InjectDirectory("c:\\tmp", 42);
390   statCache->InjectFile("c:\\tmp\\test", 43);
391 
392   manager.setStatCache(std::move(statCache));
393 
394   // Inject the virtual file:
395   const FileEntry *file1 = manager.getVirtualFile("c:\\tmp\\test", 123, 1);
396   ASSERT_TRUE(file1 != nullptr);
397   EXPECT_EQ(43U, file1->getUniqueID().getFile());
398   EXPECT_EQ(123, file1->getSize());
399 
400   // Lookup the virtual file with a different name:
401   auto file2 = manager.getFile("c:/tmp/test", 100, 1);
402   ASSERT_TRUE(file2);
403   // Check that it's the same UFE:
404   EXPECT_EQ(file1, *file2);
405   EXPECT_EQ(43U, (*file2)->getUniqueID().getFile());
406   // Check that the contents of the UFE are not overwritten by the entry in the
407   // filesystem:
408   EXPECT_EQ(123, (*file2)->getSize());
409 }
410 
411 #endif  // !_WIN32
412 
413 static StringRef getSystemRoot() {
414   return is_style_windows(llvm::sys::path::Style::native) ? "C:\\" : "/";
415 }
416 
417 TEST_F(FileManagerTest, makeAbsoluteUsesVFS) {
418   // FIXME: Should this be using a root path / call getSystemRoot()? For now,
419   // avoiding that and leaving the test as-is.
420   SmallString<64> CustomWorkingDir =
421       is_style_windows(llvm::sys::path::Style::native) ? StringRef("C:")
422                                                        : StringRef("/");
423   llvm::sys::path::append(CustomWorkingDir, "some", "weird", "path");
424 
425   auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>(
426       new llvm::vfs::InMemoryFileSystem);
427   // setCurrentworkingdirectory must finish without error.
428   ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir));
429 
430   FileSystemOptions Opts;
431   FileManager Manager(Opts, FS);
432 
433   SmallString<64> Path("a/foo.cpp");
434 
435   SmallString<64> ExpectedResult(CustomWorkingDir);
436   llvm::sys::path::append(ExpectedResult, Path);
437 
438   ASSERT_TRUE(Manager.makeAbsolutePath(Path));
439   EXPECT_EQ(Path, ExpectedResult);
440 }
441 
442 // getVirtualFile should always fill the real path.
443 TEST_F(FileManagerTest, getVirtualFileFillsRealPathName) {
444   SmallString<64> CustomWorkingDir = getSystemRoot();
445 
446   auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>(
447       new llvm::vfs::InMemoryFileSystem);
448   // setCurrentworkingdirectory must finish without error.
449   ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir));
450 
451   FileSystemOptions Opts;
452   FileManager Manager(Opts, FS);
453 
454   // Inject fake files into the file system.
455   auto statCache = std::make_unique<FakeStatCache>();
456   statCache->InjectDirectory("/tmp", 42);
457   statCache->InjectFile("/tmp/test", 43);
458 
459   Manager.setStatCache(std::move(statCache));
460 
461   // Check for real path.
462   const FileEntry *file = Manager.getVirtualFile("/tmp/test", 123, 1);
463   ASSERT_TRUE(file != nullptr);
464   SmallString<64> ExpectedResult = CustomWorkingDir;
465 
466   llvm::sys::path::append(ExpectedResult, "tmp", "test");
467   EXPECT_EQ(file->tryGetRealPathName(), ExpectedResult);
468 }
469 
470 TEST_F(FileManagerTest, getFileDontOpenRealPath) {
471   SmallString<64> CustomWorkingDir = getSystemRoot();
472 
473   auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>(
474       new llvm::vfs::InMemoryFileSystem);
475   // setCurrentworkingdirectory must finish without error.
476   ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir));
477 
478   FileSystemOptions Opts;
479   FileManager Manager(Opts, FS);
480 
481   // Inject fake files into the file system.
482   auto statCache = std::make_unique<FakeStatCache>();
483   statCache->InjectDirectory("/tmp", 42);
484   statCache->InjectFile("/tmp/test", 43);
485 
486   Manager.setStatCache(std::move(statCache));
487 
488   // Check for real path.
489   auto file = Manager.getFile("/tmp/test", /*OpenFile=*/false);
490   ASSERT_TRUE(file);
491   SmallString<64> ExpectedResult = CustomWorkingDir;
492 
493   llvm::sys::path::append(ExpectedResult, "tmp", "test");
494   EXPECT_EQ((*file)->tryGetRealPathName(), ExpectedResult);
495 }
496 
497 TEST_F(FileManagerTest, getBypassFile) {
498   SmallString<64> CustomWorkingDir;
499 #ifdef _WIN32
500   CustomWorkingDir = "C:/";
501 #else
502   CustomWorkingDir = "/";
503 #endif
504 
505   auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>(
506       new llvm::vfs::InMemoryFileSystem);
507   // setCurrentworkingdirectory must finish without error.
508   ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir));
509 
510   FileSystemOptions Opts;
511   FileManager Manager(Opts, FS);
512 
513   // Inject fake files into the file system.
514   auto Cache = std::make_unique<FakeStatCache>();
515   Cache->InjectDirectory("/tmp", 42);
516   Cache->InjectFile("/tmp/test", 43);
517   Manager.setStatCache(std::move(Cache));
518 
519   // Set up a virtual file with a different size than FakeStatCache uses.
520   const FileEntry *File = Manager.getVirtualFile("/tmp/test", /*Size=*/10, 0);
521   ASSERT_TRUE(File);
522   const FileEntry &FE = *File;
523   EXPECT_EQ(FE.getSize(), 10);
524 
525   // Calling a second time should not affect the UID or size.
526   unsigned VirtualUID = FE.getUID();
527   llvm::Optional<FileEntryRef> SearchRef;
528   ASSERT_THAT_ERROR(Manager.getFileRef("/tmp/test").moveInto(SearchRef),
529                     Succeeded());
530   EXPECT_EQ(&FE, &SearchRef->getFileEntry());
531   EXPECT_EQ(FE.getUID(), VirtualUID);
532   EXPECT_EQ(FE.getSize(), 10);
533 
534   // Bypass the file.
535   llvm::Optional<FileEntryRef> BypassRef =
536       Manager.getBypassFile(File->getLastRef());
537   ASSERT_TRUE(BypassRef);
538   EXPECT_EQ("/tmp/test", BypassRef->getName());
539 
540   // Check that it's different in the right ways.
541   EXPECT_NE(&BypassRef->getFileEntry(), File);
542   EXPECT_NE(BypassRef->getUID(), VirtualUID);
543   EXPECT_NE(BypassRef->getSize(), FE.getSize());
544 
545   // The virtual file should still be returned when searching.
546   ASSERT_THAT_ERROR(Manager.getFileRef("/tmp/test").moveInto(SearchRef),
547                     Succeeded());
548   EXPECT_EQ(&FE, &SearchRef->getFileEntry());
549 }
550 
551 } // anonymous namespace
552