|
|
using System;
|
|
|
using System.Reflection;
|
|
|
using System.Threading;
|
|
|
using Implab.Parallels;
|
|
|
using Implab.Components;
|
|
|
using Implab.Test.Mock;
|
|
|
|
|
|
#if MONO
|
|
|
|
|
|
using NUnit.Framework;
|
|
|
using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
|
|
|
using TestMethodAttribute = NUnit.Framework.TestAttribute;
|
|
|
using AssertFailedException = NUnit.Framework.AssertionException;
|
|
|
#else
|
|
|
|
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
namespace Implab.Test {
|
|
|
[TestClass]
|
|
|
public class PollingComponentTests {
|
|
|
static void ShouldThrow(Action action) {
|
|
|
try {
|
|
|
action();
|
|
|
Assert.Fail();
|
|
|
} catch (AssertFailedException) {
|
|
|
throw;
|
|
|
} catch {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
[TestMethod]
|
|
|
public void NormalFlowTest() {
|
|
|
var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, false);
|
|
|
|
|
|
Assert.AreEqual(ExecutionState.Created, comp.State);
|
|
|
|
|
|
comp.Initialize();
|
|
|
|
|
|
Assert.AreEqual(ExecutionState.Ready, comp.State);
|
|
|
|
|
|
comp.Start().Join(1000);
|
|
|
|
|
|
Assert.AreEqual(ExecutionState.Running, comp.State);
|
|
|
|
|
|
comp.Stop().Join(1000);
|
|
|
|
|
|
Assert.AreEqual(ExecutionState.Disposed, comp.State);
|
|
|
|
|
|
}
|
|
|
|
|
|
[TestMethod]
|
|
|
public void ShouldStartTicks() {
|
|
|
var signal = new Signal();
|
|
|
|
|
|
var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
|
|
|
comp.MockTick = ct => {
|
|
|
signal.Set();
|
|
|
return Promise.Success;
|
|
|
};
|
|
|
|
|
|
comp.Start().Join(1000);
|
|
|
signal.Wait(1000);
|
|
|
comp.Stop().Join(1000);
|
|
|
}
|
|
|
|
|
|
[TestMethod]
|
|
|
public void StopShouldWaitForTickToComplete() {
|
|
|
var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
|
|
|
var signal = new Signal();
|
|
|
var promise = new Promise();
|
|
|
|
|
|
// timer should tick once
|
|
|
comp.MockTick = ct => {
|
|
|
signal.Set();
|
|
|
return promise;
|
|
|
};
|
|
|
|
|
|
// start timer
|
|
|
comp.Start().Join(1000);
|
|
|
|
|
|
signal.Wait(); // wait for tick
|
|
|
|
|
|
// try to stop component
|
|
|
var stopping = comp.Stop();
|
|
|
|
|
|
Assert.AreEqual(ExecutionState.Stopping, comp.State);
|
|
|
ShouldThrow(() => stopping.Join(100));
|
|
|
Assert.AreEqual(ExecutionState.Stopping, comp.State);
|
|
|
|
|
|
// complete operation
|
|
|
promise.Resolve();
|
|
|
|
|
|
// the component should stop normally
|
|
|
stopping.Join(1000);
|
|
|
|
|
|
Assert.AreEqual(ExecutionState.Disposed, comp.State);
|
|
|
}
|
|
|
|
|
|
[TestMethod]
|
|
|
public void ShouldRecoverAfterTickError() {
|
|
|
var ticks = 0;
|
|
|
|
|
|
var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
|
|
|
var signal = new Signal(); // will signal when timer fires 10 times
|
|
|
|
|
|
comp.MockTick = ct => {
|
|
|
ticks++;
|
|
|
if (ticks == 10)
|
|
|
signal.Set();
|
|
|
// each time handler dies
|
|
|
throw new Exception("tainted handler");
|
|
|
};
|
|
|
|
|
|
comp.Start();
|
|
|
|
|
|
signal.Wait(1000);
|
|
|
|
|
|
comp.Stop().Join(1000);
|
|
|
|
|
|
Assert.AreEqual(ExecutionState.Disposed, comp.State);
|
|
|
}
|
|
|
|
|
|
[TestMethod]
|
|
|
public void StopCancelHandlerOnStop() {
|
|
|
var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
|
|
|
var started = new Signal();
|
|
|
bool cancelled = false;
|
|
|
|
|
|
// timer should tick once
|
|
|
comp.MockTick = ct => {
|
|
|
started.Set();
|
|
|
|
|
|
while(!ct.IsCancellationRequested) {
|
|
|
Thread.Sleep(1);
|
|
|
}
|
|
|
|
|
|
cancelled = true;
|
|
|
|
|
|
throw new OperationCanceledException();
|
|
|
};
|
|
|
|
|
|
// start timer
|
|
|
comp.Start().Join(1000);
|
|
|
|
|
|
started.Wait(); // wait for tick
|
|
|
|
|
|
// try to stop component
|
|
|
comp.Stop().Join(1000);
|
|
|
|
|
|
Assert.AreEqual(true, cancelled);
|
|
|
|
|
|
Assert.AreEqual(ExecutionState.Disposed, comp.State);
|
|
|
}
|
|
|
|
|
|
[TestMethod]
|
|
|
public void FailTickOnStopShouldBeIgnored() {
|
|
|
var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
|
|
|
var started = new Signal();
|
|
|
var finish = new Signal();
|
|
|
|
|
|
// timer should tick once
|
|
|
comp.MockTick = ct => {
|
|
|
started.Set();
|
|
|
finish.Wait();
|
|
|
// component is in stopping state here
|
|
|
throw new Exception("Die, die, die!!!");
|
|
|
};
|
|
|
|
|
|
|
|
|
comp.MockOnError = comp.CallComponentFail;
|
|
|
|
|
|
// start timer
|
|
|
comp.Start().Join(1000);
|
|
|
|
|
|
started.Wait(); // wait for tick
|
|
|
|
|
|
// try to stop component
|
|
|
var stopping = comp.Stop();
|
|
|
|
|
|
// the component is in stopping state but it is waiting for the tick handler to complete
|
|
|
finish.Set(); // signal the tick handler to finish
|
|
|
|
|
|
// tick handler should stop rather soon
|
|
|
stopping.Join(1000);
|
|
|
|
|
|
// validate the component is disposed
|
|
|
Assert.AreEqual(ExecutionState.Disposed, comp.State);
|
|
|
}
|
|
|
|
|
|
[TestMethod]
|
|
|
public void FailTickShouldFailComponent() {
|
|
|
var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
|
|
|
var started = new Signal();
|
|
|
var finish = new Signal();
|
|
|
|
|
|
// timer should tick once
|
|
|
comp.MockTick = ct => {
|
|
|
started.Set();
|
|
|
throw new Exception("Die, die, die!!!");
|
|
|
};
|
|
|
|
|
|
|
|
|
comp.MockOnError = err => {
|
|
|
comp.CallComponentFail(err);
|
|
|
finish.Set();
|
|
|
};
|
|
|
|
|
|
// start timer
|
|
|
comp.Start().Join(1000);
|
|
|
|
|
|
started.Wait(); // wait for tick
|
|
|
|
|
|
finish.Wait();
|
|
|
|
|
|
// try to stop component
|
|
|
ShouldThrow(() => comp.Stop());
|
|
|
|
|
|
Assert.AreEqual(ExecutionState.Failed, comp.State);
|
|
|
Assert.IsNotNull(comp.LastError);
|
|
|
Assert.AreEqual("Die, die, die!!!", comp.LastError.Message);
|
|
|
|
|
|
comp.Dispose();
|
|
|
Assert.AreEqual(ExecutionState.Disposed, comp.State);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|