Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer - #9776

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives
Jul 9, 2026
Merged

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer#9776
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#9765.

BaseSerializer.cs previously implemented every read/write primitive twice — once under #if NETCOREAPP (Span<byte> / ArrayPool<byte> / ReadExactly) and once under #else (heap byte[] + stream.Read). The two branches were structurally identical, so adding or fixing a primitive meant editing both.

This collapses the 12 duplicated primitives into a single byte[]-based implementation. The only remaining conditional is a small private ReadExactly helper:

  • On NETCOREAPP it delegates to the framework Stream.ReadExactly.
  • Otherwise it loops until the requested byte count is read, throwing EndOfStreamException on premature EOF.

Why byte[] instead of Span everywhere

Microsoft.Testing.Platform intentionally does not reference System.Memory (dropped in #4652), and this file is shared as source into several other projects. Span/ArrayPool are therefore unavailable on the netstandard2.0 target, so the unified path stays byte[]-based.

Bug fix included

The old #else branch used single stream.Read(...) calls that ignored short reads, which could silently return fewer bytes than requested and corrupt data on the .NET Framework / netstandard2.0 path. Routing all reads through ReadExactly fixes this.

Tests

Added BaseSerializerPartialReadTests, which deserialize through a stream that returns one byte per Read (forcing every length prefix / field id / string payload through the ReadExactly loop) and through a truncated stream (asserting EndOfStreamException). These run on net8.0/net9.0 (framework Stream.ReadExactly path) and net462 (the hand-written loop).

InternalAPI baseline changes (to unblock CI — not part of the refactor)

⚠️ These InternalAPI.Unshipped.txt edits are not part of the BaseSerializer refactor. They declare internal symbols that recently landed on main (via #9752 InternalAPI tracking + #9774 DotnetTest serializer dedup) but were left undeclared, so RS0051 currently fails on main and is inherited by every PR. They are included here only to keep this PR's CI green:

  • BaseSerializer.ReadFields / WriteListPayload — declared in Platform, Extensions.HangDump, Extensions.MSBuild, Extensions.Retry, Extensions.TrxReport (each compiles BaseSerializer.cs as shared source).
  • PlatformServicesConfigurationAdapter — declared in MSTest.TestAdapter. This one is unrelated to this change and was pulled in via the merge with main; happy to split it (and the other baseline updates) into a dedicated PR if preferred.

Validation

  • Full solution build passes with 0 code errors across net8.0, net9.0, and netstandard2.0 (all previously-failing RS0051 errors resolved).
  • No behavioral public/protected member changes to BaseSerializer (bodies only); the API-baseline files above are updated purely to declare pre-existing-on-main symbols.

CopilotAI review requested due to automatic review settings July 9, 2026 09:28

CopilotAI 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

Refactors BaseSerializer in Microsoft.Testing.Platform’s IPC serializers to remove duplicated #if NETCOREAPP / #else primitive read/write implementations, consolidating onto a single byte[]-based path and centralizing exact-read behavior to avoid short-read corruption on non-NETCOREAPP targets.

Changes:

  • Removed duplicated NETCOREAPP vs non-NETCOREAPP implementations for serializer primitives, keeping one byte[] implementation.
  • Introduced a private ReadExactly helper that uses Stream.ReadExactly on NETCOREAPP and a looped read on other TFMs.
  • Fixed historical short-read behavior by routing primitive reads through ReadExactly.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.csCollapses duplicated primitive serialization logic and centralizes exact-read behavior to avoid short-read corruption.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Notes on the review:

  • Algorithmic Correctness: ReadExactly loop is correct — properly handles count = 0, short reads, and EOF.
  • IPC Wire Compatibility: Wire format unchanged — same length-prefix encoding for strings, same byte ordering for primitives.
  • Cross-TFM: Stream.ReadExactly(byte[], int, int) correctly guarded behind #if NETCOREAPP (available .NET 7+); fallback loop uses standard Stream.Read.
  • Performance: The removal of stackalloc/ArrayPool on NETCOREAPP in favor of byte[] is a deliberate trade-off for maintainability (no System.Memory reference, shared source). For fixed-size primitives (4–8 bytes) the GC cost is negligible; for string buffers this is bounded by IPC message size.
  • Bug fix: Short-read correction on non-NETCOREAPP is sound — EndOfStreamException matches the framework's Stream.ReadExactly behavior.
  • Public API: No surface changes — internal abstract class with protected static members only.

@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
CopilotAI review requested due to automatic review settings July 9, 2026 10:19

CopilotAI 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: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Build Failure Analysis

Summary — The build fails with 6 RS0051 (undeclared API) errors because PlatformServicesConfigurationAdapter and its members are not listed in the InternalAPI/InternalAPI.Unshipped.txt file. This is a merge conflict with main, not caused by the PR's changes.

Root cause: Missing internal API declaration after main introduced InternalAPI tracking

A recent commit to main (merged between b81ed0d and 1e92d898) added InternalAPI/InternalAPI.Shipped.txt and InternalAPI/InternalAPI.Unshipped.txt as AdditionalFiles in MSTest.TestAdapter.csproj. These files instruct the Public API Analyzer to track all internal symbols visible through InternalsVisibleTo.

The existing internal sealed class PlatformServicesConfigurationAdapter (and its two members) were not added to either InternalAPI file when tracking was introduced. When this PR is merge-queued against the current main, the analyzer fires RS0051 for the 3 undeclared symbols (× 2 TFMs = 6 errors).

Affected file / errors

CodeTFMFile:LineSymbol
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:12PlatformServicesConfigurationAdapter (class)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:16.ctor(IConfiguration!)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:19this[string!].get

Proposed fix

After rebasing on latest main, add the following 3 lines to src/Adapter/MSTest.TestAdapter/InternalAPI/InternalAPI.Unshipped.txt:

 #nullable enable
+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.PlatformServicesConfigurationAdapter(Microsoft.Testing.Platform.Configurations.IConfiguration! configuration) -> void+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.this[string! key].get -> string?

Note: This fix targets a file that doesn't exist on the PR branch yet — it was introduced on main after this PR was created. Rebasing the branch onto current main will pull in the InternalAPI tracking infrastructure, at which point the 3 lines above can be appended to InternalAPI.Unshipped.txt.


Build overview
FieldValue
ResultFAILED
Duration241.1 s
MSBuild18.8.0-preview-26302-115
SolutionNonWindowsTests.slnf
Failed projectMSTest.TestAdapter.csproj (net8.0 + net9.0)
Error count7 (6 unique RS0051 + 1 "Build failed")
Warnings0
All MSBuild errors (7)
#CodeProjectFile:LineMessage (truncated)
1RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
2RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
3RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
4RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
5RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
6RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
7Build.projBuild failed.
Why this isn't caused by this PR's changes

This PR modifies only:

  • src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs (refactor)
  • test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/BaseSerializerPartialReadTests.cs (new test)

Neither file is in the MSTest.TestAdapter project. The failure is triggered by new InternalAPI tracking infrastructure on main that this PR hasn't picked up yet.


🤖 Generated by the Build Failure Analysis workflow using (a href="(dev.azure.com/redacted) · commit 4c23558

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 224.8 AIC · ⌖ 10.5 AIC · ⊞ 7.3K · [◷]( · )

…rimitives
Collapse the 12 dual-implemented read/write primitives into a single
byte[]-based implementation whose only conditional piece is a private
ReadExactly helper, which also fixes the historical short-read bug on the
non-NETCOREAPP path where a single Stream.Read could return fewer bytes
than requested and silently corrupt data.
Fixes#9765
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Existing serializer round-trip tests only use MemoryStream, which never
returns short reads, so the new centralized ReadExactly behavior was
untested. Add tests that deserialize through a one-byte-per-read stream
(round-trips correctly) and a truncated stream (throws EndOfStreamException).
These exercise the framework Stream.ReadExactly path on NETCOREAPP and the
hand-written read-until-complete loop on net462.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…esConfigurationAdapter in InternalAPI
These internal symbols landed on main (ReadFields/WriteListPayload via the
DotnetTest serializer dedup, and PlatformServicesConfigurationAdapter) after
InternalAPI tracking was enabled, but were never added to the declared API,
so RS0051 fires across every project that compiles BaseSerializer.cs as
shared source (Platform, HangDump, MSBuild, Retry, TrxReport) plus
MSTest.TestAdapter. Declaring them in the corresponding InternalAPI.Unshipped
files unblocks the build.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 9, 2026 11:25
@Evangelink
Amaury Levé (Evangelink)force-pushed the evangelink-baseserializer-dedup-primitives branch from 4c23558 to 0724c73CompareJuly 9, 2026 11:25
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9776

GradeTestNotes
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamIsTruncated_
ThrowsEndOfStreamException
Clear AAA; exact-type exception assertion plus inner-exception type check are solid — consider asserting InnerException is not null first for a cleaner failure message.
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamReturnsOneBytePerRead_
RoundTripsCorrectly
Three field-equality assertions confirm the partial-read round-trip reconstructs all serialized values correctly. No issues found.

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

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

CopilotAI 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: 1
  • Review effort level: Medium

@Evangelink
Amaury Levé (Evangelink) merged commit c42b18c into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink-baseserializer-dedup-primitives branch July 9, 2026 16:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code: BaseSerializer.cs Dual #if NETCOREAPP / #else Primitive Read/Write Implementations

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer - #9776

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives
Jul 9, 2026
Merged

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer#9776
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#9765.

BaseSerializer.cs previously implemented every read/write primitive twice — once under #if NETCOREAPP (Span<byte> / ArrayPool<byte> / ReadExactly) and once under #else (heap byte[] + stream.Read). The two branches were structurally identical, so adding or fixing a primitive meant editing both.

This collapses the 12 duplicated primitives into a single byte[]-based implementation. The only remaining conditional is a small private ReadExactly helper:

  • On NETCOREAPP it delegates to the framework Stream.ReadExactly.
  • Otherwise it loops until the requested byte count is read, throwing EndOfStreamException on premature EOF.

Why byte[] instead of Span everywhere

Microsoft.Testing.Platform intentionally does not reference System.Memory (dropped in #4652), and this file is shared as source into several other projects. Span/ArrayPool are therefore unavailable on the netstandard2.0 target, so the unified path stays byte[]-based.

Bug fix included

The old #else branch used single stream.Read(...) calls that ignored short reads, which could silently return fewer bytes than requested and corrupt data on the .NET Framework / netstandard2.0 path. Routing all reads through ReadExactly fixes this.

Tests

Added BaseSerializerPartialReadTests, which deserialize through a stream that returns one byte per Read (forcing every length prefix / field id / string payload through the ReadExactly loop) and through a truncated stream (asserting EndOfStreamException). These run on net8.0/net9.0 (framework Stream.ReadExactly path) and net462 (the hand-written loop).

InternalAPI baseline changes (to unblock CI — not part of the refactor)

⚠️ These InternalAPI.Unshipped.txt edits are not part of the BaseSerializer refactor. They declare internal symbols that recently landed on main (via #9752 InternalAPI tracking + #9774 DotnetTest serializer dedup) but were left undeclared, so RS0051 currently fails on main and is inherited by every PR. They are included here only to keep this PR's CI green:

  • BaseSerializer.ReadFields / WriteListPayload — declared in Platform, Extensions.HangDump, Extensions.MSBuild, Extensions.Retry, Extensions.TrxReport (each compiles BaseSerializer.cs as shared source).
  • PlatformServicesConfigurationAdapter — declared in MSTest.TestAdapter. This one is unrelated to this change and was pulled in via the merge with main; happy to split it (and the other baseline updates) into a dedicated PR if preferred.

Validation

  • Full solution build passes with 0 code errors across net8.0, net9.0, and netstandard2.0 (all previously-failing RS0051 errors resolved).
  • No behavioral public/protected member changes to BaseSerializer (bodies only); the API-baseline files above are updated purely to declare pre-existing-on-main symbols.

CopilotAI review requested due to automatic review settings July 9, 2026 09:28

CopilotAI 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

Refactors BaseSerializer in Microsoft.Testing.Platform’s IPC serializers to remove duplicated #if NETCOREAPP / #else primitive read/write implementations, consolidating onto a single byte[]-based path and centralizing exact-read behavior to avoid short-read corruption on non-NETCOREAPP targets.

Changes:

  • Removed duplicated NETCOREAPP vs non-NETCOREAPP implementations for serializer primitives, keeping one byte[] implementation.
  • Introduced a private ReadExactly helper that uses Stream.ReadExactly on NETCOREAPP and a looped read on other TFMs.
  • Fixed historical short-read behavior by routing primitive reads through ReadExactly.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.csCollapses duplicated primitive serialization logic and centralizes exact-read behavior to avoid short-read corruption.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Notes on the review:

  • Algorithmic Correctness: ReadExactly loop is correct — properly handles count = 0, short reads, and EOF.
  • IPC Wire Compatibility: Wire format unchanged — same length-prefix encoding for strings, same byte ordering for primitives.
  • Cross-TFM: Stream.ReadExactly(byte[], int, int) correctly guarded behind #if NETCOREAPP (available .NET 7+); fallback loop uses standard Stream.Read.
  • Performance: The removal of stackalloc/ArrayPool on NETCOREAPP in favor of byte[] is a deliberate trade-off for maintainability (no System.Memory reference, shared source). For fixed-size primitives (4–8 bytes) the GC cost is negligible; for string buffers this is bounded by IPC message size.
  • Bug fix: Short-read correction on non-NETCOREAPP is sound — EndOfStreamException matches the framework's Stream.ReadExactly behavior.
  • Public API: No surface changes — internal abstract class with protected static members only.

@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
CopilotAI review requested due to automatic review settings July 9, 2026 10:19

CopilotAI 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: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Build Failure Analysis

Summary — The build fails with 6 RS0051 (undeclared API) errors because PlatformServicesConfigurationAdapter and its members are not listed in the InternalAPI/InternalAPI.Unshipped.txt file. This is a merge conflict with main, not caused by the PR's changes.

Root cause: Missing internal API declaration after main introduced InternalAPI tracking

A recent commit to main (merged between b81ed0d and 1e92d898) added InternalAPI/InternalAPI.Shipped.txt and InternalAPI/InternalAPI.Unshipped.txt as AdditionalFiles in MSTest.TestAdapter.csproj. These files instruct the Public API Analyzer to track all internal symbols visible through InternalsVisibleTo.

The existing internal sealed class PlatformServicesConfigurationAdapter (and its two members) were not added to either InternalAPI file when tracking was introduced. When this PR is merge-queued against the current main, the analyzer fires RS0051 for the 3 undeclared symbols (× 2 TFMs = 6 errors).

Affected file / errors

CodeTFMFile:LineSymbol
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:12PlatformServicesConfigurationAdapter (class)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:16.ctor(IConfiguration!)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:19this[string!].get

Proposed fix

After rebasing on latest main, add the following 3 lines to src/Adapter/MSTest.TestAdapter/InternalAPI/InternalAPI.Unshipped.txt:

 #nullable enable
+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.PlatformServicesConfigurationAdapter(Microsoft.Testing.Platform.Configurations.IConfiguration! configuration) -> void+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.this[string! key].get -> string?

Note: This fix targets a file that doesn't exist on the PR branch yet — it was introduced on main after this PR was created. Rebasing the branch onto current main will pull in the InternalAPI tracking infrastructure, at which point the 3 lines above can be appended to InternalAPI.Unshipped.txt.


Build overview
FieldValue
ResultFAILED
Duration241.1 s
MSBuild18.8.0-preview-26302-115
SolutionNonWindowsTests.slnf
Failed projectMSTest.TestAdapter.csproj (net8.0 + net9.0)
Error count7 (6 unique RS0051 + 1 "Build failed")
Warnings0
All MSBuild errors (7)
#CodeProjectFile:LineMessage (truncated)
1RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
2RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
3RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
4RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
5RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
6RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
7Build.projBuild failed.
Why this isn't caused by this PR's changes

This PR modifies only:

  • src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs (refactor)
  • test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/BaseSerializerPartialReadTests.cs (new test)

Neither file is in the MSTest.TestAdapter project. The failure is triggered by new InternalAPI tracking infrastructure on main that this PR hasn't picked up yet.


🤖 Generated by the Build Failure Analysis workflow using (a href="(dev.azure.com/redacted) · commit 4c23558

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 224.8 AIC · ⌖ 10.5 AIC · ⊞ 7.3K · [◷]( · )

…rimitives
Collapse the 12 dual-implemented read/write primitives into a single
byte[]-based implementation whose only conditional piece is a private
ReadExactly helper, which also fixes the historical short-read bug on the
non-NETCOREAPP path where a single Stream.Read could return fewer bytes
than requested and silently corrupt data.
Fixes#9765
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Existing serializer round-trip tests only use MemoryStream, which never
returns short reads, so the new centralized ReadExactly behavior was
untested. Add tests that deserialize through a one-byte-per-read stream
(round-trips correctly) and a truncated stream (throws EndOfStreamException).
These exercise the framework Stream.ReadExactly path on NETCOREAPP and the
hand-written read-until-complete loop on net462.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…esConfigurationAdapter in InternalAPI
These internal symbols landed on main (ReadFields/WriteListPayload via the
DotnetTest serializer dedup, and PlatformServicesConfigurationAdapter) after
InternalAPI tracking was enabled, but were never added to the declared API,
so RS0051 fires across every project that compiles BaseSerializer.cs as
shared source (Platform, HangDump, MSBuild, Retry, TrxReport) plus
MSTest.TestAdapter. Declaring them in the corresponding InternalAPI.Unshipped
files unblocks the build.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 9, 2026 11:25
@Evangelink
Amaury Levé (Evangelink)force-pushed the evangelink-baseserializer-dedup-primitives branch from 4c23558 to 0724c73CompareJuly 9, 2026 11:25
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9776

GradeTestNotes
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamIsTruncated_
ThrowsEndOfStreamException
Clear AAA; exact-type exception assertion plus inner-exception type check are solid — consider asserting InnerException is not null first for a cleaner failure message.
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamReturnsOneBytePerRead_
RoundTripsCorrectly
Three field-equality assertions confirm the partial-read round-trip reconstructs all serialized values correctly. No issues found.

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

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

CopilotAI 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: 1
  • Review effort level: Medium

@Evangelink
Amaury Levé (Evangelink) merged commit c42b18c into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink-baseserializer-dedup-primitives branch July 9, 2026 16:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code: BaseSerializer.cs Dual #if NETCOREAPP / #else Primitive Read/Write Implementations

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer - #9776

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives
Jul 9, 2026
Merged

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer#9776
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#9765.

BaseSerializer.cs previously implemented every read/write primitive twice — once under #if NETCOREAPP (Span<byte> / ArrayPool<byte> / ReadExactly) and once under #else (heap byte[] + stream.Read). The two branches were structurally identical, so adding or fixing a primitive meant editing both.

This collapses the 12 duplicated primitives into a single byte[]-based implementation. The only remaining conditional is a small private ReadExactly helper:

  • On NETCOREAPP it delegates to the framework Stream.ReadExactly.
  • Otherwise it loops until the requested byte count is read, throwing EndOfStreamException on premature EOF.

Why byte[] instead of Span everywhere

Microsoft.Testing.Platform intentionally does not reference System.Memory (dropped in #4652), and this file is shared as source into several other projects. Span/ArrayPool are therefore unavailable on the netstandard2.0 target, so the unified path stays byte[]-based.

Bug fix included

The old #else branch used single stream.Read(...) calls that ignored short reads, which could silently return fewer bytes than requested and corrupt data on the .NET Framework / netstandard2.0 path. Routing all reads through ReadExactly fixes this.

Tests

Added BaseSerializerPartialReadTests, which deserialize through a stream that returns one byte per Read (forcing every length prefix / field id / string payload through the ReadExactly loop) and through a truncated stream (asserting EndOfStreamException). These run on net8.0/net9.0 (framework Stream.ReadExactly path) and net462 (the hand-written loop).

InternalAPI baseline changes (to unblock CI — not part of the refactor)

⚠️ These InternalAPI.Unshipped.txt edits are not part of the BaseSerializer refactor. They declare internal symbols that recently landed on main (via #9752 InternalAPI tracking + #9774 DotnetTest serializer dedup) but were left undeclared, so RS0051 currently fails on main and is inherited by every PR. They are included here only to keep this PR's CI green:

  • BaseSerializer.ReadFields / WriteListPayload — declared in Platform, Extensions.HangDump, Extensions.MSBuild, Extensions.Retry, Extensions.TrxReport (each compiles BaseSerializer.cs as shared source).
  • PlatformServicesConfigurationAdapter — declared in MSTest.TestAdapter. This one is unrelated to this change and was pulled in via the merge with main; happy to split it (and the other baseline updates) into a dedicated PR if preferred.

Validation

  • Full solution build passes with 0 code errors across net8.0, net9.0, and netstandard2.0 (all previously-failing RS0051 errors resolved).
  • No behavioral public/protected member changes to BaseSerializer (bodies only); the API-baseline files above are updated purely to declare pre-existing-on-main symbols.

CopilotAI review requested due to automatic review settings July 9, 2026 09:28

CopilotAI 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

Refactors BaseSerializer in Microsoft.Testing.Platform’s IPC serializers to remove duplicated #if NETCOREAPP / #else primitive read/write implementations, consolidating onto a single byte[]-based path and centralizing exact-read behavior to avoid short-read corruption on non-NETCOREAPP targets.

Changes:

  • Removed duplicated NETCOREAPP vs non-NETCOREAPP implementations for serializer primitives, keeping one byte[] implementation.
  • Introduced a private ReadExactly helper that uses Stream.ReadExactly on NETCOREAPP and a looped read on other TFMs.
  • Fixed historical short-read behavior by routing primitive reads through ReadExactly.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.csCollapses duplicated primitive serialization logic and centralizes exact-read behavior to avoid short-read corruption.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Notes on the review:

  • Algorithmic Correctness: ReadExactly loop is correct — properly handles count = 0, short reads, and EOF.
  • IPC Wire Compatibility: Wire format unchanged — same length-prefix encoding for strings, same byte ordering for primitives.
  • Cross-TFM: Stream.ReadExactly(byte[], int, int) correctly guarded behind #if NETCOREAPP (available .NET 7+); fallback loop uses standard Stream.Read.
  • Performance: The removal of stackalloc/ArrayPool on NETCOREAPP in favor of byte[] is a deliberate trade-off for maintainability (no System.Memory reference, shared source). For fixed-size primitives (4–8 bytes) the GC cost is negligible; for string buffers this is bounded by IPC message size.
  • Bug fix: Short-read correction on non-NETCOREAPP is sound — EndOfStreamException matches the framework's Stream.ReadExactly behavior.
  • Public API: No surface changes — internal abstract class with protected static members only.

@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
CopilotAI review requested due to automatic review settings July 9, 2026 10:19

CopilotAI 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: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Build Failure Analysis

Summary — The build fails with 6 RS0051 (undeclared API) errors because PlatformServicesConfigurationAdapter and its members are not listed in the InternalAPI/InternalAPI.Unshipped.txt file. This is a merge conflict with main, not caused by the PR's changes.

Root cause: Missing internal API declaration after main introduced InternalAPI tracking

A recent commit to main (merged between b81ed0d and 1e92d898) added InternalAPI/InternalAPI.Shipped.txt and InternalAPI/InternalAPI.Unshipped.txt as AdditionalFiles in MSTest.TestAdapter.csproj. These files instruct the Public API Analyzer to track all internal symbols visible through InternalsVisibleTo.

The existing internal sealed class PlatformServicesConfigurationAdapter (and its two members) were not added to either InternalAPI file when tracking was introduced. When this PR is merge-queued against the current main, the analyzer fires RS0051 for the 3 undeclared symbols (× 2 TFMs = 6 errors).

Affected file / errors

CodeTFMFile:LineSymbol
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:12PlatformServicesConfigurationAdapter (class)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:16.ctor(IConfiguration!)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:19this[string!].get

Proposed fix

After rebasing on latest main, add the following 3 lines to src/Adapter/MSTest.TestAdapter/InternalAPI/InternalAPI.Unshipped.txt:

 #nullable enable
+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.PlatformServicesConfigurationAdapter(Microsoft.Testing.Platform.Configurations.IConfiguration! configuration) -> void+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.this[string! key].get -> string?

Note: This fix targets a file that doesn't exist on the PR branch yet — it was introduced on main after this PR was created. Rebasing the branch onto current main will pull in the InternalAPI tracking infrastructure, at which point the 3 lines above can be appended to InternalAPI.Unshipped.txt.


Build overview
FieldValue
ResultFAILED
Duration241.1 s
MSBuild18.8.0-preview-26302-115
SolutionNonWindowsTests.slnf
Failed projectMSTest.TestAdapter.csproj (net8.0 + net9.0)
Error count7 (6 unique RS0051 + 1 "Build failed")
Warnings0
All MSBuild errors (7)
#CodeProjectFile:LineMessage (truncated)
1RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
2RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
3RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
4RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
5RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
6RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
7Build.projBuild failed.
Why this isn't caused by this PR's changes

This PR modifies only:

  • src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs (refactor)
  • test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/BaseSerializerPartialReadTests.cs (new test)

Neither file is in the MSTest.TestAdapter project. The failure is triggered by new InternalAPI tracking infrastructure on main that this PR hasn't picked up yet.


🤖 Generated by the Build Failure Analysis workflow using (a href="(dev.azure.com/redacted) · commit 4c23558

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 224.8 AIC · ⌖ 10.5 AIC · ⊞ 7.3K · [◷]( · )

…rimitives
Collapse the 12 dual-implemented read/write primitives into a single
byte[]-based implementation whose only conditional piece is a private
ReadExactly helper, which also fixes the historical short-read bug on the
non-NETCOREAPP path where a single Stream.Read could return fewer bytes
than requested and silently corrupt data.
Fixes#9765
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Existing serializer round-trip tests only use MemoryStream, which never
returns short reads, so the new centralized ReadExactly behavior was
untested. Add tests that deserialize through a one-byte-per-read stream
(round-trips correctly) and a truncated stream (throws EndOfStreamException).
These exercise the framework Stream.ReadExactly path on NETCOREAPP and the
hand-written read-until-complete loop on net462.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…esConfigurationAdapter in InternalAPI
These internal symbols landed on main (ReadFields/WriteListPayload via the
DotnetTest serializer dedup, and PlatformServicesConfigurationAdapter) after
InternalAPI tracking was enabled, but were never added to the declared API,
so RS0051 fires across every project that compiles BaseSerializer.cs as
shared source (Platform, HangDump, MSBuild, Retry, TrxReport) plus
MSTest.TestAdapter. Declaring them in the corresponding InternalAPI.Unshipped
files unblocks the build.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 9, 2026 11:25
@Evangelink
Amaury Levé (Evangelink)force-pushed the evangelink-baseserializer-dedup-primitives branch from 4c23558 to 0724c73CompareJuly 9, 2026 11:25
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9776

GradeTestNotes
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamIsTruncated_
ThrowsEndOfStreamException
Clear AAA; exact-type exception assertion plus inner-exception type check are solid — consider asserting InnerException is not null first for a cleaner failure message.
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamReturnsOneBytePerRead_
RoundTripsCorrectly
Three field-equality assertions confirm the partial-read round-trip reconstructs all serialized values correctly. No issues found.

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

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

CopilotAI 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: 1
  • Review effort level: Medium

@Evangelink
Amaury Levé (Evangelink) merged commit c42b18c into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink-baseserializer-dedup-primitives branch July 9, 2026 16:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code: BaseSerializer.cs Dual #if NETCOREAPP / #else Primitive Read/Write Implementations

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer - #9776

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives
Jul 9, 2026
Merged

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer#9776
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#9765.

BaseSerializer.cs previously implemented every read/write primitive twice — once under #if NETCOREAPP (Span<byte> / ArrayPool<byte> / ReadExactly) and once under #else (heap byte[] + stream.Read). The two branches were structurally identical, so adding or fixing a primitive meant editing both.

This collapses the 12 duplicated primitives into a single byte[]-based implementation. The only remaining conditional is a small private ReadExactly helper:

  • On NETCOREAPP it delegates to the framework Stream.ReadExactly.
  • Otherwise it loops until the requested byte count is read, throwing EndOfStreamException on premature EOF.

Why byte[] instead of Span everywhere

Microsoft.Testing.Platform intentionally does not reference System.Memory (dropped in #4652), and this file is shared as source into several other projects. Span/ArrayPool are therefore unavailable on the netstandard2.0 target, so the unified path stays byte[]-based.

Bug fix included

The old #else branch used single stream.Read(...) calls that ignored short reads, which could silently return fewer bytes than requested and corrupt data on the .NET Framework / netstandard2.0 path. Routing all reads through ReadExactly fixes this.

Tests

Added BaseSerializerPartialReadTests, which deserialize through a stream that returns one byte per Read (forcing every length prefix / field id / string payload through the ReadExactly loop) and through a truncated stream (asserting EndOfStreamException). These run on net8.0/net9.0 (framework Stream.ReadExactly path) and net462 (the hand-written loop).

InternalAPI baseline changes (to unblock CI — not part of the refactor)

⚠️ These InternalAPI.Unshipped.txt edits are not part of the BaseSerializer refactor. They declare internal symbols that recently landed on main (via #9752 InternalAPI tracking + #9774 DotnetTest serializer dedup) but were left undeclared, so RS0051 currently fails on main and is inherited by every PR. They are included here only to keep this PR's CI green:

  • BaseSerializer.ReadFields / WriteListPayload — declared in Platform, Extensions.HangDump, Extensions.MSBuild, Extensions.Retry, Extensions.TrxReport (each compiles BaseSerializer.cs as shared source).
  • PlatformServicesConfigurationAdapter — declared in MSTest.TestAdapter. This one is unrelated to this change and was pulled in via the merge with main; happy to split it (and the other baseline updates) into a dedicated PR if preferred.

Validation

  • Full solution build passes with 0 code errors across net8.0, net9.0, and netstandard2.0 (all previously-failing RS0051 errors resolved).
  • No behavioral public/protected member changes to BaseSerializer (bodies only); the API-baseline files above are updated purely to declare pre-existing-on-main symbols.

CopilotAI review requested due to automatic review settings July 9, 2026 09:28

CopilotAI 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

Refactors BaseSerializer in Microsoft.Testing.Platform’s IPC serializers to remove duplicated #if NETCOREAPP / #else primitive read/write implementations, consolidating onto a single byte[]-based path and centralizing exact-read behavior to avoid short-read corruption on non-NETCOREAPP targets.

Changes:

  • Removed duplicated NETCOREAPP vs non-NETCOREAPP implementations for serializer primitives, keeping one byte[] implementation.
  • Introduced a private ReadExactly helper that uses Stream.ReadExactly on NETCOREAPP and a looped read on other TFMs.
  • Fixed historical short-read behavior by routing primitive reads through ReadExactly.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.csCollapses duplicated primitive serialization logic and centralizes exact-read behavior to avoid short-read corruption.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Notes on the review:

  • Algorithmic Correctness: ReadExactly loop is correct — properly handles count = 0, short reads, and EOF.
  • IPC Wire Compatibility: Wire format unchanged — same length-prefix encoding for strings, same byte ordering for primitives.
  • Cross-TFM: Stream.ReadExactly(byte[], int, int) correctly guarded behind #if NETCOREAPP (available .NET 7+); fallback loop uses standard Stream.Read.
  • Performance: The removal of stackalloc/ArrayPool on NETCOREAPP in favor of byte[] is a deliberate trade-off for maintainability (no System.Memory reference, shared source). For fixed-size primitives (4–8 bytes) the GC cost is negligible; for string buffers this is bounded by IPC message size.
  • Bug fix: Short-read correction on non-NETCOREAPP is sound — EndOfStreamException matches the framework's Stream.ReadExactly behavior.
  • Public API: No surface changes — internal abstract class with protected static members only.

@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
CopilotAI review requested due to automatic review settings July 9, 2026 10:19

CopilotAI 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: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Build Failure Analysis

Summary — The build fails with 6 RS0051 (undeclared API) errors because PlatformServicesConfigurationAdapter and its members are not listed in the InternalAPI/InternalAPI.Unshipped.txt file. This is a merge conflict with main, not caused by the PR's changes.

Root cause: Missing internal API declaration after main introduced InternalAPI tracking

A recent commit to main (merged between b81ed0d and 1e92d898) added InternalAPI/InternalAPI.Shipped.txt and InternalAPI/InternalAPI.Unshipped.txt as AdditionalFiles in MSTest.TestAdapter.csproj. These files instruct the Public API Analyzer to track all internal symbols visible through InternalsVisibleTo.

The existing internal sealed class PlatformServicesConfigurationAdapter (and its two members) were not added to either InternalAPI file when tracking was introduced. When this PR is merge-queued against the current main, the analyzer fires RS0051 for the 3 undeclared symbols (× 2 TFMs = 6 errors).

Affected file / errors

CodeTFMFile:LineSymbol
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:12PlatformServicesConfigurationAdapter (class)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:16.ctor(IConfiguration!)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:19this[string!].get

Proposed fix

After rebasing on latest main, add the following 3 lines to src/Adapter/MSTest.TestAdapter/InternalAPI/InternalAPI.Unshipped.txt:

 #nullable enable
+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.PlatformServicesConfigurationAdapter(Microsoft.Testing.Platform.Configurations.IConfiguration! configuration) -> void+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.this[string! key].get -> string?

Note: This fix targets a file that doesn't exist on the PR branch yet — it was introduced on main after this PR was created. Rebasing the branch onto current main will pull in the InternalAPI tracking infrastructure, at which point the 3 lines above can be appended to InternalAPI.Unshipped.txt.


Build overview
FieldValue
ResultFAILED
Duration241.1 s
MSBuild18.8.0-preview-26302-115
SolutionNonWindowsTests.slnf
Failed projectMSTest.TestAdapter.csproj (net8.0 + net9.0)
Error count7 (6 unique RS0051 + 1 "Build failed")
Warnings0
All MSBuild errors (7)
#CodeProjectFile:LineMessage (truncated)
1RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
2RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
3RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
4RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
5RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
6RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
7Build.projBuild failed.
Why this isn't caused by this PR's changes

This PR modifies only:

  • src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs (refactor)
  • test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/BaseSerializerPartialReadTests.cs (new test)

Neither file is in the MSTest.TestAdapter project. The failure is triggered by new InternalAPI tracking infrastructure on main that this PR hasn't picked up yet.


🤖 Generated by the Build Failure Analysis workflow using (a href="(dev.azure.com/redacted) · commit 4c23558

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 224.8 AIC · ⌖ 10.5 AIC · ⊞ 7.3K · [◷]( · )

…rimitives
Collapse the 12 dual-implemented read/write primitives into a single
byte[]-based implementation whose only conditional piece is a private
ReadExactly helper, which also fixes the historical short-read bug on the
non-NETCOREAPP path where a single Stream.Read could return fewer bytes
than requested and silently corrupt data.
Fixes#9765
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Existing serializer round-trip tests only use MemoryStream, which never
returns short reads, so the new centralized ReadExactly behavior was
untested. Add tests that deserialize through a one-byte-per-read stream
(round-trips correctly) and a truncated stream (throws EndOfStreamException).
These exercise the framework Stream.ReadExactly path on NETCOREAPP and the
hand-written read-until-complete loop on net462.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…esConfigurationAdapter in InternalAPI
These internal symbols landed on main (ReadFields/WriteListPayload via the
DotnetTest serializer dedup, and PlatformServicesConfigurationAdapter) after
InternalAPI tracking was enabled, but were never added to the declared API,
so RS0051 fires across every project that compiles BaseSerializer.cs as
shared source (Platform, HangDump, MSBuild, Retry, TrxReport) plus
MSTest.TestAdapter. Declaring them in the corresponding InternalAPI.Unshipped
files unblocks the build.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 9, 2026 11:25
@Evangelink
Amaury Levé (Evangelink)force-pushed the evangelink-baseserializer-dedup-primitives branch from 4c23558 to 0724c73CompareJuly 9, 2026 11:25
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9776

GradeTestNotes
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamIsTruncated_
ThrowsEndOfStreamException
Clear AAA; exact-type exception assertion plus inner-exception type check are solid — consider asserting InnerException is not null first for a cleaner failure message.
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamReturnsOneBytePerRead_
RoundTripsCorrectly
Three field-equality assertions confirm the partial-read round-trip reconstructs all serialized values correctly. No issues found.

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

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

CopilotAI 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: 1
  • Review effort level: Medium

@Evangelink
Amaury Levé (Evangelink) merged commit c42b18c into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink-baseserializer-dedup-primitives branch July 9, 2026 16:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code: BaseSerializer.cs Dual #if NETCOREAPP / #else Primitive Read/Write Implementations

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer - #9776

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives
Jul 9, 2026
Merged

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer#9776
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#9765.

BaseSerializer.cs previously implemented every read/write primitive twice — once under #if NETCOREAPP (Span<byte> / ArrayPool<byte> / ReadExactly) and once under #else (heap byte[] + stream.Read). The two branches were structurally identical, so adding or fixing a primitive meant editing both.

This collapses the 12 duplicated primitives into a single byte[]-based implementation. The only remaining conditional is a small private ReadExactly helper:

  • On NETCOREAPP it delegates to the framework Stream.ReadExactly.
  • Otherwise it loops until the requested byte count is read, throwing EndOfStreamException on premature EOF.

Why byte[] instead of Span everywhere

Microsoft.Testing.Platform intentionally does not reference System.Memory (dropped in #4652), and this file is shared as source into several other projects. Span/ArrayPool are therefore unavailable on the netstandard2.0 target, so the unified path stays byte[]-based.

Bug fix included

The old #else branch used single stream.Read(...) calls that ignored short reads, which could silently return fewer bytes than requested and corrupt data on the .NET Framework / netstandard2.0 path. Routing all reads through ReadExactly fixes this.

Tests

Added BaseSerializerPartialReadTests, which deserialize through a stream that returns one byte per Read (forcing every length prefix / field id / string payload through the ReadExactly loop) and through a truncated stream (asserting EndOfStreamException). These run on net8.0/net9.0 (framework Stream.ReadExactly path) and net462 (the hand-written loop).

InternalAPI baseline changes (to unblock CI — not part of the refactor)

⚠️ These InternalAPI.Unshipped.txt edits are not part of the BaseSerializer refactor. They declare internal symbols that recently landed on main (via #9752 InternalAPI tracking + #9774 DotnetTest serializer dedup) but were left undeclared, so RS0051 currently fails on main and is inherited by every PR. They are included here only to keep this PR's CI green:

  • BaseSerializer.ReadFields / WriteListPayload — declared in Platform, Extensions.HangDump, Extensions.MSBuild, Extensions.Retry, Extensions.TrxReport (each compiles BaseSerializer.cs as shared source).
  • PlatformServicesConfigurationAdapter — declared in MSTest.TestAdapter. This one is unrelated to this change and was pulled in via the merge with main; happy to split it (and the other baseline updates) into a dedicated PR if preferred.

Validation

  • Full solution build passes with 0 code errors across net8.0, net9.0, and netstandard2.0 (all previously-failing RS0051 errors resolved).
  • No behavioral public/protected member changes to BaseSerializer (bodies only); the API-baseline files above are updated purely to declare pre-existing-on-main symbols.

CopilotAI review requested due to automatic review settings July 9, 2026 09:28

CopilotAI 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

Refactors BaseSerializer in Microsoft.Testing.Platform’s IPC serializers to remove duplicated #if NETCOREAPP / #else primitive read/write implementations, consolidating onto a single byte[]-based path and centralizing exact-read behavior to avoid short-read corruption on non-NETCOREAPP targets.

Changes:

  • Removed duplicated NETCOREAPP vs non-NETCOREAPP implementations for serializer primitives, keeping one byte[] implementation.
  • Introduced a private ReadExactly helper that uses Stream.ReadExactly on NETCOREAPP and a looped read on other TFMs.
  • Fixed historical short-read behavior by routing primitive reads through ReadExactly.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.csCollapses duplicated primitive serialization logic and centralizes exact-read behavior to avoid short-read corruption.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Notes on the review:

  • Algorithmic Correctness: ReadExactly loop is correct — properly handles count = 0, short reads, and EOF.
  • IPC Wire Compatibility: Wire format unchanged — same length-prefix encoding for strings, same byte ordering for primitives.
  • Cross-TFM: Stream.ReadExactly(byte[], int, int) correctly guarded behind #if NETCOREAPP (available .NET 7+); fallback loop uses standard Stream.Read.
  • Performance: The removal of stackalloc/ArrayPool on NETCOREAPP in favor of byte[] is a deliberate trade-off for maintainability (no System.Memory reference, shared source). For fixed-size primitives (4–8 bytes) the GC cost is negligible; for string buffers this is bounded by IPC message size.
  • Bug fix: Short-read correction on non-NETCOREAPP is sound — EndOfStreamException matches the framework's Stream.ReadExactly behavior.
  • Public API: No surface changes — internal abstract class with protected static members only.

@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
CopilotAI review requested due to automatic review settings July 9, 2026 10:19

CopilotAI 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: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Build Failure Analysis

Summary — The build fails with 6 RS0051 (undeclared API) errors because PlatformServicesConfigurationAdapter and its members are not listed in the InternalAPI/InternalAPI.Unshipped.txt file. This is a merge conflict with main, not caused by the PR's changes.

Root cause: Missing internal API declaration after main introduced InternalAPI tracking

A recent commit to main (merged between b81ed0d and 1e92d898) added InternalAPI/InternalAPI.Shipped.txt and InternalAPI/InternalAPI.Unshipped.txt as AdditionalFiles in MSTest.TestAdapter.csproj. These files instruct the Public API Analyzer to track all internal symbols visible through InternalsVisibleTo.

The existing internal sealed class PlatformServicesConfigurationAdapter (and its two members) were not added to either InternalAPI file when tracking was introduced. When this PR is merge-queued against the current main, the analyzer fires RS0051 for the 3 undeclared symbols (× 2 TFMs = 6 errors).

Affected file / errors

CodeTFMFile:LineSymbol
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:12PlatformServicesConfigurationAdapter (class)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:16.ctor(IConfiguration!)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:19this[string!].get

Proposed fix

After rebasing on latest main, add the following 3 lines to src/Adapter/MSTest.TestAdapter/InternalAPI/InternalAPI.Unshipped.txt:

 #nullable enable
+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.PlatformServicesConfigurationAdapter(Microsoft.Testing.Platform.Configurations.IConfiguration! configuration) -> void+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.this[string! key].get -> string?

Note: This fix targets a file that doesn't exist on the PR branch yet — it was introduced on main after this PR was created. Rebasing the branch onto current main will pull in the InternalAPI tracking infrastructure, at which point the 3 lines above can be appended to InternalAPI.Unshipped.txt.


Build overview
FieldValue
ResultFAILED
Duration241.1 s
MSBuild18.8.0-preview-26302-115
SolutionNonWindowsTests.slnf
Failed projectMSTest.TestAdapter.csproj (net8.0 + net9.0)
Error count7 (6 unique RS0051 + 1 "Build failed")
Warnings0
All MSBuild errors (7)
#CodeProjectFile:LineMessage (truncated)
1RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
2RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
3RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
4RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
5RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
6RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
7Build.projBuild failed.
Why this isn't caused by this PR's changes

This PR modifies only:

  • src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs (refactor)
  • test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/BaseSerializerPartialReadTests.cs (new test)

Neither file is in the MSTest.TestAdapter project. The failure is triggered by new InternalAPI tracking infrastructure on main that this PR hasn't picked up yet.


🤖 Generated by the Build Failure Analysis workflow using (a href="(dev.azure.com/redacted) · commit 4c23558

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 224.8 AIC · ⌖ 10.5 AIC · ⊞ 7.3K · [◷]( · )

…rimitives
Collapse the 12 dual-implemented read/write primitives into a single
byte[]-based implementation whose only conditional piece is a private
ReadExactly helper, which also fixes the historical short-read bug on the
non-NETCOREAPP path where a single Stream.Read could return fewer bytes
than requested and silently corrupt data.
Fixes#9765
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Existing serializer round-trip tests only use MemoryStream, which never
returns short reads, so the new centralized ReadExactly behavior was
untested. Add tests that deserialize through a one-byte-per-read stream
(round-trips correctly) and a truncated stream (throws EndOfStreamException).
These exercise the framework Stream.ReadExactly path on NETCOREAPP and the
hand-written read-until-complete loop on net462.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…esConfigurationAdapter in InternalAPI
These internal symbols landed on main (ReadFields/WriteListPayload via the
DotnetTest serializer dedup, and PlatformServicesConfigurationAdapter) after
InternalAPI tracking was enabled, but were never added to the declared API,
so RS0051 fires across every project that compiles BaseSerializer.cs as
shared source (Platform, HangDump, MSBuild, Retry, TrxReport) plus
MSTest.TestAdapter. Declaring them in the corresponding InternalAPI.Unshipped
files unblocks the build.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 9, 2026 11:25
@Evangelink
Amaury Levé (Evangelink)force-pushed the evangelink-baseserializer-dedup-primitives branch from 4c23558 to 0724c73CompareJuly 9, 2026 11:25
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9776

GradeTestNotes
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamIsTruncated_
ThrowsEndOfStreamException
Clear AAA; exact-type exception assertion plus inner-exception type check are solid — consider asserting InnerException is not null first for a cleaner failure message.
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamReturnsOneBytePerRead_
RoundTripsCorrectly
Three field-equality assertions confirm the partial-read round-trip reconstructs all serialized values correctly. No issues found.

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

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

CopilotAI 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: 1
  • Review effort level: Medium

@Evangelink
Amaury Levé (Evangelink) merged commit c42b18c into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink-baseserializer-dedup-primitives branch July 9, 2026 16:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code: BaseSerializer.cs Dual #if NETCOREAPP / #else Primitive Read/Write Implementations

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer - #9776

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives
Jul 9, 2026
Merged

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer#9776
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#9765.

BaseSerializer.cs previously implemented every read/write primitive twice — once under #if NETCOREAPP (Span<byte> / ArrayPool<byte> / ReadExactly) and once under #else (heap byte[] + stream.Read). The two branches were structurally identical, so adding or fixing a primitive meant editing both.

This collapses the 12 duplicated primitives into a single byte[]-based implementation. The only remaining conditional is a small private ReadExactly helper:

  • On NETCOREAPP it delegates to the framework Stream.ReadExactly.
  • Otherwise it loops until the requested byte count is read, throwing EndOfStreamException on premature EOF.

Why byte[] instead of Span everywhere

Microsoft.Testing.Platform intentionally does not reference System.Memory (dropped in #4652), and this file is shared as source into several other projects. Span/ArrayPool are therefore unavailable on the netstandard2.0 target, so the unified path stays byte[]-based.

Bug fix included

The old #else branch used single stream.Read(...) calls that ignored short reads, which could silently return fewer bytes than requested and corrupt data on the .NET Framework / netstandard2.0 path. Routing all reads through ReadExactly fixes this.

Tests

Added BaseSerializerPartialReadTests, which deserialize through a stream that returns one byte per Read (forcing every length prefix / field id / string payload through the ReadExactly loop) and through a truncated stream (asserting EndOfStreamException). These run on net8.0/net9.0 (framework Stream.ReadExactly path) and net462 (the hand-written loop).

InternalAPI baseline changes (to unblock CI — not part of the refactor)

⚠️ These InternalAPI.Unshipped.txt edits are not part of the BaseSerializer refactor. They declare internal symbols that recently landed on main (via #9752 InternalAPI tracking + #9774 DotnetTest serializer dedup) but were left undeclared, so RS0051 currently fails on main and is inherited by every PR. They are included here only to keep this PR's CI green:

  • BaseSerializer.ReadFields / WriteListPayload — declared in Platform, Extensions.HangDump, Extensions.MSBuild, Extensions.Retry, Extensions.TrxReport (each compiles BaseSerializer.cs as shared source).
  • PlatformServicesConfigurationAdapter — declared in MSTest.TestAdapter. This one is unrelated to this change and was pulled in via the merge with main; happy to split it (and the other baseline updates) into a dedicated PR if preferred.

Validation

  • Full solution build passes with 0 code errors across net8.0, net9.0, and netstandard2.0 (all previously-failing RS0051 errors resolved).
  • No behavioral public/protected member changes to BaseSerializer (bodies only); the API-baseline files above are updated purely to declare pre-existing-on-main symbols.

CopilotAI review requested due to automatic review settings July 9, 2026 09:28

CopilotAI 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

Refactors BaseSerializer in Microsoft.Testing.Platform’s IPC serializers to remove duplicated #if NETCOREAPP / #else primitive read/write implementations, consolidating onto a single byte[]-based path and centralizing exact-read behavior to avoid short-read corruption on non-NETCOREAPP targets.

Changes:

  • Removed duplicated NETCOREAPP vs non-NETCOREAPP implementations for serializer primitives, keeping one byte[] implementation.
  • Introduced a private ReadExactly helper that uses Stream.ReadExactly on NETCOREAPP and a looped read on other TFMs.
  • Fixed historical short-read behavior by routing primitive reads through ReadExactly.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.csCollapses duplicated primitive serialization logic and centralizes exact-read behavior to avoid short-read corruption.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Notes on the review:

  • Algorithmic Correctness: ReadExactly loop is correct — properly handles count = 0, short reads, and EOF.
  • IPC Wire Compatibility: Wire format unchanged — same length-prefix encoding for strings, same byte ordering for primitives.
  • Cross-TFM: Stream.ReadExactly(byte[], int, int) correctly guarded behind #if NETCOREAPP (available .NET 7+); fallback loop uses standard Stream.Read.
  • Performance: The removal of stackalloc/ArrayPool on NETCOREAPP in favor of byte[] is a deliberate trade-off for maintainability (no System.Memory reference, shared source). For fixed-size primitives (4–8 bytes) the GC cost is negligible; for string buffers this is bounded by IPC message size.
  • Bug fix: Short-read correction on non-NETCOREAPP is sound — EndOfStreamException matches the framework's Stream.ReadExactly behavior.
  • Public API: No surface changes — internal abstract class with protected static members only.

@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
CopilotAI review requested due to automatic review settings July 9, 2026 10:19

CopilotAI 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: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Build Failure Analysis

Summary — The build fails with 6 RS0051 (undeclared API) errors because PlatformServicesConfigurationAdapter and its members are not listed in the InternalAPI/InternalAPI.Unshipped.txt file. This is a merge conflict with main, not caused by the PR's changes.

Root cause: Missing internal API declaration after main introduced InternalAPI tracking

A recent commit to main (merged between b81ed0d and 1e92d898) added InternalAPI/InternalAPI.Shipped.txt and InternalAPI/InternalAPI.Unshipped.txt as AdditionalFiles in MSTest.TestAdapter.csproj. These files instruct the Public API Analyzer to track all internal symbols visible through InternalsVisibleTo.

The existing internal sealed class PlatformServicesConfigurationAdapter (and its two members) were not added to either InternalAPI file when tracking was introduced. When this PR is merge-queued against the current main, the analyzer fires RS0051 for the 3 undeclared symbols (× 2 TFMs = 6 errors).

Affected file / errors

CodeTFMFile:LineSymbol
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:12PlatformServicesConfigurationAdapter (class)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:16.ctor(IConfiguration!)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:19this[string!].get

Proposed fix

After rebasing on latest main, add the following 3 lines to src/Adapter/MSTest.TestAdapter/InternalAPI/InternalAPI.Unshipped.txt:

 #nullable enable
+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.PlatformServicesConfigurationAdapter(Microsoft.Testing.Platform.Configurations.IConfiguration! configuration) -> void+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.this[string! key].get -> string?

Note: This fix targets a file that doesn't exist on the PR branch yet — it was introduced on main after this PR was created. Rebasing the branch onto current main will pull in the InternalAPI tracking infrastructure, at which point the 3 lines above can be appended to InternalAPI.Unshipped.txt.


Build overview
FieldValue
ResultFAILED
Duration241.1 s
MSBuild18.8.0-preview-26302-115
SolutionNonWindowsTests.slnf
Failed projectMSTest.TestAdapter.csproj (net8.0 + net9.0)
Error count7 (6 unique RS0051 + 1 "Build failed")
Warnings0
All MSBuild errors (7)
#CodeProjectFile:LineMessage (truncated)
1RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
2RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
3RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
4RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
5RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
6RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
7Build.projBuild failed.
Why this isn't caused by this PR's changes

This PR modifies only:

  • src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs (refactor)
  • test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/BaseSerializerPartialReadTests.cs (new test)

Neither file is in the MSTest.TestAdapter project. The failure is triggered by new InternalAPI tracking infrastructure on main that this PR hasn't picked up yet.


🤖 Generated by the Build Failure Analysis workflow using (a href="(dev.azure.com/redacted) · commit 4c23558

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 224.8 AIC · ⌖ 10.5 AIC · ⊞ 7.3K · [◷]( · )

…rimitives
Collapse the 12 dual-implemented read/write primitives into a single
byte[]-based implementation whose only conditional piece is a private
ReadExactly helper, which also fixes the historical short-read bug on the
non-NETCOREAPP path where a single Stream.Read could return fewer bytes
than requested and silently corrupt data.
Fixes#9765
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Existing serializer round-trip tests only use MemoryStream, which never
returns short reads, so the new centralized ReadExactly behavior was
untested. Add tests that deserialize through a one-byte-per-read stream
(round-trips correctly) and a truncated stream (throws EndOfStreamException).
These exercise the framework Stream.ReadExactly path on NETCOREAPP and the
hand-written read-until-complete loop on net462.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…esConfigurationAdapter in InternalAPI
These internal symbols landed on main (ReadFields/WriteListPayload via the
DotnetTest serializer dedup, and PlatformServicesConfigurationAdapter) after
InternalAPI tracking was enabled, but were never added to the declared API,
so RS0051 fires across every project that compiles BaseSerializer.cs as
shared source (Platform, HangDump, MSBuild, Retry, TrxReport) plus
MSTest.TestAdapter. Declaring them in the corresponding InternalAPI.Unshipped
files unblocks the build.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 9, 2026 11:25
@Evangelink
Amaury Levé (Evangelink)force-pushed the evangelink-baseserializer-dedup-primitives branch from 4c23558 to 0724c73CompareJuly 9, 2026 11:25
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9776

GradeTestNotes
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamIsTruncated_
ThrowsEndOfStreamException
Clear AAA; exact-type exception assertion plus inner-exception type check are solid — consider asserting InnerException is not null first for a cleaner failure message.
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamReturnsOneBytePerRead_
RoundTripsCorrectly
Three field-equality assertions confirm the partial-read round-trip reconstructs all serialized values correctly. No issues found.

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

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

CopilotAI 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: 1
  • Review effort level: Medium

@Evangelink
Amaury Levé (Evangelink) merged commit c42b18c into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink-baseserializer-dedup-primitives branch July 9, 2026 16:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code: BaseSerializer.cs Dual #if NETCOREAPP / #else Primitive Read/Write Implementations

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer - #9776

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives
Jul 9, 2026
Merged

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer#9776
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#9765.

BaseSerializer.cs previously implemented every read/write primitive twice — once under #if NETCOREAPP (Span<byte> / ArrayPool<byte> / ReadExactly) and once under #else (heap byte[] + stream.Read). The two branches were structurally identical, so adding or fixing a primitive meant editing both.

This collapses the 12 duplicated primitives into a single byte[]-based implementation. The only remaining conditional is a small private ReadExactly helper:

  • On NETCOREAPP it delegates to the framework Stream.ReadExactly.
  • Otherwise it loops until the requested byte count is read, throwing EndOfStreamException on premature EOF.

Why byte[] instead of Span everywhere

Microsoft.Testing.Platform intentionally does not reference System.Memory (dropped in #4652), and this file is shared as source into several other projects. Span/ArrayPool are therefore unavailable on the netstandard2.0 target, so the unified path stays byte[]-based.

Bug fix included

The old #else branch used single stream.Read(...) calls that ignored short reads, which could silently return fewer bytes than requested and corrupt data on the .NET Framework / netstandard2.0 path. Routing all reads through ReadExactly fixes this.

Tests

Added BaseSerializerPartialReadTests, which deserialize through a stream that returns one byte per Read (forcing every length prefix / field id / string payload through the ReadExactly loop) and through a truncated stream (asserting EndOfStreamException). These run on net8.0/net9.0 (framework Stream.ReadExactly path) and net462 (the hand-written loop).

InternalAPI baseline changes (to unblock CI — not part of the refactor)

⚠️ These InternalAPI.Unshipped.txt edits are not part of the BaseSerializer refactor. They declare internal symbols that recently landed on main (via #9752 InternalAPI tracking + #9774 DotnetTest serializer dedup) but were left undeclared, so RS0051 currently fails on main and is inherited by every PR. They are included here only to keep this PR's CI green:

  • BaseSerializer.ReadFields / WriteListPayload — declared in Platform, Extensions.HangDump, Extensions.MSBuild, Extensions.Retry, Extensions.TrxReport (each compiles BaseSerializer.cs as shared source).
  • PlatformServicesConfigurationAdapter — declared in MSTest.TestAdapter. This one is unrelated to this change and was pulled in via the merge with main; happy to split it (and the other baseline updates) into a dedicated PR if preferred.

Validation

  • Full solution build passes with 0 code errors across net8.0, net9.0, and netstandard2.0 (all previously-failing RS0051 errors resolved).
  • No behavioral public/protected member changes to BaseSerializer (bodies only); the API-baseline files above are updated purely to declare pre-existing-on-main symbols.

CopilotAI review requested due to automatic review settings July 9, 2026 09:28

CopilotAI 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

Refactors BaseSerializer in Microsoft.Testing.Platform’s IPC serializers to remove duplicated #if NETCOREAPP / #else primitive read/write implementations, consolidating onto a single byte[]-based path and centralizing exact-read behavior to avoid short-read corruption on non-NETCOREAPP targets.

Changes:

  • Removed duplicated NETCOREAPP vs non-NETCOREAPP implementations for serializer primitives, keeping one byte[] implementation.
  • Introduced a private ReadExactly helper that uses Stream.ReadExactly on NETCOREAPP and a looped read on other TFMs.
  • Fixed historical short-read behavior by routing primitive reads through ReadExactly.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.csCollapses duplicated primitive serialization logic and centralizes exact-read behavior to avoid short-read corruption.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Notes on the review:

  • Algorithmic Correctness: ReadExactly loop is correct — properly handles count = 0, short reads, and EOF.
  • IPC Wire Compatibility: Wire format unchanged — same length-prefix encoding for strings, same byte ordering for primitives.
  • Cross-TFM: Stream.ReadExactly(byte[], int, int) correctly guarded behind #if NETCOREAPP (available .NET 7+); fallback loop uses standard Stream.Read.
  • Performance: The removal of stackalloc/ArrayPool on NETCOREAPP in favor of byte[] is a deliberate trade-off for maintainability (no System.Memory reference, shared source). For fixed-size primitives (4–8 bytes) the GC cost is negligible; for string buffers this is bounded by IPC message size.
  • Bug fix: Short-read correction on non-NETCOREAPP is sound — EndOfStreamException matches the framework's Stream.ReadExactly behavior.
  • Public API: No surface changes — internal abstract class with protected static members only.

@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
CopilotAI review requested due to automatic review settings July 9, 2026 10:19

CopilotAI 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: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Build Failure Analysis

Summary — The build fails with 6 RS0051 (undeclared API) errors because PlatformServicesConfigurationAdapter and its members are not listed in the InternalAPI/InternalAPI.Unshipped.txt file. This is a merge conflict with main, not caused by the PR's changes.

Root cause: Missing internal API declaration after main introduced InternalAPI tracking

A recent commit to main (merged between b81ed0d and 1e92d898) added InternalAPI/InternalAPI.Shipped.txt and InternalAPI/InternalAPI.Unshipped.txt as AdditionalFiles in MSTest.TestAdapter.csproj. These files instruct the Public API Analyzer to track all internal symbols visible through InternalsVisibleTo.

The existing internal sealed class PlatformServicesConfigurationAdapter (and its two members) were not added to either InternalAPI file when tracking was introduced. When this PR is merge-queued against the current main, the analyzer fires RS0051 for the 3 undeclared symbols (× 2 TFMs = 6 errors).

Affected file / errors

CodeTFMFile:LineSymbol
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:12PlatformServicesConfigurationAdapter (class)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:16.ctor(IConfiguration!)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:19this[string!].get

Proposed fix

After rebasing on latest main, add the following 3 lines to src/Adapter/MSTest.TestAdapter/InternalAPI/InternalAPI.Unshipped.txt:

 #nullable enable
+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.PlatformServicesConfigurationAdapter(Microsoft.Testing.Platform.Configurations.IConfiguration! configuration) -> void+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.this[string! key].get -> string?

Note: This fix targets a file that doesn't exist on the PR branch yet — it was introduced on main after this PR was created. Rebasing the branch onto current main will pull in the InternalAPI tracking infrastructure, at which point the 3 lines above can be appended to InternalAPI.Unshipped.txt.


Build overview
FieldValue
ResultFAILED
Duration241.1 s
MSBuild18.8.0-preview-26302-115
SolutionNonWindowsTests.slnf
Failed projectMSTest.TestAdapter.csproj (net8.0 + net9.0)
Error count7 (6 unique RS0051 + 1 "Build failed")
Warnings0
All MSBuild errors (7)
#CodeProjectFile:LineMessage (truncated)
1RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
2RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
3RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
4RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
5RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
6RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
7Build.projBuild failed.
Why this isn't caused by this PR's changes

This PR modifies only:

  • src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs (refactor)
  • test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/BaseSerializerPartialReadTests.cs (new test)

Neither file is in the MSTest.TestAdapter project. The failure is triggered by new InternalAPI tracking infrastructure on main that this PR hasn't picked up yet.


🤖 Generated by the Build Failure Analysis workflow using (a href="(dev.azure.com/redacted) · commit 4c23558

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 224.8 AIC · ⌖ 10.5 AIC · ⊞ 7.3K · [◷]( · )

…rimitives
Collapse the 12 dual-implemented read/write primitives into a single
byte[]-based implementation whose only conditional piece is a private
ReadExactly helper, which also fixes the historical short-read bug on the
non-NETCOREAPP path where a single Stream.Read could return fewer bytes
than requested and silently corrupt data.
Fixes#9765
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Existing serializer round-trip tests only use MemoryStream, which never
returns short reads, so the new centralized ReadExactly behavior was
untested. Add tests that deserialize through a one-byte-per-read stream
(round-trips correctly) and a truncated stream (throws EndOfStreamException).
These exercise the framework Stream.ReadExactly path on NETCOREAPP and the
hand-written read-until-complete loop on net462.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…esConfigurationAdapter in InternalAPI
These internal symbols landed on main (ReadFields/WriteListPayload via the
DotnetTest serializer dedup, and PlatformServicesConfigurationAdapter) after
InternalAPI tracking was enabled, but were never added to the declared API,
so RS0051 fires across every project that compiles BaseSerializer.cs as
shared source (Platform, HangDump, MSBuild, Retry, TrxReport) plus
MSTest.TestAdapter. Declaring them in the corresponding InternalAPI.Unshipped
files unblocks the build.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 9, 2026 11:25
@Evangelink
Amaury Levé (Evangelink)force-pushed the evangelink-baseserializer-dedup-primitives branch from 4c23558 to 0724c73CompareJuly 9, 2026 11:25
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9776

GradeTestNotes
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamIsTruncated_
ThrowsEndOfStreamException
Clear AAA; exact-type exception assertion plus inner-exception type check are solid — consider asserting InnerException is not null first for a cleaner failure message.
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamReturnsOneBytePerRead_
RoundTripsCorrectly
Three field-equality assertions confirm the partial-read round-trip reconstructs all serialized values correctly. No issues found.

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

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

CopilotAI 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: 1
  • Review effort level: Medium

@Evangelink
Amaury Levé (Evangelink) merged commit c42b18c into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink-baseserializer-dedup-primitives branch July 9, 2026 16:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code: BaseSerializer.cs Dual #if NETCOREAPP / #else Primitive Read/Write Implementations

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer - #9776

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives
Jul 9, 2026
Merged

Remove duplicated #if NETCOREAPP / #else primitives in BaseSerializer#9776
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
evangelink-baseserializer-dedup-primitives

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#9765.

BaseSerializer.cs previously implemented every read/write primitive twice — once under #if NETCOREAPP (Span<byte> / ArrayPool<byte> / ReadExactly) and once under #else (heap byte[] + stream.Read). The two branches were structurally identical, so adding or fixing a primitive meant editing both.

This collapses the 12 duplicated primitives into a single byte[]-based implementation. The only remaining conditional is a small private ReadExactly helper:

  • On NETCOREAPP it delegates to the framework Stream.ReadExactly.
  • Otherwise it loops until the requested byte count is read, throwing EndOfStreamException on premature EOF.

Why byte[] instead of Span everywhere

Microsoft.Testing.Platform intentionally does not reference System.Memory (dropped in #4652), and this file is shared as source into several other projects. Span/ArrayPool are therefore unavailable on the netstandard2.0 target, so the unified path stays byte[]-based.

Bug fix included

The old #else branch used single stream.Read(...) calls that ignored short reads, which could silently return fewer bytes than requested and corrupt data on the .NET Framework / netstandard2.0 path. Routing all reads through ReadExactly fixes this.

Tests

Added BaseSerializerPartialReadTests, which deserialize through a stream that returns one byte per Read (forcing every length prefix / field id / string payload through the ReadExactly loop) and through a truncated stream (asserting EndOfStreamException). These run on net8.0/net9.0 (framework Stream.ReadExactly path) and net462 (the hand-written loop).

InternalAPI baseline changes (to unblock CI — not part of the refactor)

⚠️ These InternalAPI.Unshipped.txt edits are not part of the BaseSerializer refactor. They declare internal symbols that recently landed on main (via #9752 InternalAPI tracking + #9774 DotnetTest serializer dedup) but were left undeclared, so RS0051 currently fails on main and is inherited by every PR. They are included here only to keep this PR's CI green:

  • BaseSerializer.ReadFields / WriteListPayload — declared in Platform, Extensions.HangDump, Extensions.MSBuild, Extensions.Retry, Extensions.TrxReport (each compiles BaseSerializer.cs as shared source).
  • PlatformServicesConfigurationAdapter — declared in MSTest.TestAdapter. This one is unrelated to this change and was pulled in via the merge with main; happy to split it (and the other baseline updates) into a dedicated PR if preferred.

Validation

  • Full solution build passes with 0 code errors across net8.0, net9.0, and netstandard2.0 (all previously-failing RS0051 errors resolved).
  • No behavioral public/protected member changes to BaseSerializer (bodies only); the API-baseline files above are updated purely to declare pre-existing-on-main symbols.

CopilotAI review requested due to automatic review settings July 9, 2026 09:28

CopilotAI 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

Refactors BaseSerializer in Microsoft.Testing.Platform’s IPC serializers to remove duplicated #if NETCOREAPP / #else primitive read/write implementations, consolidating onto a single byte[]-based path and centralizing exact-read behavior to avoid short-read corruption on non-NETCOREAPP targets.

Changes:

  • Removed duplicated NETCOREAPP vs non-NETCOREAPP implementations for serializer primitives, keeping one byte[] implementation.
  • Introduced a private ReadExactly helper that uses Stream.ReadExactly on NETCOREAPP and a looped read on other TFMs.
  • Fixed historical short-read behavior by routing primitive reads through ReadExactly.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.csCollapses duplicated primitive serialization logic and centralizes exact-read behavior to avoid short-read corruption.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Notes on the review:

  • Algorithmic Correctness: ReadExactly loop is correct — properly handles count = 0, short reads, and EOF.
  • IPC Wire Compatibility: Wire format unchanged — same length-prefix encoding for strings, same byte ordering for primitives.
  • Cross-TFM: Stream.ReadExactly(byte[], int, int) correctly guarded behind #if NETCOREAPP (available .NET 7+); fallback loop uses standard Stream.Read.
  • Performance: The removal of stackalloc/ArrayPool on NETCOREAPP in favor of byte[] is a deliberate trade-off for maintainability (no System.Memory reference, shared source). For fixed-size primitives (4–8 bytes) the GC cost is negligible; for string buffers this is bounded by IPC message size.
  • Bug fix: Short-read correction on non-NETCOREAPP is sound — EndOfStreamException matches the framework's Stream.ReadExactly behavior.
  • Public API: No surface changes — internal abstract class with protected static members only.

@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
CopilotAI review requested due to automatic review settings July 9, 2026 10:19

CopilotAI 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: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Build Failure Analysis

Summary — The build fails with 6 RS0051 (undeclared API) errors because PlatformServicesConfigurationAdapter and its members are not listed in the InternalAPI/InternalAPI.Unshipped.txt file. This is a merge conflict with main, not caused by the PR's changes.

Root cause: Missing internal API declaration after main introduced InternalAPI tracking

A recent commit to main (merged between b81ed0d and 1e92d898) added InternalAPI/InternalAPI.Shipped.txt and InternalAPI/InternalAPI.Unshipped.txt as AdditionalFiles in MSTest.TestAdapter.csproj. These files instruct the Public API Analyzer to track all internal symbols visible through InternalsVisibleTo.

The existing internal sealed class PlatformServicesConfigurationAdapter (and its two members) were not added to either InternalAPI file when tracking was introduced. When this PR is merge-queued against the current main, the analyzer fires RS0051 for the 3 undeclared symbols (× 2 TFMs = 6 errors).

Affected file / errors

CodeTFMFile:LineSymbol
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:12PlatformServicesConfigurationAdapter (class)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:16.ctor(IConfiguration!)
RS0051net8.0, net9.0PlatformServicesConfigurationAdapter.cs:19this[string!].get

Proposed fix

After rebasing on latest main, add the following 3 lines to src/Adapter/MSTest.TestAdapter/InternalAPI/InternalAPI.Unshipped.txt:

 #nullable enable
+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.PlatformServicesConfigurationAdapter(Microsoft.Testing.Platform.Configurations.IConfiguration! configuration) -> void+Microsoft.VisualStudio.TestTools.UnitTesting.PlatformServicesConfigurationAdapter.this[string! key].get -> string?

Note: This fix targets a file that doesn't exist on the PR branch yet — it was introduced on main after this PR was created. Rebasing the branch onto current main will pull in the InternalAPI tracking infrastructure, at which point the 3 lines above can be appended to InternalAPI.Unshipped.txt.


Build overview
FieldValue
ResultFAILED
Duration241.1 s
MSBuild18.8.0-preview-26302-115
SolutionNonWindowsTests.slnf
Failed projectMSTest.TestAdapter.csproj (net8.0 + net9.0)
Error count7 (6 unique RS0051 + 1 "Build failed")
Warnings0
All MSBuild errors (7)
#CodeProjectFile:LineMessage (truncated)
1RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
2RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
3RS0051MSTest.TestAdapter (net8.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
4RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:12Symbol '...PlatformServicesConfigurationAdapter' is not part of the declared API
5RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:16Symbol '...PlatformServicesConfigurationAdapter(...)' is not part of the declared API
6RS0051MSTest.TestAdapter (net9.0)PlatformServicesConfigurationAdapter.cs:19Symbol '...this[string! key].get' is not part of the declared API
7Build.projBuild failed.
Why this isn't caused by this PR's changes

This PR modifies only:

  • src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs (refactor)
  • test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/BaseSerializerPartialReadTests.cs (new test)

Neither file is in the MSTest.TestAdapter project. The failure is triggered by new InternalAPI tracking infrastructure on main that this PR hasn't picked up yet.


🤖 Generated by the Build Failure Analysis workflow using (a href="(dev.azure.com/redacted) · commit 4c23558

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 224.8 AIC · ⌖ 10.5 AIC · ⊞ 7.3K · [◷]( · )

…rimitives
Collapse the 12 dual-implemented read/write primitives into a single
byte[]-based implementation whose only conditional piece is a private
ReadExactly helper, which also fixes the historical short-read bug on the
non-NETCOREAPP path where a single Stream.Read could return fewer bytes
than requested and silently corrupt data.
Fixes#9765
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Existing serializer round-trip tests only use MemoryStream, which never
returns short reads, so the new centralized ReadExactly behavior was
untested. Add tests that deserialize through a one-byte-per-read stream
(round-trips correctly) and a truncated stream (throws EndOfStreamException).
These exercise the framework Stream.ReadExactly path on NETCOREAPP and the
hand-written read-until-complete loop on net462.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…esConfigurationAdapter in InternalAPI
These internal symbols landed on main (ReadFields/WriteListPayload via the
DotnetTest serializer dedup, and PlatformServicesConfigurationAdapter) after
InternalAPI tracking was enabled, but were never added to the declared API,
so RS0051 fires across every project that compiles BaseSerializer.cs as
shared source (Platform, HangDump, MSBuild, Retry, TrxReport) plus
MSTest.TestAdapter. Declaring them in the corresponding InternalAPI.Unshipped
files unblocks the build.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 9, 2026 11:25
@Evangelink
Amaury Levé (Evangelink)force-pushed the evangelink-baseserializer-dedup-primitives branch from 4c23558 to 0724c73CompareJuly 9, 2026 11:25
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9776

GradeTestNotes
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamIsTruncated_
ThrowsEndOfStreamException
Clear AAA; exact-type exception assertion plus inner-exception type check are solid — consider asserting InnerException is not null first for a cleaner failure message.
A (90–100)new BaseSerializerPartialReadTests.
Deserialize_
WhenStreamReturnsOneBytePerRead_
RoundTripsCorrectly
Three field-equality assertions confirm the partial-read round-trip reconstructs all serialized values correctly. No issues found.

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

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

CopilotAI 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: 1
  • Review effort level: Medium

@Evangelink
Amaury Levé (Evangelink) merged commit c42b18c into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink-baseserializer-dedup-primitives branch July 9, 2026 16:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code: BaseSerializer.cs Dual #if NETCOREAPP / #else Primitive Read/Write Implementations

3 participants

@Evangelink@0101