122 lines
3.4 KiB
C#
122 lines
3.4 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Reflection;
|
|
using adas_core.Application.Services.Caching;
|
|
|
|
namespace adas_core.Test.Services;
|
|
|
|
[TestFixture]
|
|
public class InMemoryLockProviderTest
|
|
{
|
|
private InMemoryLockProvider _provider = null!;
|
|
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
_provider = new InMemoryLockProvider();
|
|
}
|
|
|
|
private ConcurrentDictionary<string, SemaphoreSlim> GetLocks()
|
|
{
|
|
var field = typeof(InMemoryLockProvider)
|
|
.GetField("_locks", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
return (ConcurrentDictionary<string, SemaphoreSlim>)field!.GetValue(_provider)!;
|
|
}
|
|
|
|
#region TC-50
|
|
[Test]
|
|
public async Task AcquireAsync_ReturnsTrue_AndCreatesEntryInLocksDictionary()
|
|
{
|
|
var result = await _provider.AcquireAsync("key", TimeSpan.FromSeconds(1));
|
|
|
|
Assert.That(result, Is.True);
|
|
|
|
var locks = GetLocks();
|
|
Assert.That(locks.ContainsKey("key"), Is.True);
|
|
}
|
|
#endregion
|
|
|
|
#region TC-51
|
|
[Test]
|
|
public async Task AcquireAsync_ReturnsFalse_WhenSemaphoreOccupiedAndTimeoutExpires()
|
|
{
|
|
await _provider.AcquireAsync("key", TimeSpan.FromSeconds(5));
|
|
|
|
var result = await _provider.AcquireAsync("key", TimeSpan.FromMilliseconds(100));
|
|
|
|
Assert.That(result, Is.False);
|
|
|
|
await _provider.ReleaseAsync("key");
|
|
|
|
var locks = GetLocks();
|
|
Assert.That(locks["key"].CurrentCount, Is.EqualTo(1));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-52
|
|
[Test]
|
|
public async Task ReleaseAsync_MakesSemaphoreAvailableForNextAcquire()
|
|
{
|
|
await _provider.AcquireAsync("key", TimeSpan.FromSeconds(1));
|
|
await _provider.ReleaseAsync("key");
|
|
|
|
var result = await _provider.AcquireAsync("key", TimeSpan.FromSeconds(1));
|
|
|
|
Assert.That(result, Is.True);
|
|
|
|
var locks = GetLocks();
|
|
Assert.That(locks["key"].CurrentCount, Is.EqualTo(0));
|
|
|
|
await _provider.ReleaseAsync("key");
|
|
|
|
Assert.That(locks["key"].CurrentCount, Is.EqualTo(1));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-53
|
|
[Test]
|
|
public async Task ReleaseAsync_DoesNotThrow_WhenCalledWithoutPriorAcquire()
|
|
{
|
|
await _provider.ReleaseAsync("nonexistent-key");
|
|
|
|
await _provider.AcquireAsync("key", TimeSpan.FromSeconds(1));
|
|
await _provider.ReleaseAsync("key");
|
|
await _provider.ReleaseAsync("key");
|
|
}
|
|
#endregion
|
|
|
|
#region TC-54
|
|
[Test]
|
|
public async Task AcquireAsync_IsThreadSafe_OnlyOneHolderAtATime_SingleSemaphorePerKey()
|
|
{
|
|
const string key = "same-key";
|
|
const int threadCount = 10;
|
|
var acquiredCount = 0;
|
|
var currentHolders = 0;
|
|
|
|
var tasks = Enumerable.Range(0, threadCount).Select(_ => Task.Run(async () =>
|
|
{
|
|
var acquired = await _provider.AcquireAsync(key, TimeSpan.FromSeconds(10));
|
|
|
|
Assert.That(acquired, Is.True);
|
|
|
|
var current = Interlocked.Increment(ref currentHolders);
|
|
Assert.That(current, Is.EqualTo(1));
|
|
|
|
Interlocked.Increment(ref acquiredCount);
|
|
await Task.Delay(5);
|
|
|
|
Interlocked.Decrement(ref currentHolders);
|
|
await _provider.ReleaseAsync(key);
|
|
})).ToArray();
|
|
|
|
await Task.WhenAll(tasks);
|
|
|
|
Assert.That(acquiredCount, Is.EqualTo(threadCount));
|
|
|
|
var locks = GetLocks();
|
|
Assert.That(locks.ContainsKey(key), Is.True);
|
|
Assert.That(locks.Keys.Count(k => k == key), Is.EqualTo(1));
|
|
}
|
|
#endregion
|
|
}
|