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);
        }
Example #21
0
 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);
            }
        }
Example #25
0
 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));
Example #27
0
        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();
            }
        }