Skip to content

[Draft] Support Microsoft.Build.Traversal projects in dotnet test (MTP)#55297

Open
Evangelink wants to merge 3 commits into
mainfrom
dev/amauryleve/dotnet-test-traversal-msbuild-apis
Open

[Draft] Support Microsoft.Build.Traversal projects in dotnet test (MTP)#55297
Evangelink wants to merge 3 commits into
mainfrom
dev/amauryleve/dotnet-test-traversal-msbuild-apis

Conversation

@Evangelink

@Evangelink Evangelink commented Jul 15, 2026

Copy link
Copy Markdown
Member

Summary

Draft / proof-of-concept that unblocks traversal project (dirs.proj) support for dotnet test with Microsoft.Testing.Platform. Fixes the scenario in #51316.

The approach treats a Microsoft.Build.Traversal project as a container that forwards to its ProjectReference items — the same mental model as a solution.

Root cause

Two independent problems both had to be fixed (a third surfaced while testing):

  1. Build side — the _MTPBuild target (Microsoft.TestPlatform.ImportAfter.targets) only builds $(MSBuildProjectFullPath) when IsTestingPlatformApplication == true. A traversal project is not an MTP app, so nothing (including its referenced test projects) was built.
  2. Evaluation side — the CLI treated the traversal project as a single, non-test project, found no test module, and errored out.
  3. Arg parsing (found via testing) — .proj was not a recognized positional project extension, so dotnet test dirs.proj leaked dirs.proj to the test host as an invalid argument (MTP exit code 5).

Changes

File Change
SolutionAndProjectUtility.cs Special-case the IsTraversal property. When a project is a traversal project, expand it into its resolved ProjectReference items and evaluate each recursively (nested traversal). De-duplicates across the whole traversal graph (diamond references / cycle guard) via a threaded visited-set, and honors per-reference Configuration/Platform metadata (falling back to inherited values).
MSBuildUtility.cs Recognize any *proj extension as a positional project (mirrors ValidateProjectOrSolutionPath), so the traversal path isn't forwarded to the test host as an argument.
CliConstants.cs Added IsTraversal / ProjectReference names.
Microsoft.TestPlatform.ImportAfter.targets _MTPBuild now forwards _MTPBuild to @(ProjectReference) when IsTraversal == true, so the referenced test projects actually build.
TraversalTestProjects/ + test Basic asset (dirs.proj + two MTP apps) and RunTraversalProject_ShouldRunReferencedTestProjects.
TraversalTestProjectsNested/ + test Nested/diamond asset and RunNestedTraversalProjectWithDiamond_ShouldRunSharedProjectOnce. Test apps drop a unique marker file per launch so the test deterministically asserts the shared (diamond) project runs exactly once and the leaf-only project (reachable solely via nested traversal) also runs — proving recursion + de-dup.

Validation

  • Full build succeeds (0 warnings / 0 errors).
  • New tests pass for both Debug and Release: referenced projects are discovered, built, and executed via the traversal; diamond de-dup and nested recursion are verified via marker files.
  • Related project/solution/positional-arg tests still pass (no regression).

Notes / open questions for reviewers

  • On "MSBuild APIs": no new public MSBuild API turned out to be strictly required for a working draft — the resolved ProjectReference items from evaluation are sufficient, and MSBuild already expands globs/conditions during evaluation. A cleaner long-term design would want a public traversal API to get the effective project set (analogous to SolutionFile.ProjectsInOrder) rather than relying on raw ProjectReference items — similar in spirit to the existing ProjectShouldBuild UnsafeAccessor workaround used for solutions (see MSBuild should publish a library that helps tools interact with Solution files and other container-ish project types msbuild#12711).
  • Per-reference metadata support currently covers Configuration/Platform; the P2P Set*/AdditionalProperties forms are not yet parsed (the Set* form uses a Name=Value string that would need parsing).
  • The _MTPBuild forwarding is intentionally minimal for this draft (no explicit BuildInParallel/property forwarding). The cross-targeting _MTPBuild variant is left unchanged since traversal projects are not cross-targeting.
  • Remaining test-coverage ideas: a traversal referencing a non-test project (should be skipped silently) and a mixed MTP + VSTest traversal (should hit the existing unsupported-VSTest error).

Closes #51316 (once finalized).

Unblocks traversal (dirs.proj) support for 'dotnet test' with Microsoft.Testing.Platform (issue #51316).

Three coordinated changes mirror how solutions are handled - the traversal project is a
container that forwards to its ProjectReference items:

- CLI evaluation: special-case the 'IsTraversal' MSBuild property in
  SolutionAndProjectUtility.GetProjectProperties. When a project is a traversal project,
  expand it into its resolved ProjectReference items and evaluate each recursively (so
  nested traversal projects work).
- CLI arg parsing: recognize any '*proj' extension (not just .csproj/.vbproj/.fsproj) as a
  positional project in MSBuildUtility.GetPositionalArguments, matching
  ValidateProjectOrSolutionPath. Without this, 'dotnet test dirs.proj' leaked 'dirs.proj'
  to the test host as an invalid argument (MTP exit code 5).
- Targets: the _MTPBuild target now forwards to @(ProjectReference) when IsTraversal is
  true, so the referenced test projects actually build.

Adds a TraversalTestProjects test asset and RunTraversalProject_ShouldRunReferencedTestProjects
test.
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
2 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

…ata for traversal

- De-duplicate referenced projects across the entire traversal graph (not just within a
  single traversal file) by threading a visited-set through the recursion. This prevents a
  project referenced by multiple traversal projects (a diamond) from being tested twice and
  guards against reference cycles.
- Honor per-reference Configuration/Platform metadata on ProjectReference items, falling back
  to the values inherited from the traversal project.
- Add a nested/diamond test asset (TraversalTestProjectsNested) and
  RunNestedTraversalProjectWithDiamond_ShouldRunSharedProjectOnce. The test apps drop a unique
  marker file per launch so the test deterministically asserts the shared project runs exactly
  once and the leaf-only project (reachable solely via nested traversal) also runs once.
@Evangelink

Copy link
Copy Markdown
Member Author

@baronfel given there is no ETA or news for when transversal APIs will be available, I went ahead with doing some "hacks" as a simplified version which should give us a first support for dotnet test with traversal.

@Evangelink Evangelink marked this pull request as ready for review July 15, 2026 10:54
@Evangelink Evangelink requested a review from a team as a code owner July 15, 2026 10:54
Copilot AI review requested due to automatic review settings July 15, 2026 10:54
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
2 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds draft traversal-project (Microsoft.Build.Traversal / dirs.proj) support to dotnet test when using Microsoft.Testing.Platform by expanding traversal containers into their referenced projects for evaluation and ensuring referenced projects get built.

Changes:

  • Teach MTP project evaluation to detect traversal projects and recursively expand their ProjectReference graph (with de-dup/cycle-guard).
  • Update argument parsing to treat any *proj file as a positional project so it isn’t forwarded to the test host.
  • Add MTP traversal test assets + tests, and update _MTPBuild to forward to @(ProjectReference) for traversal projects.
Show a summary per file
File Description
src/Cli/dotnet/Commands/Test/MTP/SolutionAndProjectUtility.cs Detects traversal projects and recursively evaluates referenced projects (incl. de-dup).
src/Cli/dotnet/Commands/Test/MTP/MSBuildUtility.cs Broadens positional project detection to any extension ending in proj.
src/Cli/dotnet/Commands/Test/CliConstants.cs Adds MSBuild property/item names used for traversal support.
src/Layout/redist/MSBuildImports/Current/Microsoft.Common.targets/ImportAfter/Microsoft.TestPlatform.ImportAfter.targets For traversal projects, forwards _MTPBuild to referenced projects.
test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTests.cs Adds tests covering traversal execution, nesting, and diamond de-dup.
test/TestAssets/TestProjects/TraversalTestProjects/global.json Configures test assets to use Microsoft.Testing.Platform runner.
test/TestAssets/TestProjects/TraversalTestProjects/dirs.proj Simple traversal project referencing two MTP test apps.
test/TestAssets/TestProjects/TraversalTestProjects/TestProject/TestProject.csproj MTP “test app” project used by traversal asset.
test/TestAssets/TestProjects/TraversalTestProjects/TestProject/Program.cs Minimal dummy MTP adapter app for traversal tests.
test/TestAssets/TestProjects/TraversalTestProjects/OtherTestProject/OtherTestProject.csproj Second MTP “test app” project used by traversal asset.
test/TestAssets/TestProjects/TraversalTestProjects/OtherTestProject/Program.cs Minimal dummy MTP adapter app for traversal tests.
test/TestAssets/TestProjects/TraversalTestProjectsNested/global.json Configures nested traversal assets to use Microsoft.Testing.Platform runner.
test/TestAssets/TestProjects/TraversalTestProjectsNested/dirs.proj Top-level traversal with nested traversal and a diamond reference.
test/TestAssets/TestProjects/TraversalTestProjectsNested/sub/dirs.proj Nested traversal referencing shared + leaf MTP projects.
test/TestAssets/TestProjects/TraversalTestProjectsNested/SharedTestProject/SharedTestProject.csproj Shared MTP “test app” used to validate diamond de-dup.
test/TestAssets/TestProjects/TraversalTestProjectsNested/SharedTestProject/Program.cs Drops marker files to deterministically count runs for de-dup validation.
test/TestAssets/TestProjects/TraversalTestProjectsNested/LeafTestProject/LeafTestProject.csproj Leaf-only MTP “test app” reachable only through nested traversal.
test/TestAssets/TestProjects/TraversalTestProjectsNested/LeafTestProject/Program.cs Drops marker files to deterministically count runs for recursion validation.

Copilot's findings

  • Files reviewed: 18/18 changed files
  • Comments generated: 3

Comment thread src/Cli/dotnet/Commands/Test/MTP/SolutionAndProjectUtility.cs
Comment thread src/Cli/dotnet/Commands/Test/MTP/SolutionAndProjectUtility.cs Outdated
…targets metadata forwarding

Addresses three Copilot review comments on #55297:

- Resolve ProjectReference 'FullPath' relative to the traversal project directory via
  Path.GetFullPath(fullPath, projectDirectory) instead of the process working directory.
- Include Configuration/Platform in the traversal de-duplication key so the same project
  referenced with different Configuration/Platform is tested for each distinct combination,
  while true diamonds (same combination) are still de-duplicated. Introduces
  GetTraversalVisitKey and keys the traversal project's own visited entry the same way.
- _MTPBuild now forwards per-reference Configuration/Platform metadata as global properties,
  consistent with the evaluation logic. Property assignments are precomputed in an ItemGroup
  so empty metadata never emits 'Configuration=' (which would clear the inherited global
  property); empty segments in Properties are ignored by MSBuild.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dotnet test support for traversal projects in .NET 10

2 participants