Apply structured assertion messages (RFC 012) to AreEqual / AreNotEqual#8231
Apply structured assertion messages (RFC 012) to AreEqual / AreNotEqual#8231Evangelink wants to merge 19 commits into
Conversation
Introduce the foundational types and helpers for structured multi-line assertion failure messages as described in RFC 012: - EvidenceLine: labeled line record struct for evidence blocks - EvidenceBlock: collection of labeled lines with automatic alignment - StructuredAssertionMessage: builder producing the new multi-line format (prefix + summary + user message + evidence block + call-site) - AssertionValueRenderer: renders values per RFC 012 rules (null, quoted strings with escape sequences, booleans, collections as JSON arrays) - AssertFailedException: add ExpectedText/ActualText public properties - Assert: add ReportAssertFailed/ThrowAssertFailed overloads accepting StructuredAssertionMessage No existing assertion methods are changed yet - this PR only introduces the infrastructure that subsequent PRs will use to migrate each assertion method to the new format.
…Null - Update Assert.IsTrue/IsFalse to use StructuredAssertionMessage with evidence block showing actual value and call-site expression - Update Assert.IsNull to use StructuredAssertionMessage with evidence block showing actual value - Update Assert.IsNotNull to use StructuredAssertionMessage without evidence block (actual is always null per RFC) - Update interpolated string handlers to store condition/value for passing to the new reporting methods - Add FormatCallSiteExpression helper to Assert.cs for formatting call-site display lines - Remove unused BuildUserMessageForConditionExpression method - Update all related test expectations to match new message format
…/AreNotEqual) - Update generic AreEqual<T>/AreNotEqual<T> to use StructuredAssertionMessage - Update string AreNotEqual to use structured format - Update interpolated string handlers for generic equality assertions - Preserve expression text in numeric non-generic handler closures - Update all related test expectations to match new format
…ured-messages-equality # Conflicts: # src/TestFramework/TestFramework/Assertions/Assert.cs # src/TestFramework/TestFramework/Assertions/AssertionValueRenderer.cs # src/TestFramework/TestFramework/Assertions/EvidenceBlock.cs # src/TestFramework/TestFramework/Assertions/StructuredAssertionMessage.cs # test/UnitTests/TestFramework.UnitTests/Assertions/AssertFailedExceptionTests.cs # test/UnitTests/TestFramework.UnitTests/Assertions/AssertionValueRendererTests.cs # test/UnitTests/TestFramework.UnitTests/Assertions/EvidenceBlockTests.cs # test/UnitTests/TestFramework.UnitTests/Assertions/StructuredAssertionMessageTests.cs
Replace hardcoded English summary literals in IsTrue/IsFalse/IsNull/IsNotNull/AreEqual/AreNotEqual with FrameworkMessages resource lookups. Add new resx keys for AreEqual/AreNotEqual variants. Add WithExpectedAndActual to AreNotEqual structured message. Restore unrelated files that were silently corrupted by merge auto-resolution: Platform ServiceProvider.ReplaceService and ServerTelemetry registration, .github workflow lock files and copilot-instructions, and SoftAssertionTests.
…, equalize DoesNotReturn, restore notExpected evidence - Drop '(case-sensitive)' from AreEqualStringsFailedSummary since the generic AreEqual<T> path also accepts case-insensitive comparers. - Add [DoesNotReturn] to ReportAssertIsTrueFailed and ReportAssertIsNullFailed for symmetry with their IsFalse/IsNotNull siblings. - Restore notExpected: line in AreNotEqual evidence so failures with custom comparers retain diagnostic info.
…ured-messages-equality # Conflicts: # src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs # src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs # src/TestFramework/TestFramework/Resources/FrameworkMessages.resx # test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs # test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs # test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ScopeTests.cs
…pe.FullName, L1+L3 regression tests)
…al full-message test, N4 cache GetType, N5 hoist line break chars, N6 consolidate string branch)
There was a problem hiding this comment.
Pull request overview
Rolls RFC 012 structured assertion messages forward by migrating Assert.AreEqual / Assert.AreNotEqual failures to the structured-message format (summary + optional user message + aligned evidence block + call-site expression), updating tests and localized resources accordingly.
Changes:
- Updated
AreEqual/AreNotEqualfailure reporting to useStructuredAssertionMessage, including evidence blocks and call-site formatting (with multiline-expression placeholders). - Rewrote/added unit tests to assert full structured messages and expected/actual metadata propagation.
- Added new localized resource keys for the new summary lines and updated the RFC text examples.
Show a summary per file
| File | Description |
|---|---|
| test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ScopeTests.cs | Updates scope soft-assert expectations to the new structured AreEqual message format. |
| test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs | Rewrites AreEqual/AreNotEqual failure tests to assert full structured messages; adds regressions for metadata + multiline placeholders. |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf | Adds new structured-message summary keys (new state). |
| src/TestFramework/TestFramework/Resources/FrameworkMessages.resx | Introduces new summary strings for AreEqual / AreNotEqual structured messages. |
| src/TestFramework/TestFramework/Assertions/Assert.cs | Adds a binary call-site formatting helper supporting multiline placeholders. |
| src/TestFramework/TestFramework/Assertions/Assert.AreEqual.String.cs | Routes string AreNotEqual failures through the new structured message path. |
| src/TestFramework/TestFramework/Assertions/Assert.AreEqual.InterpolatedStringHandlers.cs | Updates interpolated-string handler failure paths to pass caller expressions into the new structured reporting. |
| src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs | Implements structured-message formatting for AreEqual/AreNotEqual (generic/object paths), including evidence blocks and expected/actual metadata. |
| docs/RFCs/012-Structured-Assertion-Messages.md | Updates RFC examples/text to remove the “(case-sensitive)” suffix for string equality summaries. |
Copilot's findings
- Files reviewed: 21/21 changed files
- Comments generated: 3
Evangelink
left a comment
There was a problem hiding this comment.
Review Summary — RFC 012: AreEqual / AreNotEqual structured messages
The overall approach is sound. The EvidenceBlock builder, FormatBinaryCallSiteExpression, multiline-expression placeholder, and ExpectedText/ActualText/Data["assert.*"] population all look correct. The test rewrites faithfully capture the new format.
Four findings worth addressing:
| # | Severity | File | Summary |
|---|---|---|---|
| 1 | Medium | Assert.AreEqual.String.cs |
Culture/ignoreCase overloads NOT migrated — still produce old "Assert.AreEqual failed." format, creating a visible format split within the same API family. At minimum needs a comment or RFC follow-up reference. |
| 2 | Low–Medium | Assert.AreEqual.InterpolatedStringHandlers.cs |
Delta (float/double/decimal/long) AreNotEqual paths still use BuildUserMessageForNotExpectedExpressionAndActualExpression (old format) while non-delta uses structured — inconsistency within the same struct. |
| 3 | Low | Assert.AreEqual.cs line 264 |
diffIndex is computed but silently dropped for different-length strings; RFC doc example shows it should appear. Either update the doc or include the index in the message. |
| 4 | Low | AssertTests.AreEqualTests.cs |
AreEqualLongStringsShowsFullStrings acknowledges removed truncation but cites no tracking reference. Long-string truncation is a genuine usability regression; a linked issue would prevent it from going stale. |
No blocking correctness bugs found. The public API additions (ExpectedText, ActualText, resx keys, FormatBinaryCallSiteExpression) follow existing conventions. XLF files were auto-generated (not hand-edited). The [DoesNotReturn] annotation is preserved on both ReportAssert*Failed methods.
Generated by Expert Code Review (on open) for issue #8231 · ● 18.3M
…ession; clarify RFC case-sensitivity note
|
@copilot resolve the merge conflicts in this pull request |
Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
Done. I merged |
# Conflicts: # test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ScopeTests.cs
…dings; extract AreEqual(1,2) test message into shared const; update OutputTests for structured AreEqual message Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@copilot resolve the merge conflicts in this pull request |
Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
Resolved. I merged |
|
@copilot resolve the merge conflicts in this pull request |
Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
Resolved. I merged |
Summary
Continues the rollout of RFC 012 — Structured Assertion Messages. Following the recently merged migration of
IsTrue/IsFalse/IsNull/IsNotNull(#8187), this PR migrates theAssert.AreEqual/Assert.AreNotEqualfamily to the structured-message format.Output format
AreEqual(generic / object):AreEqual(different types — type info now on its own evidence line):AreEqual(string — keeps the existing length/diff-index summary line):AreNotEqual:AssertFailedException.ExpectedText/ActualTextandException.Data["assert.expected"]/["assert.actual"]are populated. ForAreNotEqual,ExpectedTextis prefixed withnot(e.g."not 0") so the property accurately conveys the failed expectation.Notable design points
FormatBinaryCallSiteExpression(method, expr1, paramName1, expr2, paramName2)helper onAssert. When an argument expression contains line breaks (multi-line raw-string literal), it is replaced with a<paramName>placeholder so the call-site line stays single-line:Assert.AreEqual(<expected>, "different").EvidenceBlockbuilder padding handles the variable-width labels uniformly acrossexpected:/actual:/expected type:/actual type:/notExpected:.AreEqualFailedSummary,AreEqualDifferentTypesFailedSummary,AreEqualStringsFailedSummary,AreNotEqualFailedSummary..xlffiles were regenerated viaUpdateXlf; no manual edits.(case-sensitive)suffix from string-equal summaries (5 occurrences indocs/RFCs/012-Structured-Assertion-Messages.md) since the same format applies to ordinal-ignore-case overloads as well.Tests
test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs:AreEqual/AreNotEqualfailure tests rewritten to assert the full structured message with.Should().Be("""…""")(raw strings, noEnvironment.NewLine, no.Contain).AreNotEqual_PopulatesExpectedAndActualTextWithNotPrefix,AreNotEqual_FailsWithStructuredMessage,AreEqual_MultilineExpectedExpression_UsesPlaceholderInCallSite,AreEqual_MultilineActualExpression_UsesPlaceholderInCallSite,AreNotEqual_MultilineNotExpectedExpression_UsesPlaceholderInCallSite.AreEqualLongStringsShouldTruncateAndShowContext→AreEqualLongStringsShowsFullStringsto reflect actual behavior (truncation/preview is deferred to a follow-up PR).test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ScopeTests.cs:AreEqual-based scope soft-failure tests updated to the new structured message and switched from.Contain(...)to full.Be("""…""")equality.Build.cmdclean (0 warnings, 0 errors);TestFramework.UnitTests870/870 passing on net9.0.Out of scope (deferred to follow-up PRs)
Assert.AreEqual(string?, string?, bool ignoreCase, ...)still uses the legacy format.AreNotEqualinterpolated-string handlers (float/double/decimal/longwithdelta) still call the legacyBuildUserMessageForNotExpectedExpressionAndActualExpressionpath.StringPreviewHelper-style caret rendering).Reviewer iterations
Three rounds of expert-reviewer feedback have been applied (commits
62c39f5fa,1b7bc4a3f,ae0742c9d,46a001652):AreNotEqualwas setting equalExpectedText/ActualText; now prefixed withnot.<paramName>placeholder instead of dropping the entire call-site line.IsNullOrEmpty(vsIsNullOrWhiteSpace);Type.FullNameis treated as nullable with?? type.Namefallback.ExpectedText/ActualTextpropagation and full-message rendering forAreNotEqual+ multi-line placeholder substitution.GetType(), hoisted line-break char array to a static readonly, consolidated the duplicated builder block in the string branch.