Skip to content

Commit

Permalink
feat: implement Join for simulated Path (#567)
Browse files Browse the repository at this point in the history
Implement the `Join` methods for `Path`.
  • Loading branch information
vbreuss authored Apr 19, 2024
1 parent a43d1f0 commit e1c23f8
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 59 deletions.
104 changes: 45 additions & 59 deletions Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;

#if FEATURE_FILESYSTEM_NET7
using Testably.Abstractions.Testing.Storage;
#endif
Expand Down Expand Up @@ -342,15 +344,15 @@ public bool IsPathRooted(ReadOnlySpan<char> path)
#if FEATURE_PATH_JOIN
/// <inheritdoc cref="IPath.Join(ReadOnlySpan{char}, ReadOnlySpan{char})" />
public string Join(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2)
=> System.IO.Path.Join(path1, path2);
=> JoinInternal([path1.ToString(), path2.ToString()]);
#endif

#if FEATURE_PATH_JOIN
/// <inheritdoc cref="IPath.Join(ReadOnlySpan{char}, ReadOnlySpan{char}, ReadOnlySpan{char})" />
public string Join(ReadOnlySpan<char> path1,
ReadOnlySpan<char> path2,
ReadOnlySpan<char> path3)
=> System.IO.Path.Join(path1, path2, path3);
=> JoinInternal([path1.ToString(), path2.ToString(), path3.ToString()]);
#endif

#if FEATURE_PATH_ADVANCED
Expand All @@ -366,70 +368,19 @@ public string Join(ReadOnlySpan<char> path1,
#if FEATURE_PATH_ADVANCED
/// <inheritdoc cref="IPath.Join(string, string)" />
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
/// <inheritdoc cref="IPath.Join(string, string, string)" />
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
/// <inheritdoc cref="IPath.Join(string, string, string, string)" />
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
Expand Down Expand Up @@ -476,9 +427,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ private bool IncludeSimulatedTests(ClassModel @class)
"GetRandomFileNameTests",
"GetTempPathTests",
"IsPathRootedTests",
"JoinTests",
"Tests"
];
return @class.Namespace
Expand Down
106 changes: 106 additions & 0 deletions Tests/Testably.Abstractions.Tests/FileSystem/Path/JoinTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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?>();

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<ArgumentNullException>(paramName: "paths");
}

[SkippableTheory]
[InlineAutoData((string?)null)]
[InlineAutoData("")]
Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit e1c23f8

Please sign in to comment.