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