Skip to content

Restore bounded string difference indicators#10145

Merged
Evangelink merged 4 commits into
mainfrom
dev/amauryleve/review-visual-indicator-regression
Jul 23, 2026
Merged

Restore bounded string difference indicators#10145
Evangelink merged 4 commits into
mainfrom
dev/amauryleve/review-visual-indicator-regression

Conversation

@Evangelink

@Evangelink Evangelink commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

  • restore a visual locator for Assert.AreEqual string failures
  • use bounded expected near: / actual near: previews for long strings while retaining full expected and actual values
  • render a caret when columns are reliable and [[...]] markers otherwise
  • report Unicode scalar values for non-ASCII and multi-code-point mismatches
  • preserve full structured ExpectedText, ActualText, exception data, and MTP protocol fields
  • suppress raw-derived locator diagnostics when a custom string formatter changes the rendered values
  • amend RFC 012 with the rendering contract and rationale

Examples

Short strings use the full values directly:

expected:   "aaXa"
actual:     "aaba"
difference: ---^

Long strings receive bounded context before the unchanged full evidence:

expected near: "...field12  1234.56  field13..."
actual near:   "...field12  1234.57  field13..."
difference:    -----------------------^

expected: "<full expected value>"
actual:   "<full actual value>"

When a Unicode or grapheme prefix makes display-column alignment unreliable, the complete mismatching text elements are marked inline:

expected near: "...aaaaaaaa🌍[[X]]zzzzzzzz..."
actual near:   "...aaaaaaaa🌍[[Y]]zzzzzzzz..."
difference:    mismatch marked with [[...]]

Non-ASCII differences also include code-point diagnostics:

expected:    "Hello 🌍 World"
actual:      "Hello 🌎 World"
difference:  -------^
code points: expected U+1F30D; actual U+1F30E

Servicing assessment

This fixes the loss of the visual string-difference locator during the RFC 012 migration in MSTest 4.3. It changes no public API or passing-test behavior, preserves the existing structured expected/actual fields, and only adds diagnostics when an assertion has already failed. The affected files on rel/4.3 are identical to this PR's base, so the commits are suitable for a clean backport to the 4.3 servicing branch after validation on main.

Validation

  • focused string-difference and renderer regression tests
  • all 1,473 TestFramework net8 tests
  • relevant builds across all target frameworks
  • full repository build and 36 test configurations

Fixes #10045

Add grapheme-safe contextual previews, caret and inline mismatch markers, and Unicode code-point diagnostics while preserving full structured assertion values. Amend RFC 012 and cover short, long, escaped, culture-aware, and Unicode comparisons.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: ba230828-c09c-4eb1-a388-1cf5d2f2e2b6
Copilot AI review requested due to automatic review settings July 22, 2026 16:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Restores visual string-difference diagnostics for Assert.AreEqual.

Changes:

  • Adds bounded previews, carets, inline markers, and Unicode diagnostics.
  • Preserves full structured expected/actual values.
  • Expands tests and RFC documentation.
Show a summary per file
File Description
Assert.StringDifference.cs Implements difference rendering.
Assert.AreEqual.String.cs Integrates diagnostics into string overloads.
Assert.AreEqual.cs Integrates diagnostics into generic equality.
AssertTests.AreEqualTests.cs Adds focused diagnostic tests.
012-Structured-Assertion-Messages.md Documents the rendering contract.

Review details

Comments suppressed due to low confidence (1)

src/TestFramework/TestFramework/Assertions/Assert.StringDifference.cs:104

  • HasFormatters is global to the async flow, not specific to strings. Registering an unrelated formatter such as AddValueFormatter<DateTime> therefore changes a built-in string failure from the caret form to inline fragments, even though unrelated types are required to fall through unchanged (AssertTests.AddValueFormatter.cs:72-79). Base this decision on whether these string values were actually custom-rendered rather than on the presence of any formatter.
            bool useCaret =
                !AssertionValueFormatterRegistry.HasFormatters
                && !expectedWindow.MismatchRequiresPlaceholder
  • Files reviewed: 5/5 changed files
  • Comments generated: 4
  • Review effort level: Medium

