//===-- FileSpecTest.cpp --------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "gtest/gtest.h" #include "lldb/Utility/FileSpec.h" using namespace lldb_private; static FileSpec PosixSpec(llvm::StringRef path) { return FileSpec(path, FileSpec::Style::posix); } static FileSpec WindowsSpec(llvm::StringRef path) { return FileSpec(path, FileSpec::Style::windows); } TEST(FileSpecTest, FileAndDirectoryComponents) { FileSpec fs_posix("/foo/bar", FileSpec::Style::posix); EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString()); EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString()); FileSpec fs_windows("F:\\bar", FileSpec::Style::windows); EXPECT_STREQ("F:\\bar", fs_windows.GetCString()); // EXPECT_STREQ("F:\\", fs_windows.GetDirectory().GetCString()); // It returns // "F:/" EXPECT_STREQ("bar", fs_windows.GetFilename().GetCString()); FileSpec fs_posix_root("/", FileSpec::Style::posix); EXPECT_STREQ("/", fs_posix_root.GetCString()); EXPECT_EQ(nullptr, fs_posix_root.GetDirectory().GetCString()); EXPECT_STREQ("/", fs_posix_root.GetFilename().GetCString()); FileSpec fs_net_drive("//net", FileSpec::Style::posix); EXPECT_STREQ("//net", fs_net_drive.GetCString()); EXPECT_EQ(nullptr, fs_net_drive.GetDirectory().GetCString()); EXPECT_STREQ("//net", fs_net_drive.GetFilename().GetCString()); FileSpec fs_net_root("//net/", FileSpec::Style::posix); EXPECT_STREQ("//net/", fs_net_root.GetCString()); EXPECT_STREQ("//net", fs_net_root.GetDirectory().GetCString()); EXPECT_STREQ("/", fs_net_root.GetFilename().GetCString()); FileSpec fs_windows_drive("F:", FileSpec::Style::windows); EXPECT_STREQ("F:", fs_windows_drive.GetCString()); EXPECT_EQ(nullptr, fs_windows_drive.GetDirectory().GetCString()); EXPECT_STREQ("F:", fs_windows_drive.GetFilename().GetCString()); FileSpec fs_windows_root("F:\\", FileSpec::Style::windows); EXPECT_STREQ("F:\\", fs_windows_root.GetCString()); EXPECT_STREQ("F:", fs_windows_root.GetDirectory().GetCString()); // EXPECT_STREQ("\\", fs_windows_root.GetFilename().GetCString()); // It // returns "/" FileSpec fs_posix_long("/foo/bar/baz", FileSpec::Style::posix); EXPECT_STREQ("/foo/bar/baz", fs_posix_long.GetCString()); EXPECT_STREQ("/foo/bar", fs_posix_long.GetDirectory().GetCString()); EXPECT_STREQ("baz", fs_posix_long.GetFilename().GetCString()); FileSpec fs_windows_long("F:\\bar\\baz", FileSpec::Style::windows); EXPECT_STREQ("F:\\bar\\baz", fs_windows_long.GetCString()); // EXPECT_STREQ("F:\\bar", fs_windows_long.GetDirectory().GetCString()); // It // returns "F:/bar" EXPECT_STREQ("baz", fs_windows_long.GetFilename().GetCString()); FileSpec fs_posix_trailing_slash("/foo/bar/", FileSpec::Style::posix); EXPECT_STREQ("/foo/bar", fs_posix_trailing_slash.GetCString()); EXPECT_STREQ("/foo", fs_posix_trailing_slash.GetDirectory().GetCString()); EXPECT_STREQ("bar", fs_posix_trailing_slash.GetFilename().GetCString()); FileSpec fs_windows_trailing_slash("F:\\bar\\", FileSpec::Style::windows); EXPECT_STREQ("F:\\bar", fs_windows_trailing_slash.GetCString()); EXPECT_STREQ("bar", fs_windows_trailing_slash.GetFilename().GetCString()); } TEST(FileSpecTest, AppendPathComponent) { FileSpec fs_posix("/foo", FileSpec::Style::posix); fs_posix.AppendPathComponent("bar"); EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString()); EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString()); FileSpec fs_posix_2("/foo", FileSpec::Style::posix); fs_posix_2.AppendPathComponent("//bar/baz"); EXPECT_STREQ("/foo/bar/baz", fs_posix_2.GetCString()); EXPECT_STREQ("/foo/bar", fs_posix_2.GetDirectory().GetCString()); EXPECT_STREQ("baz", fs_posix_2.GetFilename().GetCString()); FileSpec fs_windows("F:\\bar", FileSpec::Style::windows); fs_windows.AppendPathComponent("baz"); EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString()); // EXPECT_STREQ("F:\\bar", fs_windows.GetDirectory().GetCString()); // It // returns "F:/bar" EXPECT_STREQ("baz", fs_windows.GetFilename().GetCString()); FileSpec fs_posix_root("/", FileSpec::Style::posix); fs_posix_root.AppendPathComponent("bar"); EXPECT_STREQ("/bar", fs_posix_root.GetCString()); EXPECT_STREQ("/", fs_posix_root.GetDirectory().GetCString()); EXPECT_STREQ("bar", fs_posix_root.GetFilename().GetCString()); FileSpec fs_windows_root("F:\\", FileSpec::Style::windows); fs_windows_root.AppendPathComponent("bar"); EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString()); // EXPECT_STREQ("F:\\", fs_windows_root.GetDirectory().GetCString()); // It // returns "F:/" EXPECT_STREQ("bar", fs_windows_root.GetFilename().GetCString()); } TEST(FileSpecTest, CopyByAppendingPathComponent) { FileSpec fs = PosixSpec("/foo").CopyByAppendingPathComponent("bar"); EXPECT_STREQ("/foo/bar", fs.GetCString()); EXPECT_STREQ("/foo", fs.GetDirectory().GetCString()); EXPECT_STREQ("bar", fs.GetFilename().GetCString()); } TEST(FileSpecTest, PrependPathComponent) { FileSpec fs_posix("foo", FileSpec::Style::posix); fs_posix.PrependPathComponent("/bar"); EXPECT_STREQ("/bar/foo", fs_posix.GetCString()); FileSpec fs_posix_2("foo/bar", FileSpec::Style::posix); fs_posix_2.PrependPathComponent("/baz"); EXPECT_STREQ("/baz/foo/bar", fs_posix_2.GetCString()); FileSpec fs_windows("baz", FileSpec::Style::windows); fs_windows.PrependPathComponent("F:\\bar"); EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString()); FileSpec fs_posix_root("bar", FileSpec::Style::posix); fs_posix_root.PrependPathComponent("/"); EXPECT_STREQ("/bar", fs_posix_root.GetCString()); FileSpec fs_windows_root("bar", FileSpec::Style::windows); fs_windows_root.PrependPathComponent("F:\\"); EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString()); } TEST(FileSpecTest, EqualSeparator) { EXPECT_EQ(WindowsSpec("C:\\foo\\bar"), WindowsSpec("C:/foo/bar")); } TEST(FileSpecTest, EqualDotsWindows) { std::pair tests[] = { {R"(C:\foo\bar\baz)", R"(C:\foo\foo\..\bar\baz)"}, {R"(C:\bar\baz)", R"(C:\foo\..\bar\baz)"}, {R"(C:\bar\baz)", R"(C:/foo/../bar/baz)"}, {R"(C:/bar/baz)", R"(C:\foo\..\bar\baz)"}, {R"(C:\bar)", R"(C:\foo\..\bar)"}, {R"(C:\foo\bar)", R"(C:\foo\.\bar)"}, {R"(C:\foo\bar)", R"(C:\foo\bar\.)"}, }; for (const auto &test : tests) { SCOPED_TRACE(llvm::Twine(test.first) + " <=> " + test.second); EXPECT_EQ(WindowsSpec(test.first), WindowsSpec(test.second)); } } TEST(FileSpecTest, EqualDotsPosix) { std::pair tests[] = { {R"(/foo/bar/baz)", R"(/foo/foo/../bar/baz)"}, {R"(/bar/baz)", R"(/foo/../bar/baz)"}, {R"(/bar)", R"(/foo/../bar)"}, {R"(/foo/bar)", R"(/foo/./bar)"}, {R"(/foo/bar)", R"(/foo/bar/.)"}, }; for (const auto &test : tests) { SCOPED_TRACE(llvm::Twine(test.first) + " <=> " + test.second); EXPECT_EQ(PosixSpec(test.first), PosixSpec(test.second)); } } TEST(FileSpecTest, EqualDotsPosixRoot) { std::pair tests[] = { {R"(/)", R"(/..)"}, {R"(/)", R"(/.)"}, {R"(/)", R"(/foo/..)"}, }; for (const auto &test : tests) { SCOPED_TRACE(llvm::Twine(test.first) + " <=> " + test.second); EXPECT_EQ(PosixSpec(test.first), PosixSpec(test.second)); } } TEST(FileSpecTest, GuessPathStyle) { EXPECT_EQ(FileSpec::Style::posix, FileSpec::GuessPathStyle("/foo/bar.txt")); EXPECT_EQ(FileSpec::Style::posix, FileSpec::GuessPathStyle("//net/bar.txt")); EXPECT_EQ(FileSpec::Style::windows, FileSpec::GuessPathStyle(R"(C:\foo.txt)")); EXPECT_EQ(FileSpec::Style::windows, FileSpec::GuessPathStyle(R"(\\net\foo.txt)")); EXPECT_EQ(llvm::None, FileSpec::GuessPathStyle("foo.txt")); EXPECT_EQ(llvm::None, FileSpec::GuessPathStyle("foo/bar.txt")); } TEST(FileSpecTest, GetPath) { std::pair posix_tests[] = { {"/foo/.././bar", "/bar"}, {"/foo/./../bar", "/bar"}, {"/foo/../bar", "/bar"}, {"/foo/./bar", "/foo/bar"}, {"/foo/..", "/"}, {"/foo/.", "/foo"}, {"/foo//bar", "/foo/bar"}, {"/foo//bar/baz", "/foo/bar/baz"}, {"/foo//bar/./baz", "/foo/bar/baz"}, {"/./foo", "/foo"}, {"/", "/"}, {"//", "/"}, {"//net", "//net"}, {"/..", "/"}, {"/.", "/"}, {"..", ".."}, {".", "."}, {"../..", "../.."}, {"foo/..", "."}, {"foo/../bar", "bar"}, {"../foo/..", ".."}, {"./foo", "foo"}, {"././foo", "foo"}, {"../foo", "../foo"}, {"../../foo", "../../foo"}, }; for (auto test : posix_tests) { SCOPED_TRACE(llvm::Twine("test.first = ") + test.first); EXPECT_EQ(test.second, PosixSpec(test.first).GetPath()); } std::pair windows_tests[] = { {R"(c:\bar\..\bar)", R"(c:\bar)"}, {R"(c:\bar\.\bar)", R"(c:\bar\bar)"}, {R"(c:\bar\..)", R"(c:\)"}, {R"(c:\bar\.)", R"(c:\bar)"}, {R"(c:\.\bar)", R"(c:\bar)"}, {R"(\)", R"(\)"}, {R"(\\)", R"(\)"}, {R"(\\net)", R"(\\net)"}, {R"(c:\..)", R"(c:\)"}, {R"(c:\.)", R"(c:\)"}, {R"(\..)", R"(\)"}, // {R"(c:..)", R"(c:..)"}, {R"(..)", R"(..)"}, {R"(.)", R"(.)"}, {R"(c:..\..)", R"(c:)"}, {R"(..\..)", R"(..\..)"}, {R"(foo\..)", R"(.)"}, {R"(foo\..\bar)", R"(bar)"}, {R"(..\foo\..)", R"(..)"}, {R"(.\foo)", R"(foo)"}, {R"(.\.\foo)", R"(foo)"}, {R"(..\foo)", R"(..\foo)"}, {R"(..\..\foo)", R"(..\..\foo)"}, }; for (auto test : windows_tests) { SCOPED_TRACE(llvm::Twine("test.first = ") + test.first); EXPECT_EQ(test.second, WindowsSpec(test.first).GetPath()); } } TEST(FileSpecTest, FormatFileSpec) { auto win = FileSpec::Style::windows; FileSpec F; EXPECT_EQ("(empty)", llvm::formatv("{0}", F).str()); EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str()); EXPECT_EQ("(empty)", llvm::formatv("{0:F}", F).str()); F = FileSpec("C:\\foo\\bar.txt", win); EXPECT_EQ("C:\\foo\\bar.txt", llvm::formatv("{0}", F).str()); EXPECT_EQ("C:\\foo\\", llvm::formatv("{0:D}", F).str()); EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str()); F = FileSpec("foo\\bar.txt", win); EXPECT_EQ("foo\\bar.txt", llvm::formatv("{0}", F).str()); EXPECT_EQ("foo\\", llvm::formatv("{0:D}", F).str()); EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str()); F = FileSpec("foo", win); EXPECT_EQ("foo", llvm::formatv("{0}", F).str()); EXPECT_EQ("foo", llvm::formatv("{0:F}", F).str()); EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str()); } TEST(FileSpecTest, IsRelative) { llvm::StringRef not_relative[] = { "/", "/a", "/a/", "/a/b", "/a/b/", "//", "//a/", "//a/b", "//a/b/", "~", "~/", "~/a", "~/a/", "~/a/b" "~/a/b/", "/foo/.", "/foo/..", "/foo/../", "/foo/../.", }; for (const auto &path: not_relative) { SCOPED_TRACE(path); EXPECT_FALSE(PosixSpec(path).IsRelative()); } llvm::StringRef is_relative[] = { ".", "./", ".///", "a", "./a", "./a/", "./a/", "./a/b", "./a/b/", "../foo", "foo/bar.c", "./foo/bar.c" }; for (const auto &path: is_relative) { SCOPED_TRACE(path); EXPECT_TRUE(PosixSpec(path).IsRelative()); } } TEST(FileSpecTest, RemoveLastPathComponent) { FileSpec fs_posix("/foo/bar/baz", FileSpec::Style::posix); EXPECT_STREQ("/foo/bar/baz", fs_posix.GetCString()); EXPECT_TRUE(fs_posix.RemoveLastPathComponent()); EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); EXPECT_TRUE(fs_posix.RemoveLastPathComponent()); EXPECT_STREQ("/foo", fs_posix.GetCString()); EXPECT_TRUE(fs_posix.RemoveLastPathComponent()); EXPECT_STREQ("/", fs_posix.GetCString()); EXPECT_FALSE(fs_posix.RemoveLastPathComponent()); EXPECT_STREQ("/", fs_posix.GetCString()); FileSpec fs_posix_relative("./foo/bar/baz", FileSpec::Style::posix); EXPECT_STREQ("foo/bar/baz", fs_posix_relative.GetCString()); EXPECT_TRUE(fs_posix_relative.RemoveLastPathComponent()); EXPECT_STREQ("foo/bar", fs_posix_relative.GetCString()); EXPECT_TRUE(fs_posix_relative.RemoveLastPathComponent()); EXPECT_STREQ("foo", fs_posix_relative.GetCString()); EXPECT_FALSE(fs_posix_relative.RemoveLastPathComponent()); EXPECT_STREQ("foo", fs_posix_relative.GetCString()); FileSpec fs_posix_relative2("./", FileSpec::Style::posix); EXPECT_STREQ(".", fs_posix_relative2.GetCString()); EXPECT_FALSE(fs_posix_relative2.RemoveLastPathComponent()); EXPECT_STREQ(".", fs_posix_relative2.GetCString()); EXPECT_FALSE(fs_posix_relative.RemoveLastPathComponent()); EXPECT_STREQ(".", fs_posix_relative2.GetCString()); FileSpec fs_windows("C:\\foo\\bar\\baz", FileSpec::Style::windows); EXPECT_STREQ("C:\\foo\\bar\\baz", fs_windows.GetCString()); EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); EXPECT_STREQ("C:\\foo\\bar", fs_windows.GetCString()); EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); EXPECT_STREQ("C:\\foo", fs_windows.GetCString()); EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); EXPECT_STREQ("C:\\", fs_windows.GetCString()); EXPECT_TRUE(fs_windows.RemoveLastPathComponent()); EXPECT_STREQ("C:", fs_windows.GetCString()); EXPECT_FALSE(fs_windows.RemoveLastPathComponent()); EXPECT_STREQ("C:", fs_windows.GetCString()); } TEST(FileSpecTest, Equal) { auto Eq = [](const char *a, const char *b, bool full) { return FileSpec::Equal(PosixSpec(a), PosixSpec(b), full); }; EXPECT_TRUE(Eq("/foo/bar", "/foo/bar", true)); EXPECT_TRUE(Eq("/foo/bar", "/foo/bar", false)); EXPECT_FALSE(Eq("/foo/bar", "/foo/baz", true)); EXPECT_FALSE(Eq("/foo/bar", "/foo/baz", false)); EXPECT_FALSE(Eq("/bar/foo", "/baz/foo", true)); EXPECT_FALSE(Eq("/bar/foo", "/baz/foo", false)); EXPECT_FALSE(Eq("/bar/foo", "foo", true)); EXPECT_TRUE(Eq("/bar/foo", "foo", false)); EXPECT_FALSE(Eq("foo", "/bar/foo", true)); EXPECT_TRUE(Eq("foo", "/bar/foo", false)); } TEST(FileSpecTest, Match) { auto Match = [](const char *pattern, const char *file) { return FileSpec::Match(PosixSpec(pattern), PosixSpec(file)); }; EXPECT_TRUE(Match("/foo/bar", "/foo/bar")); EXPECT_FALSE(Match("/foo/bar", "/oof/bar")); EXPECT_FALSE(Match("/foo/bar", "/foo/baz")); EXPECT_FALSE(Match("/foo/bar", "bar")); EXPECT_FALSE(Match("/foo/bar", "")); EXPECT_TRUE(Match("bar", "/foo/bar")); EXPECT_FALSE(Match("bar", "/foo/baz")); EXPECT_TRUE(Match("bar", "bar")); EXPECT_FALSE(Match("bar", "baz")); EXPECT_FALSE(Match("bar", "")); EXPECT_TRUE(Match("", "/foo/bar")); EXPECT_TRUE(Match("", "")); } TEST(FileSpecTest, Yaml) { std::string buffer; llvm::raw_string_ostream os(buffer); // Serialize. FileSpec fs_windows("F:\\bar", FileSpec::Style::windows); llvm::yaml::Output yout(os); yout << fs_windows; os.flush(); // Deserialize. FileSpec deserialized; llvm::yaml::Input yin(buffer); yin >> deserialized; EXPECT_EQ(deserialized.GetPathStyle(), fs_windows.GetPathStyle()); EXPECT_EQ(deserialized.GetFilename(), fs_windows.GetFilename()); EXPECT_EQ(deserialized.GetDirectory(), fs_windows.GetDirectory()); EXPECT_EQ(deserialized, fs_windows); } TEST(FileSpecTest, OperatorBool) { EXPECT_FALSE(FileSpec()); EXPECT_FALSE(FileSpec("")); EXPECT_TRUE(FileSpec("/foo/bar")); }