Skip to content
75 changes: 45 additions & 30 deletions src/TestFramework/TestFramework/Assertions/Assert.Count.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ internal void ComputeAssertion(string assertionName, string collectionExpression
{
if (_builder is not null)
{
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "collection", collectionExpression) + " ");
ReportAssertCountFailed(assertionName, _expectedCount, _actualCount, _builder.ToString());
ReportAssertCountFailed(assertionName, _expectedCount, _actualCount, _builder.ToString(), collectionExpression);
}
}

Expand Down Expand Up @@ -115,8 +114,7 @@ internal void ComputeAssertion(string collectionExpression)
{
if (_builder is not null)
{
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "collection", collectionExpression) + " ");
ReportAssertIsNotEmptyFailed(_builder.ToString());
ReportAssertIsNotEmptyFailed(_builder.ToString(), collectionExpression);
}
}

Expand Down Expand Up @@ -207,8 +205,7 @@ public static void IsNotEmpty<T>(IEnumerable<T> collection, string? message = ""
return;
}

string userMessage = BuildUserMessageForCollectionExpression(message, collectionExpression);
ReportAssertIsNotEmptyFailed(userMessage);
ReportAssertIsNotEmptyFailed(message, collectionExpression);
}

/// <summary>
Expand All @@ -229,8 +226,7 @@ public static void IsNotEmpty(IEnumerable collection, string? message = "", [Cal
return;
}

string userMessage = BuildUserMessageForCollectionExpression(message, collectionExpression);
ReportAssertIsNotEmptyFailed(userMessage);
ReportAssertIsNotEmptyFailed(message, collectionExpression);
}
#endregion // IsNotEmpty

