view Implab.Test/RunnableComponentTests.cs @ 196:40d7fed4a09e

fixed promise chaining behavior, the error handler doesn't handle result or cancellation handlers exceptions these exceptions are propagated to the next handlers.
author cin
date Mon, 29 Aug 2016 23:15:51 +0300
parents 3071220371f8
children 4d9830a9bbb8
line wrap: on
line source

using System;
using System.Reflection;
using System.Threading;
using Implab.Parallels;
using Implab.Components;

#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 RunnableComponentTests {

        static void ShouldThrow(Action action) {
            try {
                action();
                Assert.Fail();
            } catch (AssertFailedException) {
                throw;
            } catch {
            }
        }

        class Runnable : RunnableComponent {
            public Runnable(bool initialized) : base(initialized) {
            }
                
            public Action MockInit {
                get;
                set;
            }

            public Func<IPromise> MockStart {
                get;
                set;
            }

            public Func<IPromise> MockStop {
                get;
                set;
            }

            protected override IPromise OnStart() {
                return MockStart != null ? MockStart() : base.OnStart();
            }

            protected override IPromise OnStop() {
                return MockStop != null ? MockStop() : base.OnStart();
            }

            protected override void OnInitialize() {
                if (MockInit != null)
                    MockInit();
            }
        }

        [TestMethod]
        public void NormalFlowTest() {
            var comp = new Runnable(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 InitFailTest() {
            var comp = new Runnable(false) {
                MockInit = () => {
                    throw new Exception("BAD");
                }
            };

            ShouldThrow(() => comp.Start());
            ShouldThrow(() => comp.Stop());
            Assert.AreEqual(ExecutionState.Created, comp.State);

            ShouldThrow(comp.Init);

            Assert.AreEqual(ExecutionState.Failed, comp.State);

            ShouldThrow(() => comp.Start());
            ShouldThrow(() => comp.Stop());
            Assert.AreEqual(ExecutionState.Failed, comp.State);

            comp.Dispose();
            Assert.AreEqual(ExecutionState.Disposed, comp.State);
        }

        [TestMethod]
        public void DisposedTest() {

            var comp = new Runnable(false);
            comp.Dispose();

            ShouldThrow(() => comp.Start());
            ShouldThrow(() => comp.Stop());
            ShouldThrow(comp.Init);

            Assert.AreEqual(ExecutionState.Disposed, comp.State);
        }

        [TestMethod]
        public void StartCancelTest() {
            var comp = new Runnable(true) {
                MockStart = () => PromiseHelper.Sleep(100000, 0)
            };

            var p = comp.Start();
            Assert.AreEqual(ExecutionState.Starting, comp.State);
            p.Cancel();
            ShouldThrow(() => p.Join(1000));
            Assert.AreEqual(ExecutionState.Failed, comp.State);

            Assert.IsTrue(comp.LastError is OperationCanceledException);

            comp.Dispose();
        }

        [TestMethod]
        public void StartStopTest() {
            var stop = new Signal();
            var comp = new Runnable(true) {
                MockStart = () => PromiseHelper.Sleep(100000, 0),
                MockStop = () => AsyncPool.RunThread(stop.Wait)
            };

            var p1 = comp.Start();
            var p2 = comp.Stop();
            // should enter stopping state

            ShouldThrow(p1.Join);
            Assert.IsTrue(p1.IsCancelled);
            Assert.AreEqual(ExecutionState.Stopping, comp.State);

            stop.Set();
            p2.Join(1000);
            Assert.AreEqual(ExecutionState.Disposed, comp.State);
        }

        [TestMethod]
        public void StartStopFailTest() {
            var comp = new Runnable(true) {
                MockStart = () => PromiseHelper.Sleep(100000, 0).Then(null,null,x => { throw new Exception("I'm dead"); })
            };

            comp.Start();
            var p = comp.Stop();
            // if Start fails to cancel, should fail to stop
            ShouldThrow(() => p.Join(1000));
            Assert.AreEqual(ExecutionState.Failed, comp.State);
            Assert.IsNotNull(comp.LastError);
            Assert.AreEqual("I'm dead", comp.LastError.Message);
        }

        [TestMethod]
        public void StopCancelTest() {
            var comp = new Runnable(true) {
                MockStop = () => PromiseHelper.Sleep(100000, 0)
            };

            comp.Start();
            var p = comp.Stop();
            Assert.AreEqual(ExecutionState.Stopping, comp.State);
            p.Cancel();
            ShouldThrow(() => p.Join(1000));
            Assert.AreEqual(ExecutionState.Failed, comp.State);
            Assert.IsTrue(comp.LastError is OperationCanceledException);

            comp.Dispose();
        }

    }
}