diff --git a/TruePath.Tests/AbsolutePathEqualsTests.cs b/TruePath.Tests/AbsolutePathEqualsTests.cs new file mode 100644 index 0000000..3f35947 --- /dev/null +++ b/TruePath.Tests/AbsolutePathEqualsTests.cs @@ -0,0 +1,99 @@ +namespace TruePath.Tests; + +public partial class AbsolutePathTests +{ + [Fact] + public void EqualsUseStrictStringPathComparer_SamePaths_True() + { + // Arrange + var currentDirectory = Environment.CurrentDirectory; + var nonCanonicalPath = currentDirectory; + + var path1 = new AbsolutePath(currentDirectory); + var path2 = new AbsolutePath(nonCanonicalPath); + + // Act + var equals = path1.Equals(path2, StrictStringPathComparer.Comparer); + + // Assert + Assert.True(equals); + } + + [Fact] + public void EqualsUseStrictStringPathComparer_NotSamePaths_False() + { + // Arrange + var currentDirectory = Environment.CurrentDirectory; + var nonCanonicalPath = new string(currentDirectory.MakeNonCanonicalPath().ToArray()); + + var path1 = new AbsolutePath(currentDirectory); + var path2 = new AbsolutePath(nonCanonicalPath); + + // Act + var equals = path1.Equals(path2, StrictStringPathComparer.Comparer); + + // Assert + Assert.False(equals); + } + + [Fact] + public void OnLinux_EqualsDefault_CaseSensitive_False() + { + // Arrange + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return; + } + + var currentDirectory = Environment.CurrentDirectory; + var nonCanonicalPath = new string(currentDirectory.MakeNonCanonicalPath().ToArray()); + + var path1 = new AbsolutePath(currentDirectory); + var path2 = new AbsolutePath(nonCanonicalPath); + + // Act + var equals = path1.Equals(path2); + + // Assert + Assert.False(equals); + } + + [Fact] + public void EqualsNull_False() + { + // Arrange + var currentDirectory = Environment.CurrentDirectory; + var nonCanonicalPath = currentDirectory; + + var path1 = new AbsolutePath(currentDirectory); + var path2 = new AbsolutePath(nonCanonicalPath); + + // Act + var equals = path1.Equals(path2, null); + + // Assert + Assert.False(equals); + } + + [Fact] + public void OnWindowsOrOsx_EqualsDefault_CaseInsensitive_True() + { + // Arrange + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return; + } + + var currentDirectory = Environment.CurrentDirectory; + var nonCanonicalPath = new string(currentDirectory.MakeNonCanonicalPath().ToArray()); + + var path1 = new AbsolutePath(currentDirectory); + var path2 = new AbsolutePath(nonCanonicalPath); + + // Act + var equals = path1.Equals(path2); + + // Assert + Assert.True(equals); + } +} diff --git a/TruePath.Tests/AbsolutePathTests.cs b/TruePath.Tests/AbsolutePathTests.cs index efd7520..b907402 100644 --- a/TruePath.Tests/AbsolutePathTests.cs +++ b/TruePath.Tests/AbsolutePathTests.cs @@ -4,7 +4,7 @@ namespace TruePath.Tests; -public class AbsolutePathTests +public partial class AbsolutePathTests { [Theory] [InlineData("/home/user", "/home/user/documents")] diff --git a/TruePath.Tests/DiskUtilsTests.cs b/TruePath.Tests/DiskUtilsTests.cs index b999aac..2714323 100644 --- a/TruePath.Tests/DiskUtilsTests.cs +++ b/TruePath.Tests/DiskUtilsTests.cs @@ -35,7 +35,7 @@ public void DiskUtils_OnWindows_PassNonCanonicalPath_ReturnCanonicalPath() if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; var expected = Environment.CurrentDirectory; - var nonCanonicalPath = new string(MakeNonCanonicalPath(expected).ToArray()); + var nonCanonicalPath = new string(expected.MakeNonCanonicalPath().ToArray()); // Act var actual = DiskUtils.GetRealPath(nonCanonicalPath); @@ -76,18 +76,4 @@ private static string Back(List folders, int stepsBack, string delimiter return string.Join(delimiter, finalFolders); } - - private static IEnumerable MakeNonCanonicalPath(string path) - { - foreach (var @char in path) - { - if (char.IsLetter(@char) && Random.Shared.NextSingle() >= 0.5) - { - yield return char.ToUpper(@char); - continue; - } - - yield return @char; - } - } } diff --git a/TruePath.Tests/LocalPathEqualsTests.cs b/TruePath.Tests/LocalPathEqualsTests.cs new file mode 100644 index 0000000..391fd41 --- /dev/null +++ b/TruePath.Tests/LocalPathEqualsTests.cs @@ -0,0 +1,99 @@ +namespace TruePath.Tests; + +public partial class LocalPathTests +{ + [Fact] + public void EqualsUseStrictStringPathComparer_SamePaths_True() + { + // Arrange + var currentDirectory = Environment.CurrentDirectory; + var nonCanonicalPath = currentDirectory; + + var path1 = new LocalPath(currentDirectory); + var path2 = new LocalPath(nonCanonicalPath); + + // Act + var equals = path1.Equals(path2, StrictStringPathComparer.Comparer); + + // Assert + Assert.True(equals); + } + + [Fact] + public void EqualsUseStrictStringPathComparer_NotSamePaths_False() + { + // Arrange + var currentDirectory = Environment.CurrentDirectory; + var nonCanonicalPath = new string(currentDirectory.MakeNonCanonicalPath().ToArray()); + + var path1 = new LocalPath(currentDirectory); + var path2 = new LocalPath(nonCanonicalPath); + + // Act + var equals = path1.Equals(path2, StrictStringPathComparer.Comparer); + + // Assert + Assert.False(equals); + } + + [Fact] + public void OnLinux_EqualsDefault_CaseSensitive_False() + { + // Arrange + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return; + } + + var currentDirectory = Environment.CurrentDirectory; + var nonCanonicalPath = new string(currentDirectory.MakeNonCanonicalPath().ToArray()); + + var path1 = new LocalPath(currentDirectory); + var path2 = new LocalPath(nonCanonicalPath); + + // Act + var equals = path1.Equals(path2); + + // Assert + Assert.False(equals); + } + + [Fact] + public void EqualsNull_False() + { + // Arrange + var currentDirectory = Environment.CurrentDirectory; + var nonCanonicalPath = currentDirectory; + + var path1 = new LocalPath(currentDirectory); + var path2 = new LocalPath(nonCanonicalPath); + + // Act + var equals = path1.Equals(path2, null); + + // Assert + Assert.False(equals); + } + + [Fact] + public void OnWindowsOrOsx_EqualsDefault_CaseInsensitive_True() + { + // Arrange + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return; + } + + var currentDirectory = Environment.CurrentDirectory; + var nonCanonicalPath = new string(currentDirectory.MakeNonCanonicalPath().ToArray()); + + var path1 = new LocalPath(currentDirectory); + var path2 = new LocalPath(nonCanonicalPath); + + // Act + var equals = path1.Equals(path2); + + // Assert + Assert.True(equals); + } +} diff --git a/TruePath.Tests/LocalPathTests.cs b/TruePath.Tests/LocalPathTests.cs index 458ea5f..6877ae0 100644 --- a/TruePath.Tests/LocalPathTests.cs +++ b/TruePath.Tests/LocalPathTests.cs @@ -4,7 +4,7 @@ namespace TruePath.Tests; -public class LocalPathTests +public partial class LocalPathTests { [Theory] [InlineData("user", "user/documents")] diff --git a/TruePath.Tests/Utils.cs b/TruePath.Tests/Utils.cs new file mode 100644 index 0000000..c1f934d --- /dev/null +++ b/TruePath.Tests/Utils.cs @@ -0,0 +1,18 @@ +namespace TruePath.Tests; + +public static class Utils +{ + internal static IEnumerable MakeNonCanonicalPath(this string path) + { + foreach (var @char in path) + { + if (char.IsLetter(@char) && Random.Shared.NextSingle() >= 0.5) + { + yield return char.ToUpper(@char); + continue; + } + + yield return @char; + } + } +} diff --git a/TruePath/AbsolutePath.cs b/TruePath/AbsolutePath.cs index 1f174c9..111b3af 100644 --- a/TruePath/AbsolutePath.cs +++ b/TruePath/AbsolutePath.cs @@ -103,7 +103,29 @@ public bool IsPrefixOf(AbsolutePath other) /// Note that currently this comparison is case-sensitive. public bool Equals(AbsolutePath other) { - return Underlying.Equals(other.Underlying); + var comparer = PlatformDefaultPathComparer.Comparer; + return comparer.Compare(Underlying.Value, other.Underlying.Value) == 0; + } + + /// + /// Determines whether the specified is equal to the current using the specified string comparer. + /// + /// The to compare with the current . + /// The comparer to use for comparing the paths. + /// + /// if the specified is equal to the current using the specified string comparer; otherwise, . + /// + /// + /// If the comparer is null, this method returns . + /// + public bool Equals(AbsolutePath other, IComparer? comparer) + { + if (comparer is null) + { + return false; + } + + return comparer.Compare(Value, other.Value) == 0; } /// diff --git a/TruePath/LocalPath.cs b/TruePath/LocalPath.cs index 9cb3522..393b490 100644 --- a/TruePath/LocalPath.cs +++ b/TruePath/LocalPath.cs @@ -42,7 +42,29 @@ public readonly struct LocalPath(string value) : IEquatable, IPath, I /// Note that currently this comparison is case-sensitive. public bool Equals(LocalPath other) { - return Value == other.Value; + var comparer = PlatformDefaultPathComparer.Comparer; + return comparer.Compare(Value, other.Value) == 0; + } + + /// + /// Determines whether the specified is equal to the current using the specified string comparer. + /// + /// The to compare with the current . + /// The comparer to use for comparing the paths. + /// + /// if the specified is equal to the current using the specified string comparer; otherwise, . + /// + /// + /// If the comparer is null, this method returns . + /// + public bool Equals(LocalPath other, IComparer? comparer) + { + if (comparer is null) + { + return false; + } + + return comparer.Compare(Value, other.Value) == 0; } /// diff --git a/TruePath/PlatformDefaultPathComparer.cs b/TruePath/PlatformDefaultPathComparer.cs new file mode 100644 index 0000000..ee6f6fe --- /dev/null +++ b/TruePath/PlatformDefaultPathComparer.cs @@ -0,0 +1,45 @@ +using System.Runtime.InteropServices; + +namespace TruePath; + +/// +/// Provides a platform-specific string comparer for comparing file paths. +/// +public class PlatformDefaultPathComparer : IComparer +{ + /// + /// Gets the singleton instance of the class. + /// + public static readonly PlatformDefaultPathComparer Comparer = new(); + + private readonly StringComparer comparisonType; + + /// + /// Initializes a new instance of the class. + /// + public PlatformDefaultPathComparer() + { + // Определяем тип сравнения в зависимости от платформы + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + comparisonType = StringComparer.OrdinalIgnoreCase; + } + else + { + comparisonType = StringComparer.Ordinal; + } + } + + /// + /// Compares two strings and returns an integer that indicates their relative position in the sort order. + /// + /// The first string to compare. + /// The second string to compare. + /// + /// A value less than zero if is less than ; zero if equals ; a value greater than zero if is greater than . + /// + public int Compare(string? x, string? y) + { + return comparisonType.Compare(x, y); + } +} diff --git a/TruePath/StrictStringPathComparer.cs b/TruePath/StrictStringPathComparer.cs new file mode 100644 index 0000000..51ace22 --- /dev/null +++ b/TruePath/StrictStringPathComparer.cs @@ -0,0 +1,25 @@ +namespace TruePath; + +/// +/// Provides a strict string comparer for comparing file paths using ordinal comparison. +/// +public class StrictStringPathComparer : IComparer +{ + /// + /// Gets the singleton instance of the class. + /// + public static readonly StrictStringPathComparer Comparer = new(); + + /// + /// Compares two strings and returns an integer that indicates their relative position in the sort order using ordinal comparison. + /// + /// The first string to compare. + /// The second string to compare. + /// + /// A value less than zero if is less than ; zero if equals ; a value greater than zero if is greater than . + /// + public int Compare(string? x, string? y) + { + return StringComparer.Ordinal.Compare(x, y); + } +}