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: support nullable IFileSystemInfo assertions #31

Merged
merged 2 commits into from
Aug 21, 2023
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 @@ -4,12 +4,12 @@
/// Assertions on <see cref="IDirectoryInfo" />.
/// </summary>
public class DirectoryAssertions :
ReferenceTypeAssertions<IDirectoryInfo, DirectoryAssertions>
ReferenceTypeAssertions<IDirectoryInfo?, DirectoryAssertions>
{
/// <inheritdoc cref="ReferenceTypeAssertions{TSubject,TAssertions}.Identifier" />
protected override string Identifier => "directory";

internal DirectoryAssertions(IDirectoryInfo instance)
internal DirectoryAssertions(IDirectoryInfo? instance)
: base(instance)
{
}
Expand All @@ -19,19 +19,30 @@ internal DirectoryAssertions(IDirectoryInfo instance)
/// </summary>
public AndConstraint<DirectoryAssertions> HasFileMatching(
string searchPattern = "*", string because = "", params object[] becauseArgs)
=> HasFilesMatching(searchPattern, 1, because, becauseArgs);

/// <summary>
/// Asserts that the current directory has at least <paramref name="minimumCount"/> files which match the <paramref name="searchPattern" />.
/// </summary>
public AndConstraint<DirectoryAssertions> HasFilesMatching(
string searchPattern = "*", int minimumCount = 1, string because = "", params object[] becauseArgs)
{
Execute.Assertion
.WithDefaultIdentifier(Identifier)
.BecauseOf(because, becauseArgs)
.ForCondition(Subject != null)
.FailWith(
"You can't assert a directory having files if the DirectoryInfo is null")
.Then
.ForCondition(!string.IsNullOrEmpty(searchPattern))
.FailWith(
"You can't assert a file exist in the directory if you don't pass a proper name")
"You can't assert a directory having files if you don't pass a proper name")
.Then
.Given(() => Subject.GetFiles(searchPattern))
.ForCondition(fileInfos => fileInfos.Length > 0)
.Given(() => Subject!)
.ForCondition(directoryInfo => directoryInfo.GetFiles(searchPattern).Length > 0)
.FailWith(
"Expected {context} {1} to contain at least one file matching {0}{reason}, but none was found.",
_ => searchPattern, _ => Subject.Name);
$"Expected {{context}} {{1}} to contain at least {(minimumCount == 1 ? "one file" : $"{minimumCount} files")} matching {{0}}{{reason}}, but none was found.",
_ => searchPattern, directoryInfo => directoryInfo.Name);

return new AndConstraint<DirectoryAssertions>(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class DirectoryInfoAssertions :
/// <inheritdoc cref="ReferenceTypeAssertions{TSubject,TAssertions}.Identifier" />
protected override string Identifier => "directory";

internal DirectoryInfoAssertions(IDirectoryInfo instance)
internal DirectoryInfoAssertions(IDirectoryInfo? instance)
: base(instance)
{
}
Expand Down
44 changes: 32 additions & 12 deletions Source/Testably.Abstractions.FluentAssertions/FileAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ namespace Testably.Abstractions.FluentAssertions;
/// Assertions on <see cref="IFileInfo" />.
/// </summary>
public class FileAssertions :
ReferenceTypeAssertions<IFileInfo, FileAssertions>
ReferenceTypeAssertions<IFileInfo?, FileAssertions>
{
/// <inheritdoc cref="ReferenceTypeAssertions{TSubject,TAssertions}.Identifier" />
protected override string Identifier => "file";

internal FileAssertions(IFileInfo instance)
internal FileAssertions(IFileInfo? instance)
: base(instance)
{
}
Expand All @@ -26,13 +26,17 @@ internal FileAssertions(IFileInfo instance)
Execute.Assertion
.WithDefaultIdentifier(Identifier)
.BecauseOf(because, becauseArgs)
.Given(() => Subject)
.ForCondition(Subject != null)
.FailWith(
"You can't assert the content of a file if the FileInfo is null")
.Then
.Given(() => Subject!)
.ForCondition(fileInfo => fileInfo.FileSystem.File
.ReadAllBytes(fileInfo.FullName)
.SequenceEqual(bytes))
.FailWith(
"Expected {context} {0} to match '{1}'{reason}, but it did not.",
_ => Subject.Name, _ => bytes);
fileInfo => fileInfo.Name, _ => bytes);

return new AndConstraint<FileAssertions>(this);
}
Expand All @@ -46,12 +50,16 @@ internal FileAssertions(IFileInfo instance)
Execute.Assertion
.WithDefaultIdentifier(Identifier)
.BecauseOf(because, becauseArgs)
.Given(() => Subject)
.ForCondition(Subject != null)
.FailWith(
"You can't assert the content of a file if the FileInfo is null")
.Then
.Given(() => Subject!)
.ForCondition(fileInfo => pattern.Matches(
fileInfo.FileSystem.File.ReadAllText(fileInfo.FullName)))
.FailWith(
"Expected {context} {0} to match '{1}'{reason}, but it did not.",
_ => Subject.Name, _ => pattern);
fileInfo => fileInfo.Name, _ => pattern);

