Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
41 changes: 30 additions & 11 deletions src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.ComponentModel;
Expand All @@ -22,9 +22,11 @@ public sealed partial class Assert
public readonly struct AssertIsNullInterpolatedStringHandler
{
private readonly StringBuilder? _builder;
private readonly object? _value;

public AssertIsNullInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend)
{
_value = value;
shouldAppend = IsNullFailing(value);
if (shouldAppend)
{
Expand All @@ -36,8 +38,7 @@ internal void ComputeAssertion(string valueExpression)
{
if (_builder is not null)
{
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "value", valueExpression) + " ");
ReportAssertIsNullFailed(_builder.ToString());
ReportAssertIsNullFailed(_value, _builder.ToString(), valueExpression);
}
}

Expand Down Expand Up @@ -90,8 +91,7 @@ internal void ComputeAssertion(string valueExpression)
{
if (_builder is not null)
{
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "value", valueExpression) + " ");
ReportAssertIsNotNullFailed(_builder.ToString());
ReportAssertIsNotNullFailed(_builder.ToString(), valueExpression);
}
Comment thread
Evangelink marked this conversation as resolved.
}

Expand Down Expand Up @@ -152,14 +152,26 @@ public static void IsNull(object? value, string? message = "", [CallerArgumentEx
{
if (IsNullFailing(value))
{
ReportAssertIsNullFailed(BuildUserMessageForValueExpression(message, valueExpression));
ReportAssertIsNullFailed(value, message, valueExpression);
}
}

private static bool IsNullFailing(object? value) => value is not null;

private static void ReportAssertIsNullFailed(string? message)
=> ReportAssertFailed("Assert.IsNull", message);
private static void ReportAssertIsNullFailed(object? value, string? message, string valueExpression)
{
string actualValue = AssertionValueRenderer.RenderValue(value);
Comment thread
Evangelink marked this conversation as resolved.
EvidenceBlock evidence = EvidenceBlock.Create()
.AddLine("actual:", actualValue);

StructuredAssertionMessage structured = new("Expected value to be null.");
Comment thread
Evangelink marked this conversation as resolved.
Outdated
structured.WithUserMessage(message);
structured.WithEvidence(evidence);
structured.WithExpectedAndActual(null, actualValue);
structured.WithCallSiteExpression(FormatCallSiteExpression("Assert.IsNull", valueExpression));

ReportAssertFailed(structured);
}

/// <inheritdoc cref="IsNull(object?, string, string)" />
#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
Expand Down Expand Up @@ -191,13 +203,20 @@ public static void IsNotNull([NotNull] object? value, string? message = "", [Cal
{
if (IsNotNullFailing(value))
{
ReportAssertIsNotNullFailed(BuildUserMessageForValueExpression(message, valueExpression));
ReportAssertIsNotNullFailed(message, valueExpression);
}
}

private static bool IsNotNullFailing([NotNullWhen(false)] object? value) => value is null;

[DoesNotReturn]
private static void ReportAssertIsNotNullFailed(string? message)
=> ReportAssertFailed("Assert.IsNotNull", message);
private static void ReportAssertIsNotNullFailed(string? message, string valueExpression)
{
// RFC: IsNotNull omits the evidence block since actual is always null
StructuredAssertionMessage structured = new("Expected value to not be null.");
Comment thread
Evangelink marked this conversation as resolved.
Outdated
structured.WithUserMessage(message);
structured.WithCallSiteExpression(FormatCallSiteExpression("Assert.IsNotNull", valueExpression));

Comment thread
Evangelink marked this conversation as resolved.
ReportAssertFailed(structured);
}
}
48 changes: 37 additions & 11 deletions src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.ComponentModel;
Expand All @@ -22,9 +22,11 @@ public sealed partial class Assert
public readonly struct AssertIsTrueInterpolatedStringHandler
{
private readonly StringBuilder? _builder;
private readonly bool? _condition;

public AssertIsTrueInterpolatedStringHandler(int literalLength, int formattedCount, bool? condition, out bool shouldAppend)
{
_condition = condition;
shouldAppend = IsTrueFailing(condition);
if (shouldAppend)
{
Expand All @@ -36,8 +38,7 @@ internal void ComputeAssertion(string conditionExpression)
{
if (_builder is not null)
{
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "condition", conditionExpression) + " ");
ReportAssertIsTrueFailed(_builder.ToString());
ReportAssertIsTrueFailed(_condition, _builder.ToString(), conditionExpression);
}
}

Expand Down Expand Up @@ -74,9 +75,11 @@ internal void ComputeAssertion(string conditionExpression)
public readonly struct AssertIsFalseInterpolatedStringHandler
{
private readonly StringBuilder? _builder;
private readonly bool? _condition;

public AssertIsFalseInterpolatedStringHandler(int literalLength, int formattedCount, bool? condition, out bool shouldAppend)
{
_condition = condition;
shouldAppend = IsFalseFailing(condition);
if (shouldAppend)
{
Expand All @@ -88,8 +91,7 @@ internal void ComputeAssertion(string conditionExpression)
{
if (_builder is not null)
{
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "condition", conditionExpression) + " ");
ReportAssertIsFalseFailed(_builder.ToString());
ReportAssertIsFalseFailed(_condition, _builder.ToString(), conditionExpression);
}
}

Expand Down Expand Up @@ -150,15 +152,27 @@ public static void IsTrue([DoesNotReturnIf(false)] bool? condition, string? mess
{
if (IsTrueFailing(condition))
{
ReportAssertIsTrueFailed(BuildUserMessageForConditionExpression(message, conditionExpression));
ReportAssertIsTrueFailed(condition, message, conditionExpression);
}
}

private static bool IsTrueFailing(bool? condition)
=> condition is false or null;

private static void ReportAssertIsTrueFailed(string? message)
=> ReportAssertFailed("Assert.IsTrue", message);
private static void ReportAssertIsTrueFailed(bool? condition, string? message, string conditionExpression)
{
string actualValue = AssertionValueRenderer.RenderValue(condition);
EvidenceBlock evidence = EvidenceBlock.Create()
.AddLine("actual:", actualValue);

StructuredAssertionMessage structured = new("Expected condition to be true.");
Comment thread
Evangelink marked this conversation as resolved.
Outdated
structured.WithUserMessage(message);
structured.WithEvidence(evidence);
structured.WithExpectedAndActual(null, actualValue);
structured.WithCallSiteExpression(FormatCallSiteExpression("Assert.IsTrue", conditionExpression));
Comment thread
Evangelink marked this conversation as resolved.
Outdated
Comment thread
Evangelink marked this conversation as resolved.
Outdated
Comment thread
Evangelink marked this conversation as resolved.
Outdated
Comment thread
Evangelink marked this conversation as resolved.
Outdated

ReportAssertFailed(structured);
}

/// <inheritdoc cref="IsFalse(bool?, string, string)" />
#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
Expand Down Expand Up @@ -188,14 +202,26 @@ public static void IsFalse([DoesNotReturnIf(true)] bool? condition, string? mess
{
if (IsFalseFailing(condition))
{
ReportAssertIsFalseFailed(BuildUserMessageForConditionExpression(message, conditionExpression));
ReportAssertIsFalseFailed(condition, message, conditionExpression);
}
}

private static bool IsFalseFailing(bool? condition)
=> condition is true or null;

[DoesNotReturn]
private static void ReportAssertIsFalseFailed(string userMessage)
=> ReportAssertFailed("Assert.IsFalse", userMessage);
private static void ReportAssertIsFalseFailed(bool? condition, string? message, string conditionExpression)
{
string actualValue = AssertionValueRenderer.RenderValue(condition);
EvidenceBlock evidence = EvidenceBlock.Create()
.AddLine("actual:", actualValue);

Comment thread
Evangelink marked this conversation as resolved.
StructuredAssertionMessage structured = new("Expected condition to be false.");
Comment thread
Evangelink marked this conversation as resolved.
Outdated
structured.WithUserMessage(message);
structured.WithEvidence(evidence);
structured.WithExpectedAndActual(null, actualValue);
structured.WithCallSiteExpression(FormatCallSiteExpression("Assert.IsFalse", conditionExpression));
Comment thread
Evangelink marked this conversation as resolved.
Outdated
Comment thread
Evangelink marked this conversation as resolved.
Outdated

ReportAssertFailed(structured);
}
}
37 changes: 34 additions & 3 deletions src/TestFramework/TestFramework/Assertions/Assert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,40 @@ internal static void ThrowAssertFailed(StructuredAssertionMessage structuredMess
throw CreateAssertFailedException(structuredMessage);
}

/// <summary>
/// Formats a call-site expression for display at the bottom of a structured assertion message.
/// When the expression is empty or contains newlines, the expression is replaced with a placeholder.
/// </summary>
internal static string? FormatCallSiteExpression(string assertionMethodName, string expression)
{
if (string.IsNullOrWhiteSpace(expression))
{
return null;
}

// If expression contains newlines (multiline constant), replace with placeholder per RFC
return expression.Contains('\n') || expression.Contains('\r')
Comment thread
Evangelink marked this conversation as resolved.
Outdated
? null
Comment thread
Evangelink marked this conversation as resolved.
Outdated
: $"{assertionMethodName}({expression})";
Comment thread
Evangelink marked this conversation as resolved.
Outdated
Comment thread
Evangelink marked this conversation as resolved.
Outdated
}

/// <summary>
/// Formats a call-site expression for display at the bottom of a structured assertion message,
/// using two captured expressions.
/// </summary>
internal static string? FormatCallSiteExpression(string assertionMethodName, string expression1, string expression2)
Comment thread
Evangelink marked this conversation as resolved.
Outdated
{
if (string.IsNullOrWhiteSpace(expression1) || string.IsNullOrWhiteSpace(expression2))
{
return null;
}

string arg1 = expression1.Contains('\n') || expression1.Contains('\r') ? "<expected>" : expression1;
string arg2 = expression2.Contains('\n') || expression2.Contains('\r') ? "<actual>" : expression2;

return $"{assertionMethodName}({arg1}, {arg2})";
}

private static string FormatAssertionFailed(string assertionName, string? message)
{
string failedMessage = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AssertionFailed, assertionName);
Expand Down Expand Up @@ -240,9 +274,6 @@ private static string BuildUserMessageForThreeExpressions(string? format, string
: $"{callerArgMessagePart} {userMessage}";
}

private static string BuildUserMessageForConditionExpression(string? format, string conditionExpression)
=> BuildUserMessageForSingleExpression(format, conditionExpression, "condition");

private static string BuildUserMessageForValueExpression(string? format, string valueExpression)
=> BuildUserMessageForSingleExpression(format, valueExpression, "value");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public void IsNull_PassNonNull_ShouldFail()
{
Action action = () => Assert.IsNull(new object());
action.Should().Throw<Exception>()
.WithMessage("Assert.IsNull failed. 'value' expression: 'new object()'.");
.WithMessage($"Assertion failed. Expected value to be null.{Environment.NewLine}{Environment.NewLine}actual: System.Object{Environment.NewLine}{Environment.NewLine}Assert.IsNull(new object())");
}

public void IsNull_StringMessage_PassNull_ShouldPass()
Expand All @@ -26,7 +26,7 @@ public void IsNull_StringMessage_PassNonNull_ShouldFail()
{
Action action = () => Assert.IsNull(new object(), "User-provided message");
action.Should().Throw<Exception>()
.WithMessage("Assert.IsNull failed. 'value' expression: 'new object()'. User-provided message");
.WithMessage($"Assertion failed. Expected value to be null.{Environment.NewLine}User-provided message{Environment.NewLine}{Environment.NewLine}actual: System.Object{Environment.NewLine}{Environment.NewLine}Assert.IsNull(new object())");
}

public void IsNull_InterpolatedString_PassNull_ShouldPass()
Expand All @@ -42,7 +42,7 @@ public async Task IsNull_InterpolatedString_PassNonNull_ShouldFail()
DateTime dateTime = DateTime.Now;
Func<Task> action = async () => Assert.IsNull(new object(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}");
(await action.Should().ThrowAsync<Exception>())
.WithMessage($"Assert.IsNull failed. 'value' expression: 'new object()'. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}");
.WithMessage($"Assertion failed. Expected value to be null.{Environment.NewLine}User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}{Environment.NewLine}{Environment.NewLine}actual: System.Object{Environment.NewLine}{Environment.NewLine}Assert.IsNull(new object())");
o.WasToStringCalled.Should().BeTrue();
}

Expand Down Expand Up @@ -73,14 +73,14 @@ public void IsNotNull_PassNull_ShouldFail()
{
Action action = () => Assert.IsNotNull(null);
action.Should().Throw<Exception>()
.WithMessage("Assert.IsNotNull failed. 'value' expression: 'null'.");
.WithMessage($"Assertion failed. Expected value to not be null.{Environment.NewLine}{Environment.NewLine}Assert.IsNotNull(null)");
}

public void IsNotNull_StringMessage_PassNonNull_ShouldFail()
{
Action action = () => Assert.IsNotNull(null, "User-provided message");
action.Should().Throw<Exception>()
.WithMessage("Assert.IsNotNull failed. 'value' expression: 'null'. User-provided message");
.WithMessage($"Assertion failed. Expected value to not be null.{Environment.NewLine}User-provided message{Environment.NewLine}{Environment.NewLine}Assert.IsNotNull(null)");
}

public async Task IsNotNull_InterpolatedString_PassNonNull_ShouldFail()
Expand All @@ -89,7 +89,7 @@ public async Task IsNotNull_InterpolatedString_PassNonNull_ShouldFail()
DateTime dateTime = DateTime.Now;
Func<Task> action = async () => Assert.IsNotNull(null, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}");
(await action.Should().ThrowAsync<Exception>())
.WithMessage($"Assert.IsNotNull failed. 'value' expression: 'null'. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}");
.WithMessage($"Assertion failed. Expected value to not be null.{Environment.NewLine}User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}{Environment.NewLine}{Environment.NewLine}Assert.IsNotNull(null)");
o.WasToStringCalled.Should().BeTrue();
}
}
Loading
Loading