1 //===-- FileSystemTest.cpp --------------------------------------*- C++ -*-===// 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 "gmock/gmock.h" 11 #include "gtest/gtest.h" 12 13 #include "lldb/Host/FileSystem.h" 14 #include "llvm/Support/Errc.h" 15 16 extern const char *TestMainArgv0; 17 18 using namespace lldb_private; 19 using namespace llvm; 20 using llvm::sys::fs::UniqueID; 21 22 // Modified from llvm/unittests/Support/VirtualFileSystemTest.cpp 23 namespace { 24 struct DummyFile : public vfs::File { 25 vfs::Status S; 26 explicit DummyFile(vfs::Status S) : S(S) {} 27 llvm::ErrorOr<vfs::Status> status() override { return S; } 28 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 29 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 30 bool IsVolatile) override { 31 llvm_unreachable("unimplemented"); 32 } 33 std::error_code close() override { return std::error_code(); } 34 }; 35 36 class DummyFileSystem : public vfs::FileSystem { 37 int FSID; // used to produce UniqueIDs 38 int FileID; // used to produce UniqueIDs 39 std::string cwd; 40 std::map<std::string, vfs::Status> FilesAndDirs; 41 42 static int getNextFSID() { 43 static int Count = 0; 44 return Count++; 45 } 46 47 public: 48 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {} 49 50 ErrorOr<vfs::Status> status(const Twine &Path) override { 51 std::map<std::string, vfs::Status>::iterator I = 52 FilesAndDirs.find(Path.str()); 53 if (I == FilesAndDirs.end()) 54 return make_error_code(llvm::errc::no_such_file_or_directory); 55 return I->second; 56 } 57 ErrorOr<std::unique_ptr<vfs::File>> 58 openFileForRead(const Twine &Path) override { 59 auto S = status(Path); 60 if (S) 61 return std::unique_ptr<vfs::File>(new DummyFile{*S}); 62 return S.getError(); 63 } 64 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 65 return cwd; 66 } 67 std::error_code setCurrentWorkingDirectory(const Twine &Path) override { 68 cwd = Path.str(); 69 return std::error_code(); 70 } 71 // Map any symlink to "/symlink". 72 std::error_code getRealPath(const Twine &Path, 73 SmallVectorImpl<char> &Output) const override { 74 auto I = FilesAndDirs.find(Path.str()); 75 if (I == FilesAndDirs.end()) 76 return make_error_code(llvm::errc::no_such_file_or_directory); 77 if (I->second.isSymlink()) { 78 Output.clear(); 79 Twine("/symlink").toVector(Output); 80 return std::error_code(); 81 } 82 Output.clear(); 83 Path.toVector(Output); 84 return std::error_code(); 85 } 86 87 struct DirIterImpl : public llvm::vfs::detail::DirIterImpl { 88 std::map<std::string, vfs::Status> &FilesAndDirs; 89 std::map<std::string, vfs::Status>::iterator I; 90 std::string Path; 91 bool isInPath(StringRef S) { 92 if (Path.size() < S.size() && S.find(Path) == 0) { 93 auto LastSep = S.find_last_of('/'); 94 if (LastSep == Path.size() || LastSep == Path.size() - 1) 95 return true; 96 } 97 return false; 98 } 99 DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs, 100 const Twine &_Path) 101 : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()), 102 Path(_Path.str()) { 103 for (; I != FilesAndDirs.end(); ++I) { 104 if (isInPath(I->first)) { 105 CurrentEntry = 106 vfs::directory_entry(I->second.getName(), I->second.getType()); 107 break; 108 } 109 } 110 } 111 std::error_code increment() override { 112 ++I; 113 for (; I != FilesAndDirs.end(); ++I) { 114 if (isInPath(I->first)) { 115 CurrentEntry = 116 vfs::directory_entry(I->second.getName(), I->second.getType()); 117 break; 118 } 119 } 120 if (I == FilesAndDirs.end()) 121 CurrentEntry = vfs::directory_entry(); 122 return std::error_code(); 123 } 124 }; 125 126 vfs::directory_iterator dir_begin(const Twine &Dir, 127 std::error_code &EC) override { 128 return vfs::directory_iterator( 129 std::make_shared<DirIterImpl>(FilesAndDirs, Dir)); 130 } 131 132 void addEntry(StringRef Path, const vfs::Status &Status) { 133 FilesAndDirs[Path] = Status; 134 } 135 136 void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { 137 vfs::Status S(Path, UniqueID(FSID, FileID++), 138 std::chrono::system_clock::now(), 0, 0, 1024, 139 sys::fs::file_type::regular_file, Perms); 140 addEntry(Path, S); 141 } 142 143 void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { 144 vfs::Status S(Path, UniqueID(FSID, FileID++), 145 std::chrono::system_clock::now(), 0, 0, 0, 146 sys::fs::file_type::directory_file, Perms); 147 addEntry(Path, S); 148 } 149 150 void addSymlink(StringRef Path) { 151 vfs::Status S(Path, UniqueID(FSID, FileID++), 152 std::chrono::system_clock::now(), 0, 0, 0, 153 sys::fs::file_type::symlink_file, sys::fs::all_all); 154 addEntry(Path, S); 155 } 156 }; 157 } // namespace 158 159 TEST(FileSystemTest, FileAndDirectoryComponents) { 160 using namespace std::chrono; 161 162 const bool resolve = true; 163 #ifdef _WIN32 164 FileSpec fs1("C:\\FILE\\THAT\\DOES\\NOT\\EXIST.TXT", !resolve); 165 #else 166 FileSpec fs1("/file/that/does/not/exist.txt", !resolve); 167 #endif 168 FileSpec fs2(TestMainArgv0, resolve); 169 170 FileSystem fs; 171 172 EXPECT_EQ(system_clock::time_point(), fs.GetModificationTime(fs1)); 173 EXPECT_LT(system_clock::time_point() + hours(24 * 365 * 20), 174 fs.GetModificationTime(fs2)); 175 } 176 177 static IntrusiveRefCntPtr<DummyFileSystem> GetSimpleDummyFS() { 178 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); 179 D->addRegularFile("/foo"); 180 D->addDirectory("/bar"); 181 D->addSymlink("/baz"); 182 D->addRegularFile("/qux", ~sys::fs::perms::all_read); 183 D->setCurrentWorkingDirectory("/"); 184 return D; 185 } 186 187 TEST(FileSystemTest, Exists) { 188 FileSystem fs(GetSimpleDummyFS()); 189 190 EXPECT_TRUE(fs.Exists("/foo")); 191 EXPECT_TRUE(fs.Exists(FileSpec("/foo", false, FileSpec::Style::posix))); 192 } 193 194 TEST(FileSystemTest, Readable) { 195 FileSystem fs(GetSimpleDummyFS()); 196 197 EXPECT_TRUE(fs.Readable("/foo")); 198 EXPECT_TRUE(fs.Readable(FileSpec("/foo", false, FileSpec::Style::posix))); 199 200 EXPECT_FALSE(fs.Readable("/qux")); 201 EXPECT_FALSE(fs.Readable(FileSpec("/qux", false, FileSpec::Style::posix))); 202 } 203 204 TEST(FileSystemTest, GetByteSize) { 205 FileSystem fs(GetSimpleDummyFS()); 206 207 EXPECT_EQ((uint64_t)1024, fs.GetByteSize("/foo")); 208 EXPECT_EQ((uint64_t)1024, 209 fs.GetByteSize(FileSpec("/foo", false, FileSpec::Style::posix))); 210 } 211 212 TEST(FileSystemTest, GetPermissions) { 213 FileSystem fs(GetSimpleDummyFS()); 214 215 EXPECT_EQ(sys::fs::all_all, fs.GetPermissions("/foo")); 216 EXPECT_EQ(sys::fs::all_all, 217 fs.GetPermissions(FileSpec("/foo", false, FileSpec::Style::posix))); 218 } 219 220 TEST(FileSystemTest, MakeAbsolute) { 221 FileSystem fs(GetSimpleDummyFS()); 222 223 { 224 StringRef foo_relative = "foo"; 225 SmallString<16> foo(foo_relative); 226 auto EC = fs.MakeAbsolute(foo); 227 EXPECT_FALSE(EC); 228 EXPECT_TRUE(foo.equals("/foo")); 229 } 230 231 { 232 FileSpec file_spec("foo", false); 233 auto EC = fs.MakeAbsolute(file_spec); 234 EXPECT_FALSE(EC); 235 EXPECT_EQ("/foo", file_spec.GetPath()); 236 } 237 } 238 239 TEST(FileSystemTest, Resolve) { 240 FileSystem fs(GetSimpleDummyFS()); 241 242 { 243 StringRef foo_relative = "foo"; 244 SmallString<16> foo(foo_relative); 245 fs.Resolve(foo); 246 EXPECT_TRUE(foo.equals("/foo")); 247 } 248 249 { 250 FileSpec file_spec("foo", false); 251 fs.Resolve(file_spec); 252 EXPECT_EQ("/foo", file_spec.GetPath()); 253 } 254 255 { 256 StringRef foo_relative = "bogus"; 257 SmallString<16> foo(foo_relative); 258 fs.Resolve(foo); 259 EXPECT_TRUE(foo.equals("bogus")); 260 } 261 262 { 263 FileSpec file_spec("bogus", false); 264 fs.Resolve(file_spec); 265 EXPECT_EQ("bogus", file_spec.GetPath()); 266 } 267 } 268 269 FileSystem::EnumerateDirectoryResult 270 VFSCallback(void *baton, llvm::sys::fs::file_type file_type, 271 llvm::StringRef path) { 272 auto visited = static_cast<std::vector<std::string> *>(baton); 273 visited->push_back(path.str()); 274 return FileSystem::eEnumerateDirectoryResultNext; 275 } 276 277 TEST(FileSystemTest, EnumerateDirectory) { 278 FileSystem fs(GetSimpleDummyFS()); 279 280 std::vector<std::string> visited; 281 282 constexpr bool find_directories = true; 283 constexpr bool find_files = true; 284 constexpr bool find_other = true; 285 286 fs.EnumerateDirectory("/", find_directories, find_files, find_other, 287 VFSCallback, &visited); 288 289 EXPECT_THAT(visited, 290 testing::UnorderedElementsAre("/foo", "/bar", "/baz", "/qux")); 291 } 292