Comment thread src/TestFramework/TestFramework/Assertions/Assert.StringDifference.cs Outdated
Comment thread docs/RFCs/012-Structured-Assertion-Messages.md Outdated
Comment thread docs/RFCs/012-Structured-Assertion-Messages.md Outdated
@github-actions

This comment has been minimized.

@Evangelink Evangelink added the state/needs-review Awaiting review from the team. label Jul 22, 2026
Honor custom string formatters without leaking raw values, keep unrelated formatters from changing diagnostics, correct RFC examples and source encoding, and strengthen string-difference tests with exact full-message assertions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: ba230828-c09c-4eb1-a388-1cf5d2f2e2b6
Copilot AI review requested due to automatic review settings July 22, 2026 21:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 7/7 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment thread src/TestFramework/TestFramework/Assertions/AssertionValueRenderer.cs Outdated
Comment thread docs/RFCs/012-Structured-Assertion-Messages.md Outdated
@github-actions

This comment has been minimized.

@Evangelink
Evangelink enabled auto-merge (squash) July 23, 2026 07:48
Compare rendered strings against the built-in escaped representation without allocating another full copy, document oversized text-element placeholders, and cover escape matching plus zero-allocation behavior.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: ba230828-c09c-4eb1-a388-1cf5d2f2e2b6
Lock the bounded [[<text element>]] fallback and code-point summary with an exact full-message regression test.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: ba230828-c09c-4eb1-a388-1cf5d2f2e2b6
auto-merge was automatically disabled July 23, 2026 08:04

Head branch was modified

@Evangelink
Evangelink enabled auto-merge (squash) July 23, 2026 08:06
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #10145

