Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement additional methods for simulated Path #566

Merged
merged 7 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,11 @@ public override string GetTempPath()
/// <inheritdoc cref="IPath.IsPathRooted(string)" />
public override bool IsPathRooted(string? path)
=> path?.Length > 0 && path[0] == '/';

/// <summary>
/// https://github.com/dotnet/runtime/blob/v8.0.4/src/libraries/Common/src/System/IO/PathInternal.Unix.cs#L27
/// </summary>
protected override bool IsDirectorySeparator(char c)
=> c == DirectorySeparatorChar;
}
}
103 changes: 96 additions & 7 deletions Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics.CodeAnalysis;
#if FEATURE_FILESYSTEM_NET7
using Testably.Abstractions.Testing.Storage;
Expand Down Expand Up @@ -30,7 +30,33 @@ private abstract class SimulatedPath(MockFileSystem fileSystem) : IPath
/// <inheritdoc cref="IPath.ChangeExtension(string, string)" />
[return: NotNullIfNotNull("path")]
public string? ChangeExtension(string? path, string? extension)
=> System.IO.Path.ChangeExtension(path, extension);
{
if (path == null)
{
return null;
}

if (path == string.Empty)
{
return string.Empty;
}

if (extension == null)
{
extension = "";
}
else if (!extension.StartsWith('.'))
{
extension = "." + extension;
}

if (!TryGetExtensionIndex(path, out int? dotIndex))
{
return path + extension;
}

return path.Substring(0, dotIndex.Value) + extension;
}

/// <inheritdoc cref="IPath.Combine(string, string)" />
public string Combine(string path1, string path2)
Expand Down Expand Up @@ -108,7 +134,7 @@ public bool EndsInDirectorySeparator(ReadOnlySpan<char> path)
#if FEATURE_PATH_ADVANCED
/// <inheritdoc cref="IPath.EndsInDirectorySeparator(string)" />
public bool EndsInDirectorySeparator(string path)
=> System.IO.Path.EndsInDirectorySeparator(path);
=> !string.IsNullOrEmpty(path) && IsDirectorySeparator(path[path.Length - 1]);
#endif

#if FEATURE_FILESYSTEM_NET7
Expand Down Expand Up @@ -144,7 +170,21 @@ public ReadOnlySpan<char> GetExtension(ReadOnlySpan<char> path)
/// <inheritdoc cref="IPath.GetExtension(string)" />
[return: NotNullIfNotNull("path")]
public string? GetExtension(string? path)
=> System.IO.Path.GetExtension(path);
{
if (path == null)
{
return null;
}

if (TryGetExtensionIndex(path, out int? dotIndex))
{
return dotIndex != path.Length - 1
? path.Substring(dotIndex.Value)
: string.Empty;
}

return string.Empty;
}

#if FEATURE_SPAN
/// <inheritdoc cref="IPath.GetFileName(ReadOnlySpan{char})" />
Expand All @@ -155,7 +195,22 @@ public ReadOnlySpan<char> GetFileName(ReadOnlySpan<char> path)
/// <inheritdoc cref="IPath.GetFileName(string)" />
[return: NotNullIfNotNull("path")]
public string? GetFileName(string? path)
=> System.IO.Path.GetFileName(path);
{
if (path == null)
{
return null;
}

for (int i = path.Length - 1; i >= 0; i--)
{
if (IsDirectorySeparator(path[i]))
{
return path.Substring(i + 1);
}
}

return path;
}

#if FEATURE_SPAN
/// <inheritdoc cref="IPath.GetFileNameWithoutExtension(ReadOnlySpan{char})" />
Expand All @@ -166,7 +221,16 @@ public ReadOnlySpan<char> GetFileNameWithoutExtension(ReadOnlySpan<char> path)
/// <inheritdoc cref="IPath.GetFileNameWithoutExtension(string)" />
[return: NotNullIfNotNull("path")]
public string? GetFileNameWithoutExtension(string? path)
=> System.IO.Path.GetFileNameWithoutExtension(path);
{
if (path == null)
{
return null;
}

string fileName = GetFileName(path);
int lastPeriod = fileName.LastIndexOf('.');
return lastPeriod < 0 ? fileName : fileName.Substring(0, lastPeriod);
}

