diff Implab.Test/PollingComponentTests.cs @ 203:4d9830a9bbb8 v2

Added 'Fail' method to RunnableComponent which allows component to move from Running to Failed state. Added PollingComponent a timer based runnable component More tests Added FailPromise a thin class to wrap exceptions Fixed error handling in SuccessPromise classes.
author cin
date Tue, 18 Oct 2016 17:49:54 +0300
parents
children 8200ab154c8a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Test/PollingComponentTests.cs	Tue Oct 18 17:49:54 2016 +0300
@@ -0,0 +1,230 @@
+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.Init();
+
+            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);
+        }
+
+    }
+}
+