Skip to content

[mono-api-html] Fix placeholder leak and add a test project - #26405

Merged
rolfbjarne merged 3 commits into
mainfrom
dev/rolf/literate-memory
Aug 6, 2026
Merged

[mono-api-html] Fix placeholder leak and add a test project#26405
rolfbjarne merged 3 commits into
mainfrom
dev/rolf/literate-memory

Conversation

@rolfbjarne

Copy link
Copy Markdown
Member

We've had a few regressions in mono-api-html, so this fixes the latest one and adds a unit test project to guard against future ones.

The bug

The MultiplexedFormatter builds descriptions using the placeholder tokens %LESSERTHANREPLACEMENT% / %GREATERTHANREPLACEMENT%, because a single shared description string must be converted into each sub-formatter's own representation (&lt;/&gt; for HTML, </> for markdown). The structured methods already convert these via Replace (), but the raw Write/WriteLine string overloads did not.

The "New Type" addition path (ClassComparer.AddedInner) writes generic interface lists such as IEnumerable<AuthorizationRight> directly through Output.Write, so the placeholders leaked verbatim into the generated HTML (and markdown), e.g.:

IEnumerable%LESSERTHANREPLACEMENT%AuthorizationRight%GREATERTHANREPLACEMENT%

Applying Replace () in the six string-based Write/WriteLine overloads converts the placeholders per sub-formatter on this path too.

Tests & solution

  • Added tools/api-tools/mono-api-html-tests, an NUnit test project that references mono-api-html. The first test drives the public ApiDiffFormatted.Generate over in-memory api-info XML (the exact AuthorizationRights case), producing both HTML and markdown, and asserts no *THANREPLACEMENT tokens leak and that the generic interface renders correctly in both formats. Verified the test fails without the fix.

  • Added tools/api-tools/api-tools.slnx referencing mono-api-info, mono-api-html and the new test project, so dotnet test in tools/api-tools builds all three and runs the tests.

🤖 Pull request created by Copilot

rolfbjarne and others added 2 commits August 5, 2026 16:51
…w Write path.

The MultiplexedFormatter builds descriptions using the placeholder tokens
%LESSERTHANREPLACEMENT% / %GREATERTHANREPLACEMENT% (since a single shared
description string must be converted into each sub-formatter's own
representation: &lt;/&gt; for HTML, </> for markdown). The structured
methods already convert these via Replace (), but the raw Write/WriteLine
string overloads did not.

The "New Type" addition path (ClassComparer.AddedInner) writes generic
interface lists such as IEnumerable<AuthorizationRight> directly through
Output.Write, so the placeholders leaked verbatim into the generated HTML
(and markdown).

Apply Replace () in the six string-based Write/WriteLine overloads so the
placeholders are converted per sub-formatter on this path too.

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

We've had a few regressions in mono-api-html, so add a unit test project to
guard against them.

* Add tools/api-tools/mono-api-html-tests, an NUnit test project referencing
  mono-api-html. The first test (GenericInterfacesDoNotLeakPlaceholders)
  covers the placeholder-leak regression just fixed: it runs
  ApiDiffFormatted.Generate over in-memory api-info XML, producing both HTML
  and markdown, and asserts no %*THANREPLACEMENT% tokens leak and that the
  generic interface list renders correctly in both formats.

* Add tools/api-tools/api-tools.slnx referencing mono-api-info, mono-api-html
  and the new test project, so 'dotnet test' in tools/api-tools builds all
  three and runs the tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 5, 2026 14:52
@rolfbjarne
rolfbjarne requested a review from dalexsoto as a code owner August 5, 2026 14:52

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

This PR fixes a formatting bug in mono-api-html where placeholder tokens (%LESSERTHANREPLACEMENT% / %GREATERTHANREPLACEMENT%) could leak into generated HTML/markdown when raw Write/WriteLine string overloads were used, and adds a small NUnit regression test project to prevent recurrence.

Changes:

  • Apply placeholder replacement in the string-based Write/WriteLine overloads in MultiplexedFormatter.
  • Add mono-api-html-tests (NUnit) to exercise ApiDiffFormatted.Generate and assert correct rendering of generic interfaces + no placeholder leakage.
  • Add tools/api-tools/api-tools.slnx to make it easy to run dotnet test for the api-tools set.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
tools/api-tools/mono-api-html/MultiplexedFormatter.cs Ensures placeholder replacement occurs for raw string-based write paths across sub-formatters.
tools/api-tools/mono-api-html-tests/mono-api-html-tests.csproj Introduces a new NUnit test project for mono-api-html regressions.
tools/api-tools/mono-api-html-tests/ApiDiffTests.cs Adds a regression test covering generic interface rendering and placeholder non-leakage in HTML/markdown.
tools/api-tools/api-tools.slnx Provides a solution entrypoint for building/testing the api-tools projects together.
Suppressed comments (3)

