view Implab.Test/RunnableComponentTests.cs @ 262:f1696cdc3d7a v3 v3.0.8

Added IInitializable.Initialize() overload Added IRunnable.Start(), IRunnable.Start() overloads Fixed cancellation of the current operation when Stop() is called More tests
author cin
date Mon, 16 Apr 2018 02:12:39 +0300
parents 547a2fc0d93e
children
line wrap: on
line source

using System;
using System.Threading;
using System.Threading.Tasks;
using Implab.Components;
using Xunit;

namespace Implab.Test {

    public class RunnableComponentTests {
        [Fact]
        public async Task SimpleStartStop() {

            using (var m = new MockPollComponent(true)) {
                m.StartWorker = async (ct) => await Task.Yield();
                m.StopWorker = async (ct) => await Task.Yield();

                Assert.Equal(ExecutionState.Ready, m.State);
                Assert.NotNull(m.Completion);

                m.Start();
                await m.Completion;
                Assert.Equal(ExecutionState.Running, m.State);

                m.Stop();
                await m.Completion;
                Assert.Equal(ExecutionState.Stopped, m.State);
            }
        }

        [Fact]
        public async Task SyncStart() {
            using (var m = new MockPollComponent(true)) {
                m.Start();
                Assert.Equal(ExecutionState.Running, m.State);
                await m.Completion;
            }
        }

        [Fact]
        public async Task AsyncStarting() {
            using (var m = new MockPollComponent(true)) {
                var signal = Safe.CreateTask();

                m.StartWorker = async (ct) => await signal;
                m.Start();
                
                Assert.Equal(ExecutionState.Starting, m.State);
                Assert.False(m.Completion.IsCompleted);
                
                signal.Start();
                
                await m.Completion;
                
                Assert.Equal(ExecutionState.Running, m.State);
            }
        }

        [Fact]
        public async Task FailWhileStarting() {
            using (var m = new MockPollComponent(true)) {
                const string failMessage = "Fail me";
                var signal = new Task(() => {
                    throw new Exception(failMessage);
                });

                m.StartWorker = async (ct) => await signal;
                m.Start();

                Assert.Equal(ExecutionState.Starting, m.State);
                Assert.False(m.Completion.IsCompleted);

                signal.Start();
                try {
                    await m.Completion;
                    Assert.True(false);
                } catch (Exception e) {
                    Assert.Equal(failMessage, e.Message);
                }

                Assert.Equal(ExecutionState.Failed, m.State);
            }
        }

        [Fact]
        public async Task SyncStop() {
            using (var m = new MockPollComponent(true)) {
                m.Start();
                Assert.Equal(ExecutionState.Running, m.State);
                m.Stop();
                Assert.Equal(ExecutionState.Stopped, m.State);
                await m.Completion;
            }
        }

        [Fact]
        public async Task AsyncStopping() {
            using (var m = new MockPollComponent(true)) {
                var signal = Safe.CreateTask();

                m.StopWorker = async (ct) => await signal;
                
                // Start
                m.Start();
                Assert.Equal(ExecutionState.Running, m.State);

                // Stop
                m.Stop();
                Assert.Equal(ExecutionState.Stopping, m.State);
                Assert.False(m.Completion.IsCompleted);
                signal.Start();
                
                await m.Completion;
                
                Assert.Equal(ExecutionState.Stopped, m.State);
            }
        }

        [Fact]
        public async Task FailWhileStopping() {
            using (var m = new MockPollComponent(true)) {
                const string failMessage = "Fail me";
                var signal = new Task(() => {
                    throw new Exception(failMessage);
                });

                m.StopWorker = async (ct) => await signal;
                
                // Start
                m.Start();
                Assert.Equal(ExecutionState.Running, m.State);

                // Stop
                m.Stop();
                Assert.Equal(ExecutionState.Stopping, m.State);
                Assert.False(m.Completion.IsCompleted);

                signal.Start();
                try {
                    await m.Completion;
                    Assert.True(false);
                } catch (Exception e) {
                    Assert.Equal(failMessage, e.Message);
                }

                Assert.Equal(ExecutionState.Failed, m.State);
            }
        }

        [Fact]
        public async Task ThrowOnInvalidTrasition() {
            using (var m = new MockPollComponent(false)) {
                var started = Safe.CreateTask();
                var stopped = Safe.CreateTask();

                m.StartWorker = async (ct) => await started;
                m.StopWorker = async (ct) => await stopped;

                Assert.Throws<InvalidOperationException>(() => m.Start());

                // Initialize
                m.Initialize();
                await m.Completion;

                // Start
                m.Start();
                Assert.Equal(ExecutionState.Starting, m.State);

                // Check invalid transitions
                Assert.Throws<InvalidOperationException>(() => m.Start());

                // Component can be stopped before started
                // m.Stop(CancellationToken.None);

                // Running
                started.Start();
                await m.Completion;
                Assert.Equal(ExecutionState.Running, m.State);

                
                Assert.Throws<InvalidOperationException>(() => m.Start());

                // Stop
                m.Stop();

                // Check invalid transitions
                Assert.Throws<InvalidOperationException>(() => m.Start());
                Assert.Throws<InvalidOperationException>(() => m.Stop());

                // Stopped
                stopped.Start();
                await m.Completion;
                Assert.Equal(ExecutionState.Stopped, m.State);

                // Check invalid transitions
                Assert.Throws<InvalidOperationException>(() => m.Start());
                Assert.Throws<InvalidOperationException>(() => m.Stop());
            }
        }

        [Fact]
        public async Task CancelStart() {
            using (var m = new MockPollComponent(true)) {
                m.StartWorker = (ct) => Safe.CreateTask(ct);

                m.Start();
                var start = m.Completion;

                Assert.Equal(ExecutionState.Starting, m.State);
                m.Stop();
                await m.Completion;
                Assert.Equal(ExecutionState.Stopped, m.State);
                Assert.True(start.IsCompleted);
                Assert.True(start.IsCanceled);
            }
        }

        [Fact]
        public async Task AwaitWorker() {
            using (var m = new MockPollComponent(true)) {
                var worker = Safe.CreateTask();

                m.PollWorker = (ct) => worker;

                m.Start(CancellationToken.None);
                await m.Completion;

                Assert.Equal(ExecutionState.Running, m.State);

                m.Stop(CancellationToken.None);
                Assert.Equal(ExecutionState.Stopping, m.State);
                worker.Start();
                await m.Completion;
                Assert.Equal(ExecutionState.Stopped, m.State);
            }
        }

        [Fact]
        public async Task CancelWorker() {
            using (var m = new MockPollComponent(true)) {
                var worker = Safe.CreateTask();

                var started = Safe.CreateTask();

                m.PollWorker = async (ct) => {
                    started.Start();
                    await worker;
                    ct.ThrowIfCancellationRequested();
                };

                m.Start(CancellationToken.None);
                await m.Completion;
                await started; // await for the poll worker to start

                Assert.Equal(ExecutionState.Running, m.State);

                m.Stop(CancellationToken.None);
                Assert.Equal(ExecutionState.Stopping, m.State);
                worker.Start();
                await m.Completion;
                Assert.Equal(ExecutionState.Stopped, m.State);
            }
        }
    }
}