Skip to content

Commit 1593058

Browse files
EvangelinkCopilot
andauthored
Structured assertion messages for reference and type assertions (#8214)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 661218e commit 1593058

11 files changed

Lines changed: 786 additions & 154 deletions

src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ internal void ComputeAssertion(string expectedExpression, string actualExpressio
3838
{
3939
if (_builder is not null)
4040
{
41-
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionTwoParametersMessage, "expected", expectedExpression, "actual", actualExpression) + " ");
42-
ReportAssertAreSameFailed(_expected, _actual, _builder.ToString());
41+
ReportAssertAreSameFailed(_expected, _actual, _builder.ToString(), expectedExpression, actualExpression);
4342
}
4443
}
4544

@@ -98,8 +97,7 @@ internal void ComputeAssertion(string notExpectedExpression, string actualExpres
9897
{
9998
if (_builder is not null)
10099
{
101-
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionTwoParametersMessage, "notExpected", notExpectedExpression, "actual", actualExpression) + " ");
102-
ReportAssertAreNotSameFailed(_notExpected, _actual, _builder.ToString());
100+
ReportAssertAreNotSameFailed(_notExpected, _actual, _builder.ToString(), notExpectedExpression, actualExpression);
103101
}
104102
}
105103

@@ -181,34 +179,37 @@ public static void AreSame<T>(T? expected, T? actual, string? message = "", [Cal
181179
return;
182180
}
183181

184-
string userMessage = BuildUserMessageForExpectedExpressionAndActualExpression(message, expectedExpression, actualExpression);
185-
ReportAssertAreSameFailed(expected, actual, userMessage);
182+
ReportAssertAreSameFailed(expected, actual, message, expectedExpression, actualExpression);
186183
}
187184

188185
private static bool IsAreSameFailing<T>(T? expected, T? actual)
189186
=> !object.ReferenceEquals(expected, actual);
190187

191188
[DoesNotReturn]
192-
private static void ReportAssertAreSameFailed<T>(T? expected, T? actual, string userMessage)
189+
private static void ReportAssertAreSameFailed<T>(T? expected, T? actual, string? userMessage, string expectedExpression, string actualExpression)
193190
{
194-
string finalMessage = expected is null
195-
? string.Format(
196-
CultureInfo.CurrentCulture,
197-
FrameworkMessages.AreSameExpectedIsNull,
198-
userMessage)
199-
: actual is null
200-
? string.Format(
201-
CultureInfo.CurrentCulture,
202-
FrameworkMessages.AreSameActualIsNull,
203-
userMessage)
204-
: expected is ValueType && actual is ValueType
205-
? string.Format(
206-
CultureInfo.CurrentCulture,
207-
FrameworkMessages.AreSameGivenValues,
208-
userMessage)
209-
: userMessage;
210-
211-
ReportAssertFailed("Assert.AreSame", finalMessage);
191+
StructuredAssertionMessage msg = new("Expected both values to refer to the same object.");
192+
193+
if (expected is ValueType && actual is ValueType)
194+
{
195+
msg.WithAdditionalSummaryLine("Do not pass value types to AreSame \u2014 value types are boxed on each call, so references will never be the same.");
196+
}
197+
198+
msg.WithUserMessage(userMessage);
199+
200+
if (expected is not ValueType || actual is not ValueType)
201+
{
202+
string expectedText = expected is null ? "null" : $"{expected.GetType()} (hash: 0x{RuntimeHelpers.GetHashCode(expected):X})";
203+
string actualText = actual is null ? "null" : $"{actual.GetType()} (hash: 0x{RuntimeHelpers.GetHashCode(actual):X})";
204+
EvidenceBlock evidence = EvidenceBlock.Create()
205+
.AddLine("expected:", expectedText)
206+
.AddLine("actual:", actualText);
207+
msg.WithEvidence(evidence).WithExpectedAndActual(expectedText, actualText);
208+
}
209+
210+
msg.WithCallSiteExpression(FormatCallSiteExpression("Assert.AreSame", expectedExpression, actualExpression, "<expected>", "<actual>"));
211+
212+
ReportAssertFailed(msg);
212213
}
213214

