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); } } }