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

[pull] main from nunit:main #270

Merged
merged 5 commits into from
Jan 12, 2025
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
10 changes: 9 additions & 1 deletion .github/workflows/NUnit.CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jobs:
uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
dotnet-version: |
6.0.x
8.0.x

- name: 🛠️ Install dotnet tools
run: dotnet tool restore
Expand Down Expand Up @@ -68,6 +71,9 @@ jobs:
uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
dotnet-version: |
6.0.x
8.0.x

- name: 🛠️ Install F#
run: sudo apt-get install fsharp
Expand Down Expand Up @@ -100,7 +106,9 @@ jobs:
uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
dotnet-version: 6.x
dotnet-version: |
6.0.x
8.0.x

- name: 🛠️ Install dotnet tools
run: dotnet tool restore
Expand Down
16 changes: 10 additions & 6 deletions src/NUnitFramework/framework/Assert.Exceptions.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ public partial class Assert
public static Exception? ThrowsAsync(IResolveConstraint expression, AsyncTestDelegate code, string message, params object?[]? args)
{
Exception? caughtException = null;
try
{
AsyncToSyncAdapter.Await(TestExecutionContext.CurrentContext, code.Invoke);
}
catch (Exception e)

using (new TestExecutionContext.IsolatedContext())
{
caughtException = e;
try
{
AsyncToSyncAdapter.Await(TestExecutionContext.CurrentContext, code.Invoke);
}
catch (Exception e)
{
caughtException = e;
}
}

Assert.That(caughtException, expression, () => ConvertMessageWithArgs(message, args));
Expand Down
10 changes: 6 additions & 4 deletions src/NUnitFramework/framework/Constraints/DelayedConstraint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ private DelayedConstraint(IConstraint baseConstraint, Interval delayInterval, In
/// <summary>
/// Gets text describing a constraint
/// </summary>
public override string Description => $"{BaseConstraint.Description} after {DelayInterval} delay";
public override string Description => $"After {DelayInterval} delay";

/// <summary>
/// Test whether the constraint is satisfied by a given value
Expand Down Expand Up @@ -401,9 +401,11 @@ public DelegatingConstraintResult(IConstraint constraint, ConstraintResult inner
_innerResult = innerResult;
}

public override void WriteActualValueTo(MessageWriter writer) => _innerResult.WriteActualValueTo(writer);

public override void WriteAdditionalLinesTo(MessageWriter writer) => _innerResult.WriteAdditionalLinesTo(writer);
public override void WriteMessageTo(MessageWriter writer)
{
writer.WriteMessageLine(Description);
_innerResult.WriteMessageTo(writer);
}
}
}
}
4 changes: 4 additions & 0 deletions src/NUnitFramework/framework/Constraints/EqualConstraint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,10 @@ public EqualConstraint UsingPropertiesComparer()
/// <returns>True for success, false for failure</returns>
public override ConstraintResult ApplyTo<TActual>(TActual actual)
{
// Reset the comparer before each use, e.g. for DelayedConstraint
if (_comparer.HasFailurePoints)
_comparer.FailurePoints.Clear();

AdjustArgumentIfNeeded(ref actual);
return new EqualConstraintResult(this, actual, _comparer.AreEqual(_expected, actual, ref _tolerance));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using NUnit.Framework.Internal;

namespace NUnit.Framework.Constraints
{
Expand Down Expand Up @@ -123,7 +124,7 @@ private void DisplayDifferences(MessageWriter writer, object? expected, object?
else if (expected is Stream expectedStream && actual is Stream actualStream)
DisplayStreamDifferences(writer, expectedStream, actualStream, depth);
else if (_comparingProperties && IsPropertyFailurePoint(depth))
DisplayPropertyDifferences(writer, depth);
DisplayPropertyDifferences(writer, expected, actual, depth);
else if (_tolerance is not null)
writer.DisplayDifferences(expected, actual, _tolerance);
else
Expand Down Expand Up @@ -220,8 +221,8 @@ private void DisplayCollectionDifferenceWithFailurePoint(MessageWriter writer, I
writer.WriteMessageLine(StringsDiffer_1, expectedString.Length, mismatchExpected);
else
writer.WriteMessageLine(StringsDiffer_2, expectedString.Length, actualString.Length, mismatchExpected);
writer.WriteLine($" Expected: {MsgUtils.FormatCollection(expected)}");
writer.WriteLine($" But was: {MsgUtils.FormatCollection(actual)}");
writer.WriteLine($"{TextMessageWriter.Pfx_Expected}{MsgUtils.FormatCollection(expected)}");
writer.WriteLine($"{TextMessageWriter.Pfx_Actual}{MsgUtils.FormatCollection(actual)}");
writer.WriteLine($" First non-matching item at index [{failurePoint.Position}]: \"{failurePoint.ExpectedValue}\"");
return;
}
Expand Down Expand Up @@ -346,13 +347,15 @@ private void DisplayEnumerableDifferences(MessageWriter writer, IEnumerable expe

#region DisplayPropertyDifferences

private void DisplayPropertyDifferences(MessageWriter writer, int depth)
private void DisplayPropertyDifferences(MessageWriter writer, object? expected, object? actual, int depth)
{
if (_failurePoints.Count > depth)
{
NUnitEqualityComparer.FailurePoint failurePoint = _failurePoints[depth];

writer.WriteMessageLine($"Values differ at property {failurePoint.PropertyName}");
writer.WriteLine($"{TextMessageWriter.Pfx_Expected}{MsgUtils.FormatValueProperties(expected)}");
writer.WriteLine($"{TextMessageWriter.Pfx_Actual}{MsgUtils.FormatValueProperties(actual)}");
writer.WriteLine($" Values differ at property {failurePoint.PropertyName}:");
DisplayDifferences(writer, failurePoint.ExpectedValue, failurePoint.ActualValue, ++depth);
}
}
Expand Down
51 changes: 51 additions & 0 deletions src/NUnitFramework/framework/Constraints/MsgUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using NUnit.Framework.Internal;

Expand Down Expand Up @@ -94,6 +96,55 @@ static MsgUtils()
AddFormatter(next => val => val is Array valArray ? FormatArray(valArray) : next(val));
}

/// <summary>
/// Try to format the properties of an object as a string.
/// </summary>
/// <param name="val">The object to format.</param>
/// <returns>Formatted string for all properties.</returns>
public static string FormatValueProperties(object? val)
{
if (val is null)
return "null";

Type valueType = val.GetType();

// If the type is a low level type, call our default formatter.
if (valueType.IsPrimitive || valueType == typeof(string) || valueType == typeof(decimal))
return FormatValue(val);

// If the type implements its own ToString() method, call that.
MethodInfo? toStringMethod = valueType.GetMethod(nameof(ToString), Type.EmptyTypes);
if (toStringMethod?.DeclaringType == valueType)
return FormatValueWithoutThrowing(val);

PropertyInfo[] properties = valueType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (properties.Length == 0 || properties.Any(p => p.GetIndexParameters().Length > 0))
{
// We can't print if there are no properties.
// We also can't deal with indexer properties as we don't know the range of valid values.
return FormatValue(val);
}

var sb = new StringBuilder();
sb.Append(valueType.Name);
sb.Append(" { ");

bool firstProperty = true;
foreach (var property in properties)
{
if (!firstProperty)
sb.Append(", ");
else
firstProperty = false;

sb.Append(property.Name);
sb.Append(" = ");
sb.Append(FormatValue(property.GetValue(val, null)));
}
sb.Append(" }");
return sb.ToString();
}

#if NETFRAMEWORK
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
#endif
Expand Down
15 changes: 9 additions & 6 deletions src/NUnitFramework/framework/Internal/ExceptionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,16 @@ private static List<Exception> FlattenExceptionHierarchy(Exception exception)
{
Guard.ArgumentNotNull(parameterlessDelegate, parameterName);

try
{
await parameterlessDelegate();
}
catch (Exception e)
using (new TestExecutionContext.IsolatedContext())
{
return e;
try
{
await parameterlessDelegate();
}
catch (Exception e)
{
return e;
}
}

return null;
Expand Down
8 changes: 1 addition & 7 deletions src/NUnitFramework/tests/Assertions/AssertThatAsyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,7 @@ public async Task AssertionFails_WhenDelegateThrowsEvenWithRetry()
private static async Task AssertAssertionFailsAsync(Func<Task> assertion)
{
await Assert.ThatAsync(
async () =>
{
using (new TestExecutionContext.IsolatedContext())
{
await assertion();
}
},
async () => await assertion(),
Throws.InstanceOf<AssertionException>());
}
}
Expand Down
59 changes: 35 additions & 24 deletions src/NUnitFramework/tests/Assertions/AssertThatTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -551,37 +551,53 @@ public void AssertThatEqualsWithStructWithSomeToleranceAwareMembers()
[Test]
public void AssertThatEqualsWithStructMemberDifferences()
{
var instance = new StructWithSomeToleranceAwareMembers(1, 1.1, "1.1", SomeEnum.One);
var instance = new StructWithSomeToleranceAwareMembers(1, 0.123, "1.1", SomeEnum.One);

Assert.That(() =>
Assert.That(new StructWithSomeToleranceAwareMembers(2, 1.1, "1.1", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer()),
Throws.InstanceOf<AssertionException>().With.Message.Contains("at property StructWithSomeToleranceAwareMembers.ValueA")
.And.Message.Contains("Expected: 1"));
Assert.That(new StructWithSomeToleranceAwareMembers(2, 0.123, "1.1", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer()),
Throws.InstanceOf<AssertionException>().With
.Message.Contains("Expected: StructWithSomeToleranceAwareMembers { ValueA = 1, ValueB = 0.123d, ValueC = \"1.1\", ValueD = One }").And
.Message.Contains("But was: StructWithSomeToleranceAwareMembers { ValueA = 2, ValueB = 0.123d, ValueC = \"1.1\", ValueD = One }").And
.Message.Contains("at property StructWithSomeToleranceAwareMembers.ValueA").And
.Message.Contains("Expected: 1").And
.Message.Contains("But was: 2"));
Assert.That(() =>
Assert.That(new StructWithSomeToleranceAwareMembers(1, 1.2, "1.1", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer()),
Throws.InstanceOf<AssertionException>().With.Message.Contains("at property StructWithSomeToleranceAwareMembers.ValueB")
.And.Message.Contains("Expected: 1.1"));
Assert.That(new StructWithSomeToleranceAwareMembers(1, 0.246, "1.1", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer()),
Throws.InstanceOf<AssertionException>().With
.Message.Contains("Expected: StructWithSomeToleranceAwareMembers { ValueA = 1, ValueB = 0.123d, ValueC = \"1.1\", ValueD = One }").And
.Message.Contains("But was: StructWithSomeToleranceAwareMembers { ValueA = 1, ValueB = 0.246d, ValueC = \"1.1\", ValueD = One }").And
.Message.Contains("at property StructWithSomeToleranceAwareMembers.ValueB").And
.Message.Contains("Expected: 0.123d").And
.Message.Contains("But was: 0.246d"));
Assert.That(() =>
Assert.That(new StructWithSomeToleranceAwareMembers(1, 1.1, "1.2", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer()),
Throws.InstanceOf<AssertionException>().With.Message.Contains("at property StructWithSomeToleranceAwareMembers.ValueC")
.And.Message.Contains("Expected: \"1.1\""));
Assert.That(new StructWithSomeToleranceAwareMembers(1, 0.123, "2.2", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer()),
Throws.InstanceOf<AssertionException>().With
.Message.Contains("Expected: StructWithSomeToleranceAwareMembers { ValueA = 1, ValueB = 0.123d, ValueC = \"1.1\", ValueD = One }").And
.Message.Contains("But was: StructWithSomeToleranceAwareMembers { ValueA = 1, ValueB = 0.123d, ValueC = \"2.2\", ValueD = One }").And
.Message.Contains("at property StructWithSomeToleranceAwareMembers.ValueC").And
.Message.Contains("Expected: \"1.1\"").And
.Message.Contains("But was: \"2.2\""));
Assert.That(() =>
Assert.That(new StructWithSomeToleranceAwareMembers(1, 1.1, "1.1", SomeEnum.Two), Is.EqualTo(instance).UsingPropertiesComparer()),
Throws.InstanceOf<AssertionException>().With.Message.Contains("at property StructWithSomeToleranceAwareMembers.ValueD")
.And.Message.Contains("Expected: One"));
Assert.That(new StructWithSomeToleranceAwareMembers(1, 0.123, "1.1", SomeEnum.Two), Is.EqualTo(instance).UsingPropertiesComparer()),
Throws.InstanceOf<AssertionException>().With
.Message.Contains("Expected: StructWithSomeToleranceAwareMembers { ValueA = 1, ValueB = 0.123d, ValueC = \"1.1\", ValueD = One }").And
.Message.Contains("But was: StructWithSomeToleranceAwareMembers { ValueA = 1, ValueB = 0.123d, ValueC = \"1.1\", ValueD = Two }").And
.Message.Contains("at property StructWithSomeToleranceAwareMembers.ValueD").And
.Message.Contains("Expected: One").And
.Message.Contains("But was: Two"));

/*
* Uncomment this block to see the actual exception messages. Test will fail.
*
Assert.Multiple(() =>
{
Assert.That(new StructWithSomeToleranceAwareMembers(2, 1.1, "1.1", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer());
Assert.That(new StructWithSomeToleranceAwareMembers(1, 1.1, "1.1", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer());
Assert.That(new StructWithSomeToleranceAwareMembers(1, 1.2, "1.1", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer());
Assert.That(new StructWithSomeToleranceAwareMembers(1, 1.1, "1.2", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer());
Assert.That(new StructWithSomeToleranceAwareMembers(1, 1.1, "1.1", SomeEnum.Two), Is.EqualTo(instance).UsingPropertiesComparer());
Assert.That(new StructWithSomeToleranceAwareMembers(2, 0.123, "1.1", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer());
Assert.That(new StructWithSomeToleranceAwareMembers(1, 0.246, "1.1", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer());
Assert.That(new StructWithSomeToleranceAwareMembers(1, 0.123, "1.1", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer());
Assert.That(new StructWithSomeToleranceAwareMembers(1, 0.123, "1.2", SomeEnum.One), Is.EqualTo(instance).UsingPropertiesComparer());
Assert.That(new StructWithSomeToleranceAwareMembers(1, 0.123, "1.1", SomeEnum.Two), Is.EqualTo(instance).UsingPropertiesComparer());
});
*/
*/
}

private enum SomeEnum
Expand All @@ -604,11 +620,6 @@ public StructWithSomeToleranceAwareMembers(int valueA, double valueB, string val
public double ValueB { get; }
public string ValueC { get; }
public SomeEnum ValueD { get; }

public override string ToString()
{
return $"{ValueA} {ValueB} '{ValueC}' {ValueD}";
}
}

[Test]
Expand Down
7 changes: 7 additions & 0 deletions src/NUnitFramework/tests/Assertions/AssertThrowsAsyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ public void ThrowsConstraintReturnsCorrectException()
CheckForSpuriousAssertionResults();
}

[Test]
public void ThrowsAsyncIsNotAffectedByAssertionsInDelegate()
{
Assert.ThrowsAsync<AssertionException>(
() => Assert.ThatAsync(AsyncTestDelegates.ThrowsArgumentExceptionAsync, Throws.InvalidOperationException));
}

[Test]
public void CorrectExceptionIsReturnedToMethod()
{
Expand Down
6 changes: 1 addition & 5 deletions src/NUnitFramework/tests/Constraints/ConstraintTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,12 @@ public void SucceedsWithGoodValues(object value)
[Test, TestCaseSource(nameof(FailureData))]
public void FailsWithBadValues(object badValue, string message)
{
string nl = Environment.NewLine;

var constraintResult = TheConstraint.ApplyTo(badValue);
Assert.That(constraintResult.IsSuccess, Is.False);

TextMessageWriter writer = new TextMessageWriter();
constraintResult.WriteMessageTo(writer);
Assert.That(writer.ToString(), Is.EqualTo(
TextMessageWriter.Pfx_Expected + ExpectedDescription + nl +
TextMessageWriter.Pfx_Actual + message + nl));
Assert.That(writer.ToString(), Does.Contain(ExpectedDescription).And.Contains(message));
}
}
}
Loading
Loading