Skip to content

Add structured assertion message infrastructure (RFC 012)#8170

Merged
Evangelink merged 5 commits into
mainfrom
dev/amauryleve/structured-assertion-messages-infrastructure
May 13, 2026
Merged

Add structured assertion message infrastructure (RFC 012)#8170
Evangelink merged 5 commits into
mainfrom
dev/amauryleve/structured-assertion-messages-infrastructure

Conversation

@Evangelink
Copy link
Copy Markdown
Member

Summary

Introduces the core building blocks for structured assertion messages as described in RFC 012. This is the first in a series of PRs that will migrate all Assert.* methods to the new multi-line format.

Changes

New types

  • EvidenceBlock — represents a labeled evidence section (e.g. expected: 42 / actual: 37) with automatic column-alignment of labels.
  • StructuredAssertionMessage — builder that composes the full multi-line message: assertion prefix, summary, optional user message, evidence blocks, and call-site expression.
  • AssertionValueRenderer — renders values for evidence blocks following the RFC's value rendering rules (strings quoted, nulls unquoted, collections in [...] notation, control characters escaped).

Updated types

  • AssertFailedException — added ExpectedText and ActualText properties to carry structured expected/actual values for IDE integration.
  • Assert — added ReportAssertFailed and ThrowAssertFailed overloads that accept StructuredAssertionMessage.

Tests

  • StructuredAssertionMessageTests — verifies message layout, blank-line separators, multi-block formatting, and edge cases.
  • EvidenceBlockTests — verifies label alignment, multi-line values, and single-line formatting.
  • AssertionValueRendererTests — verifies rendering of nulls, strings, numbers, booleans, collections, and objects.
  • AssertFailedExceptionTests — verifies the new ExpectedText/ActualText properties.

Public API

New public API entries added to PublicAPI.Unshipped.txt.

Follow-up PRs

  1. This PR — Infrastructure
  2. Boolean & Null assertions (IsTrue, IsFalse, IsNull, IsNotNull)
  3. Equality assertions (AreEqual, AreNotEqual)
  4. Reference & Type assertions (AreSame, AreNotSame, IsInstanceOfType, etc.)
  5. Exception assertions (Throws, ThrowsExactly, ThrowsAsync, etc.)
  6. String assertions (Contains, StartsWith, EndsWith, MatchesRegex, etc.)
  7. Collection assertions
  8. Comparison + Fail / Inconclusive

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.
Copilot AI review requested due to automatic review settings May 13, 2026 09:23
@Evangelink Evangelink marked this pull request as ready for review May 13, 2026 09:25
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces the initial infrastructure to support RFC 012 structured assertion failure messages in the MSTest TestFramework, including message composition, evidence block formatting, and value rendering, along with plumbing for carrying structured expected/actual values on AssertFailedException.

Changes:

  • Added internal structured message builder and evidence formatting types (StructuredAssertionMessage, EvidenceBlock, EvidenceLine) plus a value renderer (AssertionValueRenderer).
  • Extended AssertFailedException with ExpectedText/ActualText and added structured-message overloads in Assert to populate them.
  • Added unit tests covering message layout, evidence alignment, and basic value rendering.
