public Exercise(ILoggerService loggerService, ISpeechService speechService, string name, int setCount, int repetitionCount, IEnumerable<MatcherWithAction> matchersWithActions) { loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); name.AssertNotNull(nameof(name)); matchersWithActions.AssertNotNull(nameof(matchersWithActions)); if (setCount < 0) { throw new ArgumentException("setCount cannot be less than zero.", "setCount"); } if (repetitionCount < 0) { throw new ArgumentException("repetitionCount cannot be less than zero.", "repetitionCount"); } this.logger = loggerService.GetLogger(this.GetType()); this.speechService = speechService; this.name = name; this.setCount = setCount; this.repetitionCount = repetitionCount; this.matchersWithActions = matchersWithActions.ToImmutableList(); using (var dummyExecutionContext = new ExecutionContext()) { this.duration = this .GetEventsWithActions(dummyExecutionContext) .SelectMany(x => x.Actions) .Select(x => x.Duration) .DefaultIfEmpty() .Aggregate((running, next) => running + next); } }
public void progress_time_span_reflects_any_progression_through_the_exercise() { var scheduler = new TestScheduler(); var model = new ExerciseBuilder() .Build(); var executionContext = new ExecutionContext(); var sut = new ExerciseViewModelBuilder() .WithSchedulerService(scheduler) .WithExecutionContext(executionContext) .WithModel(model) .Build(); executionContext.SetCurrentExercise(model); scheduler.AdvanceMinimal(); Assert.Equal(TimeSpan.Zero, sut.ProgressTimeSpan); executionContext.AddProgress(TimeSpan.FromSeconds(3)); scheduler.AdvanceMinimal(); Assert.Equal(TimeSpan.FromSeconds(3), sut.ProgressTimeSpan); executionContext.AddProgress(TimeSpan.FromSeconds(2)); scheduler.AdvanceMinimal(); Assert.Equal(TimeSpan.FromSeconds(5), sut.ProgressTimeSpan); }
public IObservable<Unit> ExecuteAsync(ExecutionContext context) { context.AssertNotNull(nameof(context)); return Observable .Concat( this .GetEventsWithActions(context) .SelectMany(eventWithActions => eventWithActions.Actions.Select(action => new { Action = action, Event = eventWithActions.Event })) .Select( actionAndEvent => { var action = actionAndEvent.Action; var @event = actionAndEvent.Event; if (context.SkipAhead > TimeSpan.Zero && context.SkipAhead >= action.Duration) { this.logger.Debug("Skipping action {0} for event {1} because its duration ({2}) is less than the remaining skip ahead ({3}).", action, @event, action.Duration, context.SkipAhead); context.AddProgress(action.Duration); return Observable.Return(Unit.Default); } this.logger.Debug("Executing action {0} for event {1}.", action, @event); return action.ExecuteAsync(context); })) .RunAsync(context.CancellationToken); }
public void wait_while_paused_async_completes_immediately_if_not_paused() { var sut = new ExecutionContext(); var completed = false; sut .WaitWhilePausedAsync() .Subscribe(_ => completed = true); Assert.True(completed); }
public void cancel_cancels_the_cancellation_token() { var sut = new ExecutionContext(); var token = sut.CancellationToken; Assert.False(sut.IsCancelled); Assert.False(token.IsCancellationRequested); sut.Cancel(); Assert.True(sut.IsCancelled); Assert.True(token.IsCancellationRequested); }
public void cancel_raises_property_changed_for_is_cancelled() { var sut = new ExecutionContext(); var called = false; sut.ObservableForProperty(x => x.IsCancelled) .Subscribe(_ => called = true); Assert.False(called); sut.Cancel(); Assert.True(called); }
public void execute_async_cancels_if_context_is_cancelled() { var sut = new AudioActionBuilder() .Build(); using (var context = new ExecutionContext()) { context.Cancel(); Assert.Throws<OperationCanceledException>(() => sut.ExecuteAsync(context)); } }
public void wait_while_paused_does_not_complete_if_the_context_is_cancelled() { var sut = new ExecutionContext(); sut.IsPaused = true; var executed = false; sut .WaitWhilePaused() .Subscribe(_ => executed = true); sut.Cancel(); Assert.False(executed); }
public void add_progress_adds_to_the_progress() { var sut = new ExecutionContext(); Assert.Equal(TimeSpan.Zero, sut.Progress); sut.AddProgress(TimeSpan.FromMilliseconds(100)); Assert.Equal(TimeSpan.FromMilliseconds(100), sut.Progress); sut.AddProgress(TimeSpan.FromMilliseconds(150)); Assert.Equal(TimeSpan.FromMilliseconds(250), sut.Progress); sut.AddProgress(TimeSpan.FromMilliseconds(13)); Assert.Equal(TimeSpan.FromMilliseconds(263), sut.Progress); }
public void execute_completes_even_if_there_is_no_delay() { var sut = new WaitActionBuilder() .WithDelay(TimeSpan.Zero) .Build(); using (var executionContext = new ExecutionContext()) { var completed = false; sut .Execute(executionContext) .Subscribe(_ => completed = true); Assert.True(completed); } }
public void wait_while_paused_async_waits_until_unpaused() { var sut = new ExecutionContext(); sut.IsPaused = true; var executed = false; sut .WaitWhilePausedAsync() .Subscribe(_ => executed = true); Assert.False(executed); sut.IsPaused = false; Assert.True(executed); }
public void progress_time_span_is_zero_if_no_progress_has_been_made_through_this_exercise() { var model1 = new ExerciseBuilder() .Build(); var model2 = new ExerciseBuilder() .Build(); var executionContext = new ExecutionContext(); var sut = new ExerciseViewModelBuilder() .WithModel(model2) .WithExecutionContext(executionContext) .Build(); executionContext.SetCurrentExercise(model1); executionContext.AddProgress(TimeSpan.FromSeconds(3)); Assert.Equal(TimeSpan.Zero, sut.ProgressTimeSpan); }
public void execute_pauses_if_context_is_paused() { var speechService = new SpeechServiceMock(MockBehavior.Loose); var sut = new SayActionBuilder() .WithSpeechService(speechService) .Build(); using (var context = new ExecutionContext()) { context.IsPaused = true; sut.Execute(context).Subscribe(); speechService .Verify(x => x.Speak(It.IsAny<string>())) .WasNotCalled(); } }
public void execute_async_pauses_if_context_is_paused() { var audioService = new AudioServiceMock(MockBehavior.Loose); var sut = new AudioActionBuilder() .WithAudioService(audioService) .Build(); using (var context = new ExecutionContext()) { context.IsPaused = true; sut.ExecuteAsync(context); audioService .Verify(x => x.PlayAsync(It.IsAny<string>())) .WasNotCalled(); } }
public void wait_while_paused_async_cancels_if_the_context_is_cancelled() { var sut = new ExecutionContext(); sut.IsPaused = true; var cancelled = false; sut .WaitWhilePausedAsync() .Catch<Unit, OperationCanceledException>( _ => { cancelled = true; return Observable.Return(Unit.Default); }) .Subscribe(); Assert.False(cancelled); sut.Cancel(); Assert.True(cancelled); }
public IObservable<Unit> ExecuteAsync(ExecutionContext context) { context.AssertNotNull(nameof(context)); return Observable .Concat( this .exercises .Select( exercise => { if (context.SkipAhead > TimeSpan.Zero && context.SkipAhead >= exercise.Duration) { this.logger.Debug("Skipping exercise '{0}' because its duration ({1}) is less than the remaining skip ahead ({2}).", exercise.Name, exercise.Duration, context.SkipAhead); context.AddProgress(exercise.Duration); return Observable.Return(Unit.Default); } this.logger.Debug("Executing exercise '{0}'.", exercise.Name); return exercise.ExecuteAsync(context); })) .RunAsync(context.CancellationToken); }
public void setting_current_exercise_resets_the_current_exercise_progress_to_zero() { var sut = new ExecutionContext(); sut.SetCurrentExercise(new ExerciseBuilder() .WithSetCount(3) .WithRepetitionCount(10) .Build()); sut.AddProgress(TimeSpan.FromMilliseconds(100)); Assert.Equal(TimeSpan.FromMilliseconds(100), sut.CurrentExerciseProgress); sut.SetCurrentExercise(new ExerciseBuilder() .WithSetCount(3) .WithRepetitionCount(10) .Build()); Assert.Equal(TimeSpan.Zero, sut.CurrentExerciseProgress); sut.AddProgress(TimeSpan.FromMilliseconds(150)); Assert.Equal(TimeSpan.FromMilliseconds(150), sut.CurrentExerciseProgress); }
public void execute_async_executes_each_exercise() { var action1 = new ActionMock(MockBehavior.Loose); var action2 = new ActionMock(MockBehavior.Loose); var sut = new ExerciseProgramBuilder() .AddExercise(new ExerciseBuilder() .WithBeforeExerciseAction(action1)) .AddExercise(new ExerciseBuilder() .WithBeforeExerciseAction(action2)) .Build(); using (var executionContext = new ExecutionContext()) { sut.ExecuteAsync(executionContext); action1 .Verify(x => x.ExecuteAsync(executionContext)) .WasCalledExactlyOnce(); action2 .Verify(x => x.ExecuteAsync(executionContext)) .WasCalledExactlyOnce(); } }
public void execute_async_ensures_progress_of_child_actions_does_not_compound_when_skipping() { var action1 = new ActionMock(); var action2 = new ActionMock(); var action3 = new ActionMock(); var action4 = new ActionMock(); action1 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(3)); action2 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(8)); action3 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(10)); action4 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(4)); action1 .When(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .Do<ExecutionContext>(ec => ec.AddProgress(action1.Duration)) .Return(Observable.Return(Unit.Default)); action2 .When(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .Do<ExecutionContext>(ec => ec.AddProgress(action2.Duration)) .Return(Observable.Return(Unit.Default)); action3 .When(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .Do<ExecutionContext>(ec => ec.AddProgress(action3.Duration)) .Return(Observable.Return(Unit.Default)); action4 .When(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .Do<ExecutionContext>(ec => ec.AddProgress(action4.Duration)) .Return(Observable.Return(Unit.Default)); var sut = new ParallelActionBuilder() .AddChild(action1) .AddChild(action2) .AddChild(action3) .AddChild(action4) .Build(); using (var context = new ExecutionContext(TimeSpan.FromSeconds(5))) { sut.ExecuteAsync(context); Assert.Equal(TimeSpan.FromSeconds(10), context.Progress); action1 .Verify(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .WasNotCalled(); action4 .Verify(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .WasNotCalled(); } }
public void add_progress_does_not_reduce_skip_ahead_if_it_is_already_zero() { var sut = new ExecutionContext(TimeSpan.FromSeconds(1)); Assert.Equal(TimeSpan.FromSeconds(1), sut.SkipAhead); sut.AddProgress(TimeSpan.FromMilliseconds(900)); Assert.Equal(TimeSpan.FromSeconds(0.1), sut.SkipAhead); sut.AddProgress(TimeSpan.FromMilliseconds(150)); Assert.Equal(TimeSpan.Zero, sut.SkipAhead); sut.AddProgress(TimeSpan.FromMilliseconds(1000)); Assert.Equal(TimeSpan.Zero, sut.SkipAhead); }
private IEnumerable<EventWithActions> GetEventsWithActions(ExecutionContext executionContext) => this .GetEvents(executionContext) .Select(x => new EventWithActions(x, this.GetActionsForEvent(x)));
public void add_progress_reduces_outstanding_skip_ahead() { var sut = new ExecutionContext(TimeSpan.FromSeconds(3)); Assert.Equal(TimeSpan.FromSeconds(3), sut.SkipAhead); sut.AddProgress(TimeSpan.FromMilliseconds(100)); Assert.Equal(TimeSpan.FromSeconds(2.9), sut.SkipAhead); sut.AddProgress(TimeSpan.FromMilliseconds(150)); Assert.Equal(TimeSpan.FromSeconds(2.75), sut.SkipAhead); sut.AddProgress(TimeSpan.FromMilliseconds(1000)); Assert.Equal(TimeSpan.FromSeconds(1.75), sut.SkipAhead); }
public void execute_async_context_can_be_paused_by_child_action_that_is_not_the_longest() { var action1 = new ActionMock(MockBehavior.Loose); var action2 = new ActionMock(MockBehavior.Loose); action1 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(8)); action2 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(13)); action1 .When(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .Do<ExecutionContext>(ec => ec.IsPaused = true) .Return(Observable.Return(Unit.Default)); var sut = new ParallelActionBuilder() .AddChild(action1) .AddChild(action2) .Build(); using (var context = new ExecutionContext()) { sut.ExecuteAsync(context); // can't assume certain actions did not execute because actions run in parallel, but we can check the context is paused Assert.True(context.IsPaused); } }
public void execute_async_correctly_handles_a_skip_ahead_value_that_exceeds_even_the_longest_child_actions_duration() { var action1 = new ActionMock(MockBehavior.Loose); var action2 = new ActionMock(MockBehavior.Loose); var action3 = new ActionMock(MockBehavior.Loose); action1 .When(x => x.Duration) .Return(TimeSpan.FromMinutes(1)); action2 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(10)); action3 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(71)); var sut = new ParallelActionBuilder() .AddChild(action1) .AddChild(action2) .AddChild(action3) .Build(); using (var context = new ExecutionContext(TimeSpan.FromMinutes(3))) { sut.ExecuteAsync(context); action1 .Verify(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .WasNotCalled(); action2 .Verify(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .WasNotCalled(); action3 .Verify(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .WasNotCalled(); Assert.Equal(TimeSpan.FromSeconds(71), context.Progress); } }
private IEnumerable <EventWithActions> GetEventsWithActions(ExecutionContext executionContext) => this .GetEvents(executionContext) .Select(x => new EventWithActions(x, this.GetActionsForEvent(x)));
public ExerciseViewModelBuilder WithExecutionContext(ExecutionContext executionContext) => this.WithExecutionContext(Observable.Return(executionContext));
private IEnumerable<IEvent> GetEvents(ExecutionContext executionContext) { executionContext.SetCurrentExercise(this); yield return new BeforeExerciseEvent(executionContext, this); for (var setNumber = 1; setNumber <= this.SetCount; ++setNumber) { executionContext.SetCurrentSet(setNumber); yield return new BeforeSetEvent(executionContext, setNumber); for (var repetitionNumber = 1; repetitionNumber <= this.RepetitionCount; ++repetitionNumber) { executionContext.SetCurrentRepetition(repetitionNumber); yield return new BeforeRepetitionEvent(executionContext, repetitionNumber); yield return new DuringRepetitionEvent(executionContext, repetitionNumber); yield return new AfterRepetitionEvent(executionContext, repetitionNumber); } yield return new AfterSetEvent(executionContext, setNumber); } yield return new AfterExerciseEvent(executionContext, this); }
public void execute_async_skips_exercises_that_are_shorter_than_the_skip_ahead() { var action1 = new ActionMock(MockBehavior.Loose); var action2 = new ActionMock(MockBehavior.Loose); var action3 = new ActionMock(MockBehavior.Loose); action1 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(13)); action2 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(10)); action3 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(5)); action1 .When(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .Throw(); action2 .When(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .Throw(); var sut = new ExerciseProgramBuilder() .AddExercise(new ExerciseBuilder() .WithBeforeExerciseAction(action1)) .AddExercise(new ExerciseBuilder() .WithBeforeExerciseAction(action2)) .AddExercise(new ExerciseBuilder() .WithBeforeExerciseAction(action3)) .Build(); using (var executionContext = new ExecutionContext(TimeSpan.FromSeconds(23))) { sut.ExecuteAsync(executionContext); action3 .Verify(x => x.ExecuteAsync(executionContext)) .WasCalledExactlyOnce(); } }
private IObservable<Unit> Start(TimeSpan skipTo = default(TimeSpan), bool isPaused = false) { this.logger.Debug("Starting {0} from {1}.", isPaused ? "paused" : "unpaused", skipTo); var executionContext = new ExecutionContext(skipTo) { IsPaused = isPaused }; var disposables = new CompositeDisposable( executionContext, Disposable.Create(() => this.ExecutionContext = null)); return Observable .Using( () => disposables, _ => Observable .Start(() => this.ExecutionContext = executionContext, this.scheduler) .SelectMany(__ => this.model.Execute(executionContext))) .Catch<Unit, OperationCanceledException>(_ => Observable.Return(Unit.Default)); }
public ExerciseViewModelBuilder WithExecutionContext(ExecutionContext executionContext) { this.executionContext = Observable.Return(executionContext); return this; }
public void execute_async_executes_each_child_action() { var action1 = new ActionMock(MockBehavior.Loose); var action2 = new ActionMock(MockBehavior.Loose); var action3 = new ActionMock(MockBehavior.Loose); var action4 = new ActionMock(MockBehavior.Loose); action1 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(1)); action2 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(2)); action3 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(3)); action4 .When(x => x.Duration) .Return(TimeSpan.FromSeconds(4)); var sut = new ParallelActionBuilder() .AddChild(action1) .AddChild(action2) .AddChild(action3) .AddChild(action4) .Build(); using (var context = new ExecutionContext()) { sut.ExecuteAsync(context); action1 .Verify(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .WasCalledExactlyOnce(); action2 .Verify(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .WasCalledExactlyOnce(); action3 .Verify(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .WasCalledExactlyOnce(); action4 .Verify(x => x.ExecuteAsync(It.IsAny<ExecutionContext>())) .WasCalledExactlyOnce(); } }