tools/api-tools/mono-api-html/MultiplexedFormatter.cs:271

  • 🤖 ⚠️ Performance — Same as the WriteLine (string format, ...) overload: string.Format is done once per formatter even though the result is identical. Cache once and reuse, only applying Replace per formatter.
		public override void Write (string format, params object [] arguments)
		{
			foreach (var formatter in formatters)
				formatter.Write (Replace (formatter, string.Format (format, arguments)));
		}

tools/api-tools/mono-api-html/MultiplexedFormatter.cs:253

  • 🤖 ⚠️ Performancesb.ToString () is called once per sub-formatter even though the string content is identical; this adds avoidable allocations. Cache sb.ToString () once and then apply Replace per formatter.
		public override void WriteLine (StringBuilder sb)
		{
			foreach (var formatter in formatters)
				formatter.WriteLine (Replace (formatter, sb.ToString ()));
		}

tools/api-tools/mono-api-html/MultiplexedFormatter.cs:277

  • 🤖 ⚠️ Performance — Same as WriteLine (StringBuilder): sb.ToString () is computed once per formatter. Compute it once outside the loop, then Replace per formatter.
		public override void Write (StringBuilder sb)
		{
			foreach (var formatter in formatters)
				formatter.Write (Replace (formatter, sb.ToString ()));
		}

Comment thread tools/api-tools/mono-api-html/MultiplexedFormatter.cs
Comment thread tools/api-tools/mono-api-html-tests/ApiDiffTests.cs Outdated
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

* MultiplexedFormatter: compute the shared string.Format (...) / sb.ToString ()
  result once outside the loop instead of once per sub-formatter, then apply
  Replace () per formatter.

* Tests: assert against the exact placeholder tokens (%LESSERTHANREPLACEMENT% /
  %GREATERTHANREPLACEMENT%) instead of the imprecise "THANREPLACEMENT"
  substring.

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

Copy link
Copy Markdown
Collaborator

✅ API diff for current PR / commit

NET (empty diffs)

✅ API diff vs stable

NET (empty diffs)

ℹ️ Generator diff

Generator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes)

Pipeline on Agent
Hash: 73703cb9165f90eba5702222cd67b12557523e19 [PR build]

@vs-mobiletools-engineering-service2

This comment has been minimized.

@rolfbjarne
rolfbjarne enabled auto-merge (squash) August 5, 2026 20:24
@rolfbjarne rolfbjarne added the ready-to-review This PR is ready to review/merge. label Aug 5, 2026
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

🚀 [CI Build #73703cb] Test results 🚀

Test results

✅ All tests passed on VSTS: test results.

🎉 All 203 tests passed 🎉

Tests counts

✅ assembly-processing: All 1 tests passed. Html Report (VSDrops) Download
✅ cecil: All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (iOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (MacCatalyst): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (macOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (Multiple platforms): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (tvOS): All 1 tests passed. Html Report (VSDrops) Download
✅ framework: All 2 tests passed. Html Report (VSDrops) Download
✅ fsharp: All 4 tests passed. Html Report (VSDrops) Download
✅ generator: All 5 tests passed. Html Report (VSDrops) Download
✅ interdependent-binding-projects: All 4 tests passed. Html Report (VSDrops) Download
✅ introspection: All 4 tests passed. Html Report (VSDrops) Download
✅ linker (iOS): All 15 tests passed. Html Report (VSDrops) Download
✅ linker (MacCatalyst): All 15 tests passed. Html Report (VSDrops) Download
✅ linker (macOS): All 21 tests passed. Html Report (VSDrops) Download
✅ linker (tvOS): All 15 tests passed. Html Report (VSDrops) Download
✅ monotouch (iOS): All 19 tests passed. [attempt 3] Html Report (VSDrops) Download
✅ monotouch (MacCatalyst): All 18 tests passed. Html Report (VSDrops) Download
✅ monotouch (macOS): All 19 tests passed. Html Report (VSDrops) Download
✅ monotouch (tvOS): All 19 tests passed. Html Report (VSDrops) Download
✅ msbuild: All 2 tests passed. Html Report (VSDrops) Download
✅ sharpie: All 1 tests passed. Html Report (VSDrops) Download
✅ windows: All 3 tests passed. Html Report (VSDrops) Download
✅ xcframework: All 4 tests passed. Html Report (VSDrops) Download
✅ xtro: All 1 tests passed. Html Report (VSDrops) Download

macOS tests

✅ Tests on macOS Monterey (12): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Ventura (13): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Sonoma (14): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Sequoia (15): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Tahoe (26): All 5 tests passed. Html Report (VSDrops) Download

Linux Build Verification

Linux build succeeded

Pipeline on Agent
Hash: 73703cb9165f90eba5702222cd67b12557523e19 [PR build]

@rolfbjarne
rolfbjarne merged commit e4325ae into main Aug 6, 2026
56 checks passed
@rolfbjarne
rolfbjarne deleted the dev/rolf/literate-memory branch August 6, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

copilot ready-to-review This PR is ready to review/merge.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants