view Implab.Test/PollingComponentTests.cs @ 209:a867536c68fc v2

Bound promise to CancellationToken Added new states to ExecutionSate enum. Added Safe.Guard() method to handle cleanup of the result of the promise
author cin
date Wed, 16 Nov 2016 03:06:08 +0300
parents 8200ab154c8a
children
line wrap: on
line source

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

    }
}