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