public async Task ShouldWaitAndRelease(SemaphoreSlimLock sut)
    {
        var mutex = GetSemaphore(sut);

        Assert.Equal(1, mutex.CurrentCount);

        await sut.WaitAsync(Cts.Token);

        Assert.Equal(0, mutex.CurrentCount);

        var second = sut.WaitAsync(Cts.Token);

        await Wait();

        Assert.False(second.IsCompleted);
        Assert.Equal(0, mutex.CurrentCount);

        await sut.ReleaseAsync(Cts.Token);

        await second;

        Assert.True(second.IsCompletedSuccessfully);
        Assert.Equal(0, mutex.CurrentCount);

        await sut.ReleaseAsync(Cts.Token);

        Assert.Equal(1, mutex.CurrentCount);

        await AssertThrowsAsync <SemaphoreFullException>(
            () => sut.ReleaseAsync(Cts.Token));
    }
    public async Task WaitAsync_ShouldThrowIfDisposed(SemaphoreSlimLock sut)
    {
        sut.Dispose();

        var exception = await AssertThrowsAsync <ObjectDisposedException>(
            () => sut.WaitAsync(Cts.Token));

        Assert.Equal(typeof(SemaphoreSlimLock).FullName, exception.ObjectName);
    }
    public async Task ShouldCancelWaiting(SemaphoreSlimLock sut)
    {
        var mutex = GetSemaphore(sut);
        await sut.WaitAsync(Cts.Token);

        var second = sut.WaitAsync(Cts.Token);

        await Wait();

        Assert.False(second.IsCompleted);
        Assert.Equal(0, mutex.CurrentCount);

        Cts.Cancel();
        await AssertThrowsAsync <OperationCanceledException>(
            () => second);

        Assert.True(second.IsCanceled);
        Assert.Equal(0, mutex.CurrentCount);
    }