using System;
|
using System.Diagnostics;
|
using System.IO;
|
using System.Threading;
|
using System.Threading.Tasks;
|
using NUnit.Framework;
|
|
namespace OpenTap.Package.UnitTests
|
{
|
[TestFixture]
|
public class NamedMutexTest
|
{
|
public class MutexUsingTestStep : TestStep
|
{
|
public string MutexName { get; set; }
|
public bool AlreadyLocked { get; set; }
|
public override void Run()
|
{
|
using var mut = FileLock.Create(MutexName);
|
if (AlreadyLocked)
|
{
|
if (mut.WaitOne(0))
|
UpgradeVerdict(Verdict.Fail);
|
else
|
UpgradeVerdict(Verdict.Pass);
|
}
|
else
|
{
|
if (mut.WaitOne(0) == false)
|
UpgradeVerdict(Verdict.Fail);
|
else
|
UpgradeVerdict(Verdict.Pass);
|
}
|
}
|
}
|
|
[TestCase(true)]
|
[TestCase(false)]
|
public void TestExclusiveAccessAcrossProcesses(bool alreadyLocked)
|
{
|
var mutexName = Path.GetTempFileName();
|
File.Delete(mutexName);
|
using (var mut = FileLock.Create(mutexName))
|
{
|
var step = new MutexUsingTestStep() { MutexName = mutexName, AlreadyLocked = alreadyLocked };
|
var processRunner = new SubProcessHost();
|
|
if (alreadyLocked)
|
{
|
Assert.IsTrue(mut.WaitOne(0), "Failed initially getting the mutex.");
|
}
|
|
var result = processRunner.Run(step, false, CancellationToken.None);
|
var msg = alreadyLocked ? "Expected the mutex to be locked." : "Expected the mutex to be available.";
|
Assert.AreEqual(Verdict.Pass, result, msg);
|
}
|
}
|
|
[Test]
|
public void TestExclusiveAccess()
|
{
|
var outerEvent = new ManualResetEventSlim(false);
|
var innerEvent = new ManualResetEventSlim(false);
|
var mutexName = Path.GetTempFileName();
|
File.Delete(mutexName);
|
|
using var innerMutex = FileLock.Create(mutexName);
|
// Manually disposed later
|
var outerMutex = FileLock.Create(mutexName);
|
|
{ // Verify the outer mutex works
|
Assert.IsTrue(outerMutex.WaitOne(0));
|
outerMutex.Release();
|
Assert.IsTrue(outerMutex.WaitOne(0));
|
outerMutex.Release();
|
}
|
|
Task.Run(() =>
|
{
|
Assert.IsTrue(innerMutex.WaitOne());
|
innerEvent.Set();
|
outerEvent.Wait();
|
// Sleep for a "long" time before releasing
|
TapThread.Sleep(TimeSpan.FromSeconds(1));
|
innerMutex.Release();
|
});
|
|
// Wait for the inner mutex to be acquired
|
innerEvent.Wait();
|
// Verify this locks the outer mutex
|
Assert.IsFalse(outerMutex.WaitOne(0));
|
// Signal the thread to release the inner mutex
|
var sw = Stopwatch.StartNew();
|
var limit = TimeSpan.FromSeconds(2);
|
outerEvent.Set();
|
// Wait for the inner mutex to be released. Also test that timeouts work
|
Assert.IsTrue(outerMutex.WaitOne(limit));
|
Assert.IsTrue(sw.Elapsed < limit, "Timeout took longer than expected!");
|
// Verify that disposing the mutex releases the lock
|
outerMutex.Dispose();
|
Assert.IsTrue(innerMutex.WaitOne(0));
|
}
|
}
|
}
|