Add regression coverage for metadata newline preservation - #14261
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds regression tests to ensure MSBuild’s XML loading path continues to preserve multi-line item metadata values (notably CRLF sequences), and updates an in-code compatibility comment explaining why MSBuild continues using XmlTextReader instead of XmlReader.Create to avoid whitespace/newline normalization behavior changes.
Changes:
- Expanded the compatibility comment in
XmlReaderExtensionto document newline/whitespace preservation rationale and link related history. - Added Windows-only regression tests that load a project from disk (including “read-only” load) and assert CRLF preservation in item metadata.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Build/Xml/XmlReaderExtension.cs | Updates compatibility commentary around XML reader choice to explain newline/whitespace behavior impact. |
| src/Build.UnitTests/Construction/XmlReaderWithoutLocation_Tests.cs | Adds regression tests validating CRLF preservation for item metadata when loading projects from disk and in read-only mode. |
|
@dotnet-policy-service agree company="Microsoft" |
There was a problem hiding this comment.
Caution
agentic threat detected
Threat detection flagged this output in warn mode. Manual review is REQUIRED before any follow-up automation.
Code Review — PR #14261
✅ 21/24 dimensions clean.
| Dimension | Severity | Finding |
|---|---|---|
| Cross-Platform Correctness | MODERATE | [WindowsOnlyFact] is unneeded — XmlTextReader (Normalization=false) preserves \r\n on Linux too; use [Fact] |
| Design / Test Placement | MODERATE | These tests don't use the XmlReaderNoIXmlLineInfo wrapper that defines the class's scope; better placed in ItemEvaluation_Tests.cs or a new XmlReaderExtension_Tests.cs |
| Code Simplification | NIT | Split([Environment.NewLine], ...).Length.ShouldBe(5) is redundant — an exact ShouldBe(...) already guarantees the count |
Design concern: test placement
XmlReaderWithoutLocation_Tests was built around one scenario: loading a project from an XmlReader that doesn't implement IXmlLineInfo. Neither new test uses the XmlReaderNoIXmlLineInfo wrapper, and loadProjectsReadOnly: true is orthogonal to "without location". Better homes would be src/Build.UnitTests/Evaluation/ItemEvaluation_Tests.cs (where metadata value semantics tests already live) or a new XmlReaderExtension_Tests.cs that explicitly targets the XmlReaderExtension behavior documented in the updated comment.
The comment update in XmlReaderExtension.cs is a clear improvement — it replaces the vague "Ignore loadAsReadOnly for now" placeholder with an accurate rationale and five cross-referenced issues. The tests themselves are correct and will catch real regressions; the findings above are structural improvements only.
Generated by Expert Code Review (on open) for #14261 · 1.8K AIC · ⊞ 30.3K
3bdd057 to
1b4c931
Compare
OvesN
left a comment
There was a problem hiding this comment.
Good expansion of our test coverage. I only have a few minor comments.
1f09485 to
d0f7e50
Compare
Updated [Microsoft.Build.Utilities.Core](https://github.com/dotnet/msbuild) from 18.9.6 to 18.10.1. <details> <summary>Release notes</summary> _Sourced from [Microsoft.Build.Utilities.Core's releases](https://github.com/dotnet/msbuild/releases)._ ## 18.10.1 ## What's Changed * [vs16.11] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13103 * [vs17.12] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13796 * [vs17.8] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13902 * [vs17.11] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13903 * [vs17.12] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13909 * [vs17.12] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13986 * Add vs18.9 to merge-flow config; retire vs18.3 by @JanProvaznik in dotnet/msbuild#14214 * Bump labeler-cache-retention to use issue-labeler v2.1.0 by @jeffhandley in dotnet/msbuild#14171 * Bump main to 18.10.0 after vs18.9 snap by @JanProvaznik in dotnet/msbuild#14216 * Improve release skill: Phase 2 DARC rules, VMR backflow, deterministic baseline by @JanProvaznik in dotnet/msbuild#14220 * Determinize release: hardcode OptProf baseline + Phase 3.2 baseline resolver by @JanProvaznik in dotnet/msbuild#14222 * Serialize BuildRequestConfiguration.RequestedTargets to fix solution metaproject MSB4057 in parallel builds by @ViktorHofer in dotnet/msbuild#14223 * [main] Update dependencies from nuget/nuget.client by @dotnet-maestro[bot] in dotnet/msbuild#14203 * Core support for AbsolutePath/FileInfo/DirectoryInfo and ITaskItem<T> as task parameters by @baronfel in dotnet/msbuild#13971 * [main] Update dependencies from dotnet/roslyn by @dotnet-maestro[bot] in dotnet/msbuild#14206 * Fix existence cache kind poisoning by @AlesProkop in dotnet/msbuild#14249 * [main] Source code updates from dotnet/dotnet by @dotnet-maestro[bot] in dotnet/msbuild#14226 * Don't disable the MSBuild server for /mt builds when node reuse is off by @AR-May in dotnet/msbuild#14248 * Enhance expert reviewer guidelines with additional checks. by @AR-May in dotnet/msbuild#14255 * [main] Source code updates from dotnet/dotnet by @dotnet-maestro[bot] in dotnet/msbuild#14253 * [main] Update dependencies from dotnet/roslyn by @dotnet-maestro[bot] in dotnet/msbuild#14268 * [main] Update dependencies from nuget/nuget.client by @dotnet-maestro[bot] in dotnet/msbuild#14267 * Bump github/gh-aw-actions/setup from 0.81.6 to 0.82.2 by @dependabot[bot] in dotnet/msbuild#14266 * Avoid boxing the struct enumerator in PropertyDictionary<T>.GetEnumerator() by @nareshjo in dotnet/msbuild#14272 * Refresh copy marker when implementation output changes by @AlesProkop in dotnet/msbuild#14231 * Send task-host build process environment as delta by @OvesN in dotnet/msbuild#14126 * Add regression coverage for metadata newline preservation by @VolPlita in dotnet/msbuild#14261 * Fix EmbedInBinlog items with relative paths from child projects by @huulinhnguyen-dev in dotnet/msbuild#13990 * Stop requiring VersionPrefix updates in servicing - insert prerelease versions to VS by @ViktorHofer in dotnet/msbuild#14277 * Fix WriteLinesToFile rewriting unchanged file when custom encoding is used by @huulinhnguyen-dev in dotnet/msbuild#14146 * Enable trim/AOT analyzers for Microsoft.Build and clean up annotations by @JeremyKuhne in dotnet/msbuild#14064 * [automated] Merge branch 'vs18.9' => 'main' by @github-actions[bot] in dotnet/msbuild#14291 * Fix MicroBuild plugin feed URL to use allowed pkgs.dev.azure.com format by @AlesProkop in dotnet/msbuild#14295 * Pass ExcludeRestorePackageImports during restore to avoid redundant evaluations by @ViktorHofer with @Copilot in dotnet/msbuild#14274 * [vs18.7] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13988 * Adopt Clever Test Selection (CTS) as parallel, non-blocking PR pipeline by @jankratochvilcz in dotnet/msbuild#14212 * Harden exceptions when connecting to server by @JanProvaznik in dotnet/msbuild#14292 * Update MicrosoftBuildVersion in analyzer template by @github-actions[bot] in dotnet/msbuild#13886 * Fix MSBuild Server client dropping build result under WaitAny race (#14172) by @JanProvaznik in dotnet/msbuild#14251 * Partially revert #13660: remove NuGet RestoreTask transient TaskHost workaround by @JanProvaznik in dotnet/msbuild#14297 * Disable daily AI credits guardrail for Expert Code Review workflow by @JanProvaznik with @Copilot in dotnet/msbuild#14314 * Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 14614733 by @dotnet-bot in dotnet/msbuild#14246 * Add opt-in partial (stop-after-pass) project evaluation by @ViktorHofer in dotnet/msbuild#14290 * Use partial evaluation for -getProperty/-getItem without a target by @ViktorHofer in dotnet/msbuild#14296 * [main] Source code updates from dotnet/dotnet by @dotnet-maestro[bot] in dotnet/msbuild#14324 * [main] Update dependencies from dotnet/roslyn by @dotnet-maestro[bot] in dotnet/msbuild#14333 * [main] Update dependencies from nuget/nuget.client by @dotnet-maestro[bot] in dotnet/msbuild#14330 * Bump github/gh-aw-actions/setup from 0.82.2 to 0.82.8 by @dependabot[bot] in dotnet/msbuild#14328 * Restrict partial evaluation to ProjectInstance by @ViktorHofer in dotnet/msbuild#14340 ... (truncated) Commits viewable in [compare view](dotnet/msbuild@v18.9.6...v18.10.1). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Fixes #4083
Context
This change strengthens regression coverage around newline handling in project file parsing.
Historically, switching from
XmlTextReadertoXmlReader.Createin read-only load paths caused newline/whitespace normalization changes that broke multiline metadata and command scenarios. This PR adds coverage and clarifies why the current reader choice is intentional for compatibility.Changes Made
src/Build.UnitTests/Construction/XmlReaderWithoutLocation_Tests.csto verify multiline metadata with CRLF is preserved:loadProjectsReadOnly: true)src/Build/Xml/XmlReaderExtension.csto clearly document whyXmlTextReaderis used and linked related issues (#4210,#4213,#4083,#6232,#6669).Testing
XmlReaderWithoutLocation_Testsand validated the new regression tests pass.Execregression tests to ensure related behavior remains covered.Notes
This PR does not change runtime parsing behavior; it improves documentation and regression protection for existing compatibility behavior.