public void canceled_transition_reverts_back_to_original_state() { var factory = new StateMachineTaskFactory <State>(); var cancellationTokenSource = new CancellationTokenSource(); factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true)); factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => TaskUtil.Delay(TimeSpan.FromSeconds(3), cancellationTokenSource.Token)); factory.StateChanged += (s, e) => { if (e.NewState == State.Stopping) { // cancel the stop cancellationTokenSource.Cancel(); } }; var startedTask = factory.TransitionTo(State.Started); var stoppedTask = factory.TransitionTo(State.Stopped, cancellationTokenSource.Token); startedTask.Wait(TimeSpan.FromSeconds(3)); try { stoppedTask.Wait(TimeSpan.FromSeconds(3)); Assert.True(false, "Failed to throw exception."); } catch (AggregateException ex) { Assert.Equal(1, ex.InnerExceptions.Count); Assert.IsType <OperationCanceledException>(ex.InnerExceptions[0]); Assert.Equal(State.Started, factory.State); } }
public void state_change_is_raised_as_state_changes() { var factory = new StateMachineTaskFactory <State>(State.Stopped); factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true)); factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => TaskUtil.FromResult(true)); var stateChanges = new List <StateChangedEventArgs <State> >(); factory.StateChanged += (s, e) => stateChanges.Add(e); factory.TransitionTo(State.Started).Wait(TimeSpan.FromSeconds(1)); factory.TransitionTo(State.Stopped).Wait(TimeSpan.FromSeconds(1)); factory.TransitionTo(State.Started).Wait(TimeSpan.FromSeconds(1)); factory.TransitionTo(State.Stopped).Wait(TimeSpan.FromSeconds(1)); Assert.Equal(8, stateChanges.Count); Assert.Equal(State.Stopped, stateChanges[0].OldState); Assert.Equal(State.Starting, stateChanges[0].NewState); Assert.Equal(State.Starting, stateChanges[1].OldState); Assert.Equal(State.Started, stateChanges[1].NewState); Assert.Equal(State.Started, stateChanges[2].OldState); Assert.Equal(State.Stopping, stateChanges[2].NewState); Assert.Equal(State.Stopping, stateChanges[3].OldState); Assert.Equal(State.Stopped, stateChanges[3].NewState); Assert.Equal(State.Stopped, stateChanges[4].OldState); Assert.Equal(State.Starting, stateChanges[4].NewState); Assert.Equal(State.Starting, stateChanges[5].OldState); Assert.Equal(State.Started, stateChanges[5].NewState); Assert.Equal(State.Started, stateChanges[6].OldState); Assert.Equal(State.Stopping, stateChanges[6].NewState); Assert.Equal(State.Stopping, stateChanges[7].OldState); Assert.Equal(State.Stopped, stateChanges[7].NewState); }
public void transition_to_returns_completed_task_if_already_in_desired_state() { var factory = new StateMachineTaskFactory <State>(); factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true)); factory.TransitionTo(State.Started).Wait(); Assert.Equal(TaskStatus.RanToCompletion, factory.TransitionTo(State.Started).Status); }
public void register_transition_throws_if_transition_already_registered() { var factory = new StateMachineTaskFactory <State>(); factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true)); var ex = Assert.Throws <InvalidOperationException>(() => factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true))); Assert.Equal("A transition to state 'Started' has already been registered.", ex.Message); }
/// <summary> /// Writes the string to the response body. /// </summary> /// <param name="content">The content.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public Task WriteAsync(string content, CancellationToken cancellationToken) { if (content == null) { return(TaskUtil.FromResult(0)); } var buff = Encoding.UTF8.GetBytes(content); return(Body.WriteAsync(buff, 0, buff.Length, cancellationToken)); }
/// <summary> /// Asynchronously closes the specified screen. /// </summary> /// <param name="screen">The <see cref="UIScreen"/> to close.</param> /// <param name="duration">The amount of time over which to transition the screen's state, or /// <see langword="null"/> to use the default transition time.</param> /// <returns>A task which represents the asynchronous operation.</returns> public Task CloseAsync(UIScreen screen, TimeSpan?duration = null) { Contract.Require(screen, nameof(screen)); if (screens.Contains(screen)) { return(screen.CloseAsync(duration)); } return(TaskUtil.FromResult(false)); }
public void transition_to_passes_any_state_to_task_creation_function() { var factory = new StateMachineTaskFactory <State>(); string receivedState = null; factory.RegisterTransition( State.Starting, State.Started, (ct, o) => { receivedState = o as string; return(TaskUtil.FromResult(true)); }); factory.TransitionTo(State.Started, CancellationToken.None, null, "here is the state").Wait(); Assert.Equal("here is the state", receivedState); }
/// <summary> /// Asynchronously closes all open screens except for the specified screen. /// </summary> /// <param name="except">The screen which should remain open when all other screens are closed.</param> /// <param name="duration">The amount of time over which to transition the screens' states, or /// <see langword="null"/> to use the default transition time.</param> /// <returns>A collection of <see cref="Task"/> objects representing the asynchronous operations.</returns> public Task CloseAllExceptAsync(UIScreen except, TimeSpan?duration = null) { var tasks = default(List <Task>); for (var current = screens.First; current != null; current = current.Next) { var screen = current.Value; if (screen != except) { tasks = tasks ?? new List <Task>(); tasks.Add(screen.CloseAsync(duration)); } } return(tasks.Count == 0 ? TaskUtil.FromResult(true) : new Task(() => Task.WaitAll(tasks.ToArray()))); }
/// <summary> /// Asynchronously closes any screens which match the specified predicate. /// </summary> /// <param name="predicate">The predicate with which to determine which screens to close.</param> /// <param name="duration">The amount of time over which to transition the screens' states, or /// <see langword="null"/> to use the default transition time.</param> /// <returns>A collection of <see cref="Task"/> objects representing the asynchronous operations.</returns> public Task CloseMatchingAsync(Func <UIScreen, Boolean> predicate, TimeSpan?duration = null) { Contract.Require(predicate, nameof(predicate)); var tasks = default(List <Task>); for (var current = screens.First; current != null; current = current.Next) { var screen = current.Value; if (predicate(screen)) { tasks = tasks ?? new List <Task>(); tasks.Add(screen.CloseAsync(duration)); } } return(tasks.Count == 0 ? TaskUtil.FromResult(true) : new Task(() => Task.WaitAll(tasks.ToArray()))); }
public void transition_to_can_be_canceled_before_transition_takes_place() { var factory = new StateMachineTaskFactory <State>(); var cancellationTokenSource = new CancellationTokenSource(); factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true)); cancellationTokenSource.Cancel(); var startedTask = factory.TransitionTo(State.Started, cancellationTokenSource.Token); try { startedTask.Wait(); Assert.True(false, "Failed to throw exception."); } catch (AggregateException ex) { Assert.Equal(1, ex.InnerExceptions.Count); Assert.IsType <OperationCanceledException>(ex.InnerExceptions[0]); } }
public void failed_transition_reverts_back_to_original_state() { var factory = new StateMachineTaskFactory <State>(); factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true)); factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => { throw new InvalidOperationException("Something went wrong"); }); var startedTask = factory.TransitionTo(State.Started); var stoppedTask = factory.TransitionTo(State.Stopped); startedTask.Wait(TimeSpan.FromSeconds(3)); try { stoppedTask.Wait(TimeSpan.FromSeconds(3)); Assert.True(false, "Failed to throw exception."); } catch (AggregateException ex) { Assert.Equal(1, ex.InnerExceptions.Count); Assert.IsType <InvalidOperationException>(ex.InnerExceptions[0]); Assert.Equal(State.Started, factory.State); } }
public void transition_to_can_be_forbidden() { var factory = new StateMachineTaskFactory <State>(); factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true)); factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => TaskUtil.FromResult(true)); var startedTask = factory.TransitionTo(State.Started, CancellationToken.None, x => x == State.Undefined); var stoppedTask = factory.TransitionTo(State.Stopped, CancellationToken.None, x => x != State.Started); startedTask.Wait(TimeSpan.FromSeconds(3)); try { stoppedTask.Wait(TimeSpan.FromSeconds(3)); Assert.True(false, "Failed to throw exception."); } catch (AggregateException ex) { Assert.Equal(1, ex.InnerExceptions.Count); var ex2 = Assert.IsType <StateTransitionForbiddenException <State> >(ex.InnerExceptions[0]); Assert.Equal(State.Stopped, ex2.TargetState); Assert.Equal(State.Started, ex2.State); Assert.Equal("A transition to state 'Stopped' was forbidden by the validate transition callback.", ex2.Message); } }
public void transition_to_can_be_canceled() { var factory = new StateMachineTaskFactory <State>(); var cancellationTokenSource = new CancellationTokenSource(); factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true)); factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => TaskUtil.Delay(TimeSpan.FromMilliseconds(150))); var startedTask = factory.TransitionTo(State.Started, cancellationTokenSource.Token); startedTask.ContinueWith(x => cancellationTokenSource.Cancel()); var stoppedTask = factory.TransitionTo(State.Stopped, cancellationTokenSource.Token); startedTask.Wait(TimeSpan.FromSeconds(3)); try { stoppedTask.Wait(TimeSpan.FromSeconds(3)); Assert.True(false, "Failed to throw exception."); } catch (AggregateException ex) { Assert.Equal(1, ex.InnerExceptions.Count); Assert.IsType <OperationCanceledException>(ex.InnerExceptions[0]); } }
public Task TransitionTo(T endTransitionState, CancellationToken cancellationToken = default(CancellationToken), ValidateTransitionCallback <T> validateTransitionCallback = null, object state = null) { lock (this.stateSync) { if (EqualityComparer <T> .Default.Equals(this.state, endTransitionState)) { // already in the requested state - nothing to do return(TaskUtil.FromResult(true)); } else if (this.transitionToState.HasValue && EqualityComparer <T> .Default.Equals(this.transitionToState.Value, endTransitionState)) { // already in the process of transitioning to the requested state - return same transition task return(this.transitionToTask); } else if (this.transitionToTask != null) { // not in the requested state, but there is an outstanding transition in progress, so come back to this request once it's done return(this.transitionToTask.Then(x => this.TransitionTo(endTransitionState, cancellationToken, validateTransitionCallback, state))); } else if (validateTransitionCallback != null && !validateTransitionCallback(this.State)) { // transition is forbidden, so return a failing task to that affect var taskCompletionSource = new TaskCompletionSource <bool>(); var exception = new StateTransitionForbiddenException <T>(endTransitionState, this.State); taskCompletionSource.TrySetException(exception); return(taskCompletionSource.Task); } // else, need to transition to the chosen state TransitionRegistrationInfo transitionRegistrationInfo; var result = this.transitionRegistrations.TryGetValue(endTransitionState, out transitionRegistrationInfo); exceptionHelper.ResolveAndThrowIf(!result, "transitionNotRegistered", endTransitionState); var beginTransitionState = transitionRegistrationInfo.BeginTransitionState; var task = transitionRegistrationInfo.TaskFactory(cancellationToken, state); exceptionHelper.ResolveAndThrowIf(task == null, "taskFactoryReturnedNull", endTransitionState); var previousState = this.State; this.State = beginTransitionState; this.transitionToState = endTransitionState; this.transitionToTask = task .ContinueWith( x => { if (x.IsFaulted || cancellationToken.IsCancellationRequested) { // faulted or canceled, so roll back to previous state lock (this.stateSync) { this.State = previousState; this.transitionToState = null; this.transitionToTask = null; } if (x.IsFaulted) { throw x.Exception; } cancellationToken.ThrowIfCancellationRequested(); } else { // succeeded, so commit to end state lock (this.stateSync) { this.State = endTransitionState; this.transitionToState = null; this.transitionToTask = null; } } }); return(this.transitionToTask); } }