diff --git a/Pipeline/Build.cs b/Pipeline/Build.cs index a07a0cc08..25356ef59 100644 --- a/Pipeline/Build.cs +++ b/Pipeline/Build.cs @@ -19,7 +19,7 @@ partial class Build : NukeBuild /// /// Afterward, you can update the package reference in `Directory.Packages.props` and reset this flag. /// - readonly BuildScope BuildScope = BuildScope.Default; + readonly BuildScope BuildScope = BuildScope.CoreOnly; [Parameter("Github Token")] readonly string GithubToken; diff --git a/Source/aweXpect.Core/Delegates/ThatDelegate.WithValue.DoesNotThrow.cs b/Source/aweXpect.Core/Delegates/ThatDelegate.WithValue.DoesNotThrow.cs index 743d9f4b0..f408b27a4 100644 --- a/Source/aweXpect.Core/Delegates/ThatDelegate.WithValue.DoesNotThrow.cs +++ b/Source/aweXpect.Core/Delegates/ThatDelegate.WithValue.DoesNotThrow.cs @@ -16,7 +16,7 @@ public sealed partial class WithValue /// /// Verifies that the delegate does not throw any exception. /// - public AndResult> DoesNotThrow() + public DelegateWithValueResult DoesNotThrow() => new(ExpectationBuilder.AddConstraint((it, grammars) => new DoesNotThrowConstraint(it, grammars, typeof(Exception))), this); @@ -24,7 +24,7 @@ public AndResult> DoesNotThrow() /// /// Verifies that the delegate does not throw an exception of type . /// - public AndResult> DoesNotThrow() + public DelegateWithValueResult DoesNotThrow() where TException : Exception => new(ExpectationBuilder.AddConstraint((it, grammars) => new DoesNotThrowConstraint(it, grammars, typeof(TException))), @@ -33,7 +33,7 @@ public AndResult> DoesNotThrow() /// /// Verifies that the delegate does not throw an exception of type . /// - public AndResult> DoesNotThrow(Type exceptionType) + public DelegateWithValueResult DoesNotThrow(Type exceptionType) => new(ExpectationBuilder.AddConstraint((it, grammars) => new DoesNotThrowConstraint(it, grammars, exceptionType)), this); diff --git a/Source/aweXpect.Core/Delegates/ThatDelegate.WithValue.DoesNotThrowExactly.cs b/Source/aweXpect.Core/Delegates/ThatDelegate.WithValue.DoesNotThrowExactly.cs index f757e3afe..0efcd2087 100644 --- a/Source/aweXpect.Core/Delegates/ThatDelegate.WithValue.DoesNotThrowExactly.cs +++ b/Source/aweXpect.Core/Delegates/ThatDelegate.WithValue.DoesNotThrowExactly.cs @@ -16,7 +16,7 @@ public sealed partial class WithValue /// /// Verifies that the delegate does not throw an exception of type . /// - public AndResult> DoesNotThrowExactly() + public DelegateWithValueResult DoesNotThrowExactly() where TException : Exception => new(ExpectationBuilder.AddConstraint((it, grammars) => new DoesNotThrowExactlyConstraint(it, grammars, typeof(TException))), @@ -25,7 +25,7 @@ public AndResult> DoesNotThrowExactly() /// /// Verifies that the delegate does not throw an exception of type . /// - public AndResult> DoesNotThrowExactly(Type exceptionType) + public DelegateWithValueResult DoesNotThrowExactly(Type exceptionType) => new(ExpectationBuilder.AddConstraint((it, grammars) => new DoesNotThrowExactlyConstraint(it, grammars, exceptionType)), this); diff --git a/Source/aweXpect.Core/Results/DelegateWithValueResult.cs b/Source/aweXpect.Core/Results/DelegateWithValueResult.cs new file mode 100644 index 000000000..9bc0a24b8 --- /dev/null +++ b/Source/aweXpect.Core/Results/DelegateWithValueResult.cs @@ -0,0 +1,84 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text; +using aweXpect.Core; +using aweXpect.Core.Constraints; +using aweXpect.Core.Sources; +using aweXpect.Delegates; + +namespace aweXpect.Results; + +/// +/// Result for a delegate with a value that does not throw. +/// +public class DelegateWithValueResult(ExpectationBuilder expectationBuilder, ThatDelegate.WithValue returnValue) + : AndResult>(expectationBuilder, returnValue) +{ + private readonly ExpectationBuilder _expectationBuilder = expectationBuilder; + + /// + /// Returns the result returned from the delegate. + /// + public IThat AndWhoseResult + { + get + { + _expectationBuilder.And("") + .AddConstraint((it, grammars) => new DoesNotThrowAnyExceptionConstraint(it, grammars)) + .ForWhich, T?>(d => d.Value, " and whose result ", "the result"); + return new ThatSubject(_expectationBuilder); + } + } + + private sealed class DoesNotThrowAnyExceptionConstraint( + string it, + ExpectationGrammars grammars) + : ConstraintResult(grammars), + IValueConstraint> + { + private DelegateValue? _actual; + + /// + public ConstraintResult IsMetBy(DelegateValue value) + { + _actual = value; + if (value.IsNull) + { + Outcome = Outcome.Failure; + return this; + } + + Outcome = value.Exception is null + ? Outcome.Success + : Outcome.Failure; + return this; + } + + public override void AppendExpectation(StringBuilder stringBuilder, string? indentation = null) + { + // Do not append any expectation + } + + public override void AppendResult(StringBuilder stringBuilder, string? indentation = null) + { + if (_actual?.Exception is not null) + { + stringBuilder.Append(it).Append(" did throw "); + stringBuilder.Append(ThatDelegate.FormatForMessage(_actual.Exception)); + } + } + + public override bool TryGetValue([NotNullWhen(true)] out TValue? value) where TValue : default + { + if (_actual is { Value: TValue typedValue, }) + { + value = typedValue; + return true; + } + + value = default; + return typeof(TValue).IsAssignableFrom(typeof(T)); + } + + public override ConstraintResult Negate() => this; + } +} diff --git a/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_net8.0.txt b/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_net8.0.txt index cab636cb1..69a2b9c35 100644 --- a/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_net8.0.txt +++ b/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_net8.0.txt @@ -463,12 +463,12 @@ namespace aweXpect.Delegates public sealed class WithValue : aweXpect.Delegates.ThatDelegate, aweXpect.Core.IExpectThat>, aweXpect.Core.IThat> { public WithValue(aweXpect.Core.ExpectationBuilder expectationBuilder) { } - public aweXpect.Results.AndResult> DoesNotThrow() { } - public aweXpect.Results.AndResult> DoesNotThrow(System.Type exceptionType) { } - public aweXpect.Results.AndResult> DoesNotThrow() + public aweXpect.Results.DelegateWithValueResult DoesNotThrow() { } + public aweXpect.Results.DelegateWithValueResult DoesNotThrow(System.Type exceptionType) { } + public aweXpect.Results.DelegateWithValueResult DoesNotThrow() where TException : System.Exception { } - public aweXpect.Results.AndResult> DoesNotThrowExactly(System.Type exceptionType) { } - public aweXpect.Results.AndResult> DoesNotThrowExactly() + public aweXpect.Results.DelegateWithValueResult DoesNotThrowExactly(System.Type exceptionType) { } + public aweXpect.Results.DelegateWithValueResult DoesNotThrowExactly() where TException : System.Exception { } public aweXpect.Results.ExecutesInResult>> ExecutesIn() { } } @@ -999,6 +999,11 @@ namespace aweXpect.Results public TTarget Once() { } public TTarget Twice() { } } + public class DelegateWithValueResult : aweXpect.Results.AndResult> + { + public DelegateWithValueResult(aweXpect.Core.ExpectationBuilder expectationBuilder, aweXpect.Delegates.ThatDelegate.WithValue returnValue) { } + public aweXpect.Core.IThat AndWhoseResult { get; } + } public class ExecutesInResult { public ExecutesInResult(TResult returnValue, aweXpect.Options.TimeSpanEqualityOptions options) { } diff --git a/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_netstandard2.0.txt b/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_netstandard2.0.txt index 5330d0db7..e5f2e678b 100644 --- a/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_netstandard2.0.txt +++ b/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_netstandard2.0.txt @@ -463,12 +463,12 @@ namespace aweXpect.Delegates public sealed class WithValue : aweXpect.Delegates.ThatDelegate, aweXpect.Core.IExpectThat>, aweXpect.Core.IThat> { public WithValue(aweXpect.Core.ExpectationBuilder expectationBuilder) { } - public aweXpect.Results.AndResult> DoesNotThrow() { } - public aweXpect.Results.AndResult> DoesNotThrow(System.Type exceptionType) { } - public aweXpect.Results.AndResult> DoesNotThrow() + public aweXpect.Results.DelegateWithValueResult DoesNotThrow() { } + public aweXpect.Results.DelegateWithValueResult DoesNotThrow(System.Type exceptionType) { } + public aweXpect.Results.DelegateWithValueResult DoesNotThrow() where TException : System.Exception { } - public aweXpect.Results.AndResult> DoesNotThrowExactly(System.Type exceptionType) { } - public aweXpect.Results.AndResult> DoesNotThrowExactly() + public aweXpect.Results.DelegateWithValueResult DoesNotThrowExactly(System.Type exceptionType) { } + public aweXpect.Results.DelegateWithValueResult DoesNotThrowExactly() where TException : System.Exception { } public aweXpect.Results.ExecutesInResult>> ExecutesIn() { } } @@ -982,6 +982,11 @@ namespace aweXpect.Results public TTarget Once() { } public TTarget Twice() { } } + public class DelegateWithValueResult : aweXpect.Results.AndResult> + { + public DelegateWithValueResult(aweXpect.Core.ExpectationBuilder expectationBuilder, aweXpect.Delegates.ThatDelegate.WithValue returnValue) { } + public aweXpect.Core.IThat AndWhoseResult { get; } + } public class ExecutesInResult { public ExecutesInResult(TResult returnValue, aweXpect.Options.TimeSpanEqualityOptions options) { } diff --git a/Tests/aweXpect.Tests/Delegates/ThatDelegate.DoesNotThrow.AndWhoseResult.Tests.cs b/Tests/aweXpect.Tests/Delegates/ThatDelegate.DoesNotThrow.AndWhoseResult.Tests.cs new file mode 100644 index 000000000..53d8e18a5 --- /dev/null +++ b/Tests/aweXpect.Tests/Delegates/ThatDelegate.DoesNotThrow.AndWhoseResult.Tests.cs @@ -0,0 +1,180 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatDelegate +{ + public sealed partial class DoesNotThrow + { + public sealed class AndWhoseResult + { + public sealed class Tests + { + [Fact] + public async Task WhenDelegateThrows_ShouldFail() + { + Func @delegate = () => throw new CustomException(); + + async Task Act() => await That(@delegate).DoesNotThrow().AndWhoseResult.IsGreaterThan(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that @delegate + does not throw any exception and whose result is greater than 5, + but it did throw a ThatDelegate.CustomException: + WhenDelegateThrows_ShouldFail + """); + } + + [Theory] + [AutoData] + public async Task WhenReturnValueDoesNotMatch_ShouldFail(int value) + { + Func @delegate = () => value; + + async Task Act() => await That(@delegate).DoesNotThrow().AndWhoseResult.IsLessThan(value); + + await That(Act).Throws() + .WithMessage($""" + Expected that @delegate + does not throw any exception and whose result is less than {value}, + but the result was {value} + """); + } + + [Theory] + [AutoData] + public async Task WhenReturnValueMatches_ShouldSucceed(int value) + { + Func @delegate = () => value; + + await That(@delegate).DoesNotThrow().AndWhoseResult.IsEqualTo(value); + } + } + + public sealed class GenericTests + { + [Fact] + public async Task WhenDelegateThrowsExpectedException_ShouldFail() + { + Func @delegate = () => throw new CustomException(); + + async Task Act() + => await That(@delegate).DoesNotThrow().AndWhoseResult.IsEqualTo(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that @delegate + does not throw a ThatDelegate.CustomException and whose result is equal to 5, + but it did throw a ThatDelegate.CustomException: + WhenDelegateThrowsExpectedException_ShouldFail + """); + } + + [Fact] + public async Task WhenDelegateThrowsOtherException_ShouldFail() + { + Func @delegate = () => throw new CustomException(); + + async Task Act() + => await That(@delegate).DoesNotThrow().AndWhoseResult.IsEqualTo(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that @delegate + does not throw a ThatDelegate.OtherException and whose result is equal to 5, + but it did throw a ThatDelegate.CustomException: + WhenDelegateThrowsOtherException_ShouldFail + """); + } + + [Theory] + [AutoData] + public async Task WhenReturnValueDoesNotMatch_ShouldFail(int value) + { + Func @delegate = () => value; + + async Task Act() => await That(@delegate).DoesNotThrow().AndWhoseResult + .IsEqualTo(value + 1); + + await That(Act).Throws() + .WithMessage($""" + Expected that @delegate + does not throw a ThatDelegate.CustomException and whose result is equal to {value + 1}, + but the result was {value} which differs by -1 + """); + } + + [Theory] + [AutoData] + public async Task WhenReturnValueMatches_ShouldSucceed(int value) + { + Func @delegate = () => value; + + await That(@delegate).DoesNotThrow().AndWhoseResult.IsEqualTo(value); + } + } + + public sealed class TypeTests + { + [Fact] + public async Task WhenDelegateThrowsExpectedException_ShouldFail() + { + Func @delegate = () => throw new CustomException(); + + async Task Act() + => await That(@delegate).DoesNotThrow(typeof(CustomException)).AndWhoseResult.IsEqualTo(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that @delegate + does not throw a ThatDelegate.CustomException and whose result is equal to 5, + but it did throw a ThatDelegate.CustomException: + WhenDelegateThrowsExpectedException_ShouldFail + """); + } + + [Fact] + public async Task WhenDelegateThrowsOtherException_ShouldFail() + { + Func @delegate = () => throw new CustomException(); + + async Task Act() + => await That(@delegate).DoesNotThrow(typeof(OtherException)).AndWhoseResult.IsEqualTo(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that @delegate + does not throw a ThatDelegate.OtherException and whose result is equal to 5, + but it did throw a ThatDelegate.CustomException: + WhenDelegateThrowsOtherException_ShouldFail + """); + } + + [Theory] + [AutoData] + public async Task WhenReturnValueDoesNotMatch_ShouldFail(int value) + { + Func @delegate = () => value; + + async Task Act() => await That(@delegate).DoesNotThrow(typeof(CustomException)).AndWhoseResult + .IsEqualTo(value + 1); + + await That(Act).Throws() + .WithMessage($""" + Expected that @delegate + does not throw a ThatDelegate.CustomException and whose result is equal to {value + 1}, + but the result was {value} which differs by -1 + """); + } + + [Theory] + [AutoData] + public async Task WhenReturnValueMatches_ShouldSucceed(int value) + { + Func @delegate = () => value; + + await That(@delegate).DoesNotThrow(typeof(CustomException)).AndWhoseResult.IsEqualTo(value); + } + } + } + } +} diff --git a/Tests/aweXpect.Tests/Delegates/ThatDelegate.DoesNotThrowExactly.AndWhoseResult.Tests.cs b/Tests/aweXpect.Tests/Delegates/ThatDelegate.DoesNotThrowExactly.AndWhoseResult.Tests.cs new file mode 100644 index 000000000..7df7f6031 --- /dev/null +++ b/Tests/aweXpect.Tests/Delegates/ThatDelegate.DoesNotThrowExactly.AndWhoseResult.Tests.cs @@ -0,0 +1,136 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatDelegate +{ + public sealed partial class DoesNotThrowExactly + { + public sealed class AndWhoseResult + { + public sealed class GenericTests + { + [Fact] + public async Task WhenDelegateThrowsExpectedException_ShouldFail() + { + Func @delegate = () => throw new CustomException(); + + async Task Act() + => await That(@delegate).DoesNotThrowExactly().AndWhoseResult.IsEqualTo(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that @delegate + does not throw exactly a ThatDelegate.CustomException and whose result is equal to 5, + but it did throw a ThatDelegate.CustomException: + WhenDelegateThrowsExpectedException_ShouldFail + """); + } + + [Fact] + public async Task WhenDelegateThrowsOtherException_ShouldFail() + { + Func @delegate = () => throw new CustomException(); + + async Task Act() + => await That(@delegate).DoesNotThrowExactly().AndWhoseResult.IsEqualTo(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that @delegate + does not throw exactly a ThatDelegate.OtherException and whose result is equal to 5, + but it did throw a ThatDelegate.CustomException: + WhenDelegateThrowsOtherException_ShouldFail + """); + } + + [Theory] + [AutoData] + public async Task WhenReturnValueDoesNotMatch_ShouldFail(int value) + { + Func @delegate = () => value; + + async Task Act() => await That(@delegate).DoesNotThrowExactly().AndWhoseResult + .IsEqualTo(value + 1); + + await That(Act).Throws() + .WithMessage($""" + Expected that @delegate + does not throw exactly a ThatDelegate.CustomException and whose result is equal to {value + 1}, + but the result was {value} which differs by -1 + """); + } + + [Theory] + [AutoData] + public async Task WhenReturnValueMatches_ShouldSucceed(int value) + { + Func @delegate = () => value; + + await That(@delegate).DoesNotThrowExactly().AndWhoseResult.IsEqualTo(value); + } + } + + public sealed class TypeTests + { + [Fact] + public async Task WhenDelegateThrowsExpectedException_ShouldFail() + { + Func @delegate = () => throw new CustomException(); + + async Task Act() + => await That(@delegate).DoesNotThrowExactly(typeof(CustomException)).AndWhoseResult.IsEqualTo(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that @delegate + does not throw exactly a ThatDelegate.CustomException and whose result is equal to 5, + but it did throw a ThatDelegate.CustomException: + WhenDelegateThrowsExpectedException_ShouldFail + """); + } + + [Fact] + public async Task WhenDelegateThrowsOtherException_ShouldFail() + { + Func @delegate = () => throw new CustomException(); + + async Task Act() + => await That(@delegate).DoesNotThrowExactly(typeof(OtherException)).AndWhoseResult.IsEqualTo(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that @delegate + does not throw exactly a ThatDelegate.OtherException and whose result is equal to 5, + but it did throw a ThatDelegate.CustomException: + WhenDelegateThrowsOtherException_ShouldFail + """); + } + + [Theory] + [AutoData] + public async Task WhenReturnValueDoesNotMatch_ShouldFail(int value) + { + Func @delegate = () => value; + + async Task Act() => await That(@delegate).DoesNotThrowExactly(typeof(CustomException)).AndWhoseResult + .IsEqualTo(value + 1); + + await That(Act).Throws() + .WithMessage($""" + Expected that @delegate + does not throw exactly a ThatDelegate.CustomException and whose result is equal to {value + 1}, + but the result was {value} which differs by -1 + """); + } + + [Theory] + [AutoData] + public async Task WhenReturnValueMatches_ShouldSucceed(int value) + { + Func @delegate = () => value; + + await That(@delegate).DoesNotThrowExactly(typeof(CustomException)).AndWhoseResult.IsEqualTo(value); + } + } + } + } +}