Skip to content

[Bug]: Keyed NotInParallel tests bypass the max parallelism cap #7074

Closed

Description

@jerrosenberg

Bug report checklist

  • I've searched existing issues to ensure this isn't a duplicate
  • I've read the documentation and this isn't expected behavior
  • I'm using the latest version of TUnit

Description

Tests with distinct NotInParallel constraint keys ignore the global parallelism cap. Unkeyed tests and [ParallelGroup] tests respect it, so the problem is specific to the NotInParallel constraint scheduler. Suspected cause: ConstraintKeyScheduler never applies GetMaxParallelism.

An explicit [assembly: ParallelLimiter<...>] does constrain keyed tests, so that is the current workaround.

Expected Behavior

all three classes stay at <= 2 concurrent tests.

Actual Behavior

UnkeyedTests and GroupedTests pass. KeyedTests fails with max concurrent = 8 (every key runs at once).

Steps to Reproduce

Run and compare:

  • dotnet run -- --maximum-parallel-tests 2
  • dotnet run
public sealed class ConcurrencyProbe
{
    int _current, _max;

    public async Task Run()
    {
        var now = Interlocked.Increment(ref _current);
        int seen;
        while (now > (seen = Volatile.Read(ref _max)))
            Interlocked.CompareExchange(ref _max, now, seen);
        await Task.Delay(500);
        Interlocked.Decrement(ref _current);
    }

    public int Max => Volatile.Read(ref _max);
}

// Control 1: no constraints
public class UnkeyedTests
{
    static readonly ConcurrencyProbe P = new();
    [Test] public Task T1() => P.Run();
    [Test] public Task T2() => P.Run();
    [Test] public Task T3() => P.Run();
    [Test] public Task T4() => P.Run();
    [Test] public Task T5() => P.Run();
    [Test] public Task T6() => P.Run();
    [Test] public Task T7() => P.Run();
    [Test] public Task T8() => P.Run();

    [After(Class)]
    public static void Check()
    {
        if (P.Max > 2) throw new Exception($"Unkeyed max concurrent = {P.Max}, expected <= 2");
    }
}

// Control 2: keyed via ParallelGroup
[ParallelGroup("Group1")]
public class GroupedTests
{
    static readonly ConcurrencyProbe P = new();
    [Test] public Task T1() => P.Run();
    [Test] public Task T2() => P.Run();
    [Test] public Task T3() => P.Run();
    [Test] public Task T4() => P.Run();
    [Test] public Task T5() => P.Run();
    [Test] public Task T6() => P.Run();
    [Test] public Task T7() => P.Run();
    [Test] public Task T8() => P.Run();

    [After(Class)]
    public static void Check()
    {
        if (P.Max > 2) throw new Exception($"Grouped max concurrent = {P.Max}, expected <= 2");
    }
}

// Bug: distinct NotInParallel keys
public class KeyedTests
{
    static readonly ConcurrencyProbe P = new();
    [Test, NotInParallel("k1")] public Task T1() => P.Run();
    [Test, NotInParallel("k2")] public Task T2() => P.Run();
    [Test, NotInParallel("k3")] public Task T3() => P.Run();
    [Test, NotInParallel("k4")] public Task T4() => P.Run();
    [Test, NotInParallel("k5")] public Task T5() => P.Run();
    [Test, NotInParallel("k6")] public Task T6() => P.Run();
    [Test, NotInParallel("k7")] public Task T7() => P.Run();
    [Test, NotInParallel("k8")] public Task T8() => P.Run();

    [After(Class)]
    public static void Check()
    {
        if (P.Max > 2) throw new Exception($"Keyed max concurrent = {P.Max}, expected <= 2");
    }
}

TUnit Version

1.75.0

.NET Version

.NET 10.0

Operating System

Windows

IDE / Test Runner

dotnet CLI (dotnet test / dotnet run)

Error Output / Stack Trace

Additional Context

No response

IDE-Specific Issue?

  • I've confirmed this issue occurs when running via dotnet test or dotnet run, not just in my IDE

Activity

  1. added a commit that references this issue on Oct 18, 2026
    a2c34f9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions