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"(C:/foo.txt)")); 201 EXPECT_EQ(FileSpec::Style::windows, 202 FileSpec::GuessPathStyle(R"(\\net\foo.txt)")); 203 EXPECT_EQ(FileSpec::Style::windows, FileSpec::GuessPathStyle(R"(Z:\)")); 204 EXPECT_EQ(FileSpec::Style::windows, FileSpec::GuessPathStyle(R"(Z:/)")); 205 EXPECT_EQ(llvm::None, FileSpec::GuessPathStyle("foo.txt")); 206 EXPECT_EQ(llvm::None, FileSpec::GuessPathStyle("foo/bar.txt")); 207 EXPECT_EQ(llvm::None, FileSpec::GuessPathStyle("Z:")); 208 } 209 210 TEST(FileSpecTest, GetPath) { 211 std::pair<const char *, const char *> posix_tests[] = { 212 {"/foo/.././bar", "/bar"}, 213 {"/foo/./../bar", "/bar"}, 214 {"/foo/../bar", "/bar"}, 215 {"/foo/./bar", "/foo/bar"}, 216 {"/foo/..", "/"}, 217 {"/foo/.", "/foo"}, 218 {"/foo//bar", "/foo/bar"}, 219 {"/foo//bar/baz", "/foo/bar/baz"}, 220 {"/foo//bar/./baz", "/foo/bar/baz"}, 221 {"/./foo", "/foo"}, 222 {"/", "/"}, 223 {"//", "/"}, 224 {"//net", "//net"}, 225 {"/..", "/"}, 226 {"/.", "/"}, 227 {"..", ".."}, 228 {".", "."}, 229 {"../..", "../.."}, 230 {"foo/..", "."}, 231 {"foo/../bar", "bar"}, 232 {"../foo/..", ".."}, 233 {"./foo", "foo"}, 234 {"././foo", "foo"}, 235 {"../foo", "../foo"}, 236 {"../../foo", "../../foo"}, 237 }; 238 for (auto test : posix_tests) { 239 SCOPED_TRACE(llvm::Twine("test.first = ") + test.first); 240 EXPECT_EQ(test.second, PosixSpec(test.first).GetPath()); 241 } 242 243 std::pair<const char *, const char *> windows_tests[] = { 244 {R"(c:\bar\..\bar)", R"(c:\bar)"}, 245 {R"(c:\bar\.\bar)", R"(c:\bar\bar)"}, 246 {R"(c:\bar\..)", R"(c:\)"}, 247 {R"(c:\bar\.)", R"(c:\bar)"}, 248 {R"(c:\.\bar)", R"(c:\bar)"}, 249 {R"(\)", R"(\)"}, 250 {R"(\\)", R"(\)"}, 251 {R"(\\net)", R"(\\net)"}, 252 {R"(c:\..)", R"(c:\)"}, 253 {R"(c:\.)", R"(c:\)"}, 254 {R"(\..)", R"(\)"}, 255 // {R"(c:..)", R"(c:..)"}, 256 {R"(..)", R"(..)"}, 257 {R"(.)", R"(.)"}, 258 {R"(c:..\..)", R"(c:)"}, 259 {R"(..\..)", R"(..\..)"}, 260 {R"(foo\..)", R"(.)"}, 261 {R"(foo\..\bar)", R"(bar)"}, 262 {R"(..\foo\..)", R"(..)"}, 263 {R"(.\foo)", R"(foo)"}, 264 {R"(.\.\foo)", R"(foo)"}, 265 {R"(..\foo)", R"(..\foo)"}, 266 {R"(..\..\foo)", R"(..\..\foo)"}, 267 }; 268 for (auto test : windows_tests) { 269 SCOPED_TRACE(llvm::Twine("test.first = ") + test.first); 270 EXPECT_EQ(test.second, WindowsSpec(test.first).GetPath()); 271 } 272 } 273 274 TEST(FileSpecTest, FormatFileSpec) { 275 auto win = FileSpec::Style::windows; 276 277 FileSpec F; 278 EXPECT_EQ("(empty)", llvm::formatv("{0}", F).str()); 279 EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str()); 280 EXPECT_EQ("(empty)", llvm::formatv("{0:F}", F).str()); 281 282 F = FileSpec("C:\\foo\\bar.txt", win); 283 EXPECT_EQ("C:\\foo\\bar.txt", llvm::formatv("{0}", F).str()); 284 EXPECT_EQ("C:\\foo\\", llvm::formatv("{0:D}", F).str()); 285 EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str()); 286 287 F = FileSpec("foo\\bar.txt", win); 288 EXPECT_EQ("foo\\bar.txt", llvm::formatv("{0}", F).str()); 289 EXPECT_EQ("foo\\", llvm::formatv("{0:D}", F).str()); 290 EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str()); 291 292 F = FileSpec("foo", win); 293 EXPECT_EQ("foo", llvm::formatv("{0}", F).str()); 294 EXPECT_EQ("foo", llvm::formatv("{0:F}", F).str()); 295 EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str()); 296 } 297 298 TEST(FileSpecTest, IsRelative) { 299 llvm::StringRef not_relative[] = { 300 "/", 301 "/a", 302 "/a/", 303 "/a/b", 304 "/a/b/", 305 "//", 306 "//a/", 307 "//a/b", 308 "//a/b/", 309 "~", 310 "~/", 311 "~/a", 312 "~/a/", 313 "~/a/b", 314 "~/a/b/", 315 "/foo/.", 316 "/foo/..", 317 "/foo/../", 318 "/foo/../.", 319 }; 320 for (const auto &path: not_relative) { 321 SCOPED_TRACE(path); 322 EXPECT_FALSE(PosixSpec(path).IsRelative()); 323 } 324 llvm::StringRef is_relative[] = { 325 ".", 326 "./", 327 ".///", 328 "a", 329 "./a", 330 "./a/", 331 "./a/", 332 "./a/b", 333 "./a/b/", 334 "../foo", 335 "foo/bar.c", 336 "./foo/bar.c" 337 }; 338 for (const auto &path: is_relative) { 339 SCOPED_TRACE(path); 340 EXPECT_TRUE(PosixSpec(path).IsRelative()); 341 } 342 } 343 344 TEST(FileSpecTest, RemoveLastPathComponent) { 345 FileSpec fs_posix("/foo/bar/baz", FileSpec::Style::posix); 346 EXPECT_STREQ("/foo/bar/baz", fs_posix.GetCString()); 347 EXPECT_TRUE(fs_posix.RemoveLastPathComponent()); 348 EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); 349 EXPECT_TRUE(fs_posix.RemoveLastPathComponent()); 350 EXPECT_STREQ("/foo", fs_posix.GetCString()); 351 EXPECT_TRUE(fs_posix.RemoveLastPathComponent()); 352 EXPECT_STREQ("/", fs_posix.GetCString()); 353 EXPECT_FALSE(fs_posix.RemoveLastPathComponent()); 354 EXPECT_STREQ("/", fs_posix.GetCString()); 355 356 FileSpec fs_posix_relative("./foo/bar/baz", FileSpec::Style::posix); 357 EXPECT_STREQ("foo/bar/baz", fs_posix_relative.GetCString()); 358 EXPECT_TRUE(fs_posix_relative.RemoveLastPathComponent()); 359 EXPECT_STREQ("foo/bar", fs_posix_relative.GetCString()); 360 EXPECT_TRUE(fs_posix_relative.RemoveLastPathComponent()); 361 EXPECT_STREQ("foo", fs_posix_relative.GetCString()); 362 EXPECT_FALSE(fs_posix_relative.RemoveLastPathComponent()); 363 EXPECT_STREQ("foo", fs_posix_relative.GetCString()); 364 365 FileSpec fs_posix_relative2("./", FileSpec::Style::posix); 366 EXPECT_STREQ(".", fs_posix_relative2.GetCString()); 367 EXPECT_FALSE(fs_posix_relative2.RemoveLastPathComponent()); 368 EXPECT_STREQ(".", fs_posix_relative2.GetCString()); 369 EXPECT_FALSE(fs_posix_relative.RemoveLastPathComponent()); 370 EXPECT_STREQ(".", fs_posix_relative2.GetCString()); 371 372 FileSpec fs_windows("C:\\foo\\bar\\baz", FileSpec::Style::windows); 373 EXPECT_STREQ("C:\\foo\\bar\\baz", fs_windows.GetCString()); 374 EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); 375 EXPECT_STREQ("C:\\foo\\bar", fs_windows.GetCString()); 376 EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); 377 EXPECT_STREQ("C:\\foo", fs_windows.GetCString()); 378 EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); 379 EXPECT_STREQ("C:\\", fs_windows.GetCString()); 380 EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); 381 EXPECT_STREQ("C:", fs_windows.GetCString()); 382 EXPECT_FALSE(fs_windows.RemoveLastPathComponent()); 383 EXPECT_STREQ("C:", fs_windows.GetCString()); 384 } 385 386 TEST(FileSpecTest, Equal) { 387 auto Eq = [](const char *a, const char *b, bool full) { 388 return FileSpec::Equal(PosixSpec(a), PosixSpec(b), full); 389 }; 390 EXPECT_TRUE(Eq("/foo/bar", "/foo/bar", true)); 391 EXPECT_TRUE(Eq("/foo/bar", "/foo/bar", false)); 392 393 EXPECT_FALSE(Eq("/foo/bar", "/foo/baz", true)); 394 EXPECT_FALSE(Eq("/foo/bar", "/foo/baz", false)); 395 396 EXPECT_FALSE(Eq("/bar/foo", "/baz/foo", true)); 397 EXPECT_FALSE(Eq("/bar/foo", "/baz/foo", false)); 398 399 EXPECT_FALSE(Eq("/bar/foo", "foo", true)); 400 EXPECT_TRUE(Eq("/bar/foo", "foo", false)); 401 402 EXPECT_FALSE(Eq("foo", "/bar/foo", true)); 403 EXPECT_TRUE(Eq("foo", "/bar/foo", false)); 404 } 405 406 TEST(FileSpecTest, Match) { 407 auto Match = [](const char *pattern, const char *file) { 408 return FileSpec::Match(PosixSpec(pattern), PosixSpec(file)); 409 }; 410 EXPECT_TRUE(Match("/foo/bar", "/foo/bar")); 411 EXPECT_FALSE(Match("/foo/bar", "/oof/bar")); 412 EXPECT_FALSE(Match("/foo/bar", "/foo/baz")); 413 EXPECT_FALSE(Match("/foo/bar", "bar")); 414 EXPECT_FALSE(Match("/foo/bar", "")); 415 416 EXPECT_TRUE(Match("bar", "/foo/bar")); 417 EXPECT_FALSE(Match("bar", "/foo/baz")); 418 EXPECT_TRUE(Match("bar", "bar")); 419 EXPECT_FALSE(Match("bar", "baz")); 420 EXPECT_FALSE(Match("bar", "")); 421 422 EXPECT_TRUE(Match("", "/foo/bar")); 423 EXPECT_TRUE(Match("", "")); 424 425 } 426 427 TEST(FileSpecTest, Yaml) { 428 std::string buffer; 429 llvm::raw_string_ostream os(buffer); 430 431 // Serialize. 432 FileSpec fs_windows("F:\\bar", FileSpec::Style::windows); 433 llvm::yaml::Output yout(os); 434 yout << fs_windows; 435 os.flush(); 436 437 // Deserialize. 438 FileSpec deserialized; 439 llvm::yaml::Input yin(buffer); 440 yin >> deserialized; 441 442 EXPECT_EQ(deserialized.GetPathStyle(), fs_windows.GetPathStyle()); 443 EXPECT_EQ(deserialized.GetFilename(), fs_windows.GetFilename()); 444 EXPECT_EQ(deserialized.GetDirectory(), fs_windows.GetDirectory()); 445 EXPECT_EQ(deserialized, fs_windows); 446 } 447 448 TEST(FileSpecTest, OperatorBool) { 449 EXPECT_FALSE(FileSpec()); 450 EXPECT_FALSE(FileSpec("")); 451 EXPECT_TRUE(FileSpec("/foo/bar")); 452 } 453