From bfaa9f3754611bf882a4bc4468d4bf64acd76338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Fri, 19 Apr 2024 16:51:03 +0200 Subject: [PATCH] feat: implement `TryJoin` for simulated `Path` (#568) Implement the `TryJoin` methods for `Path`. --- .../Helpers/Execute.SimulatedPath.cs | 30 ++++++++-- .../FileSystemClassGenerator.cs | 3 +- .../FileSystem/Path/TryJoinTests.cs | 57 +++++++++++++++++++ 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs b/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs index af3088a04..8efbaaf1d 100644 --- a/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs +++ b/Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs @@ -1,7 +1,7 @@ using System; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; - #if FEATURE_FILESYSTEM_NET7 using Testably.Abstractions.Testing.Storage; #endif @@ -407,7 +407,18 @@ public bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, Span destination, out int charsWritten) - => System.IO.Path.TryJoin(path1, path2, destination, out charsWritten); + { + string result = Join(path1, path2); + if (destination.Length < result.Length) + { + charsWritten = 0; + return false; + } + + result.AsSpan().CopyTo(destination); + charsWritten = result.Length; + return true; + } #endif #if FEATURE_PATH_JOIN @@ -417,7 +428,18 @@ public bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path3, Span destination, out int charsWritten) - => System.IO.Path.TryJoin(path1, path2, path3, destination, out charsWritten); + { + string result = Join(path1, path2, path3); + if (destination.Length < result.Length) + { + charsWritten = 0; + return false; + } + + result.AsSpan().CopyTo(destination); + charsWritten = result.Length; + return true; + } #endif #endregion @@ -440,7 +462,7 @@ private string JoinInternal(string?[] paths) return string.Empty; } - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); foreach (string? path in paths) { if (string.IsNullOrEmpty(path)) diff --git a/Tests/Helpers/Testably.Abstractions.Tests.SourceGenerator/ClassGenerators/FileSystemClassGenerator.cs b/Tests/Helpers/Testably.Abstractions.Tests.SourceGenerator/ClassGenerators/FileSystemClassGenerator.cs index 8757eb731..adfe34e6c 100644 --- a/Tests/Helpers/Testably.Abstractions.Tests.SourceGenerator/ClassGenerators/FileSystemClassGenerator.cs +++ b/Tests/Helpers/Testably.Abstractions.Tests.SourceGenerator/ClassGenerators/FileSystemClassGenerator.cs @@ -241,7 +241,8 @@ private bool IncludeSimulatedTests(ClassModel @class) "GetTempPathTests", "IsPathRootedTests", "JoinTests", - "Tests" + "Tests", + "TryJoinTests" ]; return @class.Namespace .StartsWith("Testably.Abstractions.Tests.FileSystem.Path", StringComparison.Ordinal) diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Path/TryJoinTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Path/TryJoinTests.cs index c484b35cc..8dd934b0b 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Path/TryJoinTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Path/TryJoinTests.cs @@ -27,6 +27,33 @@ public void TryJoin_2Paths_BufferTooLittle_ShouldReturnFalse( charsWritten.Should().Be(0); } + [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 TryJoin_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); + char[] buffer = new char[expectedResult.Length]; + Span destination = new(buffer); + + bool result = FileSystem.Path.TryJoin( + path1.AsSpan(), + path2.AsSpan(), + destination, + out int charsWritten); + + result.Should().BeTrue(); + charsWritten.Should().Be(expectedResult.Length); + destination.Slice(0, charsWritten).ToString().Should().Be(expectedResult); + } + [SkippableTheory] [AutoData] public void TryJoin_2Paths_ShouldReturnPathsCombinedByDirectorySeparatorChar( @@ -72,6 +99,36 @@ public void TryJoin_3Paths_BufferTooLittle_ShouldReturnFalse( charsWritten.Should().Be(0); } + [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 TryJoin_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); + char[] buffer = new char[expectedResult.Length]; + Span destination = new(buffer); + + bool result = FileSystem.Path.TryJoin( + path1.AsSpan(), + path2.AsSpan(), + path3.AsSpan(), + destination, + out int charsWritten); + + result.Should().BeTrue(); + charsWritten.Should().Be(expectedResult.Length); + destination.Slice(0, charsWritten).ToString().Should().Be(expectedResult); + } + [SkippableTheory] [AutoData] public void TryJoin_3Paths_ShouldReturnPathsCombinedByDirectorySeparatorChar(