/// <inheritdoc cref="IPath.GetFullPath(string)" />
public string GetFullPath(string path)
Expand Down Expand Up @@ -295,7 +359,8 @@ public string Join(ReadOnlySpan<char> path1,
ReadOnlySpan<char> path2,
ReadOnlySpan<char> path3,
ReadOnlySpan<char> path4)
=> JoinInternal([path1.ToString(), path2.ToString(), path3.ToString(), path4.ToString()]);
=> JoinInternal(
[path1.ToString(), path2.ToString(), path3.ToString(), path4.ToString()]);
#endif

#if FEATURE_PATH_ADVANCED
Expand Down Expand Up @@ -409,9 +474,33 @@ public bool TryJoin(ReadOnlySpan<char> path1,
private static string CombineInternal(string[] paths)
=> System.IO.Path.Combine(paths);

protected abstract bool IsDirectorySeparator(char c);

#if FEATURE_PATH_ADVANCED
private static string JoinInternal(string?[] paths)
=> System.IO.Path.Join(paths);
#endif

private bool TryGetExtensionIndex(string path, [NotNullWhen(true)] out int? dotIndex)
{
for (int i = path.Length - 1; i >= 0; i--)
{
char ch = path[i];

if (ch == '.')
{
dotIndex = i;
return true;
}

if (IsDirectorySeparator(ch))
{
break;
}
}

dotIndex = null;
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,10 @@ public override bool IsPathRooted(string? path)
}

/// <summary>
/// True if the given character is a directory separator.
/// https://github.com/dotnet/runtime/blob/v8.0.4/src/libraries/Common/src/System/IO/PathInternal.Windows.cs#L280
/// </summary>
/// <remarks>https://github.com/dotnet/runtime/blob/v8.0.3/src/libraries/Common/src/System/IO/PathInternal.Windows.cs#L280</remarks>
private static bool IsDirectorySeparator(char c)
=> c == '\\' || c == '/';
protected override bool IsDirectorySeparator(char c)
=> c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;

/// <summary>
/// Returns true if the given character is a valid drive letter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,19 @@ public override void SkipIfLongRunningTestsShouldBeSkipped()

private bool IncludeSimulatedTests(ClassModel @class)
{
string[] supportedPathTests = ["Tests", "GetRandomFileNameTests", "GetPathRootTests", "GetTempPathTests", "IsPathRootedTests"];
string[] supportedPathTests =
[
"ChangeExtensionTests",
"EndsInDirectorySeparatorTests",
"GetExtensionTests",
"GetFileNameTests",
"GetFileNameWithoutExtensionTests",
"GetPathRootTests",
"GetRandomFileNameTests",
"GetTempPathTests",
"IsPathRootedTests",
"Tests"
];
return @class.Namespace
.StartsWith("Testably.Abstractions.Tests.FileSystem.Path", StringComparison.Ordinal)
&& supportedPathTests.Contains(@class.Name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,27 @@ public void ChangeExtension_WithDotOnlyInDirectory_ShouldAppendExtensionToPath(
string directory, string fileName, string extension)
{
directory = directory + "." + "with-dot";
string path = FileSystem.Path.Combine(directory, fileName);
string path = $"{directory}{FileSystem.Path.DirectorySeparatorChar}{fileName}";
string expectedResult = path + "." + extension;

string result = FileSystem.Path.ChangeExtension(path, extension);

result.Should().Be(expectedResult);
}

[SkippableTheory]
[AutoData]
public void ChangeExtension_WithFileStartingWithDot_ShouldAppendExtensionToPath(
string fileName, string extension)
{
string path = $".{fileName}";
string expectedResult = $".{extension}";

string result = FileSystem.Path.ChangeExtension(path, extension);

result.Should().Be(expectedResult);
}

[SkippableTheory]
[AutoData]
public void ChangeExtension_WithLeadingDotInExtension_ShouldNotIncludeTwoDots(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ public void
result.Should().BeFalse();
}

[SkippableTheory]
[AutoData]
public void
EndsInDirectorySeparator_Span_WithTrailingAltDirectorySeparator_ShouldReturnTrue(
string path)
{
path += FileSystem.Path.AltDirectorySeparatorChar;

bool result = FileSystem.Path.EndsInDirectorySeparator(path.AsSpan());

result.Should().BeTrue();
}

[SkippableTheory]
[AutoData]
public void
Expand Down Expand Up @@ -71,6 +84,18 @@ public void
result.Should().BeFalse();
}

[SkippableTheory]
[AutoData]
public void EndsInDirectorySeparator_WithTrailingAltDirectorySeparator_ShouldReturnTrue(
string path)
{
path += FileSystem.Path.AltDirectorySeparatorChar;

bool result = FileSystem.Path.EndsInDirectorySeparator(path);

result.Should().BeTrue();
}

[SkippableTheory]
[AutoData]
public void EndsInDirectorySeparator_WithTrailingDirectorySeparator_ShouldReturnTrue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ public abstract partial class GetExtensionTests<TFileSystem>
: FileSystemTestBase<TFileSystem>
where TFileSystem : IFileSystem
{
[SkippableFact]
public void GetExtension_Empty_ShouldReturnEmpty()
{
string? result = FileSystem.Path.GetExtension(string.Empty);

result.Should().BeEmpty();
}

[SkippableFact]
public void GetExtension_Null_ShouldReturnNull()
{
Expand All @@ -15,7 +23,7 @@ public void GetExtension_Null_ShouldReturnNull()

[SkippableTheory]
[AutoData]
public void GetExtension_ShouldReturnExtension(
public void GetExtension_ShouldReturnExtensionWithLeadingDot(
string directory, string filename, string extension)
{
string path = directory + FileSystem.Path.DirectorySeparatorChar + filename +
Expand All @@ -29,7 +37,7 @@ public void GetExtension_ShouldReturnExtension(
#if FEATURE_SPAN
[SkippableTheory]
[AutoData]
public void GetExtension_Span_ShouldReturnExtension(
public void GetExtension_Span_ShouldReturnExtensionWithLeadingDot(
string directory, string filename, string extension)
{
string path = directory + FileSystem.Path.DirectorySeparatorChar + filename +
Expand All @@ -52,4 +60,16 @@ public void GetExtension_TrailingDot_ShouldReturnEmptyString(

result.Should().Be("");
}

[SkippableTheory]
[AutoData]
public void GetExtension_StartingDot_ShouldReturnCompleteFileName(
string directory, string filename)
{
string path = directory + FileSystem.Path.DirectorySeparatorChar + "." + filename;

string result = FileSystem.Path.GetExtension(path);

result.Should().Be("." + filename);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ public abstract partial class GetFileNameTests<TFileSystem>
: FileSystemTestBase<TFileSystem>
where TFileSystem : IFileSystem
{
[SkippableFact]
public void GetFileName_EmptyString_ShouldReturnEmptyString()
{
string result = FileSystem.Path.GetFileName(string.Empty);

result.Should().Be(string.Empty);
}

[SkippableFact]
public void GetFileName_Null_ShouldReturnNull()
{
Expand All @@ -15,7 +23,7 @@ public void GetFileName_Null_ShouldReturnNull()

[SkippableTheory]
[AutoData]
public void GetFileName_ShouldReturnDirectory(string directory, string filename,
public void GetFileName_ShouldReturnFilename(string directory, string filename,
string extension)
{
string path = directory + FileSystem.Path.DirectorySeparatorChar + filename +
Expand All @@ -41,4 +49,28 @@ public void GetFileName_Span_ShouldReturnDirectory(
result.ToString().Should().Be(filename + "." + extension);
}
#endif

[SkippableTheory]
[InlineData("foo/", "", TestOS.All)]
[InlineData("bar\\", "", TestOS.Windows)]
[InlineData("/foo", "foo", TestOS.All)]
[InlineData("\\bar", "bar", TestOS.Windows)]
public void GetFileName_SpecialCases_ShouldReturnExpectedResult(
string? path, string? expected, TestOS operatingSystem)
{
Skip.IfNot(Test.RunsOn(operatingSystem));

string? result = FileSystem.Path.GetFileName(path);

result.Should().Be(expected);
}

[SkippableTheory]
[AutoData]
public void GetFileName_WithoutDirectory_ShouldReturnFilename(string filename)
{
string result = FileSystem.Path.GetFileName(filename);

result.Should().Be(filename);
}
}
Loading
Loading