示例#1
0
        public void parse_error_message_is_null_if_a_document_is_successfully_parsed_after_one_fails_to_parse()
        {
            var badDocument             = @"# First Program

## First Exercise

 whatever!";
            var goodDocument            = "# First Program";
            var documents               = new Subject <string>();
            var exerciseDocumentService = new ExerciseDocumentServiceMock();
            var scheduler               = new TestScheduler();

            exerciseDocumentService
            .When(x => x.ExerciseDocument)
            .Return(documents);

            var sut = new ExerciseProgramsViewModelBuilder()
                      .WithExerciseDocumentService(exerciseDocumentService)
                      .WithScheduler(scheduler)
                      .Build();

            documents.OnNext(badDocument);

            scheduler.AdvanceMinimal();
            Assert.NotNull(sut.ParseErrorMessage);

            documents.OnNext(goodDocument);
            scheduler.AdvanceMinimal();
            Assert.Null(sut.ParseErrorMessage);
        }
示例#2
0
        public void execution_context_is_cancelled_if_user_navigates_away()
        {
            var scheduler = new TestScheduler();
            var sut       = new ExerciseProgramViewModelBuilder()
                            .WithModel(new ExerciseProgramBuilder()
                                       .WithExercise(new ExerciseBuilder()
                                                     .WithBeforeExerciseAction(
                                                         new WaitActionBuilder()
                                                         .WithDelayService(new DelayServiceBuilder().Build())
                                                         .WithDelay(TimeSpan.FromMinutes(1))
                                                         .Build())))
                            .WithScheduler(scheduler)
                            .Build();

            sut
            .HostScreen
            .Router
            .NavigationStack
            .Add(sut);

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.True(sut.IsStarted);

            sut
            .HostScreen
            .Router
            .NavigateBack
            .ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.False(sut.IsStarted);
        }
        public void parse_error_message_is_null_if_a_document_is_successfully_parsed_after_one_fails_to_parse()
        {
            var badDocument = @"# First Program

## First Exercise

 whatever!";
            var goodDocument = "# First Program";
            var documents = new Subject<string>();
            var exerciseDocumentService = new ExerciseDocumentServiceMock();
            var scheduler = new TestScheduler();

            exerciseDocumentService
                .When(x => x.ExerciseDocument)
                .Return(documents);

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithExerciseDocumentService(exerciseDocumentService)
                .WithScheduler(scheduler)
                .Build();

            documents.OnNext(badDocument);

            scheduler.AdvanceMinimal();
            Assert.NotNull(sut.ParseErrorMessage);

            documents.OnNext(goodDocument);
            scheduler.AdvanceMinimal();
            Assert.Null(sut.ParseErrorMessage);
        }
        public void progress_time_span_is_not_reset_to_zero_if_another_exercise_is_started()
        {
            var scheduler = new TestScheduler();
            var model1    = new ExerciseBuilder()
                            .WithName("model 1")
                            .Build();
            var model2 = new ExerciseBuilder()
                         .WithName("model 2")
                         .Build();
            var executionContext = new ExecutionContext();
            var sut = new ExerciseViewModelBuilder()
                      .WithSchedulerService(scheduler)
                      .WithExecutionContext(executionContext)
                      .WithModel(model1)
                      .Build();

            executionContext.SetCurrentExercise(model1);
            executionContext.AddProgress(TimeSpan.FromSeconds(3));

            scheduler.AdvanceMinimal();
            Assert.Equal(TimeSpan.FromSeconds(3), sut.ProgressTimeSpan);

            executionContext.SetCurrentExercise(model2);
            scheduler.AdvanceMinimal();
            Assert.Equal(TimeSpan.FromSeconds(3), sut.ProgressTimeSpan);

            executionContext.AddProgress(TimeSpan.FromSeconds(3));
            scheduler.AdvanceMinimal();
            Assert.Equal(TimeSpan.FromSeconds(3), sut.ProgressTimeSpan);
        }
示例#5
0
        public void is_resume_visible_cycles_correctly_if_start_command_is_executed_and_execution_is_paused()
        {
            var scheduler = new TestScheduler();
            var sut       = new ExerciseProgramViewModelBuilder()
                            .WithModel(new ExerciseProgramBuilder()
                                       .WithExercise(new ExerciseBuilder()
                                                     .WithBeforeExerciseAction(
                                                         new WaitActionBuilder()
                                                         .WithDelayService(new DelayServiceBuilder().Build())
                                                         .WithDelay(TimeSpan.FromMinutes(1))
                                                         .Build())))
                            .WithScheduler(scheduler)
                            .Build();

            var isResumeVisible = sut
                                  .WhenAnyValue(x => x.IsResumeVisible)
                                  .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            sut.PauseCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(2, isResumeVisible.Count);
            Assert.False(isResumeVisible[0]);
            Assert.True(isResumeVisible[1]);
        }
        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);
        }
示例#7
0
        public void current_exercise_reflects_that_in_the_execution_context()
        {
            var scheduler = new TestScheduler();
            var action    = new ActionMock();

            action
            .When(x => x.Duration)
            .Return(TimeSpan.FromSeconds(10));

            action
            .When(x => x.Execute(It.IsAny <ExecutionContext>()))
            .Return <ExecutionContext>(
                ec =>
            {
                ec.IsPaused = true;
                return(ec.WaitWhilePaused());
            });

            var exercise1 = new ExerciseBuilder()
                            .WithName("Exercise 1")
                            .WithBeforeExerciseAction(action)
                            .Build();

            var exercise2 = new ExerciseBuilder()
                            .WithName("Exercise 2")
                            .WithBeforeExerciseAction(action)
                            .Build();

            var exercise3 = new ExerciseBuilder()
                            .WithName("Exercise 3")
                            .WithBeforeExerciseAction(action)
                            .Build();

            var sut = new ExerciseProgramViewModelBuilder()
                      .WithModel(new ExerciseProgramBuilder()
                                 .WithExercise(exercise1)
                                 .WithExercise(exercise2)
                                 .WithExercise(exercise3))
                      .WithScheduler(scheduler)
                      .Build();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.Equal("Exercise 1", sut.CurrentExercise?.Name);

            sut.SkipForwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.Equal("Exercise 2", sut.CurrentExercise?.Name);

            sut.SkipForwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.Equal("Exercise 3", sut.CurrentExercise?.Name);

            sut.SkipBackwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.Equal("Exercise 2", sut.CurrentExercise?.Name);
        }
示例#8
0
        public void skip_backwards_command_restarts_the_execution_context_from_the_start_of_the_previous_exercise_if_the_current_exercise_if_only_recently_started()
        {
            var scheduler = new TestScheduler();
            var action    = new ActionMock();

            action
            .When(x => x.Duration)
            .Return(TimeSpan.FromSeconds(10));

            action
            .When(x => x.Execute(It.IsAny <ExecutionContext>()))
            .Return <ExecutionContext>(
                ec =>
            {
                ec.AddProgress(TimeSpan.FromSeconds(0.5));
                ec.IsPaused = true;

                return(ec.WaitWhilePaused());
            });

            var exercise1 = new ExerciseBuilder()
                            .WithName("Exercise 1")
                            .WithBeforeExerciseAction(action)
                            .Build();

            var exercise2 = new ExerciseBuilder()
                            .WithName("Exercise 2")
                            .WithBeforeExerciseAction(action)
                            .Build();

            var sut = new ExerciseProgramViewModelBuilder()
                      .WithModel(new ExerciseProgramBuilder()
                                 .WithExercise(exercise1)
                                 .WithExercise(exercise2))
                      .WithScheduler(scheduler)
                      .Build();

            var progress = sut
                           .WhenAnyValue(x => x.ProgressTimeSpan)
                           .CreateCollection();

            // start from the second exercise
            sut.StartCommand.ExecuteAsync(exercise1.Duration);
            scheduler.AdvanceMinimal();

            sut.SkipBackwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(5, progress.Count);
            Assert.Equal(TimeSpan.Zero, progress[0]);
            Assert.Equal(TimeSpan.FromSeconds(10), progress[1]);
            Assert.Equal(TimeSpan.FromSeconds(10.5), progress[2]);
            Assert.Equal(TimeSpan.Zero, progress[3]);
            Assert.Equal(TimeSpan.FromSeconds(10), progress[4]);
        }
示例#9
0
        public void skip_forwards_command_is_disabled_if_on_last_exercise()
        {
            var scheduler = new TestScheduler();
            var action    = new ActionMock();

            action
            .When(x => x.Duration)
            .Return(TimeSpan.FromSeconds(10));

            action
            .When(x => x.Execute(It.IsAny <ExecutionContext>()))
            .Return <ExecutionContext>(
                ec =>
            {
                ec.IsPaused = true;

                return(ec.WaitWhilePaused());
            });

            var exercise1 = new ExerciseBuilder()
                            .WithName("Exercise 1")
                            .WithBeforeExerciseAction(action)
                            .Build();

            var exercise2 = new ExerciseBuilder()
                            .WithName("Exercise 2")
                            .WithBeforeExerciseAction(action)
                            .Build();

            var sut = new ExerciseProgramViewModelBuilder()
                      .WithModel(new ExerciseProgramBuilder()
                                 .WithExercise(exercise1)
                                 .WithExercise(exercise2))
                      .WithScheduler(scheduler)
                      .Build();

            var canExecute = sut
                             .SkipForwardsCommand
                             .CanExecute
                             .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            sut.SkipForwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(5, canExecute.Count);
            Assert.False(canExecute[0]);
            Assert.True(canExecute[1]);
            Assert.False(canExecute[2]);
            Assert.True(canExecute[3]);
            Assert.False(canExecute[4]);
        }
示例#10
0
        public void skip_forwards_command_skips_to_the_next_exercise()
        {
            var scheduler = new TestScheduler();
            var action    = new ActionMock();

            action
            .When(x => x.Duration)
            .Return(TimeSpan.FromSeconds(10));

            action
            .When(x => x.Execute(It.IsAny <ExecutionContext>()))
            .Return <ExecutionContext>(
                ec =>
            {
                ec.IsPaused = true;

                return(ec.WaitWhilePaused());
            });

            var exercise1 = new ExerciseBuilder()
                            .WithName("Exercise 1")
                            .WithBeforeExerciseAction(action)
                            .Build();

            var exercise2 = new ExerciseBuilder()
                            .WithName("Exercise 2")
                            .WithBeforeExerciseAction(action)
                            .Build();

            var sut = new ExerciseProgramViewModelBuilder()
                      .WithModel(new ExerciseProgramBuilder()
                                 .WithExercise(exercise1)
                                 .WithExercise(exercise2))
                      .WithScheduler(scheduler)
                      .Build();

            var progress = sut
                           .WhenAnyValue(x => x.ProgressTimeSpan)
                           .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            sut.SkipForwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(2, progress.Count);
            Assert.Equal(TimeSpan.Zero, progress[0]);
            Assert.Equal(TimeSpan.FromSeconds(10), progress[1]);
        }
示例#11
0
        public void document_is_not_stored_in_cache_if_loaded_from_cache()
        {
            var document     = "# First Program";
            var scheduler    = new TestScheduler();
            var stateService = new StateServiceMock();

            stateService
            .When(x => x.Get <string>(It.IsAny <string>()))
            .Return(Observable.Return(document));

            stateService
            .When(x => x.Set <string>(It.IsAny <string>(), It.IsAny <string>()))
            .Return(Observable.Return(Unit.Default));

            var sut = new ExerciseProgramsViewModelBuilder()
                      .WithScheduler(scheduler)
                      .WithStateService(stateService)
                      .Build();

            scheduler.AdvanceMinimal();
            Assert.Equal(ExerciseProgramsViewModelStatus.LoadedFromCache, sut.Status);

            stateService
            .Verify(x => x.Set <string>("ExerciseProgramsDocument", document))
            .WasNotCalled();
        }
        public void delete_all_deletes_all_entities_on_provided_scheduler()
        {
            var scheduler  = new TestScheduler();
            var repository = new RepositoryMock <int, TestEntity>(MockBehavior.Loose);

            repository
            .When(x => x.Get(42))
            .Return(new TestEntity());
            var sut = new AsyncRepositoryBuilder()
                      .WithDataStoreScheduler(scheduler)
                      .WithRepository(repository)
                      .Build();

            sut
            .DeleteAll()
            .Subscribe();

            repository
            .Verify(x => x.DeleteAll())
            .WasNotCalled();
            scheduler.AdvanceMinimal();
            repository
            .Verify(x => x.DeleteAll())
            .WasCalledExactlyOnce();
        }
        public void items_retrieves_entities_on_provided_scheduler()
        {
            var scheduler  = new TestScheduler();
            var repository = new RepositoryMock <int, TestEntity>(MockBehavior.Loose);

            repository
            .When(x => x.GetAll())
            .Return(ImmutableList <TestEntity> .Empty);
            var sut = new AsyncRepositoryBuilder()
                      .WithDataStoreScheduler(scheduler)
                      .WithRepository(repository)
                      .Build();

            sut
            .Items
            .Subscribe();

            repository
            .Verify(x => x.GetAll())
            .WasNotCalled();
            scheduler.AdvanceMinimal();
            repository
            .Verify(x => x.GetAll())
            .WasCalledExactlyOnce();
        }
        public void programs_is_populated_from_cache_whilst_document_from_cloud_loads()
        {
            var cacheDocument           = "# First Program";
            var cloudDocument           = @"
# First Program
# Second Program";
            var scheduler               = new TestScheduler();
            var exerciseDocumentService = new ExerciseDocumentServiceMock(MockBehavior.Loose);

            exerciseDocumentService
            .When(x => x.ExerciseDocument)
            .Return(
                Observable
                .Return(cloudDocument)
                .Delay(TimeSpan.FromSeconds(3), scheduler));
            var sut = new ExerciseProgramsViewModelBuilder()
                      .WithExerciseDocumentService(exerciseDocumentService)
                      .WithCachedDocument(cacheDocument)
                      .WithScheduler(scheduler)
                      .Build();

            scheduler.AdvanceMinimal();

            Assert.NotNull(sut.Programs);
            Assert.Equal(1, sut.Programs.Count);

            scheduler.AdvanceBy(TimeSpan.FromSeconds(2));
            Assert.NotNull(sut.Programs);
            Assert.Equal(1, sut.Programs.Count);

            scheduler.AdvanceBy(TimeSpan.FromSeconds(2));
            Assert.NotNull(sut.Programs);
            Assert.Equal(2, sut.Programs.Count);
        }
        public void progress_is_calculated_based_on_duration_and_progress_time_span(int durationInMs, int progressInMs, double expectedProgress)
        {
            var scheduler = new TestScheduler();
            var action    = new ActionMock(MockBehavior.Loose);

            action
            .When(x => x.Duration)
            .Return(TimeSpan.FromMilliseconds(durationInMs));

            var model = new ExerciseBuilder()
                        .WithBeforeExerciseAction(action)
                        .Build();

            var executionContext = new ExecutionContext();
            var sut = new ExerciseViewModelBuilder()
                      .WithSchedulerService(scheduler)
                      .WithExecutionContext(executionContext)
                      .WithModel(model)
                      .Build();

            executionContext.SetCurrentExercise(model);
            executionContext.AddProgress(TimeSpan.FromMilliseconds(progressInMs));

            scheduler.AdvanceMinimal();

            Assert.Equal(expectedProgress, sut.Progress);
        }
示例#16
0
        public void upgrade_records_versions()
        {
            var scheduler = new TestScheduler();
            var dataStoreVersionRepository = new DataStoreVersionRepositoryMock(MockBehavior.Loose);
            var upgradeHandler1            = new UpgradeHandlerMock(MockBehavior.Loose);
            var upgradeHandler2            = new UpgradeHandlerMock(MockBehavior.Loose);

            upgradeHandler1
            .When(x => x.Version)
            .Return(new Version(1, 0, 0));
            upgradeHandler2
            .When(x => x.Version)
            .Return(new Version(2, 0, 1));
            var sut = new DataStoreServiceBuilder()
                      .WithDataStoreScheduler(scheduler)
                      .WithDataStoreVersionRepository(dataStoreVersionRepository)
                      .WithUpgradeHandlers(upgradeHandler1, upgradeHandler2)
                      .Build();

            sut
            .Upgrade()
            .Subscribe();
            scheduler.AdvanceMinimal();

            dataStoreVersionRepository
            .Verify(x => x.Save(It.Matches <DataStoreVersionEntity>(y => y.Major == 1 && y.Minor == 0 && y.Build == 0)))
            .WasCalledExactlyOnce();
            dataStoreVersionRepository
            .Verify(x => x.Save(It.Matches <DataStoreVersionEntity>(y => y.Major == 2 && y.Minor == 0 && y.Build == 1)))
            .WasCalledExactlyOnce();
        }
示例#17
0
        public void skip_backwards_command_is_disabled_if_on_first_exercise()
        {
            var scheduler = new TestScheduler();
            var action    = new ActionMock(MockBehavior.Loose);

            action
            .When(x => x.Execute(It.IsAny <ExecutionContext>()))
            .Return <ExecutionContext>(
                ec =>
            {
                ec.IsPaused = true;
                return(ec.WaitWhilePaused());
            });

            var sut = new ExerciseProgramViewModelBuilder()
                      .WithModel(new ExerciseProgramBuilder()
                                 .WithExercise(new ExerciseBuilder()
                                               .WithBeforeExerciseAction(action)))
                      .WithScheduler(scheduler)
                      .Build();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.False(sut.SkipBackwardsCommand.CanExecute.FirstAsync().Wait());
        }
示例#18
0
        public void is_pause_visible_cycles_correctly_if_start_command_is_executed()
        {
            var scheduler = new TestScheduler();
            var action    = new ActionMock(MockBehavior.Loose);

            action
            .When(x => x.Duration)
            .Return(TimeSpan.FromMinutes(1));
            action
            .When(x => x.Execute(It.IsAny <ExecutionContext>()))
            .Return <ExecutionContext>(
                ec =>
                Observable
                .Return(Unit.Default)
                .Do(_ => { })
                .Delay(TimeSpan.FromMinutes(1), scheduler)
                .Do(_ => ec.AddProgress(TimeSpan.FromMinutes(1))));
            var sut = new ExerciseProgramViewModelBuilder()
                      .WithModel(
                new ExerciseProgramBuilder()
                .WithExercise(
                    new ExerciseBuilder()
                    .WithBeforeExerciseAction(action)))
                      .WithScheduler(scheduler)
                      .Build();

            Assert.False(sut.IsPauseVisible);

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.True(sut.IsPauseVisible);

            scheduler.AdvanceBy(TimeSpan.FromMinutes(10));
            Assert.False(sut.IsPauseVisible);
        }
示例#19
0
        public void upgrade_aborts_if_any_upgrade_fails()
        {
            var scheduler       = new TestScheduler();
            var upgradeHandler1 = new UpgradeHandlerMock(MockBehavior.Loose);
            var upgradeHandler2 = new UpgradeHandlerMock(MockBehavior.Loose);

            upgradeHandler1
            .When(x => x.Version)
            .Return(new Version(1, 0, 0));
            upgradeHandler1
            .When(x => x.Apply(It.IsAny <IDatabaseConnection>()))
            .Throw();
            upgradeHandler2
            .When(x => x.Version)
            .Return(new Version(2, 0, 0));
            var sut = new DataStoreServiceBuilder()
                      .WithDataStoreScheduler(scheduler)
                      .WithUpgradeHandlers(upgradeHandler1, upgradeHandler2)
                      .Build();

            sut
            .Upgrade()
            .Subscribe();
            scheduler.AdvanceMinimal();

            upgradeHandler1
            .Verify(x => x.Apply(sut.Connection))
            .WasCalledExactlyOnce();
            upgradeHandler2
            .Verify(x => x.Apply(It.IsAny <IDatabaseConnection>()))
            .WasNotCalled();
        }
示例#20
0
        public void upgrade_applies_relevant_upgrades_only(string[] existingVersions, string[] availableVersions, bool[] applyExpected)
        {
            Assert.Equal(availableVersions.Length, applyExpected.Length);

            var scheduler = new TestScheduler();
            var versions  = existingVersions
                            .Select(
                x =>
                new DataStoreVersionEntityBuilder()
                .WithVersion(Version.Parse(x))
                .Build())
                            .ToImmutableList();
            var dataStoreVersionRepository = new DataStoreVersionRepositoryMock(MockBehavior.Loose);

            dataStoreVersionRepository
            .When(x => x.GetAll())
            .Return(versions);
            dataStoreVersionRepository
            .When(x => x.GetLatest())
            .Return(versions.Last());
            var upgradeHandlers = availableVersions
                                  .Select(
                x =>
            {
                var mock = new UpgradeHandlerMock(MockBehavior.Loose);
                mock
                .When(y => y.Version)
                .Return(Version.Parse(x));
                return(mock);
            })
                                  .ToArray();
            var sut = new DataStoreServiceBuilder()
                      .WithDataStoreScheduler(scheduler)
                      .WithDataStoreVersionRepository(dataStoreVersionRepository)
                      .WithUpgradeHandlers(upgradeHandlers)
                      .Build();

            sut
            .Upgrade()
            .Subscribe();
            scheduler.AdvanceMinimal();

            for (var i = 0; i < availableVersions.Length; ++i)
            {
                var upgradeHandler = upgradeHandlers[i];

                if (applyExpected[i])
                {
                    upgradeHandler
                    .Verify(x => x.Apply(sut.Connection))
                    .WasCalledExactlyOnce();
                }
                else
                {
                    upgradeHandler
                    .Verify(x => x.Apply(sut.Connection))
                    .WasNotCalled();
                }
            }
        }
        public void is_active_is_false_if_there_is_no_execution_context()
        {
            var scheduler = new TestScheduler();
            var sut       = new ExerciseViewModelBuilder()
                            .WithSchedulerService(scheduler)
                            .Build();

            scheduler.AdvanceMinimal();
            Assert.False(sut.IsActive);
        }
        public void is_active_is_true_if_this_exercise_is_the_current_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();

            scheduler.AdvanceMinimal();
            Assert.False(sut.IsActive);

            executionContext.SetCurrentExercise(model);
            scheduler.AdvanceMinimal();
            Assert.True(sut.IsActive);
        }
示例#23
0
        public void programs_is_null_if_both_cache_and_cloud_are_empty()
        {
            var scheduler = new TestScheduler();

            var sut = new ExerciseProgramsViewModelBuilder()
                      .WithScheduler(scheduler)
                      .Build();

            scheduler.AdvanceMinimal();
            Assert.Null(sut.Programs);
        }
示例#24
0
        public void is_start_visible_is_true_by_default()
        {
            var scheduler = new TestScheduler();
            var sut       = new ExerciseProgramViewModelBuilder()
                            .WithScheduler(scheduler)
                            .Build();

            scheduler.AdvanceMinimal();

            Assert.True(sut.IsStartVisible);
        }
示例#25
0
        public void is_started_cycles_correctly_if_start_command_is_executed()
        {
            var scheduler = new TestScheduler();
            var sut       = new ExerciseProgramViewModelBuilder()
                            .WithScheduler(scheduler)
                            .Build();

            scheduler.AdvanceMinimal();

            var isStarted = sut
                            .WhenAnyValue(x => x.IsStarted)
                            .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(3, isStarted.Count);
            Assert.False(isStarted[0]);
            Assert.True(isStarted[1]);
            Assert.False(isStarted[2]);
        }
示例#26
0
        public void status_is_loaded_from_cloud_if_document_is_successfully_loaded_from_cloud()
        {
            var document  = "# First Program";
            var scheduler = new TestScheduler();

            var sut = new ExerciseProgramsViewModelBuilder()
                      .WithCloudDocument(document)
                      .WithScheduler(scheduler)
                      .Build();

            scheduler.AdvanceMinimal();
            Assert.Equal(ExerciseProgramsViewModelStatus.LoadedFromService, sut.Status);
        }
示例#27
0
        public void skip_backwards_command_restarts_the_execution_context_from_the_start_of_the_current_exercise_if_sufficient_progress_has_been_made()
        {
            var scheduler = new TestScheduler();
            var action    = new ActionMock(MockBehavior.Loose);

            action
            .When(x => x.Execute(It.IsAny <ExecutionContext>()))
            .Return <ExecutionContext>(
                ec =>
            {
                ec.AddProgress(TimeSpan.FromSeconds(4));
                ec.IsPaused = true;

                return(ec.WaitWhilePaused());
            });

            var sut = new ExerciseProgramViewModelBuilder()
                      .WithModel(new ExerciseProgramBuilder()
                                 .WithExercise(new ExerciseBuilder()
                                               .WithBeforeExerciseAction(action)))
                      .WithScheduler(scheduler)
                      .Build();

            var progress = sut
                           .WhenAnyValue(x => x.ProgressTimeSpan)
                           .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            sut.SkipBackwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(3, progress.Count);
            Assert.Equal(TimeSpan.Zero, progress[0]);
            Assert.Equal(TimeSpan.FromSeconds(4), progress[1]);
            Assert.Equal(TimeSpan.Zero, progress[2]);
        }
示例#28
0
        public void programs_is_populated_from_cloud_if_cache_is_empty_and_cloud_is_populated()
        {
            var document  = "# First Program";
            var scheduler = new TestScheduler();

            var sut = new ExerciseProgramsViewModelBuilder()
                      .WithCloudDocument(document)
                      .WithScheduler(scheduler)
                      .Build();

            scheduler.AdvanceMinimal();
            Assert.NotNull(sut.Programs);
            Assert.Equal(1, sut.Programs.Count);
        }
        public void progress_time_span_is_reset_to_zero_if_the_execution_context_changes_to_null()
        {
            var scheduler = new TestScheduler();
            var model     = new ExerciseBuilder()
                            .Build();
            var executionContext        = new ExecutionContext();
            var executionContextSubject = new Subject <ExecutionContext>();
            var sut = new ExerciseViewModelBuilder()
                      .WithSchedulerService(scheduler)
                      .WithExecutionContext(executionContextSubject)
                      .WithModel(model)
                      .Build();

            executionContextSubject.OnNext(executionContext);
            executionContext.SetCurrentExercise(model);

            executionContext.AddProgress(TimeSpan.FromSeconds(3));
            scheduler.AdvanceMinimal();
            Assert.Equal(TimeSpan.FromSeconds(3), sut.ProgressTimeSpan);

            executionContextSubject.OnNext(null);
            scheduler.AdvanceMinimal();
            Assert.Equal(TimeSpan.Zero, sut.ProgressTimeSpan);
        }
示例#30
0
        public void programs_yields_any_successfully_parsed_programs()
        {
            var document  = "# First Program";
            var scheduler = new TestScheduler();

            var sut = new ExerciseProgramsViewModelBuilder()
                      .WithCloudDocument(document)
                      .WithScheduler(scheduler)
                      .Build();

            scheduler.AdvanceMinimal();
            Assert.NotNull(sut.Programs);
            Assert.Equal(1, sut.Programs.Count);
            Assert.Equal("First Program", sut.Programs.ElementAt(0).Name);
        }
示例#31
0
        public void is_paused_cycles_correctly_if_pause_command_is_executed()
        {
            var scheduler = new TestScheduler();
            var sut       = new ExerciseProgramViewModelBuilder()
                            .WithModel(new ExerciseProgramBuilder()
                                       .WithExercise(new ExerciseBuilder()
                                                     .WithBeforeExerciseAction(
                                                         new WaitActionBuilder()
                                                         .WithDelayService(new DelayServiceBuilder().Build())
                                                         .WithDelay(TimeSpan.FromMinutes(1))
                                                         .Build())))
                            .WithScheduler(scheduler)
                            .Build();

            Assert.False(sut.IsPaused);

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.False(sut.IsPaused);

            sut.PauseCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.True(sut.IsPaused);
        }
        public void parse_error_message_returns_appropriate_message_if_the_document_could_not_be_parsed()
        {
            var document = @"# First Program

## First Exercise

 whatever!";
            var scheduler = new TestScheduler();

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithCloudDocument(document)
                .WithScheduler(scheduler)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.NotNull(sut.ParseErrorMessage);
            Assert.Equal("Parsing failure: unexpected '#'; expected end of input (Line 3, Column 1); recently consumed: rogram\r\n\r\n", sut.ParseErrorMessage);
        }
        public void is_started_cycles_correctly_if_start_command_is_executed()
        {
            var scheduler = new TestScheduler();
            var sut = new ExerciseProgramViewModelBuilder()
                .WithScheduler(scheduler)
                .Build();
            scheduler.AdvanceMinimal();

            var isStarted = sut
                .WhenAnyValue(x => x.IsStarted)
                .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(3, isStarted.Count);
            Assert.False(isStarted[0]);
            Assert.True(isStarted[1]);
            Assert.False(isStarted[2]);
        }
        public void skip_backwards_command_is_disabled_if_on_first_exercise()
        {
            var scheduler = new TestScheduler();
            var action = new ActionMock(MockBehavior.Loose);

            action
                .When(x => x.Execute(It.IsAny<ExecutionContext>()))
                .Return<ExecutionContext>(
                    ec =>
                    {
                        ec.IsPaused = true;
                        return ec.WaitWhilePaused();
                    });

            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(new ExerciseProgramBuilder()
                    .WithExercise(new ExerciseBuilder()
                        .WithBeforeExerciseAction(action)))
                .WithScheduler(scheduler)
                .Build();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.False(sut.SkipBackwardsCommand.CanExecute.FirstAsync().Wait());
        }
        public void progress_is_updated_throughout_execution()
        {
            var scheduler = new TestScheduler();
            var action = new ActionMock(MockBehavior.Loose);

            action
                .When(x => x.Duration)
                .Return(TimeSpan.FromMinutes(1));

            action
                .When(x => x.Execute(It.IsAny<ExecutionContext>()))
                .Return<ExecutionContext>(
                    ec =>
                    {
                        ec.AddProgress(TimeSpan.FromSeconds(15));
                        ec.AddProgress(TimeSpan.FromSeconds(30));

                        ec.IsPaused = true;

                        return ec.WaitWhilePaused();
                    });

            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(new ExerciseProgramBuilder()
                    .WithExercise(new ExerciseBuilder()
                        .WithBeforeExerciseAction(action)))
                .WithScheduler(scheduler)
                .Build();

            var progress = sut
                .WhenAnyValue(x => x.Progress)
                .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(3, progress.Count);
            Assert.Equal(0, progress[0]);
            Assert.Equal(0.25, progress[1]);
            Assert.Equal(0.75, progress[2]);
        }
        public void status_is_load_failed_if_the_document_fails_to_load_altogether()
        {
            var exerciseDocumentService = new ExerciseDocumentServiceMock();
            var scheduler = new TestScheduler();

            exerciseDocumentService
                .When(x => x.ExerciseDocument)
                .Return(Observable.Throw<string>(new InvalidOperationException()));

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithExerciseDocumentService(exerciseDocumentService)
                .WithScheduler(scheduler)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.Equal(ExerciseProgramsViewModelStatus.LoadFailed, sut.Status);
        }
        public void progress_is_calculated_based_on_duration_and_progress_time_span(int durationInMs, int progressInMs, double expectedProgress)
        {
            var scheduler = new TestScheduler();
            var action = new ActionMock(MockBehavior.Loose);

            action
                .When(x => x.Duration)
                .Return(TimeSpan.FromMilliseconds(durationInMs));

            var model = new ExerciseBuilder()
                .WithBeforeExerciseAction(action)
                .Build();

            var executionContext = new ExecutionContext();
            var sut = new ExerciseViewModelBuilder()
                .WithSchedulerService(scheduler)
                .WithExecutionContext(executionContext)
                .WithModel(model)
                .Build();

            executionContext.SetCurrentExercise(model);
            executionContext.AddProgress(TimeSpan.FromMilliseconds(progressInMs));

            scheduler.AdvanceMinimal();

            Assert.Equal(expectedProgress, sut.Progress);
        }
        public void execution_context_is_cancelled_if_user_navigates_away()
        {
            var scheduler = new TestScheduler();
            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(new ExerciseProgramBuilder()
                    .WithExercise(new ExerciseBuilder()
                        .WithBeforeExerciseAction(
                            new WaitActionBuilder()
                                .WithDelayService(new DelayServiceBuilder().Build())
                                .WithDelay(TimeSpan.FromMinutes(1))
                                .Build())))
                .WithScheduler(scheduler)
                .Build();
            sut
                .HostScreen
                .Router
                .NavigationStack
                .Add(sut);

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.True(sut.IsStarted);

            sut
                .HostScreen
                .Router
                .NavigateBack
                .ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.False(sut.IsStarted);
        }
        public void skip_forwards_command_skips_to_the_next_exercise()
        {
            var scheduler = new TestScheduler();
            var action = new ActionMock();

            action
                .When(x => x.Duration)
                .Return(TimeSpan.FromSeconds(10));

            action
                .When(x => x.Execute(It.IsAny<ExecutionContext>()))
                .Return<ExecutionContext>(
                    ec =>
                    {
                        ec.IsPaused = true;

                        return ec.WaitWhilePaused();
                    });

            var exercise1 = new ExerciseBuilder()
                .WithName("Exercise 1")
                .WithBeforeExerciseAction(action)
                .Build();

            var exercise2 = new ExerciseBuilder()
                .WithName("Exercise 2")
                .WithBeforeExerciseAction(action)
                .Build();

            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(new ExerciseProgramBuilder()
                    .WithExercise(exercise1)
                    .WithExercise(exercise2))
                .WithScheduler(scheduler)
                .Build();

            var progress = sut
                .WhenAnyValue(x => x.ProgressTimeSpan)
                .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            sut.SkipForwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(2, progress.Count);
            Assert.Equal(TimeSpan.Zero, progress[0]);
            Assert.Equal(TimeSpan.FromSeconds(10), progress[1]);
        }
        public void skip_backwards_command_restarts_the_execution_context_from_the_start_of_the_previous_exercise_if_the_current_exercise_if_only_recently_started()
        {
            var scheduler = new TestScheduler();
            var action = new ActionMock();

            action
                .When(x => x.Duration)
                .Return(TimeSpan.FromSeconds(10));

            action
                .When(x => x.Execute(It.IsAny<ExecutionContext>()))
                .Return<ExecutionContext>(
                    ec =>
                    {
                        ec.AddProgress(TimeSpan.FromSeconds(0.5));
                        ec.IsPaused = true;

                        return ec.WaitWhilePaused();
                    });

            var exercise1 = new ExerciseBuilder()
                .WithName("Exercise 1")
                .WithBeforeExerciseAction(action)
                .Build();

            var exercise2 = new ExerciseBuilder()
                .WithName("Exercise 2")
                .WithBeforeExerciseAction(action)
                .Build();

            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(new ExerciseProgramBuilder()
                    .WithExercise(exercise1)
                    .WithExercise(exercise2))
                .WithScheduler(scheduler)
                .Build();

            var progress = sut
                .WhenAnyValue(x => x.ProgressTimeSpan)
                .CreateCollection();

            // start from the second exercise
            sut.StartCommand.ExecuteAsync(exercise1.Duration);
            scheduler.AdvanceMinimal();

            sut.SkipBackwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(5, progress.Count);
            Assert.Equal(TimeSpan.Zero, progress[0]);
            Assert.Equal(TimeSpan.FromSeconds(10), progress[1]);
            Assert.Equal(TimeSpan.FromSeconds(10.5), progress[2]);
            Assert.Equal(TimeSpan.Zero, progress[3]);
            Assert.Equal(TimeSpan.FromSeconds(10), progress[4]);
        }
        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 void programs_is_null_if_both_cache_and_cloud_are_empty()
        {
            var scheduler = new TestScheduler();

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithScheduler(scheduler)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.Null(sut.Programs);
        }
        public void programs_yields_any_successfully_parsed_programs()
        {
            var document = "# First Program";
            var scheduler = new TestScheduler();

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithCloudDocument(document)
                .WithScheduler(scheduler)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.NotNull(sut.Programs);
            Assert.Equal(1, sut.Programs.Count);
            Assert.Equal("First Program", sut.Programs.ElementAt(0).Name);
        }
        public void is_active_is_false_if_there_is_no_execution_context()
        {
            var scheduler = new TestScheduler();
            var sut = new ExerciseViewModelBuilder()
                .WithSchedulerService(scheduler)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.False(sut.IsActive);
        }
        public void document_is_not_stored_in_cache_if_loaded_from_cache()
        {
            var document = "# First Program";
            var scheduler = new TestScheduler();
            var stateService = new StateServiceMock();

            stateService
                .When(x => x.Get<string>(It.IsAny<string>()))
                .Return(Observable.Return(document));

            stateService
                .When(x => x.Set<string>(It.IsAny<string>(), It.IsAny<string>()))
                .Return(Observable.Return(Unit.Default));

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithScheduler(scheduler)
                .WithStateService(stateService)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.Equal(ExerciseProgramsViewModelStatus.LoadedFromCache, sut.Status);

            stateService
                .Verify(x => x.Set<string>("ExerciseProgramsDocument", document))
                .WasNotCalled();
        }
        public void skip_backwards_command_is_enabled_if_sufficient_progress_has_been_made_through_first_exercise()
        {
            var scheduler = new TestScheduler();
            var action = new ActionMock(MockBehavior.Loose);

            action
                .When(x => x.Execute(It.IsAny<ExecutionContext>()))
                .Return<ExecutionContext>(
                    ec =>
                    {
                        ec.AddProgress(TimeSpan.FromSeconds(1));
                        ec.IsPaused = true;
                        return ec.WaitWhilePaused();
                    });

            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(new ExerciseProgramBuilder()
                    .WithExercise(new ExerciseBuilder()
                        .WithBeforeExerciseAction(action)))
                .WithScheduler(scheduler)
                .Build();

            // TODO: technically, I should just check CanExecute(null) at the end, but without this subscription the RxCommand does not update CanExecute correctly
            //       try changing this once I'm using new RxCommand
            var canExecute = sut
                .SkipBackwardsCommand
                .CanExecute
                .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(2, canExecute.Count);
            Assert.False(canExecute[0]);
            Assert.True(canExecute[1]);
        }
        public void skip_backwards_command_restarts_the_execution_context_from_the_start_of_the_current_exercise_if_sufficient_progress_has_been_made()
        {
            var scheduler = new TestScheduler();
            var action = new ActionMock(MockBehavior.Loose);

            action
                .When(x => x.Execute(It.IsAny<ExecutionContext>()))
                .Return<ExecutionContext>(
                    ec =>
                    {
                        ec.AddProgress(TimeSpan.FromSeconds(4));
                        ec.IsPaused = true;

                        return ec.WaitWhilePaused();
                    });

            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(new ExerciseProgramBuilder()
                    .WithExercise(new ExerciseBuilder()
                        .WithBeforeExerciseAction(action)))
                .WithScheduler(scheduler)
                .Build();

            var progress = sut
                .WhenAnyValue(x => x.ProgressTimeSpan)
                .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            sut.SkipBackwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(3, progress.Count);
            Assert.Equal(TimeSpan.Zero, progress[0]);
            Assert.Equal(TimeSpan.FromSeconds(4), progress[1]);
            Assert.Equal(TimeSpan.Zero, progress[2]);
        }
        public void status_is_parse_failed_if_the_document_could_not_be_parsed()
        {
            var document = @"# First Program

## First Exercise

 whatever!";
            var scheduler = new TestScheduler();

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithCloudDocument(document)
                .WithScheduler(scheduler)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.Equal(ExerciseProgramsViewModelStatus.ParseFailed, sut.Status);
        }
        public void skip_forwards_command_is_disabled_if_on_last_exercise()
        {
            var scheduler = new TestScheduler();
            var action = new ActionMock();

            action
                .When(x => x.Duration)
                .Return(TimeSpan.FromSeconds(10));

            action
                .When(x => x.Execute(It.IsAny<ExecutionContext>()))
                .Return<ExecutionContext>(
                    ec =>
                    {
                        ec.IsPaused = true;

                        return ec.WaitWhilePaused();
                    });

            var exercise1 = new ExerciseBuilder()
                .WithName("Exercise 1")
                .WithBeforeExerciseAction(action)
                .Build();

            var exercise2 = new ExerciseBuilder()
                .WithName("Exercise 2")
                .WithBeforeExerciseAction(action)
                .Build();

            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(new ExerciseProgramBuilder()
                    .WithExercise(exercise1)
                    .WithExercise(exercise2))
                .WithScheduler(scheduler)
                .Build();

            var canExecute = sut
                .SkipForwardsCommand
                .CanExecute
                .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            sut.SkipForwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(5, canExecute.Count);
            Assert.False(canExecute[0]);
            Assert.True(canExecute[1]);
            Assert.False(canExecute[2]);
            Assert.True(canExecute[3]);
            Assert.False(canExecute[4]);
        }
        public void programs_is_populated_from_cache_whilst_document_from_cloud_loads()
        {
            var cacheDocument = "# First Program";
            var cloudDocument = @"
# First Program
# Second Program";
            var scheduler = new TestScheduler();

            var exerciseDocumentService = new ExerciseDocumentServiceMock(MockBehavior.Loose);

            exerciseDocumentService
                .When(x => x.ExerciseDocument)
                .Return(
                    Observable
                        .Return(cloudDocument)
                        .Delay(TimeSpan.FromSeconds(3), scheduler));

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithExerciseDocumentService(exerciseDocumentService)
                .WithCachedDocument(cacheDocument)
                .WithScheduler(scheduler)
                .Build();

            scheduler.AdvanceMinimal();

            Assert.NotNull(sut.Programs);
            Assert.Equal(1, sut.Programs.Count);

            scheduler.AdvanceBy(TimeSpan.FromSeconds(2));
            Assert.NotNull(sut.Programs);
            Assert.Equal(1, sut.Programs.Count);

            scheduler.AdvanceBy(TimeSpan.FromSeconds(2));
            Assert.NotNull(sut.Programs);
            Assert.Equal(2, sut.Programs.Count);
        }
        public void current_exercise_reflects_that_in_the_execution_context()
        {
            var scheduler = new TestScheduler();
            var action = new ActionMock();

            action
                .When(x => x.Duration)
                .Return(TimeSpan.FromSeconds(10));

            action
                .When(x => x.Execute(It.IsAny<ExecutionContext>()))
                .Return<ExecutionContext>(
                    ec =>
                    {
                        ec.IsPaused = true;
                        return ec.WaitWhilePaused();
                    });

            var exercise1 = new ExerciseBuilder()
                .WithName("Exercise 1")
                .WithBeforeExerciseAction(action)
                .Build();

            var exercise2 = new ExerciseBuilder()
                .WithName("Exercise 2")
                .WithBeforeExerciseAction(action)
                .Build();

            var exercise3 = new ExerciseBuilder()
                .WithName("Exercise 3")
                .WithBeforeExerciseAction(action)
                .Build();

            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(new ExerciseProgramBuilder()
                    .WithExercise(exercise1)
                    .WithExercise(exercise2)
                    .WithExercise(exercise3))
                .WithScheduler(scheduler)
                .Build();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.Equal("Exercise 1", sut.CurrentExercise?.Name);

            sut.SkipForwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.Equal("Exercise 2", sut.CurrentExercise?.Name);

            sut.SkipForwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.Equal("Exercise 3", sut.CurrentExercise?.Name);

            sut.SkipBackwardsCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.Equal("Exercise 2", sut.CurrentExercise?.Name);
        }
        public void programs_is_populated_from_cloud_if_cache_is_populated_and_cloud_is_populated()
        {
            var cacheDocument = "# First Program";
            var cloudDocument = @"
# First Program
# Second Program";
            var scheduler = new TestScheduler();

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithCloudDocument(cloudDocument)
                .WithCachedDocument(cacheDocument)
                .WithScheduler(scheduler)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.NotNull(sut.Programs);
            Assert.Equal(2, sut.Programs.Count);
        }
        public void programs_is_populated_from_cloud_if_cache_errors_and_cloud_is_populated()
        {
            var document = "# First Program";
            var stateService = new StateServiceMock();
            var scheduler = new TestScheduler();

            stateService
                .When(x => x.Get<string>(It.IsAny<string>()))
                .Return(Observable.Throw<string>(new InvalidOperationException()));

            stateService
                .When(x => x.Set<string>(It.IsAny<string>(), It.IsAny<string>()))
                .Return(Observable.Return(Unit.Default));

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithCloudDocument(document)
                .WithScheduler(scheduler)
                .WithStateService(stateService)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.NotNull(sut.Programs);
            Assert.Equal(1, sut.Programs.Count);
        }
        public void is_resume_visible_cycles_correctly_if_start_command_is_executed_and_execution_is_paused()
        {
            var scheduler = new TestScheduler();
            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(new ExerciseProgramBuilder()
                    .WithExercise(new ExerciseBuilder()
                        .WithBeforeExerciseAction(
                            new WaitActionBuilder()
                                .WithDelayService(new DelayServiceBuilder().Build())
                                .WithDelay(TimeSpan.FromMinutes(1))
                                .Build())))
                .WithScheduler(scheduler)
                .Build();

            var isResumeVisible = sut
                .WhenAnyValue(x => x.IsResumeVisible)
                .CreateCollection();

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            sut.PauseCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();

            Assert.Equal(2, isResumeVisible.Count);
            Assert.False(isResumeVisible[0]);
            Assert.True(isResumeVisible[1]);
        }
        public void is_start_visible_is_true_by_default()
        {
            var scheduler = new TestScheduler();
            var sut = new ExerciseProgramViewModelBuilder()
                .WithScheduler(scheduler)
                .Build();
            scheduler.AdvanceMinimal();

            Assert.True(sut.IsStartVisible);
        }
        public void progress_time_span_is_reset_to_zero_if_the_execution_context_changes_to_null()
        {
            var scheduler = new TestScheduler();
            var model = new ExerciseBuilder()
                .Build();
            var executionContext = new ExecutionContext();
            var executionContextSubject = new Subject<ExecutionContext>();
            var sut = new ExerciseViewModelBuilder()
                .WithSchedulerService(scheduler)
                .WithExecutionContext(executionContextSubject)
                .WithModel(model)
                .Build();

            executionContextSubject.OnNext(executionContext);
            executionContext.SetCurrentExercise(model);

            executionContext.AddProgress(TimeSpan.FromSeconds(3));
            scheduler.AdvanceMinimal();
            Assert.Equal(TimeSpan.FromSeconds(3), sut.ProgressTimeSpan);

            executionContextSubject.OnNext(null);
            scheduler.AdvanceMinimal();
            Assert.Equal(TimeSpan.Zero, sut.ProgressTimeSpan);
        }
        public void is_paused_cycles_correctly_if_pause_command_is_executed()
        {
            var scheduler = new TestScheduler();
            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(new ExerciseProgramBuilder()
                    .WithExercise(new ExerciseBuilder()
                        .WithBeforeExerciseAction(
                            new WaitActionBuilder()
                                .WithDelayService(new DelayServiceBuilder().Build())
                                .WithDelay(TimeSpan.FromMinutes(1))
                                .Build())))
                .WithScheduler(scheduler)
                .Build();

            Assert.False(sut.IsPaused);

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.False(sut.IsPaused);

            sut.PauseCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.True(sut.IsPaused);
        }
        public void is_pause_visible_cycles_correctly_if_start_command_is_executed()
        {
            var scheduler = new TestScheduler();
            var action = new ActionMock(MockBehavior.Loose);
            action
                .When(x => x.Duration)
                .Return(TimeSpan.FromMinutes(1));
            action
                .When(x => x.Execute(It.IsAny<ExecutionContext>()))
                .Return<ExecutionContext>(
                    ec =>
                        Observable
                            .Return(Unit.Default)
                            .Do(_ => { })
                            .Delay(TimeSpan.FromMinutes(1), scheduler)
                            .Do(_ => ec.AddProgress(TimeSpan.FromMinutes(1))));
            var sut = new ExerciseProgramViewModelBuilder()
                .WithModel(
                    new ExerciseProgramBuilder()
                        .WithExercise(
                            new ExerciseBuilder()
                                .WithBeforeExerciseAction(action)))
                .WithScheduler(scheduler)
                .Build();

            Assert.False(sut.IsPauseVisible);

            sut.StartCommand.ExecuteAsync();
            scheduler.AdvanceMinimal();
            Assert.True(sut.IsPauseVisible);

            scheduler.AdvanceBy(TimeSpan.FromMinutes(10));
            Assert.False(sut.IsPauseVisible);
        }
        public void status_is_loaded_from_cloud_if_document_is_successfully_loaded_from_cloud()
        {
            var document = "# First Program";
            var scheduler = new TestScheduler();

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithCloudDocument(document)
                .WithScheduler(scheduler)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.Equal(ExerciseProgramsViewModelStatus.LoadedFromService, sut.Status);
        }
        public void is_active_is_false_if_this_exercise_is_not_the_current_exercise()
        {
            var scheduler = new TestScheduler();
            var model1 = new ExerciseBuilder()
                .Build();
            var model2 = new ExerciseBuilder()
                .Build();
            var executionContext = new ExecutionContext();
            var sut = new ExerciseViewModelBuilder()
                .WithSchedulerService(scheduler)
                .WithExecutionContext(executionContext)
                .WithModel(model1)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.False(sut.IsActive);

            executionContext.SetCurrentExercise(model2);
            scheduler.AdvanceMinimal();
            Assert.False(sut.IsActive);
        }