Show a summary per file
File Description
test/UnitTests/TestFramework.UnitTests/Assertions/StructuredAssertionMessageTests.cs New tests validating multi-line structured message layout rules.
test/UnitTests/TestFramework.UnitTests/Assertions/EvidenceBlockTests.cs New tests validating evidence label alignment and formatting.
test/UnitTests/TestFramework.UnitTests/Assertions/AssertionValueRendererTests.cs New tests validating value rendering rules (null/strings/bools/chars/collections/etc.).
test/UnitTests/TestFramework.UnitTests/Assertions/AssertFailedExceptionTests.cs New tests validating the new ExpectedText/ActualText properties.
src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt Declares the new public API getters for ExpectedText/ActualText.
src/TestFramework/TestFramework/Exceptions/AssertFailedException.cs Adds ExpectedText/ActualText properties to the exception type.
src/TestFramework/TestFramework/Assertions/StructuredAssertionMessage.cs Implements the structured message builder and formatter.
src/TestFramework/TestFramework/Assertions/EvidenceLine.cs Adds a line-level evidence representation (label/value).
src/TestFramework/TestFramework/Assertions/EvidenceBlock.cs Implements evidence block formatting with per-block label alignment.
src/TestFramework/TestFramework/Assertions/AssertionValueRenderer.cs Implements RFC-aligned rendering for strings/control chars/collections/etc.
src/TestFramework/TestFramework/Assertions/Assert.cs Adds structured-message overloads and wires them to AssertFailedException.

Copilot's findings

  • Files reviewed: 11/11 changed files
  • Comments generated: 4

Comment thread test/UnitTests/TestFramework.UnitTests/Assertions/AssertionValueRendererTests.cs Outdated
Comment thread src/TestFramework/TestFramework/Assertions/Assert.cs Outdated
Comment thread src/TestFramework/TestFramework/Assertions/Assert.cs
Comment thread src/TestFramework/TestFramework/Assertions/EvidenceBlock.cs Outdated
Copilot AI review requested due to automatic review settings May 13, 2026 09:48
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot's findings

Comments suppressed due to low confidence (1)

src/TestFramework/TestFramework/Assertions/EvidenceBlock.cs:63

  • RFC 012 specifies that multi-line evidence values should be formatted with the label on its own line (e.g. expected:) and the value starting on the next line, flush-left. EvidenceBlock.Format currently always renders label + ' ' + value, which will produce malformed output when value contains newlines and doesn't meet the RFC/PR description ("multi-line values"). Please update the formatting logic (and add a test) to handle multi-line values explicitly.
    /// <summary>
    /// Formats the evidence block as aligned label: value lines.
    /// Labels are right-padded so all values start at the same column.
    /// </summary>
    internal string Format()
    {
        if (_lines.Count == 0)
        {
            return string.Empty;
        }

        int maxLabelLength = 0;
        foreach (EvidenceLine line in _lines)
        {
            if (line.Label.Length > maxLabelLength)
            {
                maxLabelLength = line.Label.Length;
            }
        }

        StringBuilder sb = new();
        for (int i = 0; i < _lines.Count; i++)
        {
            if (i > 0)
            {
                sb.Append(Environment.NewLine);
            }

            EvidenceLine line = _lines[i];

            // Pad label (which includes trailing colon) to align values, then append a space and value
            sb.Append(line.Label.PadRight(maxLabelLength));
            sb.Append(' ');
            sb.Append(line.Value);
        }
  • Files reviewed: 11/11 changed files
  • Comments generated: 4

Comment thread src/TestFramework/TestFramework/Assertions/EvidenceBlock.cs Outdated
Comment thread src/TestFramework/TestFramework/Assertions/AssertionValueRenderer.cs Outdated
Comment thread src/TestFramework/TestFramework/Assertions/Assert.cs
…ocks, remove IDE0046 pragma, add Data tests

- Changed EvidenceBlock from readonly struct to sealed class to avoid NullReferenceException on default instances
- Changed StructuredAssertionMessage to support multiple evidence blocks (List<EvidenceBlock>) instead of replacing a single block
- Removed file-level IDE0046 pragma from AssertionValueRenderer and converted RenderValue to a switch expression
- Added tests verifying exception.Data["assert.expected"] and exception.Data["assert.actual"] are populated by ThrowAssertFailed(StructuredAssertionMessage)
- Added test for multiple evidence blocks formatting
@Evangelink Evangelink merged commit 4ccccb6 into main May 13, 2026
31 checks passed
@Evangelink Evangelink deleted the dev/amauryleve/structured-assertion-messages-infrastructure branch May 13, 2026 12:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants