Conditional test filtering framework for PR builds#55166
Conversation
Introduces a framework for conditionally running tests on PR builds based on what source files changed, reducing Helix compute consumption. - Design document (documentation/project-docs/pr-test-filtering.md) - C# evaluation script (scripts/EvaluateConditionalTestScopes.cs) - Central config (test/ConditionalTests.props) with TemplateEngine scope - MSBuild + pipeline integration - Agent instructions update Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
| var targetBranch = GetArg("--target-branch"); | ||
| var buildReason = GetArg("--build-reason") ?? ""; | ||
|
|
||
| // Locate repo root by finding test/ConditionalTests.props relative to the script or cwd. |
There was a problem hiding this comment.
If we refactor, is this at risk of breaking? Can't you pass the repo root from the .csproj and generate a .cs file that's added to <Compile Include="..."/> That way the code always tracks the real root?
There was a problem hiding this comment.
Good point on the refactor fragility. Given this is used as a pipeline script the generated file feels more complex than necessary. I think it would be sufficient to pass in the path to the props file as an arg.
nagilson
left a comment
There was a problem hiding this comment.
Overall, I'm in favor of the proposal.
However, I want to poke on two points:
- How will we define
TriggerPaths?
I think this should be more than the folder containing relevant source code for that repo - e.g.dotnet-watchproject should not be conditioned on justdotnet-watchbut also include theProjectReference(s) such as..\..\Cli\Microsoft.DotNet.Cli.Definitions\Microsoft.DotNet.Cli.Definitions.csproj. Changes toPackageReference(s) such asMicrosoft.Build.Locator"may also result in a break.
dotnetup already uses conditional test filtering to a degree but we've seen more breaks than expected when running all of the tests. So, there is a risk involved in disabling these tests though on average I think it's the right approach. We should try to minimize that risk.
There have been times where we caught product bugs (e.g. system command line, roslyn, and others) because tests in other areas started failing even when roslyn's tests were all passing. But we shouldn't rely on test coverage of others like that.
- When can we ignore 'CoditionalTests' - I think we should be able to ignore that. We can still get a lot of the time saving on codeflow or certain dev PRs... but I think for example in dotnet/dotnet flow we will still want to run all of the tests so we get full filtering.
I'd propose to have the conditional tests condition test runs out on code flow for specific repos - e.g. a change to dotnet/templating code flow shouldn't need to run the watch tests. If we are all encompassing on dependencies with 1 then maybe we can be looser on that?
- Added 'Choosing trigger paths' subsection covering dependency awareness - Moved dependency flow RunAlways future inline with the metadata description - Consistent with how class/method-level futures are described inline Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
@noahfalk, thanks for the insightful feedback.
You are correct that defining just the src path for the affected area is often not sufficient. I did not elaborate on this in the documentation but should call it out in order to help devs and AI make better decisions when defining new test scopes.
Yes this is in ways a big hammer so it must be used correctly. Having good documentation calling these gotchas is imperative.
I do see it valuable to be able to specify a I updated the doc in d518adb in response to this feedback. |
Simplifies the evaluation script by requiring the caller to pass the props file path explicitly. Removes the FindPropsFile fallback. The pipeline YAML now passes the full path via Build.SourcesDirectory. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
| Even these five scopes together represent only ~22% of the total 2,000-minute budget. | ||
| Meaningful overall reduction requires applying this pattern broadly across many test |
There was a problem hiding this comment.
You're proposing starting with these five but potentially expanding it later to more? I assume if there were any more obvious buckets, they'd be here. Is the main remaining problem that we have too many really large or generic test assemblies (like dotnet-tests)?
There was a problem hiding this comment.
Yes, these are some initial scopes to consider mostly used to illustrate the size impact of filtering. I updated the doc to specify some initial aspirational goals which are to define scopes for 1/3 to 1/2 of the total test time. Anything beyond that is additional investment that must be weighed against the ROI.
Also note that tests do not necessarily have to be separated into separate assemblies to be filtered. The document does call out how future support could be added to condition tests at the TestClass level. The SDK currently has some classes that are rather expensive to run where splitting them into a stand alone assembly may not be the best decision.
- Added one-third minimum goal for scope coverage - Clarified 'initial candidates' framing for example scopes - Added 'Choosing trigger paths' section covering dependency awareness - Inlined dependency flow RunAlways future with metadata description Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Notes AI-based test selection and incremental build improvements as aspirational alternatives, framing PR velocity as the primary driver for the current pragmatic approach. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Summary
Introduces a framework for conditionally running tests on PR builds based on what source files changed. Tests that are unrelated to the PR's changes can be skipped, reducing Helix compute consumption and improving PR validation times. All tests continue to run unconditionally on CI builds (
main, release branches).This is posted as a draft to seek feedback on the approach. The implementation is included to illustrate the direction — it is intentionally simple and small despite addressing a complex problem.
Design
See
documentation/project-docs/pr-test-filtering.mdfor the full motivation, mechanisms, and instructions for adding new scopes.What's included
ConditionalTests.props) with TemplateEngine as the first scope