diff options
| author | JPVenson <github@jpb.email> | 2026-06-07 14:29:36 -0400 |
|---|---|---|
| committer | Bond_009 <bond.009@outlook.com> | 2026-06-07 14:29:36 -0400 |
| commit | 6ccdaad0a47351d30cfde39c7470ae26bb6f7567 (patch) | |
| tree | 70fc279ec70be18c4214b8548af17edaf8bb8336 /tests | |
| parent | ec43ea156e11705227eccbe760e2510a28d774a4 (diff) | |
Backport pull request #16944 from jellyfin/release-10.11.z
Add lockhelper for UserManager
Original-merge: 39958ad9e51f83f520803aa9bc5e70e58f3a0836
Merged-by: Bond-009 <bond.009@outlook.com>
Backported-by: Bond_009 <bond.009@outlook.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs | 89 |
1 files changed, 89 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..ab6f0fd32e --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs @@ -0,0 +1,89 @@ +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; + }); + + await firstAcquired.Task; + + var secondTask = Task.Run(async () => + { + using var secondHandle = await helper.LockAsync(key); + secondEntered = true; + }); + + await Task.Delay(100); + 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); + } + } +} |
