Skip to content

Commit 86e6258

Browse files
Speed up Lite tests: enable parallelization + share DuckDB in FinOpsTests
The CI test step was averaging ~7 minutes for 257 tests because xUnit's default parallelization was implicit and slow classes (FactCollectorTests, ScenarioTests) blocked the pipeline. Local runs of the full suite went from sequential-feeling runs to ~2m21s with explicit parallelization config. Changes: - Add Lite.Tests/xunit.runner.json with explicit parallelization settings: parallelizeAssembly=true, parallelizeTestCollections=true, maxParallelThreads=-1 (use all cores), parallelAlgorithm=aggressive. CopyToOutputDirectory wired up in the csproj. - Serialize BaselineProviderTests and AnomalyDetectorTests into a shared collection (BaselineProviderCollection, DisableParallelization=true). Both classes mutate the static BaselineProvider.CacheTtl field, so they must run sequentially relative to each other; without this, parallel runs would race on the static state. - Convert FinOpsTests to IClassFixture<FinOpsDuckDbFixture>, sharing one DuckDB across the class. Every seeder in TestDataSeeder.SeedFinOps* already calls ClearTestDataAsync first, so cross-test pollution is prevented without rewriting test code. Saves the schema-init cost on each test (modest, but free). This is a pilot for the IClassFixture approach. The agent's analysis flagged that converting other classes (FactCollectorTests etc.) would require adding ClearTestDataAsync calls to many tests, so leaving those alone for now. Parallel execution across classes is the bigger win and is fully covered by the runner.json settings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 76d825f commit 86e6258

7 files changed

Lines changed: 64 additions & 18 deletions

Lite.Tests/AnomalyDetectorTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace PerformanceMonitorLite.Tests;
1515
/// methods (batch requests, sessions, query duration, memory), per-metric thresholds,
1616
/// and baseline context metadata.
1717
/// </summary>
18+
[Collection(BaselineProviderCollection.Name)]
1819
public class AnomalyDetectorTests : IDisposable
1920
{
2021
private readonly string _tempDir;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Xunit;
2+
3+
namespace PerformanceMonitorLite.Tests;
4+
5+
/* BaselineProviderTests and AnomalyDetectorTests both mutate the static
6+
BaselineProvider.CacheTtl field. With assembly-level parallelization
7+
enabled, those two classes must share a collection so they don't run
8+
concurrently and stomp on each other's TTL writes. */
9+
[CollectionDefinition(Name, DisableParallelization = true)]
10+
public class BaselineProviderCollection
11+
{
12+
public const string Name = "BaselineProvider";
13+
}

Lite.Tests/BaselineProviderTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace PerformanceMonitorLite.Tests;
1313
/// Tests for BaselineProvider: time-bucketed baseline computation, bucket collapse
1414
/// with hysteresis, restart poisoning exclusion, and division-by-zero handling.
1515
/// </summary>
16+
[Collection(BaselineProviderCollection.Name)]
1617
public class BaselineProviderTests : IDisposable
1718
{
1819
private readonly string _tempDir;

Lite.Tests/FinOpsDuckDbFixture.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.IO;
3+
using PerformanceMonitorLite.Database;
4+
5+
namespace PerformanceMonitorLite.Tests;
6+
7+
/* Shared DuckDB fixture for FinOpsTests. xUnit instantiates this once per
8+
test class (via IClassFixture<>), so all tests in the class share a
9+
single DuckDB file. Each test seeder calls ClearTestDataAsync first
10+
(via the *Async helpers in TestDataSeeder), so cross-test pollution
11+
is prevented without paying the schema-init cost on every test. */
12+
public sealed class FinOpsDuckDbFixture : IDisposable
13+
{
14+
public string TempDir { get; }
15+
public string DbPath { get; }
16+
public DuckDbInitializer DuckDb { get; }
17+
18+
public FinOpsDuckDbFixture()
19+
{
20+
TempDir = Path.Combine(Path.GetTempPath(), "FinOpsTests_" + Guid.NewGuid().ToString("N")[..8]);
21+
Directory.CreateDirectory(TempDir);
22+
DbPath = Path.Combine(TempDir, "test.duckdb");
23+
DuckDb = new DuckDbInitializer(DbPath);
24+
}
25+
26+
public void Dispose()
27+
{
28+
try
29+
{
30+
if (Directory.Exists(TempDir))
31+
Directory.Delete(TempDir, recursive: true);
32+
}
33+
catch { /* Best-effort cleanup */ }
34+
}
35+
}

Lite.Tests/FinOpsTests.cs

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,13 @@ namespace PerformanceMonitorLite.Tests;
1515
/// Each test seeds a specific server profile into DuckDB, runs the recommendation or
1616
/// scoring engine, and validates the output (categories, findings, severity, savings).
1717
/// </summary>
18-
public class FinOpsTests : IDisposable
18+
public class FinOpsTests : IClassFixture<FinOpsDuckDbFixture>
1919
{
20-
private readonly string _tempDir;
21-
private readonly string _dbPath;
2220
private readonly DuckDbInitializer _duckDb;
2321

24-
public FinOpsTests()
22+
public FinOpsTests(FinOpsDuckDbFixture fixture)
2523
{
26-
_tempDir = Path.Combine(Path.GetTempPath(), "FinOpsTests_" + Guid.NewGuid().ToString("N")[..8]);
27-
Directory.CreateDirectory(_tempDir);
28-
_dbPath = Path.Combine(_tempDir, "test.duckdb");
29-
_duckDb = new DuckDbInitializer(_dbPath);
30-
}
31-
32-
public void Dispose()
33-
{
34-
try
35-
{
36-
if (Directory.Exists(_tempDir))
37-
Directory.Delete(_tempDir, recursive: true);
38-
}
39-
catch { /* Best-effort cleanup */ }
24+
_duckDb = fixture.DuckDb;
4025
}
4126

4227
/* ── Over-Provisioned Enterprise ── */

Lite.Tests/Lite.Tests.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,8 @@
2020
<ItemGroup>
2121
<ProjectReference Include="..\Lite\PerformanceMonitorLite.csproj" />
2222
</ItemGroup>
23+
24+
<ItemGroup>
25+
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
26+
</ItemGroup>
2327
</Project>

Lite.Tests/xunit.runner.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"$schema": "https://xunit.net/schema/v3/xunit.runner.schema.json",
3+
"parallelizeAssembly": true,
4+
"parallelizeTestCollections": true,
5+
"maxParallelThreads": -1,
6+
"parallelAlgorithm": "aggressive"
7+
}

0 commit comments

Comments
 (0)