return new AndConstraint<FileAssertions>(this);
}
Expand All @@ -66,12 +74,16 @@ internal FileAssertions(IFileInfo instance)
Execute.Assertion
.WithDefaultIdentifier(Identifier)
.BecauseOf(because, becauseArgs)
.Given(() => Subject)
.ForCondition(Subject != null)
.FailWith(
"You can't assert the content of a file if the FileInfo is null")
.Then
.Given(() => Subject!)
.ForCondition(fileInfo => pattern.Matches(
fileInfo.FileSystem.File.ReadAllText(fileInfo.FullName, encoding)))
.FailWith(
"Expected {context} {0} to match '{1}'{reason}, but it did not.",
_ => Subject.Name, _ => pattern);
fileInfo => fileInfo.Name, _ => pattern);

return new AndConstraint<FileAssertions>(this);
}
Expand All @@ -85,11 +97,15 @@ internal FileAssertions(IFileInfo instance)
Execute.Assertion
.WithDefaultIdentifier(Identifier)
.BecauseOf(because, becauseArgs)
.Given(() => Subject)
.ForCondition(Subject != null)
.FailWith(
"You can't assert that the file is not read-only if the FileInfo is null")
.Then
.Given(() => Subject!)
.ForCondition(fileInfo => !fileInfo.IsReadOnly)
.FailWith(
"Expected {context} {0} not to be read-only{reason}, but it was.",
_ => Subject.Name);
fileInfo => fileInfo.Name);

return new AndConstraint<FileAssertions>(this);
}
Expand All @@ -103,11 +119,15 @@ internal FileAssertions(IFileInfo instance)
Execute.Assertion
.WithDefaultIdentifier(Identifier)
.BecauseOf(because, becauseArgs)
.Given(() => Subject)
.ForCondition(Subject != null)
.FailWith(
"You can't assert that the file is read-only if the FileInfo is null")
.Then
.Given(() => Subject!)
.ForCondition(fileInfo => fileInfo.IsReadOnly)
.FailWith(
"Expected {context} {0} to be read-only{reason}, but it was not.",
_ => Subject.Name);
fileInfo => fileInfo.Name);

return new AndConstraint<FileAssertions>(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class FileInfoAssertions :
/// <inheritdoc cref="ReferenceTypeAssertions{TSubject,TAssertions}.Identifier" />
protected override string Identifier => "file";

internal FileInfoAssertions(IFileInfo instance)
internal FileInfoAssertions(IFileInfo? instance)
: base(instance)
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ public static class FileSystemExtensions
/// Returns a <see cref="DirectoryAssertions" /> object that can be used to
/// assert the current <see cref="IDirectoryInfo" />.
/// </summary>
public static DirectoryInfoAssertions Should(this IDirectoryInfo instance)
public static DirectoryInfoAssertions Should(this IDirectoryInfo? instance)
=> new(instance);

/// <summary>
/// Returns a <see cref="FileAssertions" /> object that can be used to
/// assert the current <see cref="IFileInfo" />.
/// </summary>
public static FileInfoAssertions Should(this IFileInfo instance)
public static FileInfoAssertions Should(this IFileInfo? instance)
=> new(instance);

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
/// </summary>
public abstract class
FileSystemInfoAssertions<TFileSystemInfo, TAssertion>
: ReferenceTypeAssertions<TFileSystemInfo, TAssertion>
: ReferenceTypeAssertions<TFileSystemInfo?, TAssertion>
where TFileSystemInfo : IFileSystemInfo
where TAssertion : ReferenceTypeAssertions<TFileSystemInfo, TAssertion>
where TAssertion : ReferenceTypeAssertions<TFileSystemInfo?, TAssertion>
{
/// <summary>
/// Initializes a new instance of <see cref="FileSystemInfoAssertions{TFileSystemInfo,TAssertion}" />
/// </summary>
protected FileSystemInfoAssertions(TFileSystemInfo subject) : base(subject)
protected FileSystemInfoAssertions(TFileSystemInfo? subject) : base(subject)
{
}

Expand All @@ -25,12 +25,15 @@ protected FileSystemInfoAssertions(TFileSystemInfo subject) : base(subject)
Execute.Assertion
.WithDefaultIdentifier(Identifier)
.BecauseOf(because, becauseArgs)
.Given(() => Subject)
.ForCondition(Subject != null)
.FailWith("You can't assert that the {context} exists if it is null")
.Then
.Given(() => Subject!)
.ForCondition(fileSystemInfo => fileSystemInfo.Exists)
.FailWith(
"Expected {context} {0} to exist{reason}, but it did not.",
_ => Subject.Name);
fileSystemInfo => fileSystemInfo.Name);

return new AndConstraint<TFileSystemInfo>(Subject);
return new AndConstraint<TFileSystemInfo>(Subject!);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ public class DirectoryInfoAssertionsTests
exception.Message.Should().NotContain(because);
}

[Theory]
[AutoData]
public void HaveFileMatching_Null_ShouldThrow(string because)
{
IDirectoryInfo? sut = null;

Exception? exception = Record.Exception(() =>
{
sut.Should().HaveFileMatching(because: because);
});

exception.Should().NotBeNull();
exception!.Message.Should().Contain("null");
exception.Message.Should().NotContain(because);
}

[Theory]
[AutoData]
public void HaveFileMatching_WithMatchingFile_ShouldNotThrow(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@ namespace Testably.Abstractions.FluentAssertions.Tests;

public class FileInfoAssertionsTests
{
[Theory]
[AutoData]
public void BeReadOnly_Null_ShouldThrow(string because)
{
IFileInfo? sut = null;

Exception? exception = Record.Exception(() =>
{
sut.Should().BeReadOnly(because);
});

exception.Should().NotBeNull();
exception!.Message.Should().Contain("null");
exception.Message.Should().NotContain(because);
}

[Theory]
[AutoData]
public void BeReadOnly_WithReadOnlyFile_ShouldNotThrow(FileDescription fileDescription)
Expand Down Expand Up @@ -61,6 +77,22 @@ public void BeReadOnly_WithReadOnlyFile_ShouldNotThrow(FileDescription fileDescr
sut.Should().HaveContent(bytes);
}

[Theory]
[AutoData]
public void HaveContent_Bytes_Null_ShouldThrow(byte[] bytes, string because)
{
IFileInfo? sut = null;

Exception? exception = Record.Exception(() =>
{
sut.Should().HaveContent(bytes, because);
});

exception.Should().NotBeNull();
exception!.Message.Should().Contain("null");
exception.Message.Should().NotContain(because);
}

[Theory]
[AutoData]
public void HaveContent_Bytes_OnlyPartOfContent_ShouldNotThrow(
Expand Down Expand Up @@ -181,6 +213,39 @@ public void BeReadOnly_WithReadOnlyFile_ShouldNotThrow(FileDescription fileDescr
sut.Should().HaveContent(pattern);
}

[Theory]
[AutoData]
public void HaveContent_StringContent_Null_ShouldThrow(string content, string because)
{
IFileInfo? sut = null;

Exception? exception = Record.Exception(() =>
{
sut.Should().HaveContent(content, because);
});

exception.Should().NotBeNull();
exception!.Message.Should().Contain("null");
exception.Message.Should().NotContain(because);
}

[Theory]
[AutoData]
public void HaveContent_StringContent_WithEncoding_Null_ShouldThrow(
Encoding encoding, string content, string because)
{
IFileInfo? sut = null;

Exception? exception = Record.Exception(() =>
{
sut.Should().HaveContent(content, encoding, because);
});

exception.Should().NotBeNull();
exception!.Message.Should().Contain("null");
exception.Message.Should().NotContain(because);
}

[Theory]
[AutoData]
public void HaveContent_WithEncodingMismatch_ShouldThrow(
Expand All @@ -204,6 +269,22 @@ public void BeReadOnly_WithReadOnlyFile_ShouldNotThrow(FileDescription fileDescr
$"Expected file \"{fileName}\" to match '{pattern}' {because}, but it did not.");
}

[Theory]
[AutoData]
public void NotBeReadOnly_Null_ShouldThrow(string because)
{
IFileInfo? sut = null;

Exception? exception = Record.Exception(() =>
{
sut.Should().NotBeReadOnly(because);
});

exception.Should().NotBeNull();
exception!.Message.Should().Contain("null");
exception.Message.Should().NotContain(because);
}

[Theory]
[AutoData]
public void NotBeReadOnly_WithReadOnlyFile_ShouldThrow(
Expand Down
Loading
Loading