From e14475c234a78da0d8e83fac332a095107f0a2bf Mon Sep 17 00:00:00 2001 From: Tyrie Vella Date: Tue, 30 Jun 2026 15:12:33 -0700 Subject: [PATCH] fix: tighten FT slicer grouping regex to class-level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The greedy bucket slicer keeps consecutive tests from the same fixture class together when they share state. The grouping regex was overly broad, using whole namespaces instead of specific class names: OLD: ^.*\.(?:EnlistmentPerFixture|MultiEnlistmentTests)\..+\. NEW: ^.*\.(?:EnlistmentPerFixture\..+|MultiEnlistmentTests\.ConfigVerbTests)\. Two problems fixed: 1. GitCommands namespace (previous PR run): included all GitCommands tests, causing all 38 CheckoutTests(Full) methods (~16 min) to land in one slice. GitCommands tests are safe to split: they use either a per-test enlistment or a git-checkout reset in [SetUp]. 2. MultiEnlistmentTests namespace (this change): included all classes in the namespace, keeping SharedCacheTests (10 tests, ~7 min) and ServiceVerbTests together unnecessarily. - SharedCacheTests: [SetUp] generates a Guid-based cache path per test — no shared state across test methods. Safe to split. - ServiceVerbTests: [NonParallelizable] blocks within-process concurrency; different slices run on different machines. Safe. - ConfigVerbTests: uses [Order(1..8)] with each test reading config written by the previous. Must stay together — kept explicitly. - EnlistmentPerFixture: still grouped per-class as before. From PR #2028 baseline: critical path was 18.9 min. After GitCommands fix: 9.85 min. Expected after this change: ~6-7 min. Assisted-by: Claude Sonnet 4.6 Signed-off-by: Tyrie Vella --- GVFS/GVFS.Tests/NUnitRunner.cs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/GVFS/GVFS.Tests/NUnitRunner.cs b/GVFS/GVFS.Tests/NUnitRunner.cs index 701aac4d4..34d30f83b 100644 --- a/GVFS/GVFS.Tests/NUnitRunner.cs +++ b/GVFS/GVFS.Tests/NUnitRunner.cs @@ -98,16 +98,29 @@ public void PrepareTestSlice(string filters, (uint, uint) testSlice) } // Now distribute the tests into the buckets. - // Tests from the same fixture class must stay in the same bucket - // when the fixture shares a single enlistment across tests (both - // EnlistmentPerFixture classes, MultiEnlistmentTests with [Order], - // and GitCommands fixture classes like GitCommandsTests, CheckoutTests, - // etc. use a shared enlistment). - // The regex captures "everything up to and including the class name" - // so that SomeClass.TestA and SomeClass.TestB share a prefix. + // + // Some test classes share state across test methods and must land in the + // same slice. The regex captures a per-class prefix so that all methods of + // those classes are grouped together. + // + // Classes that MUST stay together: + // EnlistmentPerFixture.* — share a single mounted enlistment for the + // whole fixture; tests may be order-dependent. + // MultiEnlistmentTests.ConfigVerbTests — uses [Order(1..8)]; each test + // reads/writes GVFS config set by the previous. + // + // Classes that are safe to split (do NOT add to this regex): + // GitCommands.* — each test resets state via git-checkout in [SetUp] + // or gets a fresh per-test enlistment. + // SharedCacheTests — [SetUp] generates a new Guid-based cache path per + // test; no shared state across test methods. + // ServiceVerbTests — [NonParallelizable] prevents within-process + // concurrency; different slices run on different + // machines so cross-slice isolation is guaranteed. Regex fixtureRegex = new Regex( - @"^.*\.(?:EnlistmentPerFixture|MultiEnlistmentTests|GitCommands)\..+\.", + @"^.*\.(?:EnlistmentPerFixture\..+|MultiEnlistmentTests\.ConfigVerbTests)\.", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + for (uint i = 0; i < list.Length; i++) { var test = list[i].Trim();