Skip to content
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
4 changes: 4 additions & 0 deletions Source/aweXpect.Core/Core/Nodes/WhichNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ public override async Task<ConstraintResult> IsMetBy<TValue>(
if (_parent != null)
{
parentResult = await _parent.IsMetBy(value, context, cancellationToken);
if (parentResult.FurtherProcessingStrategy == FurtherProcessingStrategy.IgnoreCompletely)
{
return parentResult;
}
}

if (_inner == null)
Expand Down
150 changes: 147 additions & 3 deletions Source/aweXpect.Core/Delegates/ThatDelegate.Throws.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ public ThatDelegateThrows<TException> Throws<TException>()
{
ThrowsOption throwOptions = new();
return new ThatDelegateThrows<TException>(ExpectationBuilder
.AddConstraint((it, grammars) => new DelegateIsNotNullWithinTimeoutConstraint(it, grammars, throwOptions))
.AddConstraint((it, grammars)
=> new DelegateThrowsWithinTimeoutConstraint<TException>(it, grammars, throwOptions))
.ForWhich<DelegateValue, TException?>(d => d.Exception as TException)
.AddConstraint((it, grammars) => new ThrowsConstraint(it, grammars, typeof(TException), throwOptions))
.AddConstraint((_, _) => new DoNothingConstraint<TException>())
.And(" "),
throwOptions);
}
Expand All @@ -32,13 +33,156 @@ public ThatDelegateThrows<Exception> Throws(Type exceptionType)
{
ThrowsOption throwOptions = new();
return new ThatDelegateThrows<Exception>(ExpectationBuilder
.AddConstraint((it, grammars) => new DelegateIsNotNullWithinTimeoutConstraint(it, grammars, throwOptions))
.AddConstraint((it, grammars)
=> new DelegateIsNotNullWithinTimeoutConstraint(it, grammars, throwOptions))
.ForWhich<DelegateValue, Exception?>(d => d.Exception)
.AddConstraint((it, grammars) => new ThrowsConstraint(it, grammars, exceptionType, throwOptions))
.And(" "),
throwOptions);
}

private sealed class DoNothingConstraint<T>()
: ConstraintResult.WithValue<T>(ExpectationGrammars.None), IValueConstraint<T>
{
public ConstraintResult IsMetBy(T actual)
{
Outcome = Outcome.Success;
return this;
}

protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null)
{
// Do nothing
}

protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null)
{
// Do nothing
}

protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null)
{
// Do nothing
}

protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null)
{
// Do nothing
}
}

private sealed class DelegateThrowsWithinTimeoutConstraint<TException>(
string it,
ExpectationGrammars grammars,
ThrowsOption options)
: ConstraintResult(grammars),
IValueConstraint<DelegateValue>
where TException : Exception
{
private DelegateValue? _actual;
private bool _tookTooLong;

public ConstraintResult IsMetBy(DelegateValue value)
{
_actual = value;
if (value.IsNull)
{
Outcome = Outcome.Failure;
return this;
}

if (options.ExecutionTimeOptions is not null &&
!options.ExecutionTimeOptions.IsWithinLimit(value.Duration))
{
_tookTooLong = true;
Outcome = Outcome.Failure;
return this;
}

if (!options.DoCheckThrow)
{
FurtherProcessingStrategy = FurtherProcessingStrategy.IgnoreCompletely;
Outcome = value.Exception is null ? Outcome.Success : Outcome.Failure;
return this;
}

if (value.Exception is null)
{
FurtherProcessingStrategy = FurtherProcessingStrategy.IgnoreResult;
}
else if (typeof(TException).IsAssignableFrom(value.Exception.GetType()))
{
Outcome = Outcome.Success;
return this;
}

Outcome = Outcome.Failure;
return this;
}

public override void AppendExpectation(StringBuilder stringBuilder, string? indentation = null)
{
if (!options.DoCheckThrow)
{
stringBuilder.Append("does not throw any exception");
}
else if (typeof(TException) == typeof(Exception))
{
stringBuilder.Append("throws an exception");
}
else
{
stringBuilder.Append("throws ").Append(Formatter.Format(typeof(TException)).PrependAOrAn());
}

if (options.ExecutionTimeOptions is not null)
{
stringBuilder.Append(' ');
options.ExecutionTimeOptions.AppendTo(stringBuilder, "in ");
}
}

public override void AppendResult(StringBuilder stringBuilder, string? indentation = null)
{
if (_actual?.IsNull != false)
{
stringBuilder.ItWasNull(it);
}
else if (_tookTooLong)
{
stringBuilder.Append(it).Append(" took ");
options.ExecutionTimeOptions?.AppendFailureResult(stringBuilder, _actual.Duration);
}
else if (options.DoCheckThrow && _actual.Exception is null)
{
stringBuilder.Append(it).Append(" did not throw any exception");
}
else
{
stringBuilder.Append(it).Append(" did throw ");
stringBuilder.Append(FormatForMessage(_actual.Exception));
}
}

public override bool TryGetValue<TValue>([NotNullWhen(true)] out TValue? value) where TValue : default
{
if (_actual is TValue typedValue)
{
value = typedValue;
return true;
}

value = default;
return typeof(TValue).IsAssignableFrom(typeof(TException));
Comment thread
vbreuss marked this conversation as resolved.
}

public override ConstraintResult Negate()
{
options.DoCheckThrow = !options.DoCheckThrow;
return this;
}
}

private sealed class ThrowsConstraint(
string it,
ExpectationGrammars grammars,
Expand Down
Loading