GradeTestNotes
B (80–89) new AssertTests.
AreEqualHundredThousand
CharacterStringsHaveBounded
MiddlePreview
Delegates entirely to a private helper; the test body itself carries no assertions — consider an inline minimal assertion so intent is self-contained.
B (80–89) new AssertTests.
AreEqualHundredThousand
CharacterStringsHaveBounded
NearEndPreview
Same as above: one-liner wrapper; all assertions live in the shared helper — worth a brief inline comment explaining the variant scenario.
A (90–100) mod AssertTests.
AreEqual_
WithEnglishCultureAnd
DoesNotIgnoreCase_
Throws
Full exact-message assertion now covers the new difference: and culture: lines. No issues found.
A (90–100) mod AssertTests.
AreEqualShorterExpected
String
Exact message upgraded with difference: and code points:; strong full-message pin. No issues found.
A (90–100) mod AssertTests.
AreEqualStringDifference
AtBeginning
Exact-message assertion gains difference: line; clear and focused. No issues found.
A (90–100) mod AssertTests.
AreEqualStringDifference
AtEnd
Same pattern as AtBeginning — exact message with new difference: line. No issues found.
A (90–100) mod AssertTests.
AreEqualStringSpecific
WithIgnoreCaseAndCulture
UsesComparisonAwareDiff
Index
Adds difference: and code points: lines to an already strong exact-message test. No issues found.
A (90–100) mod AssertTests.
AreEqualStringWithDifferent
Length
Full exact-message now includes difference: and code points:; thorough coverage of new diagnostic lines. No issues found.
A (90–100) mod AssertTests.
AreEqualStringWithEmojis
Adds difference: and code points: to exact-message assertion; emoji boundary handling well-covered. No issues found.
A (90–100) mod AssertTests.
AreEqualStringWithSpecial
CharactersShouldEscape
Upgraded from weak partial Contain checks to a full exact-message Be assertion. Significant quality improvement.
A (90–100) mod AssertTests.
AreEqualStringWith
UserMessage
Adds difference: line to exact-message; clean and focused. No issues found.
A (90–100) new AssertTests.
AreEqualGenericAnd
StringSpecificOverloads
HaveMatchingDiagnostics
Verifies message, ExpectedText, and ActualText equality across both overloads; meaningful cross-overload contract test. No issues found.
A (90–100) new AssertTests.
AreEqualLongStrings
ShowsFullStrings
Full message plus ExpectedText, ActualText, and Data entries verified; strong multi-assertion coverage. No issues found.
A (90–100) new AssertTests.
AreEqualStringDifference
ComposedAndDecomposed
TextUsesScalarDiagnostics
Exact full-message assertion covering composed vs. decomposed Unicode edge case. No issues found.
A (90–100) new AssertTests.
AreEqualStringDifference
EmojiWithAsciiPrefix
UsesCaretAndCodePoints
Exact full-message assertion for emoji boundary; clear AAA, focused scenario. No issues found.
A (90–100) new AssertTests.
AreEqualStringDifference
EscapesMapToRendered
Columns
Eight-case inline data-driven loop with full-message assertion per case; excellent escape-sequence coverage.
A (90–100) new AssertTests.
AreEqualStringDifference
InMiddle
Exact-message assertion, single focused scenario. No issues found.
A (90–100) new AssertTests.
AreEqualStringDifference
OversizedTextElements
UsePlaceholder
Full exact-message with const code-point strings; locks oversized text-element fallback precisely. No issues found.
A (90–100) new AssertTests.
AreEqualStringDifference
UnpairedSurrogates
AreDeterministic
Full message plus ExpectedText and ActualText; deterministic surrogate-pair edge case well-pinned. No issues found.
A (90–100) new AssertTests.
AreEqualStringDifference
UnsafeUnicodePrefixa
UseInlineMarkers
Six-case data-driven loop covering varied multi-codepoint prefixes; full exact-message per case. No issues found.
A (90–100) new AssertTests.
AreEqualStringDifference
WithRedactingFormatter
SuppressesRawLocator
Diagnostics
Verifies message, ExpectedText, ActualText, and Data under a custom formatter scope; thorough side-effect coverage. No issues found.
A (90–100) new AssertTests.
AreEqualStringDifference
WithUnrelatedFormatter
PreservesCaret
Confirms unrelated formatter does not pollute diff diagnostics; full exact-message assertion. No issues found.
A (90–100) new AssertTests.
AreEqualStringSpecific
CultureComparison
PreservesUnequal
DifferenceCursors
Full exact-message covering culture + ignore-case + inline-marker combination. No issues found.
A (90–100) new AssertTests.
AreEqualStringWith
CultureShouldUse
EnhancedMessage
Uses string.Join to build expected message cleanly; exact Be assertion. No issues found.
A (90–100) new AssertionValueRendererTests.
IsBuiltInStringRendering_
LongStringDoesNotAllocate
Zero-allocation guard via GC.GetAllocatedBytesForCurrentThread; guarded by #if NET8_0_OR_GREATER. No issues found.
A (90–100) new AssertionValueRendererTests.
IsBuiltInStringRendering_
MatchesAllEscapes
Three meaningful boolean assertions covering match, non-match, and redacted paths. No issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 186.2 AIC · ⌖ 7.34 AIC · ⊞ 8.9K · [◷]( · )

Copilot AI review requested due to automatic review settings July 23, 2026 08:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@Evangelink
Evangelink merged commit 595fbc1 into main Jul 23, 2026
32 checks passed
@Evangelink
Evangelink deleted the dev/amauryleve/review-visual-indicator-regression branch July 23, 2026 09:07
@Evangelink

Copy link
Copy Markdown
Member Author

/backport to rel/4.3

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to rel/4.3: https://github.com/microsoft/testfx/actions/runs/30012155771

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --3way --empty=keep --ignore-whitespace --keep-non-patch changes.patch

Patch format detection failed.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

1 similar comment
@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

15 similar comments
@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

@Evangelink backporting to rel/4.3 failed, the patch most likely resulted in conflicts:

$ git am --continue

fatal: Resolve operation not in progress, we are not resuming.
Error: The process '/usr/bin/git' failed with exit code 128

NOTE: A PR will be created, but needs to be revised manually!

@github-actions

Copy link
Copy Markdown
Contributor

Potential infinite loop guard hit. Stopping

Evangelink added a commit that referenced this pull request Jul 23, 2026
…(backport to rel/4.3) (#10172)

Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
Evangelink added a commit that referenced this pull request Jul 24, 2026
Copilot-Session: f16544a8-1d0c-4cbc-810a-dd089e86a498
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-review Awaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MSTest 4.3.2 visual indicator regression

3 participants