1 //===-- FileSpecTest.cpp --------------------------------------------------===// 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 "gtest/gtest.h" 10 11 #include "lldb/Utility/FileSpec.h" 12 13 using namespace lldb_private; 14 15 static FileSpec PosixSpec(llvm::StringRef path) { 16 return FileSpec(path, FileSpec::Style::posix); 17 } 18 19 static FileSpec WindowsSpec(llvm::StringRef path) { 20 return FileSpec(path, FileSpec::Style::windows); 21 } 22 23 TEST(FileSpecTest, FileAndDirectoryComponents) { 24 FileSpec fs_posix("/foo/bar", FileSpec::Style::posix); 25 EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); 26 EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString()); 27 EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString()); 28 29 FileSpec fs_windows("F:\\bar", FileSpec::Style::windows); 30 EXPECT_STREQ("F:\\bar", fs_windows.GetCString()); 31 // EXPECT_STREQ("F:\\", fs_windows.GetDirectory().GetCString()); // It returns 32 // "F:/" 33 EXPECT_STREQ("bar", fs_windows.GetFilename().GetCString()); 34 35 FileSpec fs_posix_root("/", FileSpec::Style::posix); 36 EXPECT_STREQ("/", fs_posix_root.GetCString()); 37 EXPECT_EQ(nullptr, fs_posix_root.GetDirectory().GetCString()); 38 EXPECT_STREQ("/", fs_posix_root.GetFilename().GetCString()); 39 40 FileSpec fs_net_drive("//net", FileSpec::Style::posix); 41 EXPECT_STREQ("//net", fs_net_drive.GetCString()); 42 EXPECT_EQ(nullptr, fs_net_drive.GetDirectory().GetCString()); 43 EXPECT_STREQ("//net", fs_net_drive.GetFilename().GetCString()); 44 45 FileSpec fs_net_root("//net/", FileSpec::Style::posix); 46 EXPECT_STREQ("//net/", fs_net_root.GetCString()); 47 EXPECT_STREQ("//net", fs_net_root.GetDirectory().GetCString()); 48 EXPECT_STREQ("/", fs_net_root.GetFilename().GetCString()); 49 50 FileSpec fs_windows_drive("F:", FileSpec::Style::windows); 51 EXPECT_STREQ("F:", fs_windows_drive.GetCString()); 52 EXPECT_EQ(nullptr, fs_windows_drive.GetDirectory().GetCString()); 53 EXPECT_STREQ("F:", fs_windows_drive.GetFilename().GetCString()); 54 55 FileSpec fs_windows_root("F:\\", FileSpec::Style::windows); 56 EXPECT_STREQ("F:\\", fs_windows_root.GetCString()); 57 EXPECT_STREQ("F:", fs_windows_root.GetDirectory().GetCString()); 58 // EXPECT_STREQ("\\", fs_windows_root.GetFilename().GetCString()); // It 59 // returns "/" 60 61 FileSpec fs_posix_long("/foo/bar/baz", FileSpec::Style::posix); 62 EXPECT_STREQ("/foo/bar/baz", fs_posix_long.GetCString()); 63 EXPECT_STREQ("/foo/bar", fs_posix_long.GetDirectory().GetCString()); 64 EXPECT_STREQ("baz", fs_posix_long.GetFilename().GetCString()); 65 66 FileSpec fs_windows_long("F:\\bar\\baz", FileSpec::Style::windows); 67 EXPECT_STREQ("F:\\bar\\baz", fs_windows_long.GetCString()); 68 // EXPECT_STREQ("F:\\bar", fs_windows_long.GetDirectory().GetCString()); // It 69 // returns "F:/bar" 70 EXPECT_STREQ("baz", fs_windows_long.GetFilename().GetCString()); 71 72 FileSpec fs_posix_trailing_slash("/foo/bar/", FileSpec::Style::posix); 73 EXPECT_STREQ("/foo/bar", fs_posix_trailing_slash.GetCString()); 74 EXPECT_STREQ("/foo", fs_posix_trailing_slash.GetDirectory().GetCString()); 75 EXPECT_STREQ("bar", fs_posix_trailing_slash.GetFilename().GetCString()); 76 77 FileSpec fs_windows_trailing_slash("F:\\bar\\", FileSpec::Style::windows); 78 EXPECT_STREQ("F:\\bar", fs_windows_trailing_slash.GetCString()); 79 EXPECT_STREQ("bar", fs_windows_trailing_slash.GetFilename().GetCString()); 80 } 81 82 TEST(FileSpecTest, AppendPathComponent) { 83 FileSpec fs_posix("/foo", FileSpec::Style::posix); 84 fs_posix.AppendPathComponent("bar"); 85 EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); 86 EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString()); 87 EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString()); 88 89 FileSpec fs_posix_2("/foo", FileSpec::Style::posix); 90 fs_posix_2.AppendPathComponent("//bar/baz"); 91 EXPECT_STREQ("/foo/bar/baz", fs_posix_2.GetCString()); 92 EXPECT_STREQ("/foo/bar", fs_posix_2.GetDirectory().GetCString()); 93 EXPECT_STREQ("baz", fs_posix_2.GetFilename().GetCString()); 94 95 FileSpec fs_windows("F:\\bar", FileSpec::Style::windows); 96 fs_windows.AppendPathComponent("baz"); 97 EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString()); 98 // EXPECT_STREQ("F:\\bar", fs_windows.GetDirectory().GetCString()); // It 99 // returns "F:/bar" 100 EXPECT_STREQ("baz", fs_windows.GetFilename().GetCString()); 101 102 FileSpec fs_posix_root("/", FileSpec::Style::posix); 103 fs_posix_root.AppendPathComponent("bar"); 104 EXPECT_STREQ("/bar", fs_posix_root.GetCString()); 105 EXPECT_STREQ("/", fs_posix_root.GetDirectory().GetCString()); 106 EXPECT_STREQ("bar", fs_posix_root.GetFilename().GetCString()); 107 108 FileSpec fs_windows_root("F:\\", FileSpec::Style::windows); 109 fs_windows_root.AppendPathComponent("bar"); 110 EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString()); 111 // EXPECT_STREQ("F:\\", fs_windows_root.GetDirectory().GetCString()); // It 112 // returns "F:/" 113 EXPECT_STREQ("bar", fs_windows_root.GetFilename().GetCString()); 114 } 115 116 TEST(FileSpecTest, CopyByAppendingPathComponent) { 117 FileSpec fs = PosixSpec("/foo").CopyByAppendingPathComponent("bar"); 118 EXPECT_STREQ("/foo/bar", fs.GetCString()); 119 EXPECT_STREQ("/foo", fs.GetDirectory().GetCString()); 120 EXPECT_STREQ("bar", fs.GetFilename().GetCString()); 121 } 122 123 TEST(FileSpecTest, PrependPathComponent) { 124 FileSpec fs_posix("foo", FileSpec::Style::posix); 125 fs_posix.PrependPathComponent("/bar"); 126 EXPECT_STREQ("/bar/foo", fs_posix.GetCString()); 127 128 FileSpec fs_posix_2("foo/bar", FileSpec::Style::posix); 129 fs_posix_2.PrependPathComponent("/baz"); 130 EXPECT_STREQ("/baz/foo/bar", fs_posix_2.GetCString()); 131 132 FileSpec fs_windows("baz", FileSpec::Style::windows); 133 fs_windows.PrependPathComponent("F:\\bar"); 134 EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString()); 135 136 FileSpec fs_posix_root("bar", FileSpec::Style::posix); 137 fs_posix_root.PrependPathComponent("/"); 138 EXPECT_STREQ("/bar", fs_posix_root.GetCString()); 139 140 FileSpec fs_windows_root("bar", FileSpec::Style::windows); 141 fs_windows_root.PrependPathComponent("F:\\"); 142 EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString()); 143 } 144 145 TEST(FileSpecTest, EqualSeparator) { 146 EXPECT_EQ(WindowsSpec("C:\\foo\\bar"), WindowsSpec("C:/foo/bar")); 147 } 148 149 TEST(FileSpecTest, EqualDotsWindows) { 150 std::pair<const char *, const char *> tests[] = { 151 {R"(C:\foo\bar\baz)", R"(C:\foo\foo\..\bar\baz)"}, 152 {R"(C:\bar\baz)", R"(C:\foo\..\bar\baz)"}, 153 {R"(C:\bar\baz)", R"(C:/foo/../bar/baz)"}, 154 {R"(C:/bar/baz)", R"(C:\foo\..\bar\baz)"}, 155 {R"(C:\bar)", R"(C:\foo\..\bar)"}, 156 {R"(C:\foo\bar)", R"(C:\foo\.\bar)"}, 157 {R"(C:\foo\bar)", R"(C:\foo\bar\.)"}, 158 }; 159 160 for (const auto &test : tests) { 161 SCOPED_TRACE(llvm::Twine(test.first) + " <=> " + test.second); 162 EXPECT_EQ(WindowsSpec(test.first), WindowsSpec(test.second)); 163 } 164 } 165 166 TEST(FileSpecTest, EqualDotsPosix) { 167 std::pair<const char *, const char *> tests[] = { 168 {R"(/foo/bar/baz)", R"(/foo/foo/../bar/baz)"}, 169 {R"(/bar/baz)", R"(/foo/../bar/baz)"}, 170 {R"(/bar)", R"(/foo/../bar)"}, 171 {R"(/foo/bar)", R"(/foo/./bar)"}, 172 {R"(/foo/bar)", R"(/foo/bar/.)"}, 173 }; 174 175 for (const auto &test : tests) { 176 SCOPED_TRACE(llvm::Twine(test.first) + " <=> " + test.second); 177 EXPECT_EQ(PosixSpec(test.first), PosixSpec(test.second)); 178 } 179 } 180 181 TEST(FileSpecTest, EqualDotsPosixRoot) { 182 std::pair<const char *, const char *> tests[] = { 183 {R"(/)", R"(/..)"}, 184 {R"(/)", R"(/.)"}, 185 {R"(/)", R"(/foo/..)"}, 186 }; 187 188 for (const auto &test : tests) { 189 SCOPED_TRACE(llvm::Twine(test.first) + " <=> " + test.second); 190 EXPECT_EQ(PosixSpec(test.first), PosixSpec(test.second)); 191 } 192 } 193 194 TEST(FileSpecTest, GuessPathStyle) { 195 EXPECT_EQ(FileSpec::Style::posix, FileSpec::GuessPathStyle("/foo/bar.txt")); 196 EXPECT_EQ(FileSpec::Style::posix, FileSpec::GuessPathStyle("//net/bar.txt")); 197 EXPECT_EQ(FileSpec::Style::windows, 198 FileSpec::GuessPathStyle(R"(C:\foo.txt)")); 199 EXPECT_EQ(FileSpec::Style::windows, 200 FileSpec::GuessPathStyle(R"(\\net\foo.txt)")); 201 EXPECT_EQ(llvm::None, FileSpec::GuessPathStyle("foo.txt")); 202 EXPECT_EQ(llvm::None, FileSpec::GuessPathStyle("foo/bar.txt")); 203 } 204 205 TEST(FileSpecTest, GetPath) { 206 std::pair<const char *, const char *> posix_tests[] = { 207 {"/foo/.././bar", "/bar"}, 208 {"/foo/./../bar", "/bar"}, 209 {"/foo/../bar", "/bar"}, 210 {"/foo/./bar", "/foo/bar"}, 211 {"/foo/..", "/"}, 212 {"/foo/.", "/foo"}, 213 {"/foo//bar", "/foo/bar"}, 214 {"/foo//bar/baz", "/foo/bar/baz"}, 215 {"/foo//bar/./baz", "/foo/bar/baz"}, 216 {"/./foo", "/foo"}, 217 {"/", "/"}, 218 {"//", "/"}, 219 {"//net", "//net"}, 220 {"/..", "/"}, 221 {"/.", "/"}, 222 {"..", ".."}, 223 {".", "."}, 224 {"../..", "../.."}, 225 {"foo/..", "."}, 226 {"foo/../bar", "bar"}, 227 {"../foo/..", ".."}, 228 {"./foo", "foo"}, 229 {"././foo", "foo"}, 230 {"../foo", "../foo"}, 231 {"../../foo", "../../foo"}, 232 }; 233 for (auto test : posix_tests) { 234 SCOPED_TRACE(llvm::Twine("test.first = ") + test.first); 235 EXPECT_EQ(test.second, PosixSpec(test.first).GetPath()); 236 } 237 238 std::pair<const char *, const char *> windows_tests[] = { 239 {R"(c:\bar\..\bar)", R"(c:\bar)"}, 240 {R"(c:\bar\.\bar)", R"(c:\bar\bar)"}, 241 {R"(c:\bar\..)", R"(c:\)"}, 242 {R"(c:\bar\.)", R"(c:\bar)"}, 243 {R"(c:\.\bar)", R"(c:\bar)"}, 244 {R"(\)", R"(\)"}, 245 {R"(\\)", R"(\)"}, 246 {R"(\\net)", R"(\\net)"}, 247 {R"(c:\..)", R"(c:\)"}, 248 {R"(c:\.)", R"(c:\)"}, 249 // TODO: fix llvm::sys::path::remove_dots() to return "\" below. 250 {R"(\..)", R"(\..)"}, 251 // {R"(c:..)", R"(c:..)"}, 252 {R"(..)", R"(..)"}, 253 {R"(.)", R"(.)"}, 254 // TODO: fix llvm::sys::path::remove_dots() to return "c:\" below. 255 {R"(c:..\..)", R"(c:\..\..)"}, 256 {R"(..\..)", R"(..\..)"}, 257 {R"(foo\..)", R"(.)"}, 258 {R"(foo\..\bar)", R"(bar)"}, 259 {R"(..\foo\..)", R"(..)"}, 260 {R"(.\foo)", R"(foo)"}, 261 {R"(.\.\foo)", R"(foo)"}, 262 {R"(..\foo)", R"(..\foo)"}, 263 {R"(..\..\foo)", R"(..\..\foo)"}, 264 }; 265 for (auto test : windows_tests) { 266 SCOPED_TRACE(llvm::Twine("test.first = ") + test.first); 267 EXPECT_EQ(test.second, WindowsSpec(test.first).GetPath()); 268 } 269 } 270 271 TEST(FileSpecTest, FormatFileSpec) { 272 auto win = FileSpec::Style::windows; 273 274 FileSpec F; 275 EXPECT_EQ("(empty)", llvm::formatv("{0}", F).str()); 276 EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str()); 277 EXPECT_EQ("(empty)", llvm::formatv("{0:F}", F).str()); 278 279 F = FileSpec("C:\\foo\\bar.txt", win); 280 EXPECT_EQ("C:\\foo\\bar.txt", llvm::formatv("{0}", F).str()); 281 EXPECT_EQ("C:\\foo\\", llvm::formatv("{0:D}", F).str()); 282 EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str()); 283 284 F = FileSpec("foo\\bar.txt", win); 285 EXPECT_EQ("foo\\bar.txt", llvm::formatv("{0}", F).str()); 286 EXPECT_EQ("foo\\", llvm::formatv("{0:D}", F).str()); 287 EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str()); 288 289 F = FileSpec("foo", win); 290 EXPECT_EQ("foo", llvm::formatv("{0}", F).str()); 291 EXPECT_EQ("foo", llvm::formatv("{0:F}", F).str()); 292 EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str()); 293 } 294 295 TEST(FileSpecTest, IsRelative) { 296 llvm::StringRef not_relative[] = { 297 "/", 298 "/a", 299 "/a/", 300 "/a/b", 301 "/a/b/", 302 "//", 303 "//a/", 304 "//a/b", 305 "//a/b/", 306 "~", 307 "~/", 308 "~/a", 309 "~/a/", 310 "~/a/b" 311 "~/a/b/", 312 "/foo/.", 313 "/foo/..", 314 "/foo/../", 315 "/foo/../.", 316 }; 317 for (const auto &path: not_relative) { 318 SCOPED_TRACE(path); 319 EXPECT_FALSE(PosixSpec(path).IsRelative()); 320 } 321 llvm::StringRef is_relative[] = { 322 ".", 323 "./", 324 ".///", 325 "a", 326 "./a", 327 "./a/", 328 "./a/", 329 "./a/b", 330 "./a/b/", 331 "../foo", 332 "foo/bar.c", 333 "./foo/bar.c" 334 }; 335 for (const auto &path: is_relative) { 336 SCOPED_TRACE(path); 337 EXPECT_TRUE(PosixSpec(path).IsRelative()); 338 } 339 } 340 341 TEST(FileSpecTest, RemoveLastPathComponent) { 342 FileSpec fs_posix("/foo/bar/baz", FileSpec::Style::posix); 343 EXPECT_STREQ("/foo/bar/baz", fs_posix.GetCString()); 344 EXPECT_TRUE(fs_posix.RemoveLastPathComponent()); 345 EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); 346 EXPECT_TRUE(fs_posix.RemoveLastPathComponent()); 347 EXPECT_STREQ("/foo", fs_posix.GetCString()); 348 EXPECT_TRUE(fs_posix.RemoveLastPathComponent()); 349 EXPECT_STREQ("/", fs_posix.GetCString()); 350 EXPECT_FALSE(fs_posix.RemoveLastPathComponent()); 351 EXPECT_STREQ("/", fs_posix.GetCString()); 352 353 FileSpec fs_posix_relative("./foo/bar/baz", FileSpec::Style::posix); 354 EXPECT_STREQ("foo/bar/baz", fs_posix_relative.GetCString()); 355 EXPECT_TRUE(fs_posix_relative.RemoveLastPathComponent()); 356 EXPECT_STREQ("foo/bar", fs_posix_relative.GetCString()); 357 EXPECT_TRUE(fs_posix_relative.RemoveLastPathComponent()); 358 EXPECT_STREQ("foo", fs_posix_relative.GetCString()); 359 EXPECT_FALSE(fs_posix_relative.RemoveLastPathComponent()); 360 EXPECT_STREQ("foo", fs_posix_relative.GetCString()); 361 362 FileSpec fs_posix_relative2("./", FileSpec::Style::posix); 363 EXPECT_STREQ(".", fs_posix_relative2.GetCString()); 364 EXPECT_FALSE(fs_posix_relative2.RemoveLastPathComponent()); 365 EXPECT_STREQ(".", fs_posix_relative2.GetCString()); 366 EXPECT_FALSE(fs_posix_relative.RemoveLastPathComponent()); 367 EXPECT_STREQ(".", fs_posix_relative2.GetCString()); 368 369 FileSpec fs_windows("C:\\foo\\bar\\baz", FileSpec::Style::windows); 370 EXPECT_STREQ("C:\\foo\\bar\\baz", fs_windows.GetCString()); 371 EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); 372 EXPECT_STREQ("C:\\foo\\bar", fs_windows.GetCString()); 373 EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); 374 EXPECT_STREQ("C:\\foo", fs_windows.GetCString()); 375 EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); 376 EXPECT_STREQ("C:\\", fs_windows.GetCString()); 377 EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); 378 EXPECT_STREQ("C:", fs_windows.GetCString()); 379 EXPECT_FALSE(fs_windows.RemoveLastPathComponent()); 380 EXPECT_STREQ("C:", fs_windows.GetCString()); 381 } 382 383 TEST(FileSpecTest, Equal) { 384 auto Eq = [](const char *a, const char *b, bool full) { 385 return FileSpec::Equal(PosixSpec(a), PosixSpec(b), full); 386 }; 387 EXPECT_TRUE(Eq("/foo/bar", "/foo/bar", true)); 388 EXPECT_TRUE(Eq("/foo/bar", "/foo/bar", false)); 389 390 EXPECT_FALSE(Eq("/foo/bar", "/foo/baz", true)); 391 EXPECT_FALSE(Eq("/foo/bar", "/foo/baz", false)); 392 393 EXPECT_FALSE(Eq("/bar/foo", "/baz/foo", true)); 394 EXPECT_FALSE(Eq("/bar/foo", "/baz/foo", false)); 395 396 EXPECT_FALSE(Eq("/bar/foo", "foo", true)); 397 EXPECT_TRUE(Eq("/bar/foo", "foo", false)); 398 399 EXPECT_FALSE(Eq("foo", "/bar/foo", true)); 400 EXPECT_TRUE(Eq("foo", "/bar/foo", false)); 401 } 402 403 TEST(FileSpecTest, Match) { 404 auto Match = [](const char *pattern, const char *file) { 405 return FileSpec::Match(PosixSpec(pattern), PosixSpec(file)); 406 }; 407 EXPECT_TRUE(Match("/foo/bar", "/foo/bar")); 408 EXPECT_FALSE(Match("/foo/bar", "/oof/bar")); 409 EXPECT_FALSE(Match("/foo/bar", "/foo/baz")); 410 EXPECT_FALSE(Match("/foo/bar", "bar")); 411 EXPECT_FALSE(Match("/foo/bar", "")); 412 413 EXPECT_TRUE(Match("bar", "/foo/bar")); 414 EXPECT_FALSE(Match("bar", "/foo/baz")); 415 EXPECT_TRUE(Match("bar", "bar")); 416 EXPECT_FALSE(Match("bar", "baz")); 417 EXPECT_FALSE(Match("bar", "")); 418 419 EXPECT_TRUE(Match("", "/foo/bar")); 420 EXPECT_TRUE(Match("", "")); 421 422 } 423