aboutsummaryrefslogtreecommitdiff
path: root/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs')
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs93
1 files changed, 93 insertions, 0 deletions
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs
new file mode 100644
index 0000000000..8149938b4d
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Threading.Tasks;
+using Jellyfin.Server.Implementations.Users;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Users
+{
+ public class UserManagerLockHelperTests
+ {
+ [Fact]
+ public async Task LockAsync_WhenNested_DoesNotAcquireSecondLockAndRestoresStateOnDispose()
+ {
+ UserManager.LockHelper.IsNestedLock.Value = 0;
+ using var helper = new UserManager.LockHelper();
+ var key = Guid.NewGuid();
+
+ Assert.True(helper.ShouldLock());
+
+ var outerHandle = await helper.LockAsync(key);
+ Assert.False(helper.ShouldLock());
+
+ var innerHandle = await helper.LockAsync(key);
+ Assert.False(helper.ShouldLock());
+
+ innerHandle.Dispose();
+ Assert.False(helper.ShouldLock());
+
+ outerHandle.Dispose();
+ Assert.True(helper.ShouldLock());
+ }
+
+ [Fact]
+ public async Task LockAsync_WithSameKey_BlocksSecondLockUntilFirstIsReleased()
+ {
+ UserManager.LockHelper.IsNestedLock.Value = 0;
+ using var helper = new UserManager.LockHelper();
+ var key = Guid.NewGuid();
+
+ var firstAcquired = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
+ var releaseFirst = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
+ var secondEntered = false;
+
+ var firstTask = Task.Run(
+ async () =>
+ {
+ using var firstHandle = await helper.LockAsync(key);
+ firstAcquired.SetResult(true);
+ await releaseFirst.Task;
+ },
+ TestContext.Current.CancellationToken);
+
+ await firstAcquired.Task;
+
+ var secondTask = Task.Run(
+ async () =>
+ {
+ using var secondHandle = await helper.LockAsync(key);
+ secondEntered = true;
+ },
+ TestContext.Current.CancellationToken);
+
+ await Task.Delay(100, TestContext.Current.CancellationToken);
+ Assert.False(secondEntered);
+
+ releaseFirst.SetResult(true);
+
+ await Task.WhenAll(firstTask, secondTask);
+ Assert.True(secondEntered);
+ }
+
+ [Fact]
+ public async Task LockAsync_WhenDisposed_ThrowsObjectDisposedException()
+ {
+ UserManager.LockHelper.IsNestedLock.Value = 0;
+ using var helper = new UserManager.LockHelper();
+ helper.Dispose();
+
+ await Assert.ThrowsAsync<ObjectDisposedException>(async () => await helper.LockAsync(Guid.NewGuid()));
+ }
+
+ [Fact]
+ public void Dispose_WhenCalledMultipleTimes_DoesNotThrow()
+ {
+ UserManager.LockHelper.IsNestedLock.Value = 0;
+ using var helper = new UserManager.LockHelper();
+
+ helper.Dispose();
+ var ex = Record.Exception(() => helper.Dispose());
+
+ Assert.Null(ex);
+ }
+ }
+}