Expand All @@ -252,7 +248,7 @@ public static void HasCount<T>(int expected, IEnumerable<T> collection, [Interpo
#pragma warning restore IDE0060 // Remove unused parameter
{
TelemetryCollector.TrackAssertionCall("Assert.HasCount");
message.ComputeAssertion("HasCount", collectionExpression);
message.ComputeAssertion(nameof(HasCount), collectionExpression);
}

/// <summary>
Expand All @@ -267,7 +263,7 @@ public static void HasCount<T>(int expected, IEnumerable<T> collection, [Interpo
/// Users shouldn't pass a value for this parameter.
/// </param>
public static void HasCount<T>(int expected, IEnumerable<T> collection, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
=> HasCount("HasCount", expected, collection, message, collectionExpression);
=> HasCount(nameof(HasCount), expected, collection, message, collectionExpression);

/// <summary>
/// Tests whether the collection has the expected count/length.
Expand All @@ -280,7 +276,7 @@ public static void HasCount<T>(int expected, IEnumerable<T> collection, string?
/// Users shouldn't pass a value for this parameter.
/// </param>
public static void HasCount(int expected, IEnumerable collection, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
=> HasCount("HasCount", expected, collection, message, collectionExpression);
=> HasCount(nameof(HasCount), expected, collection, message, collectionExpression);

#endregion // HasCount

Expand All @@ -301,7 +297,7 @@ public static void IsEmpty<T>(IEnumerable<T> collection, [InterpolatedStringHand
#pragma warning restore IDE0060 // Remove unused parameter
{
TelemetryCollector.TrackAssertionCall("Assert.IsEmpty");
message.ComputeAssertion("IsEmpty", collectionExpression);
message.ComputeAssertion(nameof(IsEmpty), collectionExpression);
}

/// <summary>
Expand All @@ -315,7 +311,7 @@ public static void IsEmpty<T>(IEnumerable<T> collection, [InterpolatedStringHand
/// Users shouldn't pass a value for this parameter.
/// </param>
public static void IsEmpty<T>(IEnumerable<T> collection, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
=> HasCount("IsEmpty", 0, collection, message, collectionExpression);
=> HasCount(nameof(IsEmpty), 0, collection, message, collectionExpression);

/// <summary>
/// Tests that the collection is empty.
Expand All @@ -327,7 +323,7 @@ public static void IsEmpty<T>(IEnumerable<T> collection, string? message = "", [
/// Users shouldn't pass a value for this parameter.
/// </param>
public static void IsEmpty(IEnumerable collection, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
=> HasCount("IsEmpty", 0, collection, message, collectionExpression);
=> HasCount(nameof(IsEmpty), 0, collection, message, collectionExpression);

#endregion // IsEmpty

Expand All @@ -343,8 +339,7 @@ private static void HasCount<T>(string assertionName, int expected, IEnumerable<
return;
}

string userMessage = BuildUserMessageForCollectionExpression(message, collectionExpression);
ReportAssertCountFailed(assertionName, expected, actualCount, userMessage);
ReportAssertCountFailed(assertionName, expected, actualCount, message, collectionExpression);
}

private static void HasCount(string assertionName, int expected, IEnumerable collection, string? message, string collectionExpression)
Expand All @@ -359,24 +354,44 @@ private static string GetTrackedAssertionName(string assertionName)
};

[DoesNotReturn]
private static void ReportAssertCountFailed(string assertionName, int expectedCount, int actualCount, string userMessage)
private static void ReportAssertCountFailed(string assertionName, int expectedCount, int actualCount, string? userMessage, string collectionExpression)
{
string finalMessage = string.Format(
CultureInfo.CurrentCulture,
FrameworkMessages.HasCountFailMsg,
userMessage,
expectedCount,
actualCount);
ReportAssertFailed($"Assert.{assertionName}", finalMessage);
bool isEmptyAssertion = string.Equals(assertionName, nameof(IsEmpty), StringComparison.Ordinal);
string summary = isEmptyAssertion
? FrameworkMessages.IsEmptyFailedSummary
: FrameworkMessages.HasCountFailedSummary;

string expectedEvidenceText = expectedCount.ToString(CultureInfo.CurrentCulture);
string expectedCallSiteText = expectedCount.ToString(CultureInfo.InvariantCulture);
string actualText = actualCount.ToString(CultureInfo.CurrentCulture);
EvidenceBlock evidence = EvidenceBlock.Create()
.AddLine("expected count:", expectedEvidenceText)
.AddLine("actual count:", actualText);

StructuredAssertionMessage structured = new(summary);
structured.WithUserMessage(userMessage);
structured.WithEvidence(evidence);
structured.WithExpectedAndActual(expectedEvidenceText, actualText);
structured.WithCallSiteExpression(isEmptyAssertion
? FormatCallSiteExpression($"Assert.{assertionName}", collectionExpression, "<collection>")
: FormatCallSiteExpression($"Assert.{assertionName}", expectedCallSiteText, collectionExpression, "<expected>", "<collection>"));

ReportAssertFailed(structured);
}

[DoesNotReturn]
private static void ReportAssertIsNotEmptyFailed(string userMessage)
private static void ReportAssertIsNotEmptyFailed(string? userMessage, string collectionExpression)
{
string finalMessage = string.Format(
CultureInfo.CurrentCulture,
FrameworkMessages.IsNotEmptyFailMsg,
userMessage);
ReportAssertFailed("Assert.IsNotEmpty", finalMessage);
string actualText = 0.ToString(CultureInfo.CurrentCulture);
EvidenceBlock evidence = EvidenceBlock.Create()
.AddLine("actual count:", actualText);

StructuredAssertionMessage structured = new(FrameworkMessages.IsNotEmptyFailedSummary);
structured.WithUserMessage(userMessage);
structured.WithEvidence(evidence);
Comment thread
Evangelink marked this conversation as resolved.
structured.WithExpectedAndActual("> 0", actualText);
structured.WithCallSiteExpression(FormatCallSiteExpression("Assert.IsNotEmpty", collectionExpression, "<collection>"));

ReportAssertFailed(structured);
}
}
26 changes: 0 additions & 26 deletions src/TestFramework/TestFramework/Assertions/Assert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,20 +244,6 @@ private static string FormatAssertionFailed(string assertionName, string? messag
internal static string BuildUserMessage(string? format)
=> format ?? string.Empty;

private static string BuildUserMessageForSingleExpression(string? format, string callerArgExpression, string parameterName)
{
string userMessage = BuildUserMessage(format);
if (string.IsNullOrEmpty(callerArgExpression))
{
return userMessage;
}

string callerArgMessagePart = string.Format(CultureInfo.InvariantCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, parameterName, callerArgExpression);
return string.IsNullOrEmpty(userMessage)
? callerArgMessagePart
: $"{callerArgMessagePart} {userMessage}";
}

private static string BuildUserMessageForTwoExpressions(string? format, string callerArgExpression1, string parameterName1, string callerArgExpression2, string parameterName2)
{
string userMessage = BuildUserMessage(format);
Expand All @@ -272,9 +258,6 @@ private static string BuildUserMessageForTwoExpressions(string? format, string c
: $"{callerArgMessagePart} {userMessage}";
}

private static string BuildUserMessageForCollectionExpression(string? format, string collectionExpression)
=> BuildUserMessageForSingleExpression(format, collectionExpression, "collection");

private static string BuildUserMessageForExpectedExpressionAndActualExpression(string? format, string expectedExpression, string actualExpression)
=> BuildUserMessageForTwoExpressions(format, expectedExpression, "expected", actualExpression, "actual");

Expand Down Expand Up @@ -305,15 +288,6 @@ internal static void CheckParameterNotNull([NotNull] object? param, string asser
internal static string ReplaceNulls(object? input)
=> input?.ToString() ?? string.Empty;

/// <summary>
/// Formats a call-site expression like <c>Assert.MethodName(expression)</c>.
/// Returns <see langword="null"/> if the expression is empty or contains a line break.
/// </summary>
private static string? FormatCallSiteExpression(string methodName, string expression)
=> string.IsNullOrEmpty(expression) || expression.IndexOfAny(['\n', '\r']) >= 0
? null
: $"{methodName}({expression})";

private static int CompareInternal(string? expected, string? actual, bool ignoreCase, CultureInfo culture)
#pragma warning disable CA1309 // Use ordinal string comparison
=> string.Compare(expected, actual, ignoreCase, culture);
Expand Down
15 changes: 9 additions & 6 deletions src/TestFramework/TestFramework/Resources/FrameworkMessages.resx
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,6 @@ Actual: {2}</value>
<data name="DynamicDataSourceShouldExistAndBeValid" xml:space="preserve">
<value>The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field.</value>
</data>
<data name="HasCountFailMsg" xml:space="preserve">
<value>Expected collection of size {1}. Actual: {2}. {0}</value>
</data>
<data name="ContainsSingleMatchFailMsg" xml:space="preserve">
<value>Expected exactly one item to match the predicate but found {1} item(s). {0}</value>
</data>
Expand All @@ -396,9 +393,6 @@ Actual: {2}</value>
<data name="DoesNotContainPredicateFailMsg" xml:space="preserve">
<value>Expected no items to match the predicate. {0}</value>
</data>
<data name="IsNotEmptyFailMsg" xml:space="preserve">
<value>Expected collection to contain any item but it is empty. {0}</value>
</data>
<data name="InvalidGitHubUrl" xml:space="preserve">
<value>Invalid GitHub ticket URL</value>
</data>
Expand Down Expand Up @@ -479,6 +473,15 @@ Actual: {2}</value>
<data name="IsNotNullFailedSummary" xml:space="preserve">
<value>Expected value to not be null.</value>
</data>
<data name="HasCountFailedSummary" xml:space="preserve">
<value>Expected collection to contain a specific number of elements.</value>
</data>
<data name="IsEmptyFailedSummary" xml:space="preserve">
<value>Expected collection to be empty.</value>
</data>
<data name="IsNotEmptyFailedSummary" xml:space="preserve">
<value>Expected collection to not be empty.</value>
</data>
<data name="AreAllNotNullFailedSummary" xml:space="preserve">
<value>Expected all items in collection to be non-null.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,9 +457,9 @@ Skutečnost: {2}</target>
<target state="new">Expected string to end with the specified suffix.</target>
<note />
</trans-unit>
<trans-unit id="HasCountFailMsg">
<source>Expected collection of size {1}. Actual: {2}. {0}</source>
<target state="translated">Očekávala se kolekce {1} velikosti. Skutečnost: {2} {0}</target>
<trans-unit id="HasCountFailedSummary">
<source>Expected collection to contain a specific number of elements.</source>
<target state="new">Expected collection to contain a specific number of elements.</target>
<note />
</trans-unit>
<trans-unit id="InvalidAccessToTestContextProperty">
Expand All @@ -472,6 +472,11 @@ Skutečnost: {2}</target>
<target state="translated">Neplatná adresa URL lístku GitHubu</target>
<note />
</trans-unit>
<trans-unit id="IsEmptyFailedSummary">
<source>Expected collection to be empty.</source>
<target state="new">Expected collection to be empty.</target>
<note />
</trans-unit>
<trans-unit id="IsFalseFailedSummary">
<source>Expected condition to be false.</source>
<target state="translated">Očekávala se podmínka false.</target>
Expand Down Expand Up @@ -527,9 +532,9 @@ Skutečnost: {2}</target>
<target state="new">Expected value to be negative.</target>
<note />
</trans-unit>
<trans-unit id="IsNotEmptyFailMsg">
<source>Expected collection to contain any item but it is empty. {0}</source>
<target state="translated">Očekávalo se, že kolekce bude obsahovat libovolnou položku, ale je prázdná. {0}</target>
<trans-unit id="IsNotEmptyFailedSummary">
<source>Expected collection to not be empty.</source>
<target state="new">Expected collection to not be empty.</target>
<note />
</trans-unit>
<trans-unit id="IsNotInstanceOfFailMsg">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,9 +457,9 @@ Tatsächlich: {2}</target>
<target state="new">Expected string to end with the specified suffix.</target>
<note />
</trans-unit>
<trans-unit id="HasCountFailMsg">
<source>Expected collection of size {1}. Actual: {2}. {0}</source>
<target state="translated">Es wurde eine Sammlung mit einer Größe {1} erwartet. Tatsächlich: {2}. {0}</target>
<trans-unit id="HasCountFailedSummary">
<source>Expected collection to contain a specific number of elements.</source>
<target state="new">Expected collection to contain a specific number of elements.</target>
<note />
</trans-unit>
<trans-unit id="InvalidAccessToTestContextProperty">
Expand All @@ -472,6 +472,11 @@ Tatsächlich: {2}</target>
<target state="translated">Ungültige GitHub-Ticket-URL.</target>
<note />
</trans-unit>
<trans-unit id="IsEmptyFailedSummary">
<source>Expected collection to be empty.</source>
<target state="new">Expected collection to be empty.</target>
<note />
</trans-unit>
<trans-unit id="IsFalseFailedSummary">
<source>Expected condition to be false.</source>
<target state="translated">Es wurde erwartet, dass die Bedingung „falsch“ lautet.</target>
Expand Down Expand Up @@ -527,9 +532,9 @@ Tatsächlich: {2}</target>
<target state="new">Expected value to be negative.</target>
<note />
</trans-unit>
<trans-unit id="IsNotEmptyFailMsg">
<source>Expected collection to contain any item but it is empty. {0}</source>
<target state="translated">Es wurde erwartet, dass die Sammlung ein beliebiges Element enthält, aber leer ist. {0}</target>
<trans-unit id="IsNotEmptyFailedSummary">
<source>Expected collection to not be empty.</source>
<target state="new">Expected collection to not be empty.</target>
<note />
</trans-unit>
<trans-unit id="IsNotInstanceOfFailMsg">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,9 +457,9 @@ Real: {2}</target>
<target state="new">Expected string to end with the specified suffix.</target>
<note />
</trans-unit>
<trans-unit id="HasCountFailMsg">
<source>Expected collection of size {1}. Actual: {2}. {0}</source>
<target state="translated">Se esperaba una colección de tamaño {1}. Real: {2}. {0}</target>
<trans-unit id="HasCountFailedSummary">
<source>Expected collection to contain a specific number of elements.</source>
<target state="new">Expected collection to contain a specific number of elements.</target>
<note />
</trans-unit>
<trans-unit id="InvalidAccessToTestContextProperty">
Expand All @@ -472,6 +472,11 @@ Real: {2}</target>
<target state="translated">Dirección URL de vale de GitHub no válida</target>
<note />
</trans-unit>
<trans-unit id="IsEmptyFailedSummary">
<source>Expected collection to be empty.</source>
<target state="new">Expected collection to be empty.</target>
<note />
</trans-unit>
<trans-unit id="IsFalseFailedSummary">
<source>Expected condition to be false.</source>
<target state="translated">Se esperaba que la condición fuera false.</target>
Expand Down Expand Up @@ -527,9 +532,9 @@ Real: {2}</target>
<target state="new">Expected value to be negative.</target>
<note />
</trans-unit>
<trans-unit id="IsNotEmptyFailMsg">
<source>Expected collection to contain any item but it is empty. {0}</source>
<target state="translated">Se esperaba que la colección contenga cualquier elemento, pero está vacía. {0}</target>
<trans-unit id="IsNotEmptyFailedSummary">
<source>Expected collection to not be empty.</source>
<target state="new">Expected collection to not be empty.</target>
<note />
</trans-unit>
<trans-unit id="IsNotInstanceOfFailMsg">
Expand Down
Loading
Loading