Index: include/lldb/Core/FileSpecList.h =================================================================== --- include/lldb/Core/FileSpecList.h +++ include/lldb/Core/FileSpecList.h @@ -119,16 +119,11 @@ /// @param[in] full /// Should FileSpec::Equal be called with "full" true or false. /// - /// @param[in] remove_backup_dots - /// Should FileSpec::Equal be called with "remove_backup_dots" true or - /// false. - /// /// @return /// The index of the file that matches \a file if it is found, /// else UINT32_MAX is returned. //------------------------------------------------------------------ - size_t FindFileIndex(size_t idx, const FileSpec &file, bool full, - bool remove_backup_dots = false) const; + size_t FindFileIndex(size_t idx, const FileSpec &file, bool full) const; //------------------------------------------------------------------ /// Get file at index. Index: include/lldb/Utility/FileSpec.h =================================================================== --- include/lldb/Utility/FileSpec.h +++ include/lldb/Utility/FileSpec.h @@ -256,8 +256,7 @@ //------------------------------------------------------------------ static int Compare(const FileSpec &lhs, const FileSpec &rhs, bool full); - static bool Equal(const FileSpec &a, const FileSpec &b, bool full, - bool remove_backups = false); + static bool Equal(const FileSpec &a, const FileSpec &b, bool full); //------------------------------------------------------------------ /// Case sensitivity of path. @@ -488,12 +487,6 @@ size_t MemorySize() const; //------------------------------------------------------------------ - /// Normalize a pathname by collapsing redundant separators and - /// up-level references. - //------------------------------------------------------------------ - FileSpec GetNormalizedPath() const; - - //------------------------------------------------------------------ /// Change the file specified with a new path. /// /// Update the contents of this object with a new path. The path will Index: source/Breakpoint/BreakpointResolverFileLine.cpp =================================================================== --- source/Breakpoint/BreakpointResolverFileLine.cpp +++ source/Breakpoint/BreakpointResolverFileLine.cpp @@ -121,8 +121,15 @@ return; // Nothing to do. Contexts are precise. llvm::StringRef relative_path; - if (is_relative) - relative_path = m_file_spec.GetNormalizedPath().GetDirectory().GetStringRef(); + if (is_relative) { + relative_path = m_file_spec.GetDirectory().GetStringRef(); + // Don't let any path start with "./" or "../". Since all paths are + // normalized now, paths can only start with "./" or "../" if they are + // relative. This means we can safely skip any leading "./" or "../" but + // we must leave the slash character though. + while (relative_path.consume_front(".")) + /* Do nothing. */; + } Log * log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS); for(uint32_t i = 0; i < sc_list.GetSize(); ++i) { Index: source/Core/FileSpecList.cpp =================================================================== --- source/Core/FileSpecList.cpp +++ source/Core/FileSpecList.cpp @@ -82,7 +82,7 @@ // it is found, else std::numeric_limits::max() is returned. //------------------------------------------------------------------ size_t FileSpecList::FindFileIndex(size_t start_idx, const FileSpec &file_spec, - bool full, bool remove_dots) const { + bool full) const { const size_t num_files = m_files.size(); // When looking for files, we will compare only the filename if the @@ -96,7 +96,7 @@ file_spec.IsCaseSensitive() || m_files[idx].IsCaseSensitive())) return idx; } else { - if (FileSpec::Equal(m_files[idx], file_spec, full, remove_dots)) + if (FileSpec::Equal(m_files[idx], file_spec, full)) return idx; } } Index: source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp =================================================================== --- source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -5098,9 +5098,6 @@ // It is OK to resolve this path because we must find a file on // disk for us to accept it anyway if it is rpath relative. FileSpec file_spec(path, true); - // Remove any redundant parts of the path (like "../foo") since - // LC_RPATH values often contain "..". - file_spec = file_spec.GetNormalizedPath(); if (file_spec.Exists() && files.AppendIfUnique(file_spec)) { count++; break; @@ -5118,7 +5115,6 @@ for (const auto &at_exec_relative_path : at_exec_relative_paths) { FileSpec file_spec = exec_dir.CopyByAppendingPathComponent(at_exec_relative_path); - file_spec = file_spec.GetNormalizedPath(); if (file_spec.Exists() && files.AppendIfUnique(file_spec)) count++; } Index: source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h +++ source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h @@ -90,7 +90,7 @@ include_directories.clear(); file_names.clear(); } - bool GetFile(uint32_t file_idx, const char *comp_dir, + bool GetFile(uint32_t file_idx, const lldb_private::FileSpec &cu_comp_dir, lldb_private::FileSpec &file) const; }; @@ -199,7 +199,8 @@ static bool ParseSupportFiles(const lldb::ModuleSP &module_sp, const lldb_private::DWARFDataExtractor &debug_line_data, - const char *cu_comp_dir, dw_offset_t stmt_list, + const lldb_private::FileSpec &cu_comp_dir, + dw_offset_t stmt_list, lldb_private::FileSpecList &support_files); static bool ParsePrologue(const lldb_private::DWARFDataExtractor &debug_line_data, Index: source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp @@ -444,7 +444,7 @@ bool DWARFDebugLine::ParseSupportFiles( const lldb::ModuleSP &module_sp, const DWARFDataExtractor &debug_line_data, - const char *cu_comp_dir, dw_offset_t stmt_list, + const lldb_private::FileSpec &cu_comp_dir, dw_offset_t stmt_list, FileSpecList &support_files) { lldb::offset_t offset = stmt_list; @@ -861,8 +861,8 @@ // buff.Append8(0); // Terminate the file names section with empty string //} -bool DWARFDebugLine::Prologue::GetFile(uint32_t file_idx, const char *comp_dir, - FileSpec &file) const { +bool DWARFDebugLine::Prologue::GetFile(uint32_t file_idx, + const lldb_private::FileSpec &comp_dir, FileSpec &file) const { uint32_t idx = file_idx - 1; // File indexes are 1 based... if (idx < file_names.size()) { file.SetFile(file_names[idx].name, false); @@ -876,7 +876,7 @@ } } - if (comp_dir && comp_dir[0]) + if (comp_dir) file.PrependPathComponent(comp_dir); } return true; Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -174,38 +174,39 @@ return colon_pos + 1; } -static const char *resolveCompDir(const char *path_from_dwarf) { +static FileSpec resolveCompDir(const char *path_from_dwarf) { if (!path_from_dwarf) - return nullptr; + return FileSpec(); // DWARF2/3 suggests the form hostname:pathname for compilation directory. // Remove the host part if present. const char *local_path = removeHostnameFromPathname(path_from_dwarf); if (!local_path) - return nullptr; + return FileSpec(); bool is_symlink = false; - FileSpec local_path_spec(local_path, false); + // Always normalize our compile unit directory to get rid of redundant + // slashes and other path anomalies before we use it for path prepending + FileSpec local_spec(local_path, false); const auto &file_specs = GetGlobalPluginProperties()->GetSymLinkPaths(); for (size_t i = 0; i < file_specs.GetSize() && !is_symlink; ++i) is_symlink = FileSpec::Equal(file_specs.GetFileSpecAtIndex(i), - local_path_spec, true); + local_spec, true); if (!is_symlink) - return local_path; + return local_spec; namespace fs = llvm::sys::fs; - if (fs::get_file_type(local_path_spec.GetPath(), false) != + if (fs::get_file_type(local_spec.GetPath(), false) != fs::file_type::symlink_file) - return local_path; + return local_spec; - FileSpec resolved_local_path_spec; - const auto error = - FileSystem::Readlink(local_path_spec, resolved_local_path_spec); + FileSpec resolved_symlink; + const auto error = FileSystem::Readlink(local_spec, resolved_symlink); if (error.Success()) - return resolved_local_path_spec.GetCString(); + return resolved_symlink; - return nullptr; + return local_spec; } DWARFUnit *SymbolFileDWARF::GetBaseCompileUnit() { @@ -912,7 +913,7 @@ const DWARFDIE cu_die = dwarf_cu->GetUnitDIEOnly(); if (cu_die) { - const char *cu_comp_dir = resolveCompDir( + FileSpec cu_comp_dir = resolveCompDir( cu_die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr)); const dw_offset_t stmt_list = cu_die.GetAttributeValueAsUnsigned( DW_AT_stmt_list, DW_INVALID_OFFSET); Index: source/Symbol/CompileUnit.cpp =================================================================== --- source/Symbol/CompileUnit.cpp +++ source/Symbol/CompileUnit.cpp @@ -287,9 +287,8 @@ // when finding file indexes std::vector file_indexes; const bool full_match = (bool)file_spec.GetDirectory(); - const bool remove_backup_dots = true; bool file_spec_matches_cu_file_spec = - FileSpec::Equal(file_spec, *this, full_match, remove_backup_dots); + FileSpec::Equal(file_spec, *this, full_match); // If we are not looking for inlined functions and our file spec doesn't // match then we are done... @@ -297,11 +296,10 @@ return 0; uint32_t file_idx = - GetSupportFiles().FindFileIndex(1, file_spec, true, remove_backup_dots); + GetSupportFiles().FindFileIndex(1, file_spec, true); while (file_idx != UINT32_MAX) { file_indexes.push_back(file_idx); - file_idx = GetSupportFiles().FindFileIndex(file_idx + 1, file_spec, true, - remove_backup_dots); + file_idx = GetSupportFiles().FindFileIndex(file_idx + 1, file_spec, true); } const size_t num_file_indexes = file_indexes.size(); Index: source/Symbol/Declaration.cpp =================================================================== --- source/Symbol/Declaration.cpp +++ source/Symbol/Declaration.cpp @@ -91,7 +91,7 @@ return lhs.GetFile() == rhs.GetFile(); #else if (lhs.GetLine() == rhs.GetLine()) - return FileSpec::Equal(lhs.GetFile(), rhs.GetFile(), true, true); + return FileSpec::Equal(lhs.GetFile(), rhs.GetFile(), true); #endif return false; } Index: source/Utility/FileSpec.cpp =================================================================== --- source/Utility/FileSpec.cpp +++ source/Utility/FileSpec.cpp @@ -62,17 +62,156 @@ return value == '/' || (!PathSyntaxIsPosix(syntax) && value == '\\'); } -void Normalize(llvm::SmallVectorImpl &path, FileSpec::PathSyntax syntax) { - if (PathSyntaxIsPosix(syntax)) +size_t RootDirStart(llvm::StringRef str, FileSpec::PathSyntax syntax); + +//------------------------------------------------------------------ +/// Safely get a character at the specified index. +/// +/// @param[in] path +/// A full, partial, or relative path to a file. +/// +/// @param[in] i +/// An index into path which may or may not be valid. +/// +/// @return +/// The character at index \a i if the index is valid, or 0 if +/// the index is not valid. +//------------------------------------------------------------------ +inline char safeCharAtIndex(const llvm::StringRef &path, size_t i) { + if (i < path.size()) + return path[i]; + return 0; +} + +//------------------------------------------------------------------ +/// Check if a path needs to be normalized. +/// +/// Check if a path needs to be normalized. We currently consider a +/// path to need normalization if any of the following are true +/// - path contains "/./" +/// - path contains "/../" +/// - path contains "//" +/// - path ends with "/" +/// Paths that start with "./" or with "../" are not considered to +/// need normalization since we aren't trying to resolve the path, +/// we are just trying to remove redundant things from the path. +/// +/// @param[in] path +/// A full, partial, or relative path to a file. +/// +/// @return +/// Returns \b true if the path needs to be normalized. +//------------------------------------------------------------------ +bool needsNormalization(const llvm::StringRef &path) { + for (auto i = path.find('/'); i != llvm::StringRef::npos; + i = path.find('/', i + 1)) { + const auto nextChar = safeCharAtIndex(path, i+1); + switch (nextChar) { + case 0: + // '/'' at the end of the string which should be stripped unless + // it is the one and only character + return i > 0; + case '/': + // "//" in the middle of a path needs to be normalized + if (i > 0) + return true; + ++i; + break; + + case '.': { + const auto nextNextChar = safeCharAtIndex(path, i+2); + switch (nextNextChar) { + default: break; + case 0: return true; // ends with "/." + case '/': return true; // contains "/./" + case '.': + switch (safeCharAtIndex(path, i+3)) { + default: break; + case 0: return true; // ends with "/.." + case '/': return true; // contains "/../" + } + break; + } + } + break; + + default: + break; + } + } + return false; +} + +void Normalize(llvm::SmallVectorImpl &path, + FileSpec::PathSyntax syntax) { + + if (syntax == FileSpec::ePathSyntaxWindows) { + std::replace(path.begin(), path.end(), '\\', '/'); + // Windows path can have \\ slashes which can be changed by replace + // call above to //. Here we remove the duplicate. Also remove duplicate + // '/' chara + auto iter = std::unique(path.begin(), path.end(), [](char &c1, char &c2) { + return (c1 == '/' && c2 == '/'); + }); + path.erase(iter, path.end()); + } + llvm::StringRef path_ref(path.data(), path.size()); + + if (!needsNormalization(path_ref)) return; - std::replace(path.begin(), path.end(), '\\', '/'); - // Windows path can have \\ slashes which can be changed by replace - // call above to //. Here we remove the duplicate. - auto iter = std::unique(path.begin(), path.end(), [](char &c1, char &c2) { - return (c1 == '/' && c2 == '/'); - }); - path.erase(iter, path.end()); + llvm::SmallString<64> normalized; + + // We will not go below root dir. + size_t root_dir_start = RootDirStart(path_ref, syntax); + const bool absolute = root_dir_start != llvm::StringRef::npos; + if (absolute) { + normalized += path_ref.take_front(root_dir_start + 1); + path_ref = path_ref.drop_front(root_dir_start + 1); + } else { + if (syntax == FileSpec::ePathSyntaxWindows && path.size() > 2 && + path[1] == ':') { + normalized += path_ref.take_front(2); + path_ref = path_ref.drop_front(2); + } + } + + bool anything_added = false; + llvm::SmallVector components, processed; + path_ref.split(components, '/', -1, false); + processed.reserve(components.size()); + for (auto component : components) { + if (component == ".") { + if (!normalized.empty()) + continue; // Skip '.' unless it is the first thing + } + else if (component != "..") { + processed.push_back(component); + continue; // Regular file name. + } + if (!processed.empty()) { + processed.pop_back(); + continue; // Dots. Go one level up if we can. + } + if (absolute) + continue; // We're at the top level. Cannot go higher than that. Skip. + + normalized += component; // We're a relative path. We need to keep these. + normalized += '/'; + anything_added = true; + } + for (auto component : processed) { + normalized += component; + normalized += '/'; + anything_added = true; + } + + if (anything_added) + normalized.pop_back(); // Pop last '/'. + else if (normalized.empty()) + normalized = "."; + + path.swap(normalized); } void Denormalize(llvm::SmallVectorImpl &path, @@ -256,10 +395,15 @@ filename_begin != root_dir_start && IsPathSeparator(resolve_path_ref[filename_begin], m_syntax)) ++filename_begin; - m_filename.SetString((filename_begin == llvm::StringRef::npos || - filename_begin >= resolve_path_ref.size()) - ? "." - : resolve_path_ref.substr(filename_begin)); + if (filename_begin < resolve_path_ref.size()) + m_filename.SetString(resolve_path_ref.substr(filename_begin)); + else { + // Make sure we don't end up with "." in both the directory and filename. + // If we do, clear the directory. + m_filename.SetString("."); + if (m_filename == m_directory) + m_directory.Clear(); + } } void FileSpec::SetFile(llvm::StringRef path, bool resolve, @@ -408,108 +552,24 @@ return ConstString::Compare(a.m_filename, b.m_filename, case_sensitive); } -bool FileSpec::Equal(const FileSpec &a, const FileSpec &b, bool full, - bool remove_backups) { - static ConstString g_dot_string("."); - static ConstString g_dot_dot_string(".."); +bool FileSpec::Equal(const FileSpec &a, const FileSpec &b, bool full) { // case sensitivity of equality test const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive(); - bool filenames_equal = ConstString::Equals(a.m_filename, - b.m_filename, - case_sensitive); + const bool filenames_equal = ConstString::Equals(a.m_filename, + b.m_filename, + case_sensitive); - // The only way two FileSpecs can be equal if their filenames are - // unequal is if we are removing backups and one or the other filename - // is a backup string: - - if (!filenames_equal && !remove_backups) + if (!filenames_equal) return false; - bool last_component_is_dot = ConstString::Equals(a.m_filename, g_dot_string) - || ConstString::Equals(a.m_filename, - g_dot_dot_string) - || ConstString::Equals(b.m_filename, - g_dot_string) - || ConstString::Equals(b.m_filename, - g_dot_dot_string); - - if (!filenames_equal && !last_component_is_dot) - return false; - if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty())) return filenames_equal; - if (remove_backups == false) - return a == b; - - if (a == b) - return true; - - return Equal(a.GetNormalizedPath(), b.GetNormalizedPath(), full, false); + return a == b; } -FileSpec FileSpec::GetNormalizedPath() const { - // Fast path. Do nothing if the path is not interesting. - if (!m_directory.GetStringRef().contains(".") && - !m_directory.GetStringRef().contains("//") && - m_filename.GetStringRef() != ".." && m_filename.GetStringRef() != ".") - return *this; - - llvm::SmallString<64> path, result; - const bool normalize = false; - GetPath(path, normalize); - llvm::StringRef rest(path); - - // We will not go below root dir. - size_t root_dir_start = RootDirStart(path, m_syntax); - const bool absolute = root_dir_start != llvm::StringRef::npos; - if (absolute) { - result += rest.take_front(root_dir_start + 1); - rest = rest.drop_front(root_dir_start + 1); - } else { - if (m_syntax == ePathSyntaxWindows && path.size() > 2 && path[1] == ':') { - result += rest.take_front(2); - rest = rest.drop_front(2); - } - } - - bool anything_added = false; - llvm::SmallVector components, processed; - rest.split(components, '/', -1, false); - processed.reserve(components.size()); - for (auto component : components) { - if (component == ".") - continue; // Skip these. - if (component != "..") { - processed.push_back(component); - continue; // Regular file name. - } - if (!processed.empty()) { - processed.pop_back(); - continue; // Dots. Go one level up if we can. - } - if (absolute) - continue; // We're at the top level. Cannot go higher than that. Skip. - - result += component; // We're a relative path. We need to keep these. - result += '/'; - anything_added = true; - } - for (auto component : processed) { - result += component; - result += '/'; - anything_added = true; - } - if (anything_added) - result.pop_back(); // Pop last '/'. - else if (result.empty()) - result = "."; - - return FileSpec(result, false, m_syntax); -} - //------------------------------------------------------------------ // Dump the object to the supplied stream. If the object contains // a valid directory name, it will be displayed followed by a @@ -793,7 +853,8 @@ continue; result += components[i]; if (i != components.size() - 1 && - !IsPathSeparator(components[i].back(), syntax)) + !IsPathSeparator(components[i].back(), syntax) && + (result.empty() || !IsPathSeparator(result.back(), syntax))) result += GetPreferredPathSeparator(syntax); } Index: unittests/Utility/FileSpecTest.cpp =================================================================== --- unittests/Utility/FileSpecTest.cpp +++ unittests/Utility/FileSpecTest.cpp @@ -54,17 +54,14 @@ FileSpec fs_posix_trailing_slash("/foo/bar/", false, FileSpec::ePathSyntaxPosix); - EXPECT_STREQ("/foo/bar/.", fs_posix_trailing_slash.GetCString()); - EXPECT_STREQ("/foo/bar", fs_posix_trailing_slash.GetDirectory().GetCString()); - EXPECT_STREQ(".", fs_posix_trailing_slash.GetFilename().GetCString()); + 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\\", false, FileSpec::ePathSyntaxWindows); - EXPECT_STREQ("F:\\bar\\.", fs_windows_trailing_slash.GetCString()); - // EXPECT_STREQ("F:\\bar", - // fs_windows_trailing_slash.GetDirectory().GetCString()); // It returns - // "F:/bar" - EXPECT_STREQ(".", fs_windows_trailing_slash.GetFilename().GetCString()); + EXPECT_STREQ("F:\\bar", fs_windows_trailing_slash.GetCString()); + EXPECT_STREQ("bar", fs_windows_trailing_slash.GetFilename().GetCString()); } TEST(FileSpecTest, AppendPathComponent) { @@ -131,32 +128,13 @@ EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString()); } -static void Compare(const FileSpec &one, const FileSpec &two, bool full_match, - bool remove_backup_dots, bool result) { - EXPECT_EQ(result, FileSpec::Equal(one, two, full_match, remove_backup_dots)) - << "File one: " << one.GetCString() << "\nFile two: " << two.GetCString() - << "\nFull match: " << full_match - << "\nRemove backup dots: " << remove_backup_dots; -} - TEST(FileSpecTest, EqualSeparator) { FileSpec backward("C:\\foo\\bar", false, FileSpec::ePathSyntaxWindows); FileSpec forward("C:/foo/bar", false, FileSpec::ePathSyntaxWindows); EXPECT_EQ(forward, backward); - - const bool full_match = true; - const bool remove_backup_dots = true; - const bool match = true; - Compare(forward, backward, full_match, remove_backup_dots, match); - Compare(forward, backward, full_match, !remove_backup_dots, match); - Compare(forward, backward, !full_match, remove_backup_dots, match); - Compare(forward, backward, !full_match, !remove_backup_dots, match); } TEST(FileSpecTest, EqualDotsWindows) { - const bool full_match = true; - const bool remove_backup_dots = true; - const bool match = true; std::pair tests[] = { {R"(C:\foo\bar\baz)", R"(C:\foo\foo\..\bar\baz)"}, {R"(C:\bar\baz)", R"(C:\foo\..\bar\baz)"}, @@ -170,18 +148,11 @@ for (const auto &test : tests) { FileSpec one(test.first, false, FileSpec::ePathSyntaxWindows); FileSpec two(test.second, false, FileSpec::ePathSyntaxWindows); - EXPECT_NE(one, two); - Compare(one, two, full_match, remove_backup_dots, match); - Compare(one, two, full_match, !remove_backup_dots, !match); - Compare(one, two, !full_match, remove_backup_dots, match); - Compare(one, two, !full_match, !remove_backup_dots, !match); + EXPECT_EQ(one, two); } } TEST(FileSpecTest, EqualDotsPosix) { - const bool full_match = true; - const bool remove_backup_dots = true; - const bool match = true; std::pair tests[] = { {R"(/foo/bar/baz)", R"(/foo/foo/../bar/baz)"}, {R"(/bar/baz)", R"(/foo/../bar/baz)"}, @@ -193,18 +164,11 @@ for (const auto &test : tests) { FileSpec one(test.first, false, FileSpec::ePathSyntaxPosix); FileSpec two(test.second, false, FileSpec::ePathSyntaxPosix); - EXPECT_NE(one, two); - Compare(one, two, full_match, remove_backup_dots, match); - Compare(one, two, full_match, !remove_backup_dots, !match); - Compare(one, two, !full_match, remove_backup_dots, match); - Compare(one, two, !full_match, !remove_backup_dots, !match); + EXPECT_EQ(one, two); } } TEST(FileSpecTest, EqualDotsPosixRoot) { - const bool full_match = true; - const bool remove_backup_dots = true; - const bool match = true; std::pair tests[] = { {R"(/)", R"(/..)"}, {R"(/)", R"(/.)"}, @@ -214,11 +178,7 @@ for (const auto &test : tests) { FileSpec one(test.first, false, FileSpec::ePathSyntaxPosix); FileSpec two(test.second, false, FileSpec::ePathSyntaxPosix); - EXPECT_NE(one, two); - Compare(one, two, full_match, remove_backup_dots, match); - Compare(one, two, full_match, !remove_backup_dots, !match); - Compare(one, two, !full_match, remove_backup_dots, !match); - Compare(one, two, !full_match, !remove_backup_dots, !match); + EXPECT_EQ(one, two); } } @@ -245,12 +205,14 @@ {"foo/..", "."}, {"foo/../bar", "bar"}, {"../foo/..", ".."}, - {"./foo", "foo"}, + {"./foo", "./foo"}, + {"././foo", "./foo"}, + {"../foo", "../foo"}, + {"../../foo", "../../foo"}, }; for (auto test : posix_tests) { EXPECT_EQ(test.second, FileSpec(test.first, false, FileSpec::ePathSyntaxPosix) - .GetNormalizedPath() .GetPath()); } @@ -274,12 +236,14 @@ {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)"}, + {R"(..\..\foo)", R"(..\..\foo)"}, }; for (auto test : windows_tests) { EXPECT_EQ(test.second, FileSpec(test.first, false, FileSpec::ePathSyntaxWindows) - .GetNormalizedPath() .GetPath()) << "Original path: " << test.first; }