This repository has been archived on 2023-02-02. You can view files and clone it, but cannot push or open issues or pull requests.
mfgames-locking-cil/tests/MfGames.Locking.Tests/LockTests.cs

229 lines
6.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using KellermanSoftware.CompareNetObjects;
using Xunit;
using Xunit.Sdk;
namespace MfGames.Locking.Tests
{
public class LockTests
{
private readonly ReaderWriterLockSlim locker;
private readonly List<(int, LockAction)> sequenceRecord;
private readonly object syncLock;
public LockTests()
{
this.syncLock = new object();
this.sequenceRecord = new List<(int, LockAction)>();
this.locker = new ReaderWriterLockSlim();
}
[Fact]
public void ReadBlocksWrite()
{
Task.WaitAll(
Task.Run(() => this.TestRead(1, 5, 1)),
Task.Run(() => this.TestWrite(2, 2, 2)));
this.VerifySequenceRecord(
(1, LockAction.BeforeLock),
(1, LockAction.InLock),
(2, LockAction.BeforeLock),
(1, LockAction.AfterLock),
(2, LockAction.InLock),
(2, LockAction.AfterLock));
}
[Fact]
public void ReadsDoNotBlockReads()
{
Task.WaitAll(
Task.Run(() => this.TestRead(1, 8, 1)),
Task.Run(() => this.TestRead(2, 1, 2)),
Task.Run(() => this.TestRead(4, 1, 3)));
this.VerifySequenceRecord(
(1, LockAction.BeforeLock),
(1, LockAction.InLock),
(2, LockAction.BeforeLock),
(2, LockAction.InLock),
(2, LockAction.AfterLock),
(3, LockAction.BeforeLock),
(3, LockAction.InLock),
(3, LockAction.AfterLock),
(1, LockAction.AfterLock));
}
[Fact]
public void UpgradableBlocksUpgradable()
{
Task.WaitAll(
Task.Run(() => this.TestUpgradable(1, 10, 1)),
Task.Run(() => this.TestUpgradable(2, 2, 2)),
Task.Run(() => this.TestUpgradable(5, 1, 3)));
this.VerifySequenceRecord(
(1, LockAction.BeforeLock),
(1, LockAction.InLock),
(2, LockAction.BeforeLock),
(3, LockAction.BeforeLock),
(1, LockAction.AfterLock),
(2, LockAction.InLock),
(2, LockAction.AfterLock),
(3, LockAction.InLock),
(3, LockAction.AfterLock));
}
[Fact]
public void UpgradableDoesNotBlockReads()
{
Task.WaitAll(
Task.Run(() => this.TestUpgradable(1, 6, 1)),
Task.Run(() => this.TestRead(2, 1, 2)),
Task.Run(() => this.TestRead(4, 1, 3)));
this.VerifySequenceRecord(
(1, LockAction.BeforeLock),
(1, LockAction.InLock),
(2, LockAction.BeforeLock),
(2, LockAction.InLock),
(2, LockAction.AfterLock),
(3, LockAction.BeforeLock),
(3, LockAction.InLock),
(3, LockAction.AfterLock),
(1, LockAction.AfterLock));
}
[Fact]
public void WriteBlockRead()
{
Task.WaitAll(
Task.Run(() => this.TestWrite(1, 6, 1)),
Task.Run(() => this.TestRead(2, 1, 2)));
this.VerifySequenceRecord(
(1, LockAction.BeforeLock),
(1, LockAction.InLock),
(2, LockAction.BeforeLock),
(1, LockAction.AfterLock),
(2, LockAction.InLock),
(2, LockAction.AfterLock));
}
[Fact]
public void WriteBlockReads()
{
Task.WaitAll(
Task.Run(() => this.TestWrite(1, 6, 1)),
Task.Run(() => this.TestRead(2, 1, 2)));
this.VerifySequenceRecord(
(1, LockAction.BeforeLock),
(1, LockAction.InLock),
(2, LockAction.BeforeLock),
(1, LockAction.AfterLock),
(2, LockAction.InLock),
(2, LockAction.AfterLock));
}
private void RecordSequenceAction(int sequence, LockAction action)
{
lock (this.syncLock)
{
this.sequenceRecord.Add((sequence, action));
}
}
private void Test(
Func<ReaderWriterLockSlim, IDisposable> getLock,
int settleTocks,
int waitTocks,
int sequence)
{
const int TocksInMilliseconds = 200;
Thread.Sleep(TocksInMilliseconds * settleTocks);
this.RecordSequenceAction(sequence, LockAction.BeforeLock);
using (getLock(this.locker))
{
Thread.Sleep(100);
this.RecordSequenceAction(sequence, LockAction.InLock);
Thread.Sleep(TocksInMilliseconds * waitTocks);
this.RecordSequenceAction(sequence, LockAction.AfterLock);
}
}
private void TestRead(
int settleTocks,
int waitTocks,
int sequence)
{
this.Test(
x => new ReadLock(x),
settleTocks,
waitTocks,
sequence);
}
private void TestUpgradable(
int settleTocks,
int waitTocks,
int sequence)
{
this.Test(
x => new UpgradableLock(x),
settleTocks,
waitTocks,
sequence);
}
private void TestWrite(
int settleTocks,
int waitTocks,
int sequence)
{
this.Test(
x => new WriteLock(x),
settleTocks,
waitTocks,
sequence);
}
private void VerifySequenceRecord(
params (int, LockAction BeforeLock)[] values)
{
lock (this.syncLock)
{
var compareLogic = new CompareLogic
{
Config =
{
IgnoreObjectTypes = true,
MaxDifferences = 2,
}
};
ComparisonResult result = compareLogic.Compare(
values,
this.sequenceRecord);
if (!result.AreEqual)
{
throw new XunitException(result.DifferencesString);
}
}
}
}
}