| /* |
| * 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 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 |
| Assert.True(actual.IsCompleted); |
| |
| //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 |
| Assert.False(actual.IsCompleted); |
| |
| //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 |
| Assert.IsType<AsyncLockDisposedException>(exception); |
| } |
| |
| [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 |
| Assert.IsType<TaskCanceledException>(exception); |
| |
| //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 |
| Assert.IsType<TaskCanceledException>(exception); |
| |
| //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 |
| Assert.True(disposeTask.IsCompleted); |
| |
| //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 |
| Assert.Null(exception); |
| } |
| } |
| } |