From 025c52a613f0a4d33f79dc2245923f974676f0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Fri, 19 Apr 2024 14:57:27 +0200 Subject: [PATCH 1/2] Add support for Join --- .../Helpers/Execute.SimulatedPath.cs | 47 +++++++++++++++++-- .../FileSystemClassGenerator.cs | 1 + 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs b/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs index c92aadb6d..78975ae0a 100644 --- a/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs +++ b/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs @@ -1,5 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Text; + #if FEATURE_FILESYSTEM_NET7 using Testably.Abstractions.Testing.Storage; #endif @@ -342,7 +344,7 @@ public bool IsPathRooted(ReadOnlySpan path) #if FEATURE_PATH_JOIN /// public string Join(ReadOnlySpan path1, ReadOnlySpan path2) - => System.IO.Path.Join(path1, path2); + => JoinInternal([path1.ToString(), path2.ToString()]); #endif #if FEATURE_PATH_JOIN @@ -350,7 +352,7 @@ public string Join(ReadOnlySpan path1, ReadOnlySpan path2) public string Join(ReadOnlySpan path1, ReadOnlySpan path2, ReadOnlySpan path3) - => System.IO.Path.Join(path1, path2, path3); + => JoinInternal([path1.ToString(), path2.ToString(), path3.ToString()]); #endif #if FEATURE_PATH_ADVANCED @@ -476,9 +478,44 @@ private static string CombineInternal(string[] paths) protected abstract bool IsDirectorySeparator(char c); -#if FEATURE_PATH_ADVANCED - private static string JoinInternal(string?[] paths) - => System.IO.Path.Join(paths); +#if FEATURE_PATH_JOIN || FEATURE_PATH_ADVANCED + private string JoinInternal(string?[] paths) + { + if (paths == null) + { + throw new ArgumentNullException(nameof(paths)); + } + + if (paths.Length == 0) + { + return string.Empty; + } + + StringBuilder sb = new StringBuilder(); + foreach (string? path in paths) + { + if (string.IsNullOrEmpty(path)) + { + continue; + } + + if (sb.Length == 0) + { + sb.Append(path); + } + else + { + if (!IsDirectorySeparator(sb[sb.Length - 1]) && !IsDirectorySeparator(path[0])) + { + sb.Append(DirectorySeparatorChar); + } + + sb.Append(path); + } + } + + return sb.ToString(); + } #endif private bool TryGetExtensionIndex(string path, [NotNullWhen(true)] out int? dotIndex) diff --git a/Tests/Helpers/Testably.Abstractions.Tests.SourceGenerator/ClassGenerators/FileSystemClassGenerator.cs b/Tests/Helpers/Testably.Abstractions.Tests.SourceGenerator/ClassGenerators/FileSystemClassGenerator.cs index de2e7d541..8757eb731 100644 --- a/Tests/Helpers/Testably.Abstractions.Tests.SourceGenerator/ClassGenerators/FileSystemClassGenerator.cs +++ b/Tests/Helpers/Testably.Abstractions.Tests.SourceGenerator/ClassGenerators/FileSystemClassGenerator.cs @@ -240,6 +240,7 @@ private bool IncludeSimulatedTests(ClassModel @class) "GetRandomFileNameTests", "GetTempPathTests", "IsPathRootedTests", + "JoinTests", "Tests" ]; return @class.Namespace From c531db89038a0093e71b1ca44f56aaa38afd50ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Fri, 19 Apr 2024 15:35:56 +0200 Subject: [PATCH 2/2] Add missing tests --- .../Helpers/Execute.SimulatedPath.cs | 57 +--------- .../FileSystem/Path/JoinTests.cs | 106 ++++++++++++++++++ 2 files changed, 109 insertions(+), 54 deletions(-) diff --git a/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs b/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs index 78975ae0a..af3088a04 100644 --- a/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs +++ b/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs @@ -368,70 +368,19 @@ public string Join(ReadOnlySpan path1, #if FEATURE_PATH_ADVANCED /// public string Join(string? path1, string? path2) - { - if (string.IsNullOrEmpty(path1)) - { - return path2 ?? string.Empty; - } - - if (string.IsNullOrEmpty(path2)) - { - return path1; - } - - return JoinInternal([path1, path2]); - } + => JoinInternal([path1, path2]); #endif #if FEATURE_PATH_ADVANCED /// public string Join(string? path1, string? path2, string? path3) - { - if (string.IsNullOrEmpty(path1)) - { - return Join(path2, path3); - } - - if (string.IsNullOrEmpty(path2)) - { - return Join(path1, path3); - } - - if (string.IsNullOrEmpty(path3)) - { - return Join(path1, path2); - } - - return JoinInternal([path1, path2, path3]); - } + => JoinInternal([path1, path2, path3]); #endif #if FEATURE_PATH_ADVANCED /// public string Join(string? path1, string? path2, string? path3, string? path4) - { - if (string.IsNullOrEmpty(path1)) - { - return Join(path2, path3, path4); - } - - if (string.IsNullOrEmpty(path2)) - { - return Join(path1, path3, path4); - } - - if (string.IsNullOrEmpty(path3)) - { - return Join(path1, path2, path4); - } - - if (string.IsNullOrEmpty(path4)) - { - return Join(path1, path2, path3); - } - - return JoinInternal([path1, path2, path3, path4]); - } + => JoinInternal([path1, path2, path3, path4]); #endif #if FEATURE_PATH_ADVANCED diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Path/JoinTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Path/JoinTests.cs index 764a1c5f9..8f8eff37c 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Path/JoinTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Path/JoinTests.cs @@ -19,6 +19,25 @@ public void Join_2Paths_OneNullOrEmpty_ShouldReturnCombinationOfOtherParts( result2.Should().Be(path); } + [SkippableTheory] + [InlineAutoData("/foo/", "/bar/", "/foo//bar/")] + [InlineAutoData("foo/", "/bar", "foo//bar")] + [InlineAutoData("foo/", "bar", "foo/bar")] + [InlineAutoData("foo", "/bar", "foo/bar")] + [InlineAutoData("foo", "bar", "foo/bar")] + [InlineAutoData("/foo", "bar/", "/foo/bar/")] + public void Join_2Paths_ShouldReturnExpectedResult( + string path1, string path2, string expectedResult) + { + path1 = path1.Replace('/', FileSystem.Path.DirectorySeparatorChar); + path2 = path2.Replace('/', FileSystem.Path.DirectorySeparatorChar); + expectedResult = expectedResult.Replace('/', FileSystem.Path.DirectorySeparatorChar); + + string result = FileSystem.Path.Join(path1, path2); + + result.Should().Be(expectedResult); + } + [SkippableTheory] [AutoData] public void Join_2Paths_ShouldReturnPathsCombinedByDirectorySeparatorChar( @@ -64,6 +83,27 @@ public void Join_3Paths_OneNullOrEmpty_ShouldReturnCombinationOfOtherParts( result3.Should().Be(expectedPath); } + [SkippableTheory] + [InlineAutoData("/foo/", "/bar/", "/baz/", "/foo//bar//baz/")] + [InlineAutoData("foo/", "/bar/", "/baz", "foo//bar//baz")] + [InlineAutoData("foo/", "bar", "/baz", "foo/bar/baz")] + [InlineAutoData("foo", "/bar", "/baz", "foo/bar/baz")] + [InlineAutoData("foo", "/bar/", "baz", "foo/bar/baz")] + [InlineAutoData("foo", "bar", "baz", "foo/bar/baz")] + [InlineAutoData("/foo", "bar", "baz/", "/foo/bar/baz/")] + public void Join_3Paths_ShouldReturnExpectedResult( + string path1, string path2, string path3, string expectedResult) + { + path1 = path1.Replace('/', FileSystem.Path.DirectorySeparatorChar); + path2 = path2.Replace('/', FileSystem.Path.DirectorySeparatorChar); + path3 = path3.Replace('/', FileSystem.Path.DirectorySeparatorChar); + expectedResult = expectedResult.Replace('/', FileSystem.Path.DirectorySeparatorChar); + + string result = FileSystem.Path.Join(path1, path2, path3); + + result.Should().Be(expectedResult); + } + [SkippableTheory] [AutoData] public void Join_3Paths_ShouldReturnPathsCombinedByDirectorySeparatorChar( @@ -115,6 +155,28 @@ public void Join_4Paths_OneNullOrEmpty_ShouldReturnCombinationOfOtherParts( result4.Should().Be(expectedPath); } + [SkippableTheory] + [InlineAutoData("/foo/", "/bar/", "/baz/", "/muh/", "/foo//bar//baz//muh/")] + [InlineAutoData("foo/", "/bar/", "/baz/", "/muh", "foo//bar//baz//muh")] + [InlineAutoData("foo/", "bar", "/baz", "/muh", "foo/bar/baz/muh")] + [InlineAutoData("foo", "/bar", "/baz", "/muh", "foo/bar/baz/muh")] + [InlineAutoData("foo", "/bar/", "baz/", "muh", "foo/bar/baz/muh")] + [InlineAutoData("foo", "bar", "baz", "muh", "foo/bar/baz/muh")] + [InlineAutoData("/foo", "bar", "baz", "muh/", "/foo/bar/baz/muh/")] + public void Join_4Paths_ShouldReturnExpectedResult( + string path1, string path2, string path3, string path4, string expectedResult) + { + path1 = path1.Replace('/', FileSystem.Path.DirectorySeparatorChar); + path2 = path2.Replace('/', FileSystem.Path.DirectorySeparatorChar); + path3 = path3.Replace('/', FileSystem.Path.DirectorySeparatorChar); + path4 = path4.Replace('/', FileSystem.Path.DirectorySeparatorChar); + expectedResult = expectedResult.Replace('/', FileSystem.Path.DirectorySeparatorChar); + + string result = FileSystem.Path.Join(path1, path2, path3, path4); + + result.Should().Be(expectedResult); + } + [SkippableTheory] [AutoData] public void Join_4Paths_ShouldReturnPathsCombinedByDirectorySeparatorChar( @@ -149,6 +211,27 @@ public void Join_4Paths_Span_ShouldReturnPathsCombinedByDirectorySeparatorChar( result.Should().Be(expectedResult); } + [SkippableFact] + public void Join_ParamPaths_Empty_ShouldReturnEmptyString() + { + string?[] paths = Array.Empty(); + + string result = FileSystem.Path.Join(paths); + + result.Should().Be(string.Empty); + } + + [SkippableFact] + public void Join_ParamPaths_Null_ShouldThrow() + { + Exception? exception = Record.Exception(() => + { + _ = FileSystem.Path.Join(null!); + }); + + exception.Should().BeException(paramName: "paths"); + } + [SkippableTheory] [InlineAutoData((string?)null)] [InlineAutoData("")] @@ -176,6 +259,29 @@ public void Join_ParamPaths_OneNullOrEmpty_ShouldReturnCombinationOfOtherParts( result5.Should().Be(expectedPath); } + [SkippableTheory] + [InlineAutoData("/foo/", "/bar/", "/baz/", "/muh/", "/maeh/", "/foo//bar//baz//muh//maeh/")] + [InlineAutoData("foo/", "/bar/", "/baz/", "/muh", "/maeh", "foo//bar//baz//muh/maeh")] + [InlineAutoData("foo/", "bar", "/baz", "/muh", "/maeh", "foo/bar/baz/muh/maeh")] + [InlineAutoData("foo", "/bar", "/baz", "/muh", "/maeh", "foo/bar/baz/muh/maeh")] + [InlineAutoData("foo", "/bar/", "baz/", "muh/", "maeh", "foo/bar/baz/muh/maeh")] + [InlineAutoData("foo", "bar", "baz", "muh", "maeh", "foo/bar/baz/muh/maeh")] + [InlineAutoData("/foo", "bar", "baz", "muh", "maeh/", "/foo/bar/baz/muh/maeh/")] + public void Join_ParamPaths_ShouldReturnExpectedResult( + string path1, string path2, string path3, string path4, string path5, string expectedResult) + { + path1 = path1.Replace('/', FileSystem.Path.DirectorySeparatorChar); + path2 = path2.Replace('/', FileSystem.Path.DirectorySeparatorChar); + path3 = path3.Replace('/', FileSystem.Path.DirectorySeparatorChar); + path4 = path4.Replace('/', FileSystem.Path.DirectorySeparatorChar); + path5 = path5.Replace('/', FileSystem.Path.DirectorySeparatorChar); + expectedResult = expectedResult.Replace('/', FileSystem.Path.DirectorySeparatorChar); + + string result = FileSystem.Path.Join(path1, path2, path3, path4, path5); + + result.Should().Be(expectedResult); + } + [SkippableTheory] [AutoData] public void Join_ParamPaths_ShouldReturnPathsCombinedByDirectorySeparatorChar(