214215
/// <inheritdoc cref="AreNotSame{T}(T, T, string?, string, string)" />
@@ -252,23 +253,27 @@ public static void AreNotSame<T>(T? notExpected, T? actual, string? message = ""
252253
{
253254
if (IsAreNotSameFailing(notExpected, actual))
254255
{
255-
ReportAssertAreNotSameFailed(notExpected, actual, BuildUserMessageForNotExpectedExpressionAndActualExpression(message, notExpectedExpression, actualExpression));
256+
ReportAssertAreNotSameFailed(notExpected, actual, message, notExpectedExpression, actualExpression);
256257
}
257258
}
258259

259260
private static bool IsAreNotSameFailing<T>(T? notExpected, T? actual)
260261
=> object.ReferenceEquals(notExpected, actual);
261262

262263
[DoesNotReturn]
263-
private static void ReportAssertAreNotSameFailed<T>(T? notExpected, T? actual, string userMessage)
264+
private static void ReportAssertAreNotSameFailed<T>(T? notExpected, T? actual, string? userMessage, string notExpectedExpression, string actualExpression)
264265
{
265-
string finalMessage = notExpected is null && actual is null
266-
? string.Format(
267-
CultureInfo.CurrentCulture,
268-
FrameworkMessages.AreNotSameBothNull,
269-
userMessage)
270-
: userMessage;
271-
272-
ReportAssertFailed("Assert.AreNotSame", finalMessage);
266+
StructuredAssertionMessage msg = new("Expected values to refer to different objects.");
267+
268+
msg.WithAdditionalSummaryLine(
269+
notExpected is null && actual is null
270+
? "Both values are null."
271+
: "Both values refer to the same object.");
272+
273+
msg.WithUserMessage(userMessage);
274+
275+
msg.WithCallSiteExpression(FormatCallSiteExpression("Assert.AreNotSame", notExpectedExpression, actualExpression, "<notExpected>", "<actual>"));
276+
277+
ReportAssertFailed(msg);
273278
}
274279
}

src/TestFramework/TestFramework/Assertions/Assert.IsExactInstanceOfType.cs

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation. All rights reserved.
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System.ComponentModel;
@@ -38,8 +38,7 @@ internal void ComputeAssertion(string valueExpression)
3838
{
3939
if (_builder is not null)
4040
{
41-
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "value", valueExpression) + " ");
42-
ReportAssertIsExactInstanceOfTypeFailed(_value, _expectedType, _builder.ToString());
41+
ReportAssertIsExactInstanceOfTypeFailed(_value, _expectedType, _builder.ToString(), valueExpression);
4342
}
4443
}
4544

@@ -98,8 +97,7 @@ internal void ComputeAssertion(string valueExpression)
9897
{
9998
if (_builder is not null)
10099
{
101-
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "value", valueExpression) + " ");
102-
ReportAssertIsExactInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString());
100+
ReportAssertIsExactInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString(), valueExpression);
103101
}
104102
}
105103

@@ -160,8 +158,7 @@ internal void ComputeAssertion(string valueExpression)
160158
{
161159
if (_builder is not null)
162160
{
163-
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "value", valueExpression) + " ");
164-
ReportAssertIsNotExactInstanceOfTypeFailed(_value, _wrongType, _builder.ToString());
161+
ReportAssertIsNotExactInstanceOfTypeFailed(_value, _wrongType, _builder.ToString(), valueExpression);
165162
}
166163
}
167164

@@ -220,8 +217,7 @@ internal void ComputeAssertion(string valueExpression)
220217
{
221218
if (_builder is not null)
222219
{
223-
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "value", valueExpression) + " ");
224-
ReportAssertIsNotExactInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString());
220+
ReportAssertIsNotExactInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString(), valueExpression);
225221
}
226222
}
227223

@@ -291,7 +287,7 @@ public static void IsExactInstanceOfType([NotNull] object? value, [NotNull] Type
291287
{
292288
if (IsExactInstanceOfTypeFailing(value, expectedType))
293289
{
294-
ReportAssertIsExactInstanceOfTypeFailed(value, expectedType, BuildUserMessageForValueExpression(message, valueExpression));
290+
ReportAssertIsExactInstanceOfTypeFailed(value, expectedType, message, valueExpression);
295291
}
296292
}
297293

@@ -329,18 +325,25 @@ private static bool IsExactInstanceOfTypeFailing([NotNullWhen(false)] object? va
329325
=> expectedType is null || value is null || value.GetType() != expectedType;
330326

331327
[DoesNotReturn]
332-
private static void ReportAssertIsExactInstanceOfTypeFailed(object? value, Type? expectedType, string userMessage)
328+
private static void ReportAssertIsExactInstanceOfTypeFailed(object? value, Type? expectedType, string? userMessage, string valueExpression)
333329
{
334-
string finalMessage = expectedType is not null
335-
? string.Format(
336-
CultureInfo.CurrentCulture,
337-
FrameworkMessages.IsExactInstanceOfFailMsg,
338-
userMessage,
339-
expectedType.ToString(),
340-
value?.GetType().ToString() ?? "null")
341-
: userMessage;
342-
343-
ReportAssertFailed("Assert.IsExactInstanceOfType", finalMessage);
330+
StructuredAssertionMessage msg = expectedType is null
331+
? new("Cannot check type because the expected type argument is null.")
332+
: new($"Expected value to be exactly of type {expectedType.Name}.");
333+
msg.WithUserMessage(userMessage);
334+
335+
if (expectedType is not null)
336+
{
337+
string actualTypeText = value?.GetType().ToString() ?? "null";
338+
EvidenceBlock evidence = EvidenceBlock.Create()
339+
.AddLine("expected type:", expectedType.ToString())
340+
.AddLine(value is null ? "actual:" : "actual type:", actualTypeText);
341+
msg.WithEvidence(evidence)
342+
.WithExpectedAndActual(expectedType.ToString(), actualTypeText);
343+
}
344+
345+
msg.WithCallSiteExpression(FormatCallSiteExpression("Assert.IsExactInstanceOfType", valueExpression, "<value>"));
346+
ReportAssertFailed(msg);
344347
}
345348

346349
/// <summary>
@@ -371,7 +374,7 @@ public static void IsNotExactInstanceOfType(object? value, [NotNull] Type? wrong
371374
{
372375
if (IsNotExactInstanceOfTypeFailing(value, wrongType))
373376
{
374-
ReportAssertIsNotExactInstanceOfTypeFailed(value, wrongType, BuildUserMessageForValueExpression(message, valueExpression));
377+
ReportAssertIsNotExactInstanceOfTypeFailed(value, wrongType, message, valueExpression);
375378
}
376379
}
377380

@@ -403,19 +406,24 @@ private static bool IsNotExactInstanceOfTypeFailing(object? value, [NotNullWhen(
403406
(value is not null && value.GetType() == wrongType);
404407

405408
[DoesNotReturn]
406-
private static void ReportAssertIsNotExactInstanceOfTypeFailed(object? value, Type? wrongType, string userMessage)
409+
private static void ReportAssertIsNotExactInstanceOfTypeFailed(object? value, Type? wrongType, string? userMessage, string valueExpression)
407410
{
408-
string finalMessage = userMessage;
411+
StructuredAssertionMessage msg = wrongType is null
412+
? new("Cannot check type because the not-expected type argument is null.")
413+
: new($"Expected value to not be exactly of type {wrongType.Name}.");
414+
msg.WithUserMessage(userMessage);
415+
409416
if (wrongType is not null)
410417
{
411-
finalMessage = string.Format(
412-
CultureInfo.CurrentCulture,
413-
FrameworkMessages.IsNotExactInstanceOfFailMsg,
414-
userMessage,
415-
wrongType.ToString(),
416-
value!.GetType().ToString());
418+
string actualTypeText = value?.GetType().ToString() ?? "null";
419+
EvidenceBlock evidence = EvidenceBlock.Create()
420+
.AddLine("not expected type:", wrongType.ToString())
421+
.AddLine("actual type:", actualTypeText);
422+
msg.WithEvidence(evidence)
423+
.WithExpectedAndActual(wrongType.ToString(), actualTypeText);
417424
}
418425

419-
ReportAssertFailed("Assert.IsNotExactInstanceOfType", finalMessage);
426+
msg.WithCallSiteExpression(FormatCallSiteExpression("Assert.IsNotExactInstanceOfType", valueExpression, "<value>"));
427+
ReportAssertFailed(msg);
420428
}
421429
}

src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation. All rights reserved.
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System.ComponentModel;
@@ -38,8 +38,7 @@ internal void ComputeAssertion(string valueExpression)
3838
{
3939
if (_builder is not null)
4040
{
41-
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "value", valueExpression) + " ");
42-
ReportAssertIsInstanceOfTypeFailed(_value, _expectedType, _builder.ToString());
41+
ReportAssertIsInstanceOfTypeFailed(_value, _expectedType, _builder.ToString(), valueExpression);
4342
}
4443
}
4544

@@ -98,8 +97,7 @@ internal void ComputeAssertion(string valueExpression)
9897
{
9998
if (_builder is not null)
10099
{
101-
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "value", valueExpression) + " ");
102-
ReportAssertIsInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString());
100+
ReportAssertIsInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString(), valueExpression);
103101
}
104102
}
105103

@@ -160,8 +158,7 @@ internal void ComputeAssertion(string valueExpression)
160158
{
161159
if (_builder is not null)
162160
{
163-
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "value", valueExpression) + " ");
164-
ReportAssertIsNotInstanceOfTypeFailed(_value, _wrongType, _builder.ToString());
161+
ReportAssertIsNotInstanceOfTypeFailed(_value, _wrongType, _builder.ToString(), valueExpression);
165162
}
166163
}
167164

@@ -220,8 +217,7 @@ internal void ComputeAssertion(string valueExpression)
220217
{
221218
if (_builder is not null)
222219
{
223-
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "value", valueExpression) + " ");
224-
ReportAssertIsNotInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString());
220+
ReportAssertIsNotInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString(), valueExpression);
225221
}
226222
}
227223

@@ -292,7 +288,7 @@ public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? exp
292288
{
293289
if (IsInstanceOfTypeFailing(value, expectedType))
294290
{
295-
ReportAssertIsInstanceOfTypeFailed(value, expectedType, BuildUserMessageForValueExpression(message, valueExpression));
291+
ReportAssertIsInstanceOfTypeFailed(value, expectedType, message, valueExpression);
296292
}
297293
}
298294

@@ -331,18 +327,25 @@ private static bool IsInstanceOfTypeFailing([NotNullWhen(false)] object? value,
331327
=> expectedType == null || value == null || !expectedType.IsInstanceOfType(value);
332328

333329
[DoesNotReturn]
334-
private static void ReportAssertIsInstanceOfTypeFailed(object? value, Type? expectedType, string userMessage)
330+
private static void ReportAssertIsInstanceOfTypeFailed(object? value, Type? expectedType, string? userMessage, string valueExpression)
335331
{
336-
string finalMessage = expectedType is not null
337-
? string.Format(
338-
CultureInfo.CurrentCulture,
339-
FrameworkMessages.IsInstanceOfFailMsg,
340-
userMessage,
341-
expectedType.ToString(),
342-
value?.GetType().ToString() ?? "null")
343-
: userMessage;
344-
345-
ReportAssertFailed("Assert.IsInstanceOfType", finalMessage);
332+
StructuredAssertionMessage msg = expectedType is null
333+
? new("Cannot check type because the expected type argument is null.")
334+
: new($"Expected value to be of type {expectedType.Name} (or derived).");
335+
msg.WithUserMessage(userMessage);
336+
337+
if (expectedType is not null)
338+
{
339+
string actualTypeText = value?.GetType().ToString() ?? "null";
340+
EvidenceBlock evidence = EvidenceBlock.Create()
341+
.AddLine("expected type:", $"{expectedType} (or derived)")
342+
.AddLine(value is null ? "actual:" : "actual type:", actualTypeText);
343+
msg.WithEvidence(evidence)
344+
.WithExpectedAndActual($"{expectedType} (or derived)", actualTypeText);
345+
}
346+
347+
msg.WithCallSiteExpression(FormatCallSiteExpression("Assert.IsInstanceOfType", valueExpression, "<value>"));
348+
ReportAssertFailed(msg);
346349
}
347350

348351
/// <summary>
@@ -374,7 +377,7 @@ public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType,
374377
{
375378
if (IsNotInstanceOfTypeFailing(value, wrongType))
376379
{
377-
ReportAssertIsNotInstanceOfTypeFailed(value, wrongType, BuildUserMessageForValueExpression(message, valueExpression));
380+
ReportAssertIsNotInstanceOfTypeFailed(value, wrongType, message, valueExpression);
378381
}
379382
}
380383

@@ -407,19 +410,24 @@ private static bool IsNotInstanceOfTypeFailing(object? value, [NotNullWhen(false
407410
(value is not null && wrongType.IsInstanceOfType(value));
408411

409412
[DoesNotReturn]
410-
private static void ReportAssertIsNotInstanceOfTypeFailed(object? value, Type? wrongType, string userMessage)
413+
private static void ReportAssertIsNotInstanceOfTypeFailed(object? value, Type? wrongType, string? userMessage, string valueExpression)
411414
{
412-
string finalMessage = userMessage;
415+
StructuredAssertionMessage msg = wrongType is null
416+
? new("Cannot check type because the not-expected type argument is null.")
417+
: new($"Expected value to not be of type {wrongType.Name} (or derived).");
418+
msg.WithUserMessage(userMessage);
419+
413420
if (wrongType is not null)
414421
{
415-
finalMessage = string.Format(
416-
CultureInfo.CurrentCulture,
417-
FrameworkMessages.IsNotInstanceOfFailMsg,
418-
userMessage,
419-
wrongType.ToString(),
420-
value!.GetType().ToString());
422+
string actualTypeText = value?.GetType().ToString() ?? "null";
423+
EvidenceBlock evidence = EvidenceBlock.Create()
424+
.AddLine("not expected type:", $"{wrongType} (or derived)")
425+
.AddLine("actual type:", actualTypeText);
426+
msg.WithEvidence(evidence)
427+
.WithExpectedAndActual($"{wrongType} (or derived)", actualTypeText);
421428
}
422429

423-
ReportAssertFailed("Assert.IsNotInstanceOfType", finalMessage);
430+
msg.WithCallSiteExpression(FormatCallSiteExpression("Assert.IsNotInstanceOfType", valueExpression, "<value>"));
431+
ReportAssertFailed(msg);
424432
}
425433
}

0 commit comments

Comments
 (0)