blob: bbd85948c529c232f66163aa40604fd13c2e08db [file] [log] [blame]
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace DotPulsar.Tests.Internal
{
using DotPulsar.Internal;
using DotPulsar.Internal.Exceptions;
using FluentAssertions;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
public class AsyncLockTests
{
[Fact]
public async Task Lock_GivenLockIsFree_ShouldReturnCompletedTask()
{
//Arrange
var sut = new AsyncLock();
//Act
var actual = sut.Lock(CancellationToken.None);
//Assert
actual.IsCompleted.Should().BeTrue();
//Annihilate
actual.Result.Dispose();
await sut.DisposeAsync().ConfigureAwait(false);
}
[Fact]
public async Task Lock_GivenLockIsTaken_ShouldReturnIncompletedTask()
{
//Arrange
var sut = new AsyncLock();
var alreadyTaken = await sut.Lock(CancellationToken.None).ConfigureAwait(false);
//Act
var actual = sut.Lock(CancellationToken.None);
//Assert
actual.IsCompleted.Should().BeFalse();
//Annihilate
alreadyTaken.Dispose();
actual.Result.Dispose();
await sut.DisposeAsync().ConfigureAwait(false);
}
[Fact]
public async Task Lock_GivenLockIsDisposed_ShouldThrowAsyncLockDisposedException()
{
//Arrange
var sut = new AsyncLock();
await sut.DisposeAsync().ConfigureAwait(false);
//Act
var exception = await Record.ExceptionAsync(() => sut.Lock(CancellationToken.None)).ConfigureAwait(false);
//Assert
exception.Should().BeOfType<AsyncLockDisposedException>();
}
[Fact]
public async Task Lock_GivenLockIsDisposedWhileAwaitingLock_ShouldThrowTaskCanceledException()
{
//Arrange
var sut = new AsyncLock();
var gotLock = await sut.Lock(CancellationToken.None).ConfigureAwait(false);
var awaiting = sut.Lock(CancellationToken.None);
_ = Task.Run(async () => await sut.DisposeAsync().ConfigureAwait(false));
//Act
var exception = await Record.ExceptionAsync(() => awaiting).ConfigureAwait(false);
//Assert
exception.Should().BeOfType<TaskCanceledException>();
//Annihilate
await sut.DisposeAsync().ConfigureAwait(false);
gotLock.Dispose();
}
[Fact]
public async Task Lock_GivenLockIsTakenAndCancellationTokenIsActivated_ShouldThrowTaskCanceledException()
{
//Arrange
var cts = new CancellationTokenSource();
var sut = new AsyncLock();
var gotLock = await sut.Lock(CancellationToken.None).ConfigureAwait(false);
var awaiting = sut.Lock(cts.Token);
//Act
cts.Cancel();
var exception = await Record.ExceptionAsync(() => awaiting).ConfigureAwait(false);
//Assert
exception.Should().BeOfType<TaskCanceledException>();
//Annihilate
cts.Dispose();
gotLock.Dispose();
await sut.DisposeAsync().ConfigureAwait(false);
}
[Fact]
public async Task Dispose_GivenLockIsDisposedWhileItIsTaken_ShouldNotCompleteBeforeItIsReleased()
{
//Arrange
var sut = new AsyncLock();
var gotLock = await sut.Lock(CancellationToken.None).ConfigureAwait(false);
var disposeTask = Task.Run(async () => await sut.DisposeAsync().ConfigureAwait(false));
Assert.False(disposeTask.IsCompleted);
//Act
gotLock.Dispose();
await disposeTask.ConfigureAwait(false);
//Assert
disposeTask.IsCompleted.Should().BeTrue();
//Annihilate
await sut.DisposeAsync().ConfigureAwait(false);
}
[Fact]
public async Task Dispose_WhenCalledMultipleTimes_ShouldBeSafeToDoSo()
{
//Arrange
var sut = new AsyncLock();
//Act
await sut.DisposeAsync().ConfigureAwait(false);
var exception = await Record.ExceptionAsync(() => sut.DisposeAsync().AsTask()).ConfigureAwait(false); // xUnit can't record ValueTask yet
//Assert
exception.Should().BeNull();
}
}
}