diff --git a/app/src/main/java/org/oppia/android/app/customview/SegmentedCircularProgressView.kt b/app/src/main/java/org/oppia/android/app/customview/SegmentedCircularProgressView.kt index ba84f4cbff2..dfb764fa592 100644 --- a/app/src/main/java/org/oppia/android/app/customview/SegmentedCircularProgressView.kt +++ b/app/src/main/java/org/oppia/android/app/customview/SegmentedCircularProgressView.kt @@ -27,21 +27,29 @@ class SegmentedCircularProgressView : View { private val isRTL = TextUtilsCompat .getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL - private var baseRect: RectF? = null + private lateinit var baseRect: RectF private lateinit var chapterFinishedArcPaint: Paint - private lateinit var chapterNotFinishedArcPaint: Paint + private lateinit var chapterInProgressArcPaint: Paint + private lateinit var chapterNotStartedArcPaint: Paint - private var chaptersNotFinished: Int = 0 + private var chaptersNotStarted: Int = 0 private var chaptersFinished: Int = 0 + private var chaptersInProgress: Int = 0 private var totalChapters: Int = 0 - fun setStoryChapterDetails(totalChaptersCount: Int, chaptersFinishedCount: Int) { + fun setStoryChapterDetails( + totalChaptersCount: Int, + chaptersFinishedCount: Int, + chaptersInProgressCount: Int + ) { if (this.totalChapters != totalChaptersCount || - this.chaptersFinished != chaptersFinishedCount + this.chaptersFinished != chaptersFinishedCount || + this.chaptersInProgress != chaptersInProgressCount ) { this.totalChapters = totalChaptersCount this.chaptersFinished = chaptersFinishedCount - this.chaptersNotFinished = totalChaptersCount - chaptersFinishedCount + this.chaptersInProgress = chaptersInProgressCount + this.chaptersNotStarted = totalChaptersCount - chaptersFinishedCount - chaptersInProgressCount initialise() } } @@ -55,26 +63,21 @@ class SegmentedCircularProgressView : View { ) private fun initialise() { - chaptersNotFinished = totalChapters - chaptersFinished + chaptersNotStarted = totalChapters - chaptersFinished - chaptersInProgress strokeWidth = dpToPx(4) calculateSweepAngle() chapterFinishedArcPaint = Paint(Paint.ANTI_ALIAS_FLAG) - chapterFinishedArcPaint.style = Paint.Style.STROKE - chapterFinishedArcPaint.strokeCap = Paint.Cap.ROUND - chapterFinishedArcPaint.strokeWidth = strokeWidth - chapterFinishedArcPaint.color = - ContextCompat.getColor(context, R.color.oppiaProgressChapterFinished) - - chapterNotFinishedArcPaint = Paint(Paint.ANTI_ALIAS_FLAG) - chapterNotFinishedArcPaint.style = Paint.Style.STROKE - chapterNotFinishedArcPaint.strokeCap = Paint.Cap.ROUND - chapterNotFinishedArcPaint.strokeWidth = strokeWidth + setupArcPaint(chapterFinishedArcPaint, R.color.oppiaProgressChapterFinished) + + chapterInProgressArcPaint = Paint(Paint.ANTI_ALIAS_FLAG) + setupArcPaint(chapterInProgressArcPaint, R.color.oppiaProgressChapterInProgress) + + chapterNotStartedArcPaint = Paint(Paint.ANTI_ALIAS_FLAG) if (chaptersFinished != 0) { - chapterNotFinishedArcPaint.color = - ContextCompat.getColor(context, R.color.oppiaProgressChapterNotFinished) + setupArcPaint(chapterNotStartedArcPaint, R.color.oppiaProgressChapterNotFinished) } else { - chapterNotFinishedArcPaint.color = ContextCompat.getColor(context, R.color.grey_shade_20) + setupArcPaint(chapterNotStartedArcPaint, R.color.grey_shade_20) } } @@ -82,7 +85,7 @@ class SegmentedCircularProgressView : View { if (isRTL) rotationY = 180f super.onDraw(canvas) - if (baseRect == null) { + if (!this::baseRect.isInitialized) { val centerX = measuredWidth / 2 val centerY = measuredHeight / 2 val radius = Math.min(centerX, centerY) @@ -106,33 +109,49 @@ class SegmentedCircularProgressView : View { val startAngle = angleStartPoint + i * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE) + STROKE_DASH_GAP_IN_DEGREE / 2 - canvas.drawArc(baseRect!!, startAngle, sweepAngle, false, chapterFinishedArcPaint) + canvas.drawArc(baseRect, startAngle, sweepAngle, false, chapterFinishedArcPaint) } angleStartPoint += chaptersFinished * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE) - // Draws arc for every unfinished chapter. - for (i in 0 until chaptersNotFinished) { + // Draws arc for every chapter that is in progress. + for (i in 0 until chaptersInProgress) { val startAngle = angleStartPoint + i * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE) + STROKE_DASH_GAP_IN_DEGREE / 2 - canvas.drawArc(baseRect!!, startAngle, sweepAngle, false, chapterNotFinishedArcPaint) + canvas.drawArc(baseRect, startAngle, sweepAngle, false, chapterInProgressArcPaint) + } + angleStartPoint += chaptersInProgress * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE) + // Draws arc for every chapter that is not started. + for (i in 0 until chaptersNotStarted) { + val startAngle = + angleStartPoint + i * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE) + + STROKE_DASH_GAP_IN_DEGREE / 2 + canvas.drawArc(baseRect, startAngle, sweepAngle, false, chapterNotStartedArcPaint) } } else if (totalChapters == 1) { // Draws entire circle for finished an unfinished chapter. if (chaptersFinished == 1) { canvas.drawArc( - baseRect!!, + baseRect, angleStartPoint, 360f, false, chapterFinishedArcPaint ) + } else if (chaptersInProgress == 1) { + canvas.drawArc( + baseRect, + angleStartPoint, + 360f, + false, + chapterInProgressArcPaint + ) } else { canvas.drawArc( - baseRect!!, + baseRect, angleStartPoint, 360f, false, - chapterNotFinishedArcPaint + chapterNotStartedArcPaint ) } } @@ -150,4 +169,13 @@ class SegmentedCircularProgressView : View { resources.displayMetrics ) } + + private fun setupArcPaint(arcPaint: Paint, color: Int) { + arcPaint.apply { + style = Paint.Style.STROKE + strokeCap = Paint.Cap.ROUND + strokeWidth = this@SegmentedCircularProgressView.strokeWidth + this.color = ContextCompat.getColor(context, color) + } + } } diff --git a/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragmentPresenter.kt index 6589d0e51cc..0e208e291c8 100755 --- a/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragmentPresenter.kt @@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView import org.oppia.android.R import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.home.RouteToExplorationListener +import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.PromotedActivityList import org.oppia.android.app.model.PromotedStory @@ -224,7 +225,9 @@ class RecentlyPlayedFragmentPresenter @Inject constructor( topicId, storyId, explorationId, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + // Pass an empty checkpoint if the exploration does not have to be resumed. + ExplorationCheckpoint.getDefaultInstance() ).observe( fragment, Observer> { result -> diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt index 6ebd407be15..5a73ba27a91 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt @@ -123,7 +123,7 @@ class StateFragment : fun scrollToTop() = stateFragmentPresenter.scrollToTop() fun revealHint(saveUserChoice: Boolean, hintIndex: Int) { - stateFragmentPresenter.revealHint(saveUserChoice, hintIndex) + stateFragmentPresenter.revealHint() } fun revealSolution() = stateFragmentPresenter.revealSolution() diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt index bfbf4fdba75..9adeba07dd9 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt @@ -84,6 +84,8 @@ class StateFragmentPresenter @Inject constructor( private lateinit var binding: StateFragmentBinding private lateinit var recyclerViewAdapter: RecyclerView.Adapter<*> + private var isCurrentStatePendingState: Boolean = false + private val viewModel: StateViewModel by lazy { getStateViewModel() } @@ -155,7 +157,7 @@ class StateFragmentPresenter @Inject constructor( } subscribeToCurrentState() - markExplorationAsRecentlyPlayed() + markExplorationAsStartedNotCompleted() return binding.root } @@ -202,17 +204,41 @@ class StateFragmentPresenter @Inject constructor( } fun onHintAvailable(helpIndex: HelpIndex) { + if ( + !isCurrentStatePendingState && + helpIndex.indexTypeCase != HelpIndex.IndexTypeCase.INDEXTYPE_NOT_SET + ) { + // If current state is not pending state and HelpIndex does not equal default instance, + // do not allow any new hints, new solutions or the hint icon to be visible. + return + } when (helpIndex.indexTypeCase) { - HelpIndex.IndexTypeCase.HINT_INDEX, HelpIndex.IndexTypeCase.SHOW_SOLUTION -> { - if (helpIndex.indexTypeCase == HelpIndex.IndexTypeCase.HINT_INDEX) { - viewModel.newAvailableHintIndex = helpIndex.hintIndex - } - viewModel.allHintsExhausted = - helpIndex.indexTypeCase == HelpIndex.IndexTypeCase.SHOW_SOLUTION - viewModel.setHintOpenedAndUnRevealedVisibility(true) + HelpIndex.IndexTypeCase.HINT_INDEX -> { + // Update the ViewModel with the index of the un-revealed hint. + viewModel.newAvailableHintIndex = helpIndex.hintIndex.index + viewModel.allHintsExhausted = false + viewModel.setHintOpenedAndUnRevealedVisibility(!helpIndex.hintIndex.isHintRevealed) + viewModel.setHintBulbVisibility(true) + // Notify the ExplorationProgressController that an un-revealed hint is visible. + unrevealedHintIsVisible(helpIndex.hintIndex.index) + } + HelpIndex.IndexTypeCase.SHOW_SOLUTION -> { + // Solution being visible implies that all hints have been viewed by the user. + // 1 is subtracted from the hint count because hints are indexed from 0. + viewModel.newAvailableHintIndex = currentState.interaction.hintCount - 1 + viewModel.allHintsExhausted = true + // Notify the ExplorationProgressController that un-revealed solution is visible. + unrevealedSolutionIsVisible() + viewModel.setHintOpenedAndUnRevealedVisibility( + !currentState.interaction.solution.solutionIsRevealed + ) viewModel.setHintBulbVisibility(true) } HelpIndex.IndexTypeCase.EVERYTHING_REVEALED -> { + // EVERYTHING_REVEALED implies that all hints and solution have been viewed by the user. + viewModel.allHintsExhausted = true + // 1 is subtracted from the hint count because hints are indexed from 0. + viewModel.newAvailableHintIndex = currentState.interaction.hintCount - 1 viewModel.setHintOpenedAndUnRevealedVisibility(false) viewModel.setHintBulbVisibility(true) } @@ -267,18 +293,63 @@ class StateFragmentPresenter @Inject constructor( .build() } - fun revealHint(saveUserChoice: Boolean, hintIndex: Int) { + fun revealHint() { + val previousHintState = recyclerViewAssembler.createLatestHintState() + val updatedHintIndex = + previousHintState.helpIndex.hintIndex.toBuilder().setIsHintRevealed(true).build() + val updatedHelpIndex = + previousHintState.helpIndex.toBuilder().setHintIndex(updatedHintIndex).build() + subscribeToHint( explorationProgressController.submitHintIsRevealed( currentState, - saveUserChoice, - hintIndex + previousHintState.toBuilder().setHelpIndex(updatedHelpIndex).build() ) ) } fun revealSolution() { - subscribeToSolution(explorationProgressController.submitSolutionIsRevealed(currentState)) + subscribeToSolution( + explorationProgressController.submitSolutionIsRevealed( + currentState, + recyclerViewAssembler.createLatestHintState() + ) + ) + } + + private fun unrevealedSolutionIsVisible() { + if ( + currentState.interaction.solution.unrevealedSolutionIsVisible || + currentState.interaction.solution.solutionIsRevealed + ) { + // If solution is already marked as un-revealed but visible or if the solution has been + // revealed by the user, do nothing. + return + } + + subscribeToUnRevealedSolution( + explorationProgressController.submitUnrevealedSolutionIsVisible( + currentState, + recyclerViewAssembler.createLatestHintState() + ) + ) + } + + private fun unrevealedHintIsVisible(indexOfHint: Int) { + if ( + currentState.interaction.hintList[indexOfHint].unrevealedHintIsVisible || + currentState.interaction.hintList[indexOfHint].hintIsRevealed + ) { + // If solution is already marked as unrevealed or if the solution has been revealed by the + // user, do nothing. + return + } + subscribeToUnrevealedHint( + explorationProgressController.submitUnrevealedHintIsVisible( + currentState, + recyclerViewAssembler.createLatestHintState() + ) + ) } private fun getStateViewModel(): StateViewModel { @@ -322,6 +393,9 @@ class StateFragmentPresenter @Inject constructor( val ephemeralState = result.getOrThrow() explorationCheckpointState = ephemeralState.checkpointState + + recyclerViewAssembler.updateHintState(ephemeralState.hintState) + val shouldSplit = splitScreenManager.shouldSplitScreen(ephemeralState.state.interaction.id) if (shouldSplit) { viewModel.isSplitView.set(true) @@ -336,6 +410,10 @@ class StateFragmentPresenter @Inject constructor( currentState = ephemeralState.state currentStateName = ephemeralState.state.name + + isCurrentStatePendingState = + ephemeralState.stateTypeCase == EphemeralState.StateTypeCase.PENDING_STATE + showOrHideAudioByState(ephemeralState.state) val dataPair = recyclerViewAssembler.compute( @@ -376,6 +454,45 @@ class StateFragmentPresenter @Inject constructor( ) } + /** + * This function listens to the result of UnrevealedHintIsVisible. + * Whenever a un-revealed hint is visible, this function will wait for the response from that + * function and based on which we can move to next state. + */ + private fun subscribeToUnrevealedHint(hintResultLiveData: LiveData>) { + val hintLiveData = getUnrevealedHintIsVisible(hintResultLiveData) + hintLiveData.observe( + fragment, + Observer { result -> + // If the un-revealed hint is visible, show dot and radar. + if (result.unrevealedHintIsVisible) { + viewModel.setHintOpenedAndUnRevealedVisibility(true) + } + } + ) + } + + /** + * This function listens to the result of UnrevealedSolutionIsVisible. + * Whenever a hint is revealed using ExplorationProgressController.submitHintIsRevealed function, + * this function will wait for the response from that function and based on which we can move to + * next state. + */ + private fun subscribeToUnRevealedSolution( + solutionResultLiveData: LiveData> + ) { + val solutionLiveData = getUnrevealedSolutionIsVisible(solutionResultLiveData) + solutionLiveData.observe( + fragment, + Observer { result -> + // If the hint was revealed remove dot and radar. + if (result.unrevealedSolutionIsVisible) { + viewModel.setHintOpenedAndUnRevealedVisibility(true) + } + } + ) + } + /** * This function listens to the result of RevealSolution. * Whenever a hint is revealed using ExplorationProgressController.submitHintIsRevealed function, @@ -432,11 +549,23 @@ class StateFragmentPresenter @Inject constructor( return Transformations.map(hint, ::processSolution) } + /** Helper for [subscribeToSolution]. */ + private fun getUnrevealedSolutionIsVisible( + hint: LiveData> + ): LiveData { + return Transformations.map(hint, ::processUnrevealedSolution) + } + /** Helper for [subscribeToHint]. */ private fun getHintIsRevealed(hint: LiveData>): LiveData { return Transformations.map(hint, ::processHint) } + /** Helper for [unrevealedHintIsVisible]. */ + private fun getUnrevealedHintIsVisible(hint: LiveData>): LiveData { + return Transformations.map(hint, ::processUnrevealedHint) + } + /** Helper for subscribeToAnswerOutcome. */ private fun getAnswerOutcome( answerOutcome: LiveData> @@ -458,6 +587,18 @@ class StateFragmentPresenter @Inject constructor( return ephemeralStateResult.getOrDefault(AnswerOutcome.getDefaultInstance()) } + /** Helper for [subscribeToUnrevealedHint]. */ + private fun processUnrevealedHint(hintResult: AsyncResult): Hint { + if (hintResult.isFailure()) { + oppiaLogger.e( + "StateFragment", + "Failed to show new hint", + hintResult.getErrorOrNull()!! + ) + } + return hintResult.getOrDefault(Hint.getDefaultInstance()) + } + /** Helper for [subscribeToHint]. */ private fun processHint(hintResult: AsyncResult): Hint { if (hintResult.isFailure()) { @@ -470,6 +611,18 @@ class StateFragmentPresenter @Inject constructor( return hintResult.getOrDefault(Hint.getDefaultInstance()) } + /** Helper for [subscribeToUnRevealedSolution]. */ + private fun processUnrevealedSolution(solutionResult: AsyncResult): Solution { + if (solutionResult.isFailure()) { + oppiaLogger.e( + "StateFragment", + "Failed to show new solution", + solutionResult.getErrorOrNull()!! + ) + } + return solutionResult.getOrDefault(Solution.getDefaultInstance()) + } + /** Helper for [subscribeToSolution]. */ private fun processSolution(solutionResult: AsyncResult): Solution { if (solutionResult.isFailure()) { @@ -483,7 +636,12 @@ class StateFragmentPresenter @Inject constructor( } private fun handleSubmitAnswer(answer: UserAnswer) { - subscribeToAnswerOutcome(explorationProgressController.submitAnswer(answer)) + subscribeToAnswerOutcome( + explorationProgressController.submitAnswer( + answer, + recyclerViewAssembler.createLatestHintState() + ) + ) } fun dismissConceptCard() { @@ -496,12 +654,13 @@ class StateFragmentPresenter @Inject constructor( private fun moveToNextState() { viewModel.setCanSubmitAnswer(canSubmitAnswer = false) - explorationProgressController.moveToNextState().observe( - fragment, - Observer { - recyclerViewAssembler.collapsePreviousResponses() - } - ) + explorationProgressController.moveToNextState() + .observe( + fragment, + Observer { + recyclerViewAssembler.collapsePreviousResponses() + } + ) } private fun hideKeyboard() { @@ -532,8 +691,8 @@ class StateFragmentPresenter @Inject constructor( /** Returns the checkpoint state for the current exploration. */ fun getExplorationCheckpointState() = explorationCheckpointState - private fun markExplorationAsRecentlyPlayed() { - storyProgressController.recordRecentlyPlayedChapter( + private fun markExplorationAsStartedNotCompleted() { + storyProgressController.recordChapterAsStartedNotCompleted( profileId, topicId, storyId, diff --git a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt index c490971ad17..72d50e34b6e 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt @@ -23,6 +23,8 @@ import org.oppia.android.app.model.EphemeralState import org.oppia.android.app.model.EphemeralState.StateTypeCase import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.HelpIndex.IndexTypeCase.INDEXTYPE_NOT_SET +import org.oppia.android.app.model.HintIndex +import org.oppia.android.app.model.HintState import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.PendingState import org.oppia.android.app.model.State @@ -195,6 +197,24 @@ class StatePlayerRecyclerViewAssembler private constructor( .showNow(fragment.childFragmentManager, CONCEPT_CARD_DIALOG_FRAGMENT_TAG) } + fun createLatestHintState(): HintState { + return HintState.newBuilder().apply { + isHintVisibleInLatestState = hintHandler.isHintVisibleInLatestState + hintSequenceNumber = hintHandler.hintSequenceNumber + trackedAnswerCount = hintHandler.trackedWrongAnswerCount + helpIndex = hintHandler.helpIndex + }.build() + } + + fun updateHintState(hintState: HintState) { + hintHandler.apply { + isHintVisibleInLatestState = hintState.isHintVisibleInLatestState + hintSequenceNumber = hintState.hintSequenceNumber + trackedWrongAnswerCount = hintState.trackedAnswerCount + helpIndex = hintState.helpIndex + } + } + /** * Computes a list of view models corresponding to the specified [EphemeralState] and the * configuration of this assembler, as well as the GCS entity ID that should be associated with @@ -1452,15 +1472,15 @@ class StatePlayerRecyclerViewAssembler private constructor( private val delayShowAdditionalHintsMs: Long, private val delayShowAdditionalHintsFromWrongAnswerMs: Long ) { - private var trackedWrongAnswerCount = 0 - private var previousHelpIndex: HelpIndex = HelpIndex.getDefaultInstance() - private var hintSequenceNumber = 0 - private var isHintVisibleInLatestState = false + var trackedWrongAnswerCount = 0 + var helpIndex: HelpIndex = HelpIndex.getDefaultInstance() + var hintSequenceNumber = 0 + var isHintVisibleInLatestState = false /** Resets this handler to prepare it for a new state, cancelling any pending hints. */ fun reset() { trackedWrongAnswerCount = 0 - previousHelpIndex = HelpIndex.getDefaultInstance() + helpIndex = HelpIndex.getDefaultInstance() // Cancel any potential pending hints by advancing the sequence number. Note that this isn't // reset to 0 to ensure that all previous hint tasks are cancelled, and new tasks can be // scheduled without overlapping with past sequence numbers. @@ -1476,7 +1496,7 @@ class StatePlayerRecyclerViewAssembler private constructor( } /** - * Handles potentially new wrong answers that were submnitted, and if so schedules a hint to be + * Handles potentially new wrong answers that were submitted, and if so schedules a hint to be * shown to the user if hints are available. */ fun maybeScheduleShowHint(state: State, pendingState: PendingState) { @@ -1489,22 +1509,14 @@ class StatePlayerRecyclerViewAssembler private constructor( // state. If any hint was revealed and user move between current and completed states, then // show those revealed hints back by making icon visible else use the previous help index. if (isHintVisibleInLatestState) { - if (state.interaction.hintList[previousHelpIndex.hintIndex].hintIsRevealed) { - (fragment as ShowHintAvailabilityListener).onHintAvailable( - HelpIndex.newBuilder().setEverythingRevealed(true).build() - ) - } else { - (fragment as ShowHintAvailabilityListener).onHintAvailable( - previousHelpIndex - ) - } + (fragment as ShowHintAvailabilityListener).onHintAvailable(helpIndex) } // Start showing hints after a wrong answer is submitted or if the user appears stuck (e.g. // doesn't answer after some duration). Note that if there's already a timer to show a hint, // it will be reset for each subsequent answer. val nextUnrevealedHintIndex = getNextHintIndexToReveal(state) - val isFirstHint = previousHelpIndex.indexTypeCase == INDEXTYPE_NOT_SET + val isFirstHint = helpIndex.indexTypeCase == INDEXTYPE_NOT_SET val wrongAnswerCount = pendingState.wrongAnswerList.size if (wrongAnswerCount == trackedWrongAnswerCount) { // If no answers have been submitted, schedule a task to automatically help after a fixed @@ -1554,7 +1566,11 @@ class StatePlayerRecyclerViewAssembler private constructor( return if (!hasHelp) { HelpIndex.getDefaultInstance() } else if (lastUnrevealedHintIndex != null) { - HelpIndex.newBuilder().setHintIndex(lastUnrevealedHintIndex).build() + HelpIndex.newBuilder() + .setHintIndex( + HintIndex.newBuilder() + .setIndex(lastUnrevealedHintIndex).setIsHintRevealed(false).build() + ).build() } else if (solution.hasCorrectAnswer() && !solution.solutionIsRevealed) { HelpIndex.newBuilder().setShowSolution(true).build() } else { @@ -1587,12 +1603,14 @@ class StatePlayerRecyclerViewAssembler private constructor( private fun showHint(targetSequenceNumber: Int, helpIndexToShow: HelpIndex) { // Only finish this timer if no other hints were scheduled and no cancellations occurred. if (targetSequenceNumber == hintSequenceNumber) { - if (previousHelpIndex != helpIndexToShow) { + if (helpIndex != helpIndexToShow) { + val hintIndex = helpIndexToShow.hintIndex.toBuilder().setIsHintRevealed(true).build() + helpIndex = helpIndexToShow.toBuilder().setHintIndex(hintIndex).build() + isHintVisibleInLatestState = true + helpIndex = helpIndexToShow // Only indicate the hint is available if its index is actually new (including if it // becomes null such as in the case of the solution becoming available). (fragment as ShowHintAvailabilityListener).onHintAvailable(helpIndexToShow) - previousHelpIndex = helpIndexToShow - isHintVisibleInLatestState = true } } } diff --git a/app/src/main/java/org/oppia/android/app/player/state/listener/ShowHintAvailabilityListener.kt b/app/src/main/java/org/oppia/android/app/player/state/listener/ShowHintAvailabilityListener.kt index d8012967f89..72fd99ebe97 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/listener/ShowHintAvailabilityListener.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/listener/ShowHintAvailabilityListener.kt @@ -4,6 +4,6 @@ import org.oppia.android.app.model.HelpIndex /** Callback interface for when hints can be made available to the learner. */ interface ShowHintAvailabilityListener { - /** Called when a hint is available to be shown, or null if all hints have been revealed. */ + /** Called when a hint is available to be shown, or if hints have been revealed. */ fun onHintAvailable(helpIndex: HelpIndex) } diff --git a/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt index 5185c910bd2..c7981d3123b 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt @@ -6,6 +6,7 @@ import androidx.databinding.DataBindingUtil import androidx.lifecycle.Observer import org.oppia.android.R import org.oppia.android.app.activity.ActivityScope +import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.model.ProfileId import org.oppia.android.app.player.exploration.HintsAndSolutionExplorationManagerFragment import org.oppia.android.app.player.exploration.TAG_HINTS_AND_SOLUTION_EXPLORATION_MANAGER @@ -98,7 +99,8 @@ class StateFragmentTestActivityPresenter @Inject constructor( topicId, storyId, explorationId, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) .observe( activity, diff --git a/app/src/main/java/org/oppia/android/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt b/app/src/main/java/org/oppia/android/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt index 942257c5a93..c52a5d49b92 100644 --- a/app/src/main/java/org/oppia/android/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt @@ -3,6 +3,7 @@ package org.oppia.android.app.story.storyitemviewmodel import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import org.oppia.android.app.model.ChapterSummary +import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.model.LessonThumbnail import org.oppia.android.app.story.ExplorationSelectionListener import org.oppia.android.domain.exploration.ExplorationDataController @@ -40,7 +41,9 @@ class StoryChapterSummaryViewModel( topicId, storyId, explorationId, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + // Pass an empty checkpoint if the exploration does not have to be resumed. + ExplorationCheckpoint.getDefaultInstance() ).observe( fragment, Observer> { result -> diff --git a/app/src/main/java/org/oppia/android/app/testing/ExplorationTestActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/testing/ExplorationTestActivityPresenter.kt index f1e60cb9380..1862123e99a 100644 --- a/app/src/main/java/org/oppia/android/app/testing/ExplorationTestActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/testing/ExplorationTestActivityPresenter.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.Observer import org.oppia.android.R import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.home.RouteToExplorationListener +import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.domain.exploration.ExplorationDataController import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.topic.TEST_EXPLORATION_ID_2 @@ -44,7 +45,9 @@ class ExplorationTestActivityPresenter @Inject constructor( TOPIC_ID, STORY_ID, EXPLORATION_ID, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + // Pass an empty checkpoint if there exploration does not have to be resumed. + ExplorationCheckpoint.getDefaultInstance() ).observe( activity, Observer> { result -> diff --git a/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt index d39ffc8c70f..d10aea10be1 100644 --- a/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt @@ -10,6 +10,7 @@ import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.home.RouteToExplorationListener import org.oppia.android.app.model.ChapterPlayState import org.oppia.android.app.model.ChapterSummary +import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.model.StorySummary import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.topic.RouteToStoryListener @@ -28,7 +29,7 @@ class TopicLessonsFragmentPresenter @Inject constructor( activity: AppCompatActivity, private val fragment: Fragment, private val oppiaLogger: OppiaLogger, - private val explorationDataController: ExplorationDataController, + private val explorationDataController: ExplorationDataController ) { // TODO(#3479): Enable checkpointing once mechanism to resume exploration with checkpoints is // implemented. @@ -64,6 +65,7 @@ class TopicLessonsFragmentPresenter @Inject constructor( this.storyId = storyId this.currentExpandedChapterListIndex = currentExpandedChapterListIndex this.expandedChapterListIndexListener = expandedChapterListIndexListener + binding = TopicLessonsFragmentBinding.inflate( inflater, container, @@ -149,12 +151,20 @@ class TopicLessonsFragmentPresenter @Inject constructor( it == ChapterPlayState.COMPLETED } .size + val inProgressChapterCount = + chapterSummaries.map(ChapterSummary::getChapterPlayState) + .filter { + it == ChapterPlayState.IN_PROGRESS_SAVED + } + .size + val storyPercentage: Int = (completedChapterCount * 100) / storySummaryViewModel.storySummary.chapterCount binding.storyPercentage = storyPercentage binding.storyProgressView.setStoryChapterDetails( storySummaryViewModel.storySummary.chapterCount, - completedChapterCount + completedChapterCount, + inProgressChapterCount ) binding.topicPlayStoryDashedLineView.setLayerType( View.LAYER_TYPE_SOFTWARE, @@ -209,7 +219,9 @@ class TopicLessonsFragmentPresenter @Inject constructor( topicId, storyId, explorationId, - shouldSavePartialProgress = false + shouldSavePartialProgress = true, + // Pass an empty checkpoint if the exploration does not have to be resumed. + ExplorationCheckpoint.getDefaultInstance() ).observe( fragment, Observer> { result -> @@ -228,7 +240,7 @@ class TopicLessonsFragmentPresenter @Inject constructor( storyId, explorationId, backflowScreen, - isCheckpointingEnabled = false + isCheckpointingEnabled = true ) } } diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt index 1783ae49bd8..799f0d6a0ea 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt @@ -131,7 +131,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( when (helpIndex.indexTypeCase) { HelpIndex.IndexTypeCase.HINT_INDEX, HelpIndex.IndexTypeCase.SHOW_SOLUTION -> { if (helpIndex.indexTypeCase == HelpIndex.IndexTypeCase.HINT_INDEX) { - questionViewModel.newAvailableHintIndex = helpIndex.hintIndex + questionViewModel.newAvailableHintIndex = helpIndex.hintIndex.index } questionViewModel.allHintsExhausted = helpIndex.indexTypeCase == HelpIndex.IndexTypeCase.SHOW_SOLUTION diff --git a/app/src/main/res/drawable/ic_pending_24dp.xml b/app/src/main/res/drawable/ic_pending_24dp.xml new file mode 100644 index 00000000000..f48b88b93d2 --- /dev/null +++ b/app/src/main/res/drawable/ic_pending_24dp.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/layout-land/lessons_chapter_view.xml b/app/src/main/res/layout-land/lessons_chapter_view.xml index bf4473d0b57..4260c5c9300 100644 --- a/app/src/main/res/layout-land/lessons_chapter_view.xml +++ b/app/src/main/res/layout-land/lessons_chapter_view.xml @@ -25,14 +25,14 @@ android:paddingEnd="4dp"> + android:contentDescription="@{viewModel.chapterPlayState == ChapterPlayState.COMPLETED?String.format(@string/chapter_in_progress, (viewModel.index + 1), viewModel.chapterName):String.format(@string/chapter_completed, (viewModel.index + 1), viewModel.chapterName)}" + android:src="@{viewModel.chapterPlayState == ChapterPlayState.COMPLETED?@drawable/ic_check_24dp:@drawable/ic_pending_24dp}" + android:visibility="@{(viewModel.chapterPlayState == ChapterPlayState.COMPLETED || viewModel.chapterPlayState == ChapterPlayState.IN_PROGRESS_SAVED)?View.VISIBLE : View.INVISIBLE}" /> + android:contentDescription="@{viewModel.chapterPlayState == ChapterPlayState.COMPLETED?String.format(@string/chapter_in_progress, (viewModel.index + 1), viewModel.chapterName):String.format(@string/chapter_completed, (viewModel.index + 1), viewModel.chapterName)}" + android:src="@{viewModel.chapterPlayState == ChapterPlayState.COMPLETED?@drawable/ic_check_24dp:@drawable/ic_pending_24dp}" + android:visibility="@{(viewModel.chapterPlayState == ChapterPlayState.COMPLETED || viewModel.chapterPlayState == ChapterPlayState.IN_PROGRESS_SAVED)?View.VISIBLE : View.INVISIBLE}" /> #4DA5D3EC #CC333333 #E76F51 + #E7AC9B #999999 #555555 #707070 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f906494bd3c..a802bb4c1a9 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -81,6 +81,7 @@ %s. Chapter %s: %s Chapter is completed + Chapter is in progress Complete Chapter %s: %s to unlock this chapter. Enter text. Enter a fraction in the form x/x, or a mixed number in the form x x/x. diff --git a/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt index 794e9a6788c..67f10b5b1cc 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt @@ -243,11 +243,11 @@ class HomeActivityTest { @Test fun testHomeActivity_recentlyPlayedStoriesTextIsDisplayed() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) @@ -265,11 +265,11 @@ class HomeActivityTest { @Test fun testHomeActivity_viewAllTextIsDisplayed() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = true ) @@ -288,12 +288,12 @@ class HomeActivityTest { @Test fun testHomeActivity_storiesPlayedOneWeekAgo_displaysLastPlayedStoriesText() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = true ) testCoroutineDispatchers.runCurrent() - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = true ) @@ -390,7 +390,7 @@ class HomeActivityTest { @Test fun testHomeActivity_forPromotedActivityList_hideViewAll() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) @@ -518,11 +518,11 @@ class HomeActivityTest { profileId = profileId1, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedTestTopic1Story0( + storyProgressTestHelper.markInProgressSavedTestTopic1Story0( profileId = profileId1, timestampOlderThanOneWeek = false ) @@ -570,7 +570,7 @@ class HomeActivityTest { profileId = profileId1, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0( + storyProgressTestHelper.markInProgressSavedTestTopic0Story0( profileId = profileId1, timestampOlderThanOneWeek = false ) @@ -648,15 +648,15 @@ class HomeActivityTest { @Test fun testHomeActivity_clickViewAll_opensRecentlyPlayedActivity() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedTestTopic1( + storyProgressTestHelper.markInProgressSavedTestTopic1( profileId = profileId1, timestampOlderThanOneWeek = false ) @@ -677,7 +677,7 @@ class HomeActivityTest { @Test fun testHomeActivity_promotedCard_chapterNameIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) @@ -695,7 +695,7 @@ class HomeActivityTest { @Test fun testHomeActivity_promotedCard_storyNameIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) @@ -713,11 +713,11 @@ class HomeActivityTest { @Test fun testHomeActivity_configChange_promotedCard_storyNameIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = true ) @@ -736,7 +736,7 @@ class HomeActivityTest { @Test fun testHomeActivity_markFullProgressForFractions_playRatios_displaysRecommendedStories() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) @@ -770,7 +770,7 @@ class HomeActivityTest { @Test fun testHomeActivity_clickPromotedStory_opensTopicActivity() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) @@ -794,11 +794,11 @@ class HomeActivityTest { @Test fun testHomeActivity_promotedCard_topicNameIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = true ) @@ -842,7 +842,7 @@ class HomeActivityTest { @Test fun testHomeActivity_secondTestTopic_topicSummary_allTopics_topicNameIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId1, timestampOlderThanOneWeek = false ) @@ -1204,11 +1204,11 @@ class HomeActivityTest { @Test fun testHomeActivity_multipleRecentlyPlayedStories_mobileShows3PromotedStories() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedTestTopic1Story2Exp0( + storyProgressTestHelper.markInProgressSavedTestTopic1Story2Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) @@ -1240,11 +1240,11 @@ class HomeActivityTest { @Test fun testHomeActivity_multipleRecentlyPlayedStories_tabletPortraitShows3PromotedStories() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedTestTopic1Story2Exp0( + storyProgressTestHelper.markInProgressNotSavedTestTopic1Story2Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) @@ -1277,11 +1277,11 @@ class HomeActivityTest { @Test fun testHomeActivity_multipleRecentlyPlayedStories_tabletLandscapeShows4PromotedStories() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedTestTopic1Story2Exp0( + storyProgressTestHelper.markInProgressSavedTestTopic1Story2Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) diff --git a/app/src/sharedTest/java/org/oppia/android/app/home/RecentlyPlayedFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/RecentlyPlayedFragmentTest.kt index bedb7b93190..f4f0c940424 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/home/RecentlyPlayedFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/home/RecentlyPlayedFragmentTest.kt @@ -173,12 +173,50 @@ class RecentlyPlayedFragmentTest { } @Test - fun testRecentlyPlayedTestActivity_defaultRecentlyPlayedToolbarTitleIsDisplayed() { + fun testRecentlyPlayedTestAct_chapsPlayedEarlierThanAWeek_toolbarTitleIsDisplayed() { + fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( + profileId = profileId, + timestampOlderThanOneWeek = false + ) + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( + profileId = profileId, + timestampOlderThanOneWeek = false + ) + ActivityScenario.launch( + createRecentlyPlayedActivityIntent( + internalProfileId = internalProfileId + ) + ).use { + testCoroutineDispatchers.runCurrent() + onView( + allOf( + instanceOf(TextView::class.java), + withParent(withId(R.id.recently_played_toolbar)) + ) + ).check( + matches(withText(R.string.recently_played_activity)) + ) + } + } + + @Test + fun testRecentlyPlayedTestAct_chapsPlayedLaterThanAWeek_toolbarTitleIsDisplayed() { + fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( + profileId = profileId, + timestampOlderThanOneWeek = true + ) + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( + profileId = profileId, + timestampOlderThanOneWeek = true + ) ActivityScenario.launch( createRecentlyPlayedActivityIntent( internalProfileId = internalProfileId ) ).use { + testCoroutineDispatchers.runCurrent() onView( allOf( instanceOf(TextView::class.java), @@ -193,11 +231,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_sectionDividerIsNotDisplayed() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -227,11 +265,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_lastWeekSectionTitleIsDisplayed() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -256,7 +294,7 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_fractionsPlayed_storiesForYouToolbarTitleIsDisplayed() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) @@ -280,7 +318,7 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_configChange_showsRecommendedSectionTitle() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) @@ -310,7 +348,7 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_showsRecommendedSectionTitle() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) @@ -335,7 +373,7 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_recommendedSection_topicNameIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) @@ -365,11 +403,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_storyNameIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -399,11 +437,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_topicNameIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -433,11 +471,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_lessonThumbnailIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -467,11 +505,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_clickStory_opensExplorationActivity() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -520,11 +558,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_lastMonthSectionTitleIsDisplayed() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -554,11 +592,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_sectionDividerIsDisplayed() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -584,12 +622,48 @@ class RecentlyPlayedFragmentTest { } @Test - fun testRecentlyPlayedTestActivity_configChange_toolbarTitleIsDisplayed() { + fun testRecentlyPlayedTestAct_chapsPlayedEarlierThanAWeek_configChange_toolbarTitleIsDisplayed() { + fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( + profileId = profileId, + timestampOlderThanOneWeek = false + ) + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( + profileId = profileId, + timestampOlderThanOneWeek = false + ) + ActivityScenario.launch( + createRecentlyPlayedActivityIntent( + internalProfileId = internalProfileId + ) + ).use { + testCoroutineDispatchers.runCurrent() + onView(isRoot()).perform(orientationLandscape()) + onView( + allOf(instanceOf(TextView::class.java), withParent(withId(R.id.recently_played_toolbar))) + ).check( + matches(withText(R.string.recently_played_activity)) + ) + } + } + + @Test + fun testRecentlyPlayedTestAct_chapsPlayedLaterThanAWeek_configChange_toolbarTitleIsDisplayed() { + fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( + profileId = profileId, + timestampOlderThanOneWeek = true + ) + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( + profileId = profileId, + timestampOlderThanOneWeek = true + ) ActivityScenario.launch( createRecentlyPlayedActivityIntent( internalProfileId = internalProfileId ) ).use { + testCoroutineDispatchers.runCurrent() onView(isRoot()).perform(orientationLandscape()) onView( allOf(instanceOf(TextView::class.java), withParent(withId(R.id.recently_played_toolbar))) @@ -602,11 +676,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_configChange_sectionDividerIsNotDisplayed() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -635,11 +709,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_configChange_lastWeekSectionTitleIsDisplayed() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -670,11 +744,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_configChange_storyNameIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -705,11 +779,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_configChange_topicNameIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -740,11 +814,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_configChange_lessonThumbnailIsCorrect() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -775,11 +849,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_configChange_lastMonthSectionTitleIsDisplayed() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -810,11 +884,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_checkSpanForItem1_spanSizeIsOne() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -841,11 +915,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_checkSpanForItem3_spanSizeIsOne() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -872,11 +946,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_configChange_checkSpanForItem1_spanSizeIsOne() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) @@ -904,11 +978,11 @@ class RecentlyPlayedFragmentTest { @Test fun testRecentlyPlayedTestActivity_configChange_checkSpanForItem3_spanSizeIsOne() { fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId, timestampOlderThanOneWeek = true ) diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt index 9ef561876d5..422c3b3ca2d 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt @@ -61,6 +61,7 @@ import org.oppia.android.app.application.ApplicationStartupListenerModule import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule import org.oppia.android.app.help.HelpActivity +import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.options.OptionsActivity import org.oppia.android.app.player.state.hintsandsolution.HintsAndSolutionConfigModule import org.oppia.android.app.shim.ViewBindingShimModule @@ -177,7 +178,8 @@ class ExplorationActivityTest { topicId: String, storyId: String, explorationId: String, - shouldSavePartialProgress: Boolean + shouldSavePartialProgress: Boolean, + explorationCheckpoint: ExplorationCheckpoint ) { launch(ExplorationInjectionActivity::class.java).use { it.onActivity { activity -> @@ -188,7 +190,8 @@ class ExplorationActivityTest { topicId, storyId, explorationId, - shouldSavePartialProgress + shouldSavePartialProgress, + explorationCheckpoint ) } } @@ -234,7 +237,8 @@ class ExplorationActivityTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() onView(withId(R.id.exploration_toolbar_title)) @@ -259,7 +263,8 @@ class ExplorationActivityTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) onView(isRoot()).perform(orientationLandscape()) testCoroutineDispatchers.runCurrent() @@ -286,7 +291,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) testCoroutineDispatchers.runCurrent() @@ -313,7 +319,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) testCoroutineDispatchers.runCurrent() @@ -341,7 +348,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) testCoroutineDispatchers.runCurrent() @@ -369,7 +377,8 @@ class ExplorationActivityTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) openActionBarOverflowOrOptionsMenu(context) onView(withText(context.getString(R.string.menu_options))).check(matches(isDisplayed())) @@ -394,7 +403,8 @@ class ExplorationActivityTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) openActionBarOverflowOrOptionsMenu(context) onView(withText(context.getString(R.string.menu_help))).perform(click()) @@ -420,7 +430,8 @@ class ExplorationActivityTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) openActionBarOverflowOrOptionsMenu(context) onView(withText(context.getString(R.string.menu_options))).perform(click()) @@ -451,7 +462,8 @@ class ExplorationActivityTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) onView(withId(R.id.action_audio_player)).check(matches(not(isDisplayed()))) } @@ -474,7 +486,8 @@ class ExplorationActivityTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) onView(isRoot()).perform(orientationLandscape()) onView(withId(R.id.action_audio_player)).check(matches(not(isDisplayed()))) @@ -499,7 +512,8 @@ class ExplorationActivityTest { RATIOS_TOPIC_ID, RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.NONE) testCoroutineDispatchers.runCurrent() @@ -526,7 +540,8 @@ class ExplorationActivityTest { RATIOS_TOPIC_ID, RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus( NetworkConnectionUtil.ConnectionStatus.CELLULAR @@ -555,7 +570,8 @@ class ExplorationActivityTest { RATIOS_TOPIC_ID, RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus( NetworkConnectionUtil.ConnectionStatus.CELLULAR @@ -585,7 +601,8 @@ class ExplorationActivityTest { RATIOS_TOPIC_ID, RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus( NetworkConnectionUtil.ConnectionStatus.CELLULAR @@ -626,7 +643,8 @@ class ExplorationActivityTest { RATIOS_TOPIC_ID, RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus( NetworkConnectionUtil.ConnectionStatus.CELLULAR @@ -672,7 +690,8 @@ class ExplorationActivityTest { RATIOS_TOPIC_ID, RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus( NetworkConnectionUtil.ConnectionStatus.CELLULAR @@ -715,7 +734,8 @@ class ExplorationActivityTest { RATIOS_TOPIC_ID, RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus( NetworkConnectionUtil.ConnectionStatus.CELLULAR @@ -753,7 +773,8 @@ class ExplorationActivityTest { RATIOS_TOPIC_ID, RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) launch( @@ -803,7 +824,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) testCoroutineDispatchers.runCurrent() @@ -856,7 +878,8 @@ class ExplorationActivityTest { RATIOS_TOPIC_ID, RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) launch( @@ -920,7 +943,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() pressBack() @@ -949,7 +973,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() onView(withContentDescription(R.string.nav_app_bar_navigate_up_description)).perform(click()) @@ -979,7 +1004,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() pressBack() @@ -1008,7 +1034,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1036,7 +1063,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1069,7 +1097,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1102,7 +1131,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1129,7 +1159,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1160,7 +1191,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1194,7 +1226,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1223,7 +1256,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1253,7 +1287,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1286,7 +1321,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1320,7 +1356,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1353,7 +1390,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1398,7 +1436,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1444,7 +1483,8 @@ class ExplorationActivityTest { FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, FRACTIONS_EXPLORATION_ID_0, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() diff --git a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt index 54cdffda0dc..c651986737e 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt @@ -449,7 +449,7 @@ class ProfileProgressFragmentTest { @Test fun testProfileProgressFragment_configChange_fractionStory_storyNameIsCorrect() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId, timestampOlderThanOneWeek = false ) @@ -469,7 +469,7 @@ class ProfileProgressFragmentTest { @Test fun testProfileProgressFragment_fractionsStory_storyNameIsCorrect() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId, timestampOlderThanOneWeek = false ) @@ -490,7 +490,7 @@ class ProfileProgressFragmentTest { @Test fun testProfileProgressFragment_fractionsStory_topicNameIsCorrect() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId, timestampOlderThanOneWeek = false ) @@ -511,7 +511,7 @@ class ProfileProgressFragmentTest { @Test fun testProfileProgressFragment_clickFractionsStory_opensTopicActivity() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId, timestampOlderThanOneWeek = false ) @@ -533,7 +533,7 @@ class ProfileProgressFragmentTest { @Test fun testProfileProgressFragment_clickViewAll_opensRecentlyPlayedActivity() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build(), timestampOlderThanOneWeek = false ) diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentTest.kt index bbb40a7e3a2..32bfb724c96 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentTest.kt @@ -406,6 +406,96 @@ class TopicLessonsFragmentTest { } } + @Test + fun testLessonPlayFrag_loadRatiosTopic_partialProg_partialProgIconIsDisplayed() { + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( + profileId, + timestampOlderThanOneWeek = false + ) + launch(createTopicActivityIntent(internalProfileId, RATIOS_TOPIC_ID)).use { + clickLessonTab() + clickStoryItem(position = 1, targetViewId = R.id.chapter_list_drop_down_icon) + scrollToPosition(position = 1) + verifyChapterPlayStateIconIsVisible(itemPosition = 0) + verifyPartialProgressIconIsDisplayed(itemPosition = 0) + } + } + + @Test + fun testLessonPlayFrag_loadRatiosTopic_partialProg_configChange_partialProgIconIsDisplayed() { + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( + profileId, + timestampOlderThanOneWeek = false + ) + launch(createTopicActivityIntent(internalProfileId, RATIOS_TOPIC_ID)).use { + clickLessonTab() + clickStoryItem(position = 1, targetViewId = R.id.chapter_list_drop_down_icon) + scrollToPosition(position = 1) + onView(isRoot()).perform(orientationLandscape()) + verifyChapterPlayStateIconIsVisible(itemPosition = 0) + verifyPartialProgressIconIsDisplayed(itemPosition = 0) + } + } + + @Test + fun testLessonPlayFrag_loadRatiosTopic_chapterCompleted_completedIconIsDisplayed() { + storyProgressTestHelper.markCompletedRatiosStory0Exp0( + profileId, + timestampOlderThanOneWeek = false + ) + launch(createTopicActivityIntent(internalProfileId, RATIOS_TOPIC_ID)).use { + clickLessonTab() + clickStoryItem(position = 1, targetViewId = R.id.chapter_list_drop_down_icon) + scrollToPosition(position = 1) + verifyChapterPlayStateIconIsVisible(itemPosition = 0) + verifyChapterCompletedIconIsDisplayed(itemPosition = 0) + } + } + + @Test + fun testLessonPlayFrag_loadRatiosTopic_chapterCompleted_configChange_completedIconIsDisplayed() { + storyProgressTestHelper.markCompletedRatiosStory0Exp0( + profileId, + timestampOlderThanOneWeek = false + ) + launch(createTopicActivityIntent(internalProfileId, RATIOS_TOPIC_ID)).use { + clickLessonTab() + clickStoryItem(position = 1, targetViewId = R.id.chapter_list_drop_down_icon) + scrollToPosition(position = 1) + onView(isRoot()).perform(orientationLandscape()) + verifyChapterPlayStateIconIsVisible(itemPosition = 0) + verifyChapterCompletedIconIsDisplayed(itemPosition = 0) + } + } + + @Test + fun testLessonPlayFrag_loadRatiosTopic_startedNotCompleted_chapterPlayStateIconIsNotVisible() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory0Exp0( + profileId, + timestampOlderThanOneWeek = false + ) + launch(createTopicActivityIntent(internalProfileId, RATIOS_TOPIC_ID)).use { + clickLessonTab() + clickStoryItem(position = 1, targetViewId = R.id.chapter_list_drop_down_icon) + scrollToPosition(position = 1) + verifyChapterPlayStateIconIsNotVisible(itemPosition = 0) + } + } + + @Test + fun testLessonPlayFrag_loadRatiosTopic_unsavedPartialProg_chapterPlayStateIconIsNotVisible() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory0Exp0( + profileId, + timestampOlderThanOneWeek = false + ) + launch(createTopicActivityIntent(internalProfileId, RATIOS_TOPIC_ID)).use { + clickLessonTab() + clickStoryItem(position = 1, targetViewId = R.id.chapter_list_drop_down_icon) + scrollToPosition(position = 1) + verifyChapterPlayStateIconIsNotVisible(itemPosition = 0) + } + } + private fun createTopicActivityIntent(internalProfileId: Int, topicId: String): Intent { return TopicActivity.createTopicActivityIntent( ApplicationProvider.getApplicationContext(), @@ -477,6 +567,46 @@ class TopicLessonsFragmentTest { ).check(matches(withContentDescription(stringToMatch))) } + private fun verifyChapterPlayStateIconIsVisible(itemPosition: Int) { + onView( + atPositionOnView( + recyclerViewId = R.id.chapter_recycler_view, + position = itemPosition, + targetViewId = R.id.chapter_play_state_icon + ) + ).check(matches(isDisplayed())) + } + + private fun verifyChapterPlayStateIconIsNotVisible(itemPosition: Int) { + onView( + atPositionOnView( + recyclerViewId = R.id.chapter_recycler_view, + position = itemPosition, + targetViewId = R.id.chapter_play_state_icon + ) + ).check(matches(not(isDisplayed()))) + } + + private fun verifyPartialProgressIconIsDisplayed(itemPosition: Int) { + onView( + atPositionOnView( + recyclerViewId = R.id.chapter_recycler_view, + position = itemPosition, + targetViewId = R.id.chapter_play_state_icon + ) + ).check(matches(withDrawable(R.drawable.ic_pending_24dp))) + } + + private fun verifyChapterCompletedIconIsDisplayed(itemPosition: Int) { + onView( + atPositionOnView( + recyclerViewId = R.id.chapter_recycler_view, + position = itemPosition, + targetViewId = R.id.chapter_play_state_icon + ) + ).check(matches(withDrawable(R.drawable.ic_check_24dp))) + } + // TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them. @Singleton @Component( diff --git a/app/src/test/java/org/oppia/android/app/player/exploration/ExplorationActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/player/exploration/ExplorationActivityLocalTest.kt index 77b68993dd1..6865f5f00d7 100644 --- a/app/src/test/java/org/oppia/android/app/player/exploration/ExplorationActivityLocalTest.kt +++ b/app/src/test/java/org/oppia/android/app/player/exploration/ExplorationActivityLocalTest.kt @@ -23,6 +23,7 @@ import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule import org.oppia.android.app.model.EventLog import org.oppia.android.app.model.EventLog.Context.ActivityContextCase.EXPLORATION_CONTEXT +import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.player.state.hintsandsolution.HintsAndSolutionConfigModule import org.oppia.android.app.shim.IntentFactoryShimModule import org.oppia.android.app.shim.ViewBindingShimModule @@ -107,7 +108,8 @@ class ExplorationActivityLocalTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) launch( createExplorationActivityIntent( @@ -134,7 +136,8 @@ class ExplorationActivityLocalTest { topicId: String, storyId: String, explorationId: String, - shouldSavePartialProgress: Boolean + shouldSavePartialProgress: Boolean, + explorationCheckpoint: ExplorationCheckpoint ) { launch(ExplorationInjectionActivity::class.java).use { it.onActivity { activity -> @@ -145,7 +148,8 @@ class ExplorationActivityLocalTest { topicId, storyId, explorationId, - shouldSavePartialProgress + shouldSavePartialProgress, + explorationCheckpoint ) } } diff --git a/app/src/test/java/org/oppia/android/app/testing/RecentlyPlayedSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/RecentlyPlayedSpanTest.kt index b0186648d42..20c9ec2240d 100644 --- a/app/src/test/java/org/oppia/android/app/testing/RecentlyPlayedSpanTest.kt +++ b/app/src/test/java/org/oppia/android/app/testing/RecentlyPlayedSpanTest.kt @@ -96,11 +96,11 @@ class RecentlyPlayedSpanTest { testCoroutineDispatchers.registerIdlingResource() profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_UPTIME_MILLIS) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId, timestampOlderThanOneWeek = true ) diff --git a/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationDataController.kt b/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationDataController.kt index 2e963599dc4..62657e1387d 100644 --- a/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationDataController.kt +++ b/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationDataController.kt @@ -3,6 +3,7 @@ package org.oppia.android.domain.exploration import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import org.oppia.android.app.model.Exploration +import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.model.ProfileId import org.oppia.android.domain.exploration.lightweightcheckpointing.ExplorationCheckpointController import org.oppia.android.domain.oppialogger.exceptions.ExceptionsController @@ -53,6 +54,7 @@ class ExplorationDataController @Inject constructor( * @param explorationId the ID of the exploration which has to be played * @param shouldSavePartialProgress the boolean that indicates if partial progress has to be saved * for the current exploration + * @param explorationCheckpoint the checkpoint which may be used to resume the exploration * @return a one-time [LiveData] to observe whether initiating the play request succeeded. * The exploration may still fail to load, but this provides early-failure detection. */ @@ -61,7 +63,8 @@ class ExplorationDataController @Inject constructor( topicId: String, storyId: String, explorationId: String, - shouldSavePartialProgress: Boolean + shouldSavePartialProgress: Boolean, + explorationCheckpoint: ExplorationCheckpoint ): LiveData> { return try { explorationProgressController.beginExplorationAsync( @@ -69,7 +72,8 @@ class ExplorationDataController @Inject constructor( topicId, storyId, explorationId, - shouldSavePartialProgress + shouldSavePartialProgress, + explorationCheckpoint ) MutableLiveData(AsyncResult.success(null)) } catch (e: Exception) { diff --git a/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationProgress.kt b/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationProgress.kt index 9b57321b61a..f68763b916c 100644 --- a/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationProgress.kt +++ b/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationProgress.kt @@ -2,6 +2,8 @@ package org.oppia.android.domain.exploration import org.oppia.android.app.model.CheckpointState import org.oppia.android.app.model.Exploration +import org.oppia.android.app.model.ExplorationCheckpoint +import org.oppia.android.app.model.HintState import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.State import org.oppia.android.domain.state.StateDeck @@ -24,6 +26,8 @@ internal class ExplorationProgress { internal var shouldSavePartialProgress: Boolean = false internal var checkpointState = CheckpointState.CHECKPOINT_UNSAVED + internal lateinit var explorationCheckpoint: ExplorationCheckpoint + internal lateinit var hintState: HintState internal var playStage = PlayStage.NOT_PLAYING internal val stateGraph: StateGraph by lazy { diff --git a/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationProgressController.kt b/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationProgressController.kt index 88cc2259a75..5f7c8dfebe0 100644 --- a/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationProgressController.kt +++ b/domain/src/main/java/org/oppia/android/domain/exploration/ExplorationProgressController.kt @@ -7,7 +7,9 @@ import org.oppia.android.app.model.CheckpointState import org.oppia.android.app.model.EphemeralState import org.oppia.android.app.model.Exploration import org.oppia.android.app.model.ExplorationCheckpoint +import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.Hint +import org.oppia.android.app.model.HintState import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.Solution import org.oppia.android.app.model.State @@ -78,7 +80,8 @@ class ExplorationProgressController @Inject constructor( topicId: String, storyId: String, explorationId: String, - shouldSavePartialProgress: Boolean + shouldSavePartialProgress: Boolean, + explorationCheckpoint: ExplorationCheckpoint ) { explorationProgressLock.withLock { check(explorationProgress.playStage == ExplorationProgress.PlayStage.NOT_PLAYING) { @@ -91,6 +94,8 @@ class ExplorationProgressController @Inject constructor( currentStoryId = storyId currentExplorationId = explorationId this.shouldSavePartialProgress = shouldSavePartialProgress + this.explorationCheckpoint = explorationCheckpoint + checkpointState = CheckpointState.CHECKPOINT_UNSAVED } explorationProgress.advancePlayStageTo(ExplorationProgress.PlayStage.LOADING_EXPLORATION) asyncDataSubscriptionManager.notifyChangeAsync(CURRENT_STATE_DATA_PROVIDER_ID) @@ -137,7 +142,10 @@ class ExplorationProgressController @Inject constructor( * [LiveData] from [getCurrentState]. Also note that the returned [LiveData] will only have a * single value and not be reused after that point. */ - fun submitAnswer(userAnswer: UserAnswer): LiveData> { + fun submitAnswer( + userAnswer: UserAnswer, + hintState: HintState + ): LiveData> { try { explorationProgressLock.withLock { check( @@ -180,6 +188,17 @@ class ExplorationProgressController @Inject constructor( explorationProgress.stateGraph.getState(answerOutcome.stateName), prohibitSameStateName = true ) + // Reset the hintState if answer leads to a different part of the graph. + explorationProgress.hintState = HintState.newBuilder().apply { + trackedAnswerCount = 0 + hintSequenceNumber = hintState.hintSequenceNumber + 1 + isHintVisibleInLatestState = false + helpIndex = HelpIndex.getDefaultInstance() + }.build() + } else { + // Update explorationProgress.hintState with the latest hintState if answer is not + // does not lead to a new state. + explorationProgress.hintState = hintState } } finally { // If the answer was submitted on behalf of the Continue interaction, don't save @@ -204,10 +223,65 @@ class ExplorationProgressController @Inject constructor( } } + fun submitUnrevealedHintIsVisible( + state: State, + hintState: HintState + ): LiveData> { + try { + explorationProgressLock.withLock { + check( + explorationProgress.playStage != + ExplorationProgress.PlayStage.NOT_PLAYING + ) { + "Cannot submit an answer if an exploration is not being played." + } + check( + explorationProgress.playStage != + ExplorationProgress.PlayStage.LOADING_EXPLORATION + ) { + "Cannot submit an answer while the exploration is being loaded." + } + check( + explorationProgress.playStage != + ExplorationProgress.PlayStage.SUBMITTING_ANSWER + ) { + "Cannot submit an answer while another answer is pending." + } + explorationProgress.hintState = hintState + lateinit var hint: Hint + try { + explorationProgress.stateDeck.submitUnrevealedHintIsVisible( + state, + hintState.helpIndex.hintIndex.isHintRevealed, + hintState.helpIndex.hintIndex.index + ) + hint = explorationProgress.stateGraph.computeHintForResult( + state, + hintState.helpIndex.hintIndex.isHintRevealed, + hintState.helpIndex.hintIndex.index + ) + explorationProgress.stateDeck.pushStateForHint(state, hintState.helpIndex.hintIndex.index) + } finally { + explorationProgress.hintState = hintState + // Mark a checkpoint in the exploration everytime a new hint is revealed. + saveExplorationCheckpoint() + // Ensure that the user always returns to the VIEWING_STATE stage to avoid getting stuck + // in an 'always unrevealed hint visible' situation. This can specifically happen if hint + // throws an exception. + explorationProgress.advancePlayStageTo(ExplorationProgress.PlayStage.VIEWING_STATE) + } + asyncDataSubscriptionManager.notifyChangeAsync(CURRENT_STATE_DATA_PROVIDER_ID) + return MutableLiveData(AsyncResult.success(hint)) + } + } catch (e: Exception) { + exceptionsController.logNonFatalException(e) + return MutableLiveData(AsyncResult.failed(e)) + } + } + fun submitHintIsRevealed( state: State, - hintIsRevealed: Boolean, - hintIndex: Int + hintState: HintState ): LiveData> { try { explorationProgressLock.withLock { @@ -231,14 +305,19 @@ class ExplorationProgressController @Inject constructor( } lateinit var hint: Hint try { - explorationProgress.stateDeck.submitHintRevealed(state, hintIsRevealed, hintIndex) + explorationProgress.stateDeck.submitHintRevealed( + state, + hintState.helpIndex.hintIndex.isHintRevealed, + hintState.helpIndex.hintIndex.index + ) hint = explorationProgress.stateGraph.computeHintForResult( state, - hintIsRevealed, - hintIndex + hintState.helpIndex.hintIndex.isHintRevealed, + hintState.helpIndex.hintIndex.index ) - explorationProgress.stateDeck.pushStateForHint(state, hintIndex) + explorationProgress.stateDeck.pushStateForHint(state, hintState.helpIndex.hintIndex.index) } finally { + explorationProgress.hintState = hintState // Mark a checkpoint in the exploration everytime a new hint is revealed. saveExplorationCheckpoint() // Ensure that the user always returns to the VIEWING_STATE stage to avoid getting stuck @@ -255,8 +334,61 @@ class ExplorationProgressController @Inject constructor( } } + fun submitUnrevealedSolutionIsVisible( + state: State, + hintState: HintState + ): LiveData> { + try { + explorationProgressLock.withLock { + check( + explorationProgress.playStage != + ExplorationProgress.PlayStage.NOT_PLAYING + ) { + "Cannot submit an answer if an exploration is not being played." + } + check( + explorationProgress.playStage != + ExplorationProgress.PlayStage.LOADING_EXPLORATION + ) { + "Cannot submit an answer while the exploration is being loaded." + } + check( + explorationProgress.playStage != + ExplorationProgress.PlayStage.SUBMITTING_ANSWER + ) { + "Cannot submit an answer while another answer is pending." + } + lateinit var solution: Solution + try { + + explorationProgress.stateDeck.submitUnrevealedSolutionIsVisible(state) + solution = explorationProgress.stateGraph.computeSolutionForResult( + state, + solutionIsRevealed = false + ) + explorationProgress.stateDeck.pushStateForSolution(state) + } finally { + explorationProgress.hintState = hintState + // Mark a checkpoint in the exploration if the solution is revealed. + saveExplorationCheckpoint() + // Ensure that the user always returns to the VIEWING_STATE stage to avoid getting stuck + // in an 'always showing solution' situation. This can specifically happen if solution + // throws an exception. + explorationProgress.advancePlayStageTo(ExplorationProgress.PlayStage.VIEWING_STATE) + } + + asyncDataSubscriptionManager.notifyChangeAsync(CURRENT_STATE_DATA_PROVIDER_ID) + return MutableLiveData(AsyncResult.success(solution)) + } + } catch (e: Exception) { + exceptionsController.logNonFatalException(e) + return MutableLiveData(AsyncResult.failed(e)) + } + } + fun submitSolutionIsRevealed( - state: State + state: State, + hintState: HintState ): LiveData> { try { explorationProgressLock.withLock { @@ -282,9 +414,13 @@ class ExplorationProgressController @Inject constructor( try { explorationProgress.stateDeck.submitSolutionRevealed(state) - solution = explorationProgress.stateGraph.computeSolutionForResult(state) + solution = explorationProgress.stateGraph.computeSolutionForResult( + state, + solutionIsRevealed = true + ) explorationProgress.stateDeck.pushStateForSolution(state) } finally { + explorationProgress.hintState = hintState // Mark a checkpoint in the exploration if the solution is revealed. saveExplorationCheckpoint() // Ensure that the user always returns to the VIEWING_STATE stage to avoid getting stuck @@ -382,7 +518,6 @@ class ExplorationProgressController @Inject constructor( "Cannot navigate to a next state if an answer submission is pending." } explorationProgress.stateDeck.navigateToNextState() - // Only mark checkpoint if current state is pending state. This ensures that checkpoints // will not be marked on any of the completed states. if (explorationProgress.stateDeck.isCurrentStateTopOfDeck()) { @@ -409,22 +544,24 @@ class ExplorationProgressController @Inject constructor( // Do not save checkpoints if shouldSavePartialProgress is false. This is expected to happen // when the current exploration has been already completed previously. if (!explorationProgress.shouldSavePartialProgress) return - val profileId: ProfileId = explorationProgress.currentProfileId - val topicId: String = explorationProgress.currentTopicId - val storyId: String = explorationProgress.currentStoryId - val explorationId: String = explorationProgress.currentExplorationId - val checkpoint: ExplorationCheckpoint = + explorationProgress.explorationCheckpoint = explorationProgress.stateDeck.createExplorationCheckpoint( + explorationProgress.hintState, explorationProgress.currentExploration.version, explorationProgress.currentExploration.title, oppiaClock.getCurrentTimeMs() ) + val profileId: ProfileId = explorationProgress.currentProfileId + val topicId: String = explorationProgress.currentTopicId + val storyId: String = explorationProgress.currentStoryId + val explorationId: String = explorationProgress.currentExplorationId + val deferred = explorationCheckpointController.recordExplorationCheckpointAsync( profileId, explorationId, - checkpoint + explorationProgress.explorationCheckpoint ) deferred.invokeOnCompletion { @@ -570,6 +707,7 @@ class ExplorationProgressController @Inject constructor( explorationProgress.stateDeck.getCurrentEphemeralState() .toBuilder() .setCheckpointState(explorationProgress.checkpointState) + .setHintState(explorationProgress.hintState) .build() ) } catch (e: Exception) { @@ -582,6 +720,7 @@ class ExplorationProgressController @Inject constructor( explorationProgress.stateDeck.getCurrentEphemeralState() .toBuilder() .setCheckpointState(explorationProgress.checkpointState) + .setHintState(explorationProgress.hintState) .build() ) ExplorationProgress.PlayStage.SUBMITTING_ANSWER -> AsyncResult.pending() @@ -593,7 +732,12 @@ class ExplorationProgressController @Inject constructor( // The exploration must be initialized first since other lazy fields depend on it being inited. progress.currentExploration = exploration progress.stateGraph.reset(exploration.statesMap) - progress.stateDeck.resetDeck(progress.stateGraph.getState(exploration.initStateName)) + + // Load hintState with a checkpoint or to reset it if checkpoint is of default instance. + loadHintState(progress) + + // Either resume or reset the StateDeck depending upon the exploration checkpoint. + loadStateDeck(progress, exploration) // Advance the stage, but do not notify observers since the current state can be reported // immediately to the UI. @@ -641,4 +785,166 @@ class ExplorationProgressController @Inject constructor( lastPlayedTimestamp ) } + + private fun loadHintState(progress: ExplorationProgress) { + progress.hintState = + if (progress.explorationCheckpoint == ExplorationCheckpoint.getDefaultInstance()) { + HintState.newBuilder().apply { + isHintVisibleInLatestState = false + helpIndex = HelpIndex.getDefaultInstance() + trackedAnswerCount = 0 + hintSequenceNumber = 0 + }.build() + } else { + HintState.newBuilder().apply { + isHintVisibleInLatestState = + progress.explorationCheckpoint.helpIndex != HelpIndex.getDefaultInstance() + helpIndex = progress.explorationCheckpoint.helpIndex + trackedAnswerCount = progress.explorationCheckpoint.pendingUserAnswersCount + hintSequenceNumber = 0 + }.build() + } + } + + /** + * Initializes the variables of [StateDeck]. If the [ExplorationCheckpoint] is of type default + * instance, the values of [StateDeck] are reset. Otherwise, the variables of [StateDeck] are + * re-initialized with the values created from the saved [ExplorationCheckpoint]. + * + * This function expects explorationProgress.hintState to be initialized with the correct values, + * so it should only be called after the function [loadHintState] has executed. + */ + private fun loadStateDeck(progress: ExplorationProgress, exploration: Exploration) { + if (progress.explorationCheckpoint == ExplorationCheckpoint.getDefaultInstance()) { + progress.stateDeck.resetDeck(progress.stateGraph.getState(exploration.initStateName)) + } else { + progress.stateDeck.resumeDeck( + createPendingTopStateFromCheckpoint(progress), + getPreviousStatesFromCheckpoint(progress), + progress.explorationCheckpoint.pendingUserAnswersList, + progress.explorationCheckpoint.stateIndex + ) + } + } + + /** + * Creates a pending top state for the current exploration as it was when the checkpoint was + * created. + * + * @return the pending [State] for the current exploration + */ + private fun createPendingTopStateFromCheckpoint(progress: ExplorationProgress): State { + val pendingTopState = + progress.stateGraph.getState(progress.explorationCheckpoint.pendingStateName) + val hintList = createHintListFromCheckpoint( + pendingTopState.interaction.hintList, + progress.hintState.helpIndex + ) + val solution = createSolutionFromCheckpoint(pendingTopState, progress.hintState.helpIndex) + val interactionBuilder = + pendingTopState.interaction.toBuilder() + .clearHint() + .addAllHint(hintList) + .setSolution(solution) + .build() + return pendingTopState.toBuilder().setInteraction(interactionBuilder).build() + } + + /** + * Mark all hints as reveled in the pendingState that were revealed for the current state pending + * state before the checkpoint was saved. + * + * @param pendingStateHintList the list of hint for the current pending state + * @param helpIndex the state of hints for the exploration which was generated using the saved + * checkpoint + */ + private fun createHintListFromCheckpoint( + pendingStateHintList: List, + helpIndex: HelpIndex + ): List { + val updatedHintList: MutableList = ArrayList() + if (helpIndex.indexTypeCase == HelpIndex.IndexTypeCase.HINT_INDEX) { + pendingStateHintList.forEachIndexed { index, hint -> + if (index <= helpIndex.hintIndex.index) { + // Mark all hints as visible and revealed which have an index less than that stored in + // the HintState. + updatedHintList.add( + hint.toBuilder().apply { + hintIsRevealed = true + unrevealedHintIsVisible = false + }.build() + ) + } else if (index == helpIndex.hintIndex.index) { + updatedHintList.add( + hint.toBuilder().apply { + hintIsRevealed = helpIndex.hintIndex.isHintRevealed + unrevealedHintIsVisible = !helpIndex.hintIndex.isHintRevealed + }.build() + ) + } + } + } else { + pendingStateHintList.forEach { hint -> + updatedHintList.add( + hint.toBuilder().apply { + hintIsRevealed = true + unrevealedHintIsVisible = false + }.build() + ) + } + } + return updatedHintList + } + + /** + * Set solution is reveled in the pendingState to true or false depending upon if solution was + * revealed for the current state pending state before the checkpoint was saved. + * + * @param pendingTopState the pending state created from the checkpoint + * @param helpIndex the state of solution for the exploration which was generated using the saved + * checkpoint + */ + private fun createSolutionFromCheckpoint( + pendingTopState: State, + helpIndex: HelpIndex + ): Solution { + return when (helpIndex.indexTypeCase) { + HelpIndex.IndexTypeCase.SHOW_SOLUTION -> { + pendingTopState.interaction.solution.toBuilder() + .setSolutionIsRevealed(false) + .setUnrevealedSolutionIsVisible(true) + .build() + } + HelpIndex.IndexTypeCase.EVERYTHING_REVEALED -> { + pendingTopState.interaction.solution.toBuilder() + .setSolutionIsRevealed(true) + .setUnrevealedSolutionIsVisible(false) + .build() + } + else -> pendingTopState.interaction.solution + } + } + + /** + * Creates a list of completed states from the saved [ExplorationCheckpoint]. + * + * @return [List] of [EphemeralState] containing all the states that were completed before the + * checkpoint was created + */ + private fun getPreviousStatesFromCheckpoint( + progress: ExplorationProgress + ): List { + val previousStates: MutableList = ArrayList() + progress.explorationCheckpoint.completedStatesInCheckpointList.forEachIndexed { index, state -> + previousStates.add( + EphemeralState.newBuilder() + .setState(progress.stateGraph.getState(state.stateName)) + .setHasPreviousState(index != 0) + .setCompletedState(state.completedState) + .setHasNextState(index != progress.explorationCheckpoint.stateIndex) + .build() + ) + } + return previousStates + } } diff --git a/domain/src/main/java/org/oppia/android/domain/exploration/lightweightcheckpointing/ExplorationCheckpointState.kt b/domain/src/main/java/org/oppia/android/domain/exploration/lightweightcheckpointing/ExplorationCheckpointState.kt deleted file mode 100644 index 1c5885c6569..00000000000 --- a/domain/src/main/java/org/oppia/android/domain/exploration/lightweightcheckpointing/ExplorationCheckpointState.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.oppia.android.domain.exploration.lightweightcheckpointing - -/** Different states in which checkpoint saving exploration progress can exist. */ -enum class ExplorationCheckpointState { - /** - * Progress made in the exploration is saved and the size of the checkpoint database has - * not exceeded the allocated limit. - */ - CHECKPOINT_SAVED_DATABASE_NOT_EXCEEDED_LIMIT, - - /** - * Progress made in the exploration is saved and the size of the checkpoint database has - * exceeded the allocated limit. - */ - CHECKPOINT_SAVED_DATABASE_EXCEEDED_LIMIT, - - /** Progress made in the exploration is not saved. */ - UNSAVED -} diff --git a/domain/src/main/java/org/oppia/android/domain/state/StateDeck.kt b/domain/src/main/java/org/oppia/android/domain/state/StateDeck.kt index 9bb1fb28bc8..985c7b07269 100644 --- a/domain/src/main/java/org/oppia/android/domain/state/StateDeck.kt +++ b/domain/src/main/java/org/oppia/android/domain/state/StateDeck.kt @@ -6,6 +6,7 @@ import org.oppia.android.app.model.CompletedStateInCheckpoint import org.oppia.android.app.model.EphemeralState import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.model.Hint +import org.oppia.android.app.model.HintState import org.oppia.android.app.model.PendingState import org.oppia.android.app.model.Solution import org.oppia.android.app.model.State @@ -28,9 +29,6 @@ internal class StateDeck internal constructor( private val hintList: MutableList = ArrayList() private lateinit var solution: Solution private var stateIndex: Int = 0 - // The value -1 indicates that hint has not been revealed yet. - private var revealedHintIndex: Int = -1 - private var solutionIsRevealed: Boolean = false /** Resets this deck to a new, specified initial [State]. */ internal fun resetDeck(initialState: State) { @@ -39,10 +37,22 @@ internal class StateDeck internal constructor( currentDialogInteractions.clear() hintList.clear() stateIndex = 0 - // Initialize the variable revealedHintIndex with -1 to indicate that no hint has been - // revealed yet. - revealedHintIndex = -1 - solutionIsRevealed = false + } + + /** Resumes this deck to continue the exploration from the last marked checkpoint. */ + internal fun resumeDeck( + initialState: State, + previousStates: List, + currentDialogInteractions: List, + stateIndex: Int + ) { + this.pendingTopState = initialState + this.previousStates.clear() + this.currentDialogInteractions.clear() + this.hintList.clear() + this.previousStates.addAll(previousStates) + this.currentDialogInteractions.addAll(currentDialogInteractions) + this.stateIndex = stateIndex } /** Navigates to the previous State in the deck, or fails if this isn't possible. */ @@ -117,10 +127,6 @@ internal class StateDeck internal constructor( currentDialogInteractions.clear() hintList.clear() pendingTopState = state - // Re-initialize the variable revealedHintIndex with -1 to indicate that no hint has been - // revealed on the new pendingTopState. - revealedHintIndex = -1 - solutionIsRevealed = false } internal fun pushStateForHint(state: State, hintIndex: Int): EphemeralState { @@ -138,8 +144,6 @@ internal class StateDeck internal constructor( .build() pendingTopState = newState hintList.clear() - // Increment the value of revealHintIndex by 1 every-time a new hint is revealed. - revealedHintIndex++ return ephemeralState } @@ -154,7 +158,6 @@ internal class StateDeck internal constructor( ) .build() pendingTopState = newState - solutionIsRevealed = true return ephemeralState } @@ -175,13 +178,37 @@ internal class StateDeck internal constructor( internal fun submitHintRevealed(state: State, hintIsRevealed: Boolean, hintIndex: Int) { hintList += Hint.newBuilder() .setHintIsRevealed(hintIsRevealed) + .setUnrevealedHintIsVisible(!hintIsRevealed) + .setHintContent(state.interaction.getHint(hintIndex).hintContent) + .build() + } + + internal fun submitUnrevealedHintIsVisible( + state: State, + hintIsRevealed: Boolean, + hintIndex: Int + ) { + hintList += Hint.newBuilder() + .setHintIsRevealed(hintIsRevealed) + .setUnrevealedHintIsVisible(!hintIsRevealed) .setHintContent(state.interaction.getHint(hintIndex).hintContent) .build() } + internal fun submitUnrevealedSolutionIsVisible(state: State) { + solution = Solution.newBuilder() + .setSolutionIsRevealed(false) + .setUnrevealedSolutionIsVisible(true) + .setAnswerIsExclusive(state.interaction.solution.answerIsExclusive) + .setCorrectAnswer(state.interaction.solution.correctAnswer) + .setExplanation(state.interaction.solution.explanation) + .build() + } + internal fun submitSolutionRevealed(state: State) { solution = Solution.newBuilder() .setSolutionIsRevealed(true) + .setUnrevealedSolutionIsVisible(false) .setAnswerIsExclusive(state.interaction.solution.answerIsExclusive) .setCorrectAnswer(state.interaction.solution.correctAnswer) .setExplanation(state.interaction.solution.explanation) @@ -193,6 +220,7 @@ internal class StateDeck internal constructor( * [StateDeck] that are used in light weight checkpointing. */ internal fun createExplorationCheckpoint( + hintState: HintState, explorationVersion: Int, explorationTitle: String, timestamp: Long @@ -206,11 +234,11 @@ internal class StateDeck internal constructor( }.build() } ) + pendingStateName = pendingTopState.name - hintIndex = revealedHintIndex addAllPendingUserAnswers(currentDialogInteractions) - this.solutionIsRevealed = this@StateDeck.solutionIsRevealed this.stateIndex = this@StateDeck.stateIndex + this.helpIndex = hintState.helpIndex this.explorationVersion = explorationVersion this.explorationTitle = explorationTitle timestampOfFirstCheckpoint = timestamp diff --git a/domain/src/main/java/org/oppia/android/domain/state/StateGraph.kt b/domain/src/main/java/org/oppia/android/domain/state/StateGraph.kt index 0e41e54efd6..d53805f48af 100644 --- a/domain/src/main/java/org/oppia/android/domain/state/StateGraph.kt +++ b/domain/src/main/java/org/oppia/android/domain/state/StateGraph.kt @@ -48,6 +48,7 @@ internal class StateGraph internal constructor( ): Hint { return Hint.newBuilder() .setHintIsRevealed(hintIsRevealed) + .setUnrevealedHintIsVisible(!hintIsRevealed) .setHintContent(currentState.interaction.getHint(hintIndex).hintContent) .setState(currentState) .build() @@ -55,10 +56,12 @@ internal class StateGraph internal constructor( /** Returns an [Solution] based on the current state and revealed [Solution] from the learner's answer. */ internal fun computeSolutionForResult( - currentState: State + currentState: State, + solutionIsRevealed: Boolean ): Solution { return Solution.newBuilder() - .setSolutionIsRevealed(true) + .setSolutionIsRevealed(solutionIsRevealed) + .setUnrevealedSolutionIsVisible(!solutionIsRevealed) .setAnswerIsExclusive(currentState.interaction.solution.answerIsExclusive) .setCorrectAnswer(currentState.interaction.solution.correctAnswer) .setExplanation(currentState.interaction.solution.explanation).build() diff --git a/domain/src/main/java/org/oppia/android/domain/topic/StoryProgressController.kt b/domain/src/main/java/org/oppia/android/domain/topic/StoryProgressController.kt index 7b26df8c5d9..9dc05ebbe66 100644 --- a/domain/src/main/java/org/oppia/android/domain/topic/StoryProgressController.kt +++ b/domain/src/main/java/org/oppia/android/domain/topic/StoryProgressController.kt @@ -42,8 +42,12 @@ private const val RETRIEVE_STORY_PROGRESS_DATA_PROVIDER_ID = private const val RETRIEVE_CHAPTER_PLAY_STATE_DATA_PROVIDER_ID = "retrieve_chapter_play_state_data_provider_id" private const val RECORD_COMPLETED_CHAPTER_PROVIDER_ID = "record_completed_chapter_provider_id" -private const val RECORD_RECENTLY_PLAYED_CHAPTER_PROVIDER_ID = - "record_recently_played_chapter_provider_id" +private const val RECORD_IN_PROGRESS_SAVED_CHAPTER_PROVIDER_ID = + "record_in_progress_saved_chapter_provider_id" +private const val RECORD_STARTED_NOT_COMPLETED_CHAPTER_PROVIDER_ID = + "record_STARTED_NOT_COMPLETED_chapter_provider_id" +private const val RECORD_IN_PROGRESS_NOT_SAVED_CHAPTER_PROVIDER_ID = + "record_in_progress_not_saved_chapter_provider_id" /** * Controller that records and provides completion statuses of chapters within the context of a @@ -71,12 +75,12 @@ class StoryProgressController @Inject constructor( * topic. Returns a [DataProvider] that provides exactly one [AsyncResult] to indicate whether * this operation has succeeded. This method will never return a pending result. * - * @param profileId the ID corresponding to the profile for which progress needs to be stored. - * @param topicId the ID corresponding to the topic for which progress needs to be stored. - * @param storyId the ID corresponding to the story for which progress needs to be stored. + * @param profileId the ID corresponding to the profile for which progress needs to be stored + * @param topicId the ID corresponding to the topic for which progress needs to be stored + * @param storyId the ID corresponding to the story for which progress needs to be stored * @param explorationId the chapter id which will marked as [ChapterPlayState.COMPLETED] - * @param completionTimestamp the timestamp at the exploration was finished. - * @return a [DataProvider] that indicates the success/failure of this record progress operation. + * @param completionTimestamp the timestamp at the exploration was finished + * @return a [DataProvider] that indicates the success/failure of this record progress operation */ fun recordCompletedChapter( profileId: ProfileId, @@ -158,7 +162,9 @@ class StoryProgressController @Inject constructor( explorationId ) - val chapterProgressBuilder = if (previousChapterProgress != null) { + val chapterProgressBuilder = if (previousChapterProgress != null && + previousChapterProgress.chapterPlayState == ChapterPlayState.COMPLETED + ) { previousChapterProgress.toBuilder() } else { ChapterProgress.newBuilder() @@ -203,7 +209,7 @@ class StoryProgressController @Inject constructor( } return dataProviders.createInMemoryDataProviderAsync( - RECORD_RECENTLY_PLAYED_CHAPTER_PROVIDER_ID + RECORD_IN_PROGRESS_SAVED_CHAPTER_PROVIDER_ID ) { return@createInMemoryDataProviderAsync getDeferredResult(deferred) } @@ -218,8 +224,8 @@ class StoryProgressController @Inject constructor( * @param topicId the ID corresponding to the topic for which progress needs to be stored * @param storyId the ID corresponding to the story for which progress needs to be stored * @param explorationId the chapter id which will marked as [ChapterPlayState.IN_PROGRESS_NOT_SAVED] - * if it has not been [ChapterPlayState.COMPLETED] already - * @param lastPlayedTimestamp the timestamp at the exploration was finished. + * if it has not been [ChapterPlayState.COMPLETED] already + * @param lastPlayedTimestamp the timestamp at the exploration was finished * @return a [DataProvider] that indicates the success/failure of this record progress operation */ fun recordChapterAsInProgressNotSaved( @@ -239,7 +245,9 @@ class StoryProgressController @Inject constructor( explorationId ) - val chapterProgressBuilder = if (previousChapterProgress != null) { + val chapterProgressBuilder = if (previousChapterProgress != null && + previousChapterProgress.chapterPlayState == ChapterPlayState.COMPLETED + ) { previousChapterProgress.toBuilder() } else { ChapterProgress.newBuilder() @@ -284,7 +292,7 @@ class StoryProgressController @Inject constructor( } return dataProviders.createInMemoryDataProviderAsync( - RECORD_RECENTLY_PLAYED_CHAPTER_PROVIDER_ID + RECORD_IN_PROGRESS_NOT_SAVED_CHAPTER_PROVIDER_ID ) { return@createInMemoryDataProviderAsync getDeferredResult(deferred) } @@ -295,15 +303,15 @@ class StoryProgressController @Inject constructor( * [DataProvider] that provides exactly one [AsyncResult] to indicate whether this operation has * succeeded. This method will never return a pending result. * - * @param profileId the ID corresponding to the profile for which progress needs to be stored. - * @param topicId the ID corresponding to the topic for which progress needs to be stored. - * @param storyId the ID corresponding to the story for which progress needs to be stored. + * @param profileId the ID corresponding to the profile for which progress needs to be stored + * @param topicId the ID corresponding to the topic for which progress needs to be stored + * @param storyId the ID corresponding to the story for which progress needs to be stored * @param explorationId the chapter id which will marked as [ChapterPlayState.NOT_STARTED] if it - * has not been [ChapterPlayState.COMPLETED] already. - * @param lastPlayedTimestamp the timestamp at which the exploration was last played. - * @return a [DataProvider] that indicates the success/failure of this record progress operation. + * has not been [ChapterPlayState.COMPLETED] already + * @param lastPlayedTimestamp the timestamp at which the exploration was last played + * @return a [DataProvider] that indicates the success/failure of this record progress operation */ - fun recordRecentlyPlayedChapter( + fun recordChapterAsStartedNotCompleted( profileId: ProfileId, topicId: String, storyId: String, @@ -365,7 +373,7 @@ class StoryProgressController @Inject constructor( } return dataProviders.createInMemoryDataProviderAsync( - RECORD_RECENTLY_PLAYED_CHAPTER_PROVIDER_ID + RECORD_STARTED_NOT_COMPLETED_CHAPTER_PROVIDER_ID ) { return@createInMemoryDataProviderAsync getDeferredResult(deferred) } diff --git a/domain/src/main/java/org/oppia/android/domain/topic/TopicListController.kt b/domain/src/main/java/org/oppia/android/domain/topic/TopicListController.kt index 896eb2025f8..d5b542b3b8e 100644 --- a/domain/src/main/java/org/oppia/android/domain/topic/TopicListController.kt +++ b/domain/src/main/java/org/oppia/android/domain/topic/TopicListController.kt @@ -365,10 +365,26 @@ class TopicListController @Inject constructor( } != null } - private fun getStartedChapterProgressList(storyProgress: StoryProgress): List = - getSortedChapterProgressListByPlayState( - storyProgress, playState = ChapterPlayState.STARTED_NOT_COMPLETED - ) + private fun getStartedChapterProgressList(storyProgress: StoryProgress): List { + val startedNotCompletedChapterList = + getSortedChapterProgressListByPlayState( + storyProgress, playState = ChapterPlayState.STARTED_NOT_COMPLETED + ) + val inProgressSavedChapterList = + getSortedChapterProgressListByPlayState( + storyProgress, playState = ChapterPlayState.IN_PROGRESS_SAVED + ) + val inProgressNotSavedChapterList = + getSortedChapterProgressListByPlayState( + storyProgress, playState = ChapterPlayState.IN_PROGRESS_NOT_SAVED + ) + return ( + startedNotCompletedChapterList + + inProgressSavedChapterList + + inProgressNotSavedChapterList + ) + .sortedByDescending { chapterProgress -> chapterProgress.lastPlayedTimestamp } + } private fun getCompletedChapterProgressList(storyProgress: StoryProgress): List = getSortedChapterProgressListByPlayState( diff --git a/domain/src/test/java/org/oppia/android/domain/exploration/ExplorationDataControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/exploration/ExplorationDataControllerTest.kt index 330f4019601..331cd572410 100644 --- a/domain/src/test/java/org/oppia/android/domain/exploration/ExplorationDataControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/exploration/ExplorationDataControllerTest.kt @@ -23,6 +23,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.oppia.android.app.model.EphemeralState import org.oppia.android.app.model.Exploration +import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.domain.classify.InteractionsModule import org.oppia.android.domain.classify.rules.continueinteraction.ContinueModule import org.oppia.android.domain.classify.rules.dragAndDropSortInput.DragDropSortInputModule @@ -260,14 +261,16 @@ class ExplorationDataControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) explorationDataController.startPlayingExploration( internalProfileId, TEST_TOPIC_ID_1, TEST_STORY_ID_2, TEST_EXPLORATION_ID_4, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() diff --git a/domain/src/test/java/org/oppia/android/domain/exploration/ExplorationProgressControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/exploration/ExplorationProgressControllerTest.kt index f6ce58dccb4..8db174015c0 100644 --- a/domain/src/test/java/org/oppia/android/domain/exploration/ExplorationProgressControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/exploration/ExplorationProgressControllerTest.kt @@ -33,7 +33,10 @@ import org.oppia.android.app.model.EphemeralState.StateTypeCase.PENDING_STATE import org.oppia.android.app.model.EphemeralState.StateTypeCase.TERMINAL_STATE import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.model.Fraction +import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.Hint +import org.oppia.android.app.model.HintIndex +import org.oppia.android.app.model.HintState import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ListOfSetsOfTranslatableHtmlContentIds import org.oppia.android.app.model.Point2d @@ -71,7 +74,6 @@ import org.oppia.android.testing.environment.TestEnvironmentConfig import org.oppia.android.testing.robolectric.RobolectricModule import org.oppia.android.testing.threading.TestCoroutineDispatchers import org.oppia.android.testing.threading.TestDispatcherModule -import org.oppia.android.testing.time.FakeOppiaClock import org.oppia.android.testing.time.FakeOppiaClockModule import org.oppia.android.util.caching.CacheAssetsLocally import org.oppia.android.util.caching.LoadLessonProtosFromAssets @@ -128,9 +130,6 @@ class ExplorationProgressControllerTest { @Inject lateinit var testCoroutineDispatchers: TestCoroutineDispatchers - @Inject - lateinit var oppiaClock: FakeOppiaClock - @Inject lateinit var explorationCheckpointController: ExplorationCheckpointController @@ -197,7 +196,8 @@ class ExplorationProgressControllerTest { INVALID_TOPIC_ID, INVALID_STORY_ID, INVALID_EXPLORATION_ID, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) resultLiveData.observeForever(mockAsyncResultLiveDataObserver) testCoroutineDispatchers.runCurrent() @@ -219,7 +219,8 @@ class ExplorationProgressControllerTest { INVALID_TOPIC_ID, INVALID_STORY_ID, INVALID_EXPLORATION_ID, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) verify( @@ -240,7 +241,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) resultLiveData.observeForever(mockAsyncResultLiveDataObserver) testCoroutineDispatchers.runCurrent() @@ -261,7 +263,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) // The second-to-latest result stays pending since the exploration was loading (the actual @@ -282,7 +285,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) val currentStateLiveData = @@ -308,7 +312,8 @@ class ExplorationProgressControllerTest { INVALID_TOPIC_ID, INVALID_STORY_ID, INVALID_EXPLORATION_ID, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) endExploration() @@ -318,7 +323,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) val currentStateLiveData = explorationProgressController.getCurrentState().toLiveData() @@ -358,7 +364,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) // Try playing another exploration without finishing the previous one. @@ -368,7 +375,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) resultLiveData.observeForever(mockAsyncResultLiveDataObserver) testCoroutineDispatchers.runCurrent() @@ -391,7 +399,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) endExploration() @@ -401,7 +410,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_1, TEST_STORY_ID_2, TEST_EXPLORATION_ID_4, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) // The latest result should correspond to the valid ID, and the progress controller should @@ -420,7 +430,10 @@ class ExplorationProgressControllerTest { @Test fun testSubmitAnswer_beforePlaying_failsWithError() { val result = - explorationProgressController.submitAnswer(createMultipleChoiceAnswer(0)) + explorationProgressController.submitAnswer( + userAnswer = createMultipleChoiceAnswer(0), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -444,11 +457,15 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) val result = - explorationProgressController.submitAnswer(createMultipleChoiceAnswer(0)) + explorationProgressController.submitAnswer( + userAnswer = createMultipleChoiceAnswer(0), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -471,12 +488,16 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeMultipleChoiceState() val result = - explorationProgressController.submitAnswer(createMultipleChoiceAnswer(2)) + explorationProgressController.submitAnswer( + userAnswer = createMultipleChoiceAnswer(2), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -496,12 +517,16 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeMultipleChoiceState() val result = - explorationProgressController.submitAnswer(createMultipleChoiceAnswer(2)) + explorationProgressController.submitAnswer( + userAnswer = createMultipleChoiceAnswer(2), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -523,12 +548,16 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeMultipleChoiceState() val result = - explorationProgressController.submitAnswer(createMultipleChoiceAnswer(0)) + explorationProgressController.submitAnswer( + userAnswer = createMultipleChoiceAnswer(0), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -548,12 +577,16 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeMultipleChoiceState() val result = - explorationProgressController.submitAnswer(createMultipleChoiceAnswer(0)) + explorationProgressController.submitAnswer( + userAnswer = createMultipleChoiceAnswer(0), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -577,7 +610,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeMultipleChoiceState() @@ -607,7 +641,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeMultipleChoiceState() @@ -636,7 +671,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeMultipleChoiceState() submitMultipleChoiceAnswer(0) @@ -682,7 +718,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) val moveToStateResult = explorationProgressController.moveToNextState() @@ -703,7 +740,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) val moveToStateResult = explorationProgressController.moveToNextState() @@ -726,7 +764,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) submitPrototypeState1Answer() @@ -748,7 +787,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) submitPrototypeState1Answer() @@ -772,7 +812,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) submitPrototypeState1Answer() moveToNextState() @@ -814,7 +855,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) val moveToStateResult = @@ -837,7 +879,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) val moveToStateResult = @@ -861,7 +904,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) submitPrototypeState1Answer() @@ -886,7 +930,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() @@ -911,7 +956,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() @@ -938,7 +984,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() moveToPreviousState() @@ -965,12 +1012,16 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeTextInputState() val result = - explorationProgressController.submitAnswer(createTextInputAnswer("Finnish")) + explorationProgressController.submitAnswer( + userAnswer = createTextInputAnswer("Finnish"), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -992,12 +1043,16 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeTextInputState() val result = - explorationProgressController.submitAnswer(createTextInputAnswer("Klingon")) + explorationProgressController.submitAnswer( + userAnswer = createTextInputAnswer("Klingon"), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -1020,7 +1075,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeFractionInputState() @@ -1052,7 +1108,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeFractionInputState() submitWrongAnswerForPrototypeState2() @@ -1067,8 +1124,13 @@ class ExplorationProgressControllerTest { val result = explorationProgressController.submitHintIsRevealed( state = currentState.state, - hintIsRevealed = true, - hintIndex = 0, + hintState = + HintState.newBuilder() + .setHelpIndex( + HelpIndex.newBuilder() + .setHintIndex(HintIndex.newBuilder().setIndex(0).setIsHintRevealed(true).build()) + .build() + ).build() ) result.observeForever(mockAsyncHintObserver) testCoroutineDispatchers.runCurrent() @@ -1097,7 +1159,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeFractionInputState() submitWrongAnswerForPrototypeState2() @@ -1110,7 +1173,14 @@ class ExplorationProgressControllerTest { assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() val currentState = currentStateResultCaptor.value.getOrThrow() - val result = explorationProgressController.submitSolutionIsRevealed(currentState.state) + val result = explorationProgressController.submitSolutionIsRevealed( + state = currentState.state, + hintState = + HintState.newBuilder() + .setHelpIndex( + HelpIndex.newBuilder().setEverythingRevealed(true).build() + ).build() + ) result.observeForever(mockAsyncSolutionObserver) testCoroutineDispatchers.runCurrent() @@ -1135,7 +1205,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeFractionInputState() @@ -1170,12 +1241,16 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeTextInputState() val result = - explorationProgressController.submitAnswer(createTextInputAnswer("Finnish")) + explorationProgressController.submitAnswer( + createTextInputAnswer("Finnish"), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -1201,12 +1276,16 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeTextInputState() val result = - explorationProgressController.submitAnswer(createTextInputAnswer("Finnish ")) + explorationProgressController.submitAnswer( + userAnswer = createTextInputAnswer("Finnish "), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -1234,12 +1313,16 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeTextInputState() val result = - explorationProgressController.submitAnswer(createTextInputAnswer("Klingon")) + explorationProgressController.submitAnswer( + userAnswer = createTextInputAnswer("Klingon"), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -1268,7 +1351,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() @@ -1296,7 +1380,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() submitPrototypeState2Answer() // Submit the answer but do not proceed to the next state. @@ -1323,7 +1408,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() playThroughPrototypeState2AndMoveToNextState() @@ -1358,7 +1444,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) // The initial state should not have a next state. @@ -1380,7 +1467,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) submitPrototypeState1Answer() @@ -1405,7 +1493,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() @@ -1429,7 +1518,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() @@ -1454,7 +1544,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() @@ -1478,11 +1569,15 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeNumericInputState() - val result = explorationProgressController.submitAnswer(createNumericInputAnswer(121.0)) + val result = explorationProgressController.submitAnswer( + createNumericInputAnswer(121.0), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -1504,12 +1599,14 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeNumericInputState() val result = explorationProgressController.submitAnswer( - createNumericInputAnswer(122.0) + createNumericInputAnswer(122.0), + hintState = HintState.getDefaultInstance() ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -1532,11 +1629,15 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) // The first state of the exploration is the Continue interaction. - val result = explorationProgressController.submitAnswer(createContinueButtonAnswer()) + val result = explorationProgressController.submitAnswer( + createContinueButtonAnswer(), + hintState = HintState.getDefaultInstance() + ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -1560,7 +1661,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeExploration() @@ -1585,7 +1687,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() playThroughPrototypeState2AndMoveToNextState() @@ -1619,7 +1722,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeExploration() @@ -1646,7 +1750,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_5, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) submitImageRegionAnswer(clickX = 0.5f, clickY = 0.5f, clickedRegion = "Saturn") moveToNextState() @@ -1674,7 +1779,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_5, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) submitImageRegionAnswer(clickX = 0.2f, clickY = 0.5f, clickedRegion = "Jupiter") submitImageRegionAnswer(clickX = 0.5f, clickY = 0.5f, clickedRegion = "Saturn") @@ -1700,7 +1806,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeExploration() endExploration() @@ -1710,7 +1817,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_5, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) submitImageRegionAnswer(clickX = 0.2f, clickY = 0.5f, clickedRegion = "Jupiter") @@ -1746,7 +1854,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() moveToPreviousState() @@ -1765,7 +1874,8 @@ class ExplorationProgressControllerTest { @Test fun testSubmitAnswer_beforePlaying_failsWithError_logsException() { val result = explorationProgressController.submitAnswer( - createMultipleChoiceAnswer(0) + createMultipleChoiceAnswer(0), + hintState = HintState.getDefaultInstance() ) result.observeForever(mockAsyncAnswerOutcomeObserver) testCoroutineDispatchers.runCurrent() @@ -1787,7 +1897,8 @@ class ExplorationProgressControllerTest { INVALID_TOPIC_ID, INVALID_STORY_ID, INVALID_EXPLORATION_ID, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) val exception = fakeExceptionLogger.getMostRecentException() @@ -1803,7 +1914,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -1824,7 +1936,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) verifyCheckpointHasCorrectPendingStateName( profileId, @@ -1862,7 +1975,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() playThroughPrototypeState2AndMoveToNextState() @@ -1888,7 +2002,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() playThroughPrototypeState2AndMoveToNextState() @@ -1911,7 +2026,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() playThroughPrototypeState2AndMoveToNextState() @@ -1957,7 +2073,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() playThroughPrototypeState2AndMoveToNextState() @@ -1980,7 +2097,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() playThroughPrototypeState2AndMoveToNextState() @@ -2001,7 +2119,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() playThroughPrototypeState2AndMoveToNextState() @@ -2024,7 +2143,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeFractionInputState() submitWrongAnswerForPrototypeState2() @@ -2038,8 +2158,13 @@ class ExplorationProgressControllerTest { val result = explorationProgressController.submitHintIsRevealed( state = currentState.state, - hintIsRevealed = true, - hintIndex = 0, + hintState = + HintState.newBuilder() + .setHelpIndex( + HelpIndex.newBuilder() + .setHintIndex(HintIndex.newBuilder().setIndex(0).setIsHintRevealed(true).build()) + .build() + ).build() ) result.observeForever(mockAsyncHintObserver) testCoroutineDispatchers.runCurrent() @@ -2059,7 +2184,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) navigateToPrototypeFractionInputState() submitWrongAnswerForPrototypeState2() @@ -2071,7 +2197,13 @@ class ExplorationProgressControllerTest { assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() val currentState = currentStateResultCaptor.value.getOrThrow() - val result = explorationProgressController.submitSolutionIsRevealed(currentState.state) + val result = explorationProgressController.submitSolutionIsRevealed( + currentState.state, + hintState = HintState.newBuilder() + .setHelpIndex( + HelpIndex.newBuilder().setEverythingRevealed(true).build() + ).build() + ) result.observeForever(mockAsyncSolutionObserver) testCoroutineDispatchers.runCurrent() @@ -2090,7 +2222,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) playThroughPrototypeState1AndMoveToNextState() // Verify that checkpoint is saved when the exploration moves to the new pendingTopState. @@ -2109,7 +2242,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = false + shouldSavePartialProgress = false, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -2129,7 +2263,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -2149,7 +2284,8 @@ class ExplorationProgressControllerTest { TEST_TOPIC_ID_0, TEST_STORY_ID_0, TEST_EXPLORATION_ID_2, - shouldSavePartialProgress = true + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() ) testCoroutineDispatchers.runCurrent() @@ -2166,6 +2302,387 @@ class ExplorationProgressControllerTest { .isEqualTo(CheckpointState.CHECKPOINT_SAVED_DATABASE_EXCEEDED_LIMIT) } + @Test + fun testCheckpointing_OnSecondState_resumeExploration_expResumedFromCorrectPendingState() { + subscribeToCurrentStateToAllowExplorationToLoad() + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() + ) + playThroughPrototypeState1AndMoveToNextState() + endExploration() + + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + retrieveExplorationCheckpoint(profileId, TEST_EXPLORATION_ID_2) + ) + + // Verify that we're on the second state of the second exploration. + verify(mockCurrentStateLiveDataObserver, atLeastOnce()) + .onChanged(currentStateResultCaptor.capture()) + assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() + val currentState = currentStateResultCaptor.value.getOrThrow() + assertThat(currentState.stateTypeCase).isEqualTo(PENDING_STATE) + assertThat(currentState.state.name).isEqualTo("Fractions") + } + + @Test + fun testCheckpointing_OnSecondState_navigateBack_resumeExploration_checkResumedFromSecondState() { + subscribeToCurrentStateToAllowExplorationToLoad() + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() + ) + playThroughPrototypeState1AndMoveToNextState() + moveToPreviousState() + endExploration() + + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + retrieveExplorationCheckpoint(profileId, TEST_EXPLORATION_ID_2) + ) + + // Verify that we're on the second state of the second exploration. + verify(mockCurrentStateLiveDataObserver, atLeastOnce()) + .onChanged(currentStateResultCaptor.capture()) + assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() + val currentState = currentStateResultCaptor.value.getOrThrow() + assertThat(currentState.stateTypeCase).isEqualTo(PENDING_STATE) + assertThat(currentState.state.name).isEqualTo("Fractions") + } + + @Test + fun testCheckpointing_OnSecondState_submitWrongAns_resumeExploration_checkWrongAnswersVisible() { + subscribeToCurrentStateToAllowExplorationToLoad() + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() + ) + playThroughPrototypeState1AndMoveToNextState() + submitWrongAnswerForPrototypeState2() + submitWrongAnswerForPrototypeState2() + submitWrongAnswerForPrototypeState2() + endExploration() + + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + retrieveExplorationCheckpoint(profileId, TEST_EXPLORATION_ID_2) + ) + + // Verify that three wrong answers are visible to the user. + verify(mockCurrentStateLiveDataObserver, atLeastOnce()) + .onChanged(currentStateResultCaptor.capture()) + assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() + val currentState = currentStateResultCaptor.value.getOrThrow() + assertThat(currentState.stateTypeCase).isEqualTo(PENDING_STATE) + assertThat(currentState.pendingState.wrongAnswerCount).isEqualTo(3) + } + + @Test + fun testCheckpointing_OnSecondState_submitRightAns_resumeExploration_expResumedFromCompState() { + subscribeToCurrentStateToAllowExplorationToLoad() + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() + ) + playThroughPrototypeState1AndMoveToNextState() + submitWrongAnswerForPrototypeState2() + submitWrongAnswerForPrototypeState2() + submitPrototypeState2Answer() + endExploration() + + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + retrieveExplorationCheckpoint(profileId, TEST_EXPLORATION_ID_2) + ) + + // Verify that we're on the second state of the second exploration because the continue button + // was not pressed after submitting the correct answer. + verify(mockCurrentStateLiveDataObserver, atLeastOnce()) + .onChanged(currentStateResultCaptor.capture()) + assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() + val currentState = currentStateResultCaptor.value.getOrThrow() + assertThat(currentState.stateTypeCase).isEqualTo(COMPLETED_STATE) + assertThat(currentState.state.name).isEqualTo("Fractions") + } + + @Test + fun testCheckpointing_submitAns_moveToNextState_resumeExploration_answersVisibleOnPrevState() { + subscribeToCurrentStateToAllowExplorationToLoad() + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() + ) + playThroughPrototypeState1AndMoveToNextState() + submitWrongAnswerForPrototypeState2() + submitWrongAnswerForPrototypeState2() + playThroughPrototypeState2AndMoveToNextState() + endExploration() + + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + retrieveExplorationCheckpoint(profileId, TEST_EXPLORATION_ID_2) + ) + moveToPreviousState() + + // Verify that we're on the second state of the second exploration because the continue button + // was not pressed after submitting the correct answer. + verify(mockCurrentStateLiveDataObserver, atLeastOnce()) + .onChanged(currentStateResultCaptor.capture()) + assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() + val currentState = currentStateResultCaptor.value.getOrThrow() + assertThat(currentState.stateTypeCase).isEqualTo(COMPLETED_STATE) + assertThat(currentState.state.name).isEqualTo("Fractions") + assertThat(currentState.completedState.answerCount).isEqualTo(3) + } + + @Test + fun testCheckpointing_onSecondState_resumeExploration_checkPendingStateDoesNotHaveANextState() { + subscribeToCurrentStateToAllowExplorationToLoad() + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() + ) + playThroughPrototypeState1AndMoveToNextState() + endExploration() + + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + retrieveExplorationCheckpoint(profileId, TEST_EXPLORATION_ID_2) + ) + + // Verify that we're on the second state of the second exploration because the continue button + // was not pressed after submitting the correct answer. + verify(mockCurrentStateLiveDataObserver, atLeastOnce()) + .onChanged(currentStateResultCaptor.capture()) + assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() + val currentState = currentStateResultCaptor.value.getOrThrow() + assertThat(currentState.stateTypeCase).isEqualTo(PENDING_STATE) + assertThat(currentState.state.name).isEqualTo("Fractions") + assertThat(currentState.hasNextState).isFalse() + } + + @Test + fun testCheckpointing_onFirstState_resumeExploration_checkStateDoesNotHaveAPrevState() { + subscribeToCurrentStateToAllowExplorationToLoad() + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() + ) + + endExploration() + + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + retrieveExplorationCheckpoint(profileId, TEST_EXPLORATION_ID_2) + ) + + // Verify that we're on the second state of the second exploration because the continue button + // was not pressed after submitting the correct answer. + verify(mockCurrentStateLiveDataObserver, atLeastOnce()) + .onChanged(currentStateResultCaptor.capture()) + assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() + val currentState = currentStateResultCaptor.value.getOrThrow() + assertThat(currentState.stateTypeCase).isEqualTo(PENDING_STATE) + assertThat(currentState.state.name).isEqualTo("Continue") + assertThat(currentState.hasPreviousState).isFalse() + } + + @Test + fun testCheckpointing_onSecondState_resumeExploration_checkFirstStateHasANextState() { + subscribeToCurrentStateToAllowExplorationToLoad() + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() + ) + playThroughPrototypeState1AndMoveToNextState() + endExploration() + + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + retrieveExplorationCheckpoint(profileId, TEST_EXPLORATION_ID_2) + ) + moveToPreviousState() + + // Verify that we're on the second state of the second exploration because the continue button + // was not pressed after submitting the correct answer. + verify(mockCurrentStateLiveDataObserver, atLeastOnce()) + .onChanged(currentStateResultCaptor.capture()) + assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() + val currentState = currentStateResultCaptor.value.getOrThrow() + assertThat(currentState.stateTypeCase).isEqualTo(COMPLETED_STATE) + assertThat(currentState.state.name).isEqualTo("Continue") + assertThat(currentState.hasNextState).isTrue() + } + + @Test + fun testCheckpointing_onSecondState_resumeExploration_checkFirstStateDoesNotHaveAPrevState() { + subscribeToCurrentStateToAllowExplorationToLoad() + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() + ) + playThroughPrototypeState1AndMoveToNextState() + endExploration() + + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + retrieveExplorationCheckpoint(profileId, TEST_EXPLORATION_ID_2) + ) + moveToPreviousState() + + // Verify that we're on the second state of the second exploration because the continue button + // was not pressed after submitting the correct answer. + verify(mockCurrentStateLiveDataObserver, atLeastOnce()) + .onChanged(currentStateResultCaptor.capture()) + assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() + val currentState = currentStateResultCaptor.value.getOrThrow() + assertThat(currentState.stateTypeCase).isEqualTo(COMPLETED_STATE) + assertThat(currentState.state.name).isEqualTo("Continue") + assertThat(currentState.hasPreviousState).isFalse() + } + + @Test + fun testCheckpointing_onSecondState_resumeExploration_checkSecondStateHasAPrevState() { + subscribeToCurrentStateToAllowExplorationToLoad() + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() + ) + playThroughPrototypeState1AndMoveToNextState() + endExploration() + + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + retrieveExplorationCheckpoint(profileId, TEST_EXPLORATION_ID_2) + ) + + // Verify that we're on the second state of the second exploration because the continue button + // was not pressed after submitting the correct answer. + verify(mockCurrentStateLiveDataObserver, atLeastOnce()) + .onChanged(currentStateResultCaptor.capture()) + assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() + val currentState = currentStateResultCaptor.value.getOrThrow() + assertThat(currentState.stateTypeCase).isEqualTo(PENDING_STATE) + assertThat(currentState.state.name).isEqualTo("Fractions") + assertThat(currentState.hasPreviousState).isTrue() + } + + @Test + fun testCheckpointing_submitAns_doNotPressContinueBtn_resumeExp_pendingStateHasNoNextState() { + subscribeToCurrentStateToAllowExplorationToLoad() + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + ExplorationCheckpoint.getDefaultInstance() + ) + playThroughPrototypeState1AndMoveToNextState() + submitPrototypeState2Answer() + endExploration() + + playExploration( + profileId.internalId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + shouldSavePartialProgress = true, + retrieveExplorationCheckpoint(profileId, TEST_EXPLORATION_ID_2) + ) + + // Verify that the current state is a completed state but has no next state because we have + // not navigated to the latest pending state yet. + verify(mockCurrentStateLiveDataObserver, atLeastOnce()) + .onChanged(currentStateResultCaptor.capture()) + assertThat(currentStateResultCaptor.value.isSuccess()).isTrue() + val currentState = currentStateResultCaptor.value.getOrThrow() + assertThat(currentState.stateTypeCase).isEqualTo(COMPLETED_STATE) + assertThat(currentState.state.name).isEqualTo("Fractions") + assertThat(currentState.hasNextState).isFalse() + } + private fun setUpTestApplicationComponent() { ApplicationProvider.getApplicationContext().inject(this) } @@ -2187,7 +2704,8 @@ class ExplorationProgressControllerTest { topicId: String, storyId: String, explorationId: String, - shouldSavePartialProgress: Boolean + shouldSavePartialProgress: Boolean, + explorationCheckpoint: ExplorationCheckpoint ) { verifyOperationSucceeds( explorationDataController.startPlayingExploration( @@ -2195,57 +2713,80 @@ class ExplorationProgressControllerTest { topicId, storyId, explorationId, - shouldSavePartialProgress + shouldSavePartialProgress, + explorationCheckpoint ) ) } private fun submitContinueButtonAnswer() { verifyOperationSucceeds( - explorationProgressController.submitAnswer(createContinueButtonAnswer()) + explorationProgressController.submitAnswer( + createContinueButtonAnswer(), + hintState = HintState.getDefaultInstance() + ) ) } private fun submitFractionAnswer(fraction: Fraction) { verifyOperationSucceeds( - explorationProgressController.submitAnswer(createFractionAnswer(fraction)) + explorationProgressController.submitAnswer( + createFractionAnswer(fraction), + hintState = HintState.getDefaultInstance() + ) ) } private fun submitMultipleChoiceAnswer(choiceIndex: Int) { verifyOperationSucceeds( - explorationProgressController.submitAnswer(createMultipleChoiceAnswer(choiceIndex)) + explorationProgressController.submitAnswer( + createMultipleChoiceAnswer(choiceIndex), + hintState = HintState.getDefaultInstance() + ) ) } private fun submitItemSelectionAnswer(vararg contentIds: String) { verifyOperationSucceeds( - explorationProgressController.submitAnswer(createItemSelectionAnswer(contentIds.toList())) + explorationProgressController.submitAnswer( + createItemSelectionAnswer(contentIds.toList()), + hintState = HintState.getDefaultInstance() + ) ) } private fun submitNumericInputAnswer(numericAnswer: Double) { verifyOperationSucceeds( - explorationProgressController.submitAnswer(createNumericInputAnswer(numericAnswer)) + explorationProgressController.submitAnswer( + createNumericInputAnswer(numericAnswer), + hintState = HintState.getDefaultInstance() + ) ) } private fun submitRatioInputAnswer(ratioExpression: RatioExpression) { verifyOperationSucceeds( - explorationProgressController.submitAnswer(createRatioInputAnswer(ratioExpression)) + explorationProgressController.submitAnswer( + createRatioInputAnswer(ratioExpression), + hintState = HintState.getDefaultInstance() + ) ) } private fun submitTextInputAnswer(@Suppress("SameParameterValue") textAnswer: String) { verifyOperationSucceeds( - explorationProgressController.submitAnswer(createTextInputAnswer(textAnswer)) + explorationProgressController.submitAnswer( + createTextInputAnswer(textAnswer), + hintState = HintState.getDefaultInstance() + ) ) } private fun submitDragAndDropAnswer(vararg selectedChoicesLists: List) { verifyOperationSucceeds( explorationProgressController.submitAnswer( - createDragAndDropAnswer(selectedChoicesLists.toList()) + createDragAndDropAnswer(selectedChoicesLists.toList()), + hintState = HintState.getDefaultInstance() ) ) } @@ -2253,7 +2794,8 @@ class ExplorationProgressControllerTest { private fun submitImageRegionAnswer(clickX: Float, clickY: Float, clickedRegion: String) { verifyOperationSucceeds( explorationProgressController.submitAnswer( - createImageRegionAnswer(clickX, clickY, clickedRegion) + createImageRegionAnswer(clickX, clickY, clickedRegion), + hintState = HintState.getDefaultInstance() ) ) } @@ -2544,6 +3086,27 @@ class ExplorationProgressControllerTest { return UserAnswer.newBuilder().setAnswer(answer).setPlainAnswer(answer.toAnswerString()).build() } + private fun retrieveExplorationCheckpoint( + profileId: ProfileId, + explorationId: String + ): ExplorationCheckpoint { + testCoroutineDispatchers.runCurrent() + reset(mockExplorationCheckpointObserver) + val explorationCheckpointLiveData = + explorationCheckpointController.retrieveExplorationCheckpoint( + profileId, + explorationId + ).toLiveData() + explorationCheckpointLiveData.observeForever(mockExplorationCheckpointObserver) + testCoroutineDispatchers.runCurrent() + + verify(mockExplorationCheckpointObserver, atLeastOnce()) + .onChanged(explorationCheckpointCaptor.capture()) + assertThat(explorationCheckpointCaptor.value.isSuccess()).isTrue() + + return explorationCheckpointCaptor.value.getOrThrow() + } + private fun verifyCheckpointHasCorrectPendingStateName( profileId: ProfileId, explorationId: String, @@ -2632,7 +3195,7 @@ class ExplorationProgressControllerTest { .onChanged(explorationCheckpointCaptor.capture()) assertThat(explorationCheckpointCaptor.value.isSuccess()).isTrue() - assertThat(explorationCheckpointCaptor.value.getOrThrow().hintIndex) + assertThat(explorationCheckpointCaptor.value.getOrThrow().helpIndex.hintIndex.index) .isEqualTo(indexOfRevealedHint) } @@ -2655,8 +3218,8 @@ class ExplorationProgressControllerTest { .onChanged(explorationCheckpointCaptor.capture()) assertThat(explorationCheckpointCaptor.value.isSuccess()).isTrue() - assertThat(explorationCheckpointCaptor.value.getOrThrow().solutionIsRevealed) - .isEqualTo(isSolutionRevealed) + assertThat(explorationCheckpointCaptor.value.getOrThrow().helpIndex.indexTypeCase) + .isEqualTo(HelpIndex.IndexTypeCase.EVERYTHING_REVEALED) } /** diff --git a/domain/src/test/java/org/oppia/android/domain/topic/StoryProgressControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/topic/StoryProgressControllerTest.kt index 3dd34398914..c29540627a4 100644 --- a/domain/src/test/java/org/oppia/android/domain/topic/StoryProgressControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/topic/StoryProgressControllerTest.kt @@ -110,8 +110,8 @@ class StoryProgressControllerTest { } @Test - fun testStoryProgressController_recordRecentlyPlayedChapter_isSuccessful() { - storyProgressController.recordRecentlyPlayedChapter( + fun testStoryProgressController_recordChapterAsStartedNotCompleted_isSuccessful() { + storyProgressController.recordChapterAsStartedNotCompleted( profileId, FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, @@ -191,7 +191,39 @@ class StoryProgressControllerTest { } @Test - fun testStoryProgressController_recordChapterAsInProgressNotSaved_isSuccessful() { + fun testStoryProgressController_markChapterAsNotSaved_markChapterAsSaved_playStateIsSaved() { + storyProgressController.recordChapterAsInProgressNotSaved( + profileId, + FRACTIONS_TOPIC_ID, + FRACTIONS_STORY_ID_0, + FRACTIONS_EXPLORATION_ID_0, + fakeOppiaClock.getCurrentTimeMs() + ).toLiveData().observeForever(mockRecordProgressObserver) + testCoroutineDispatchers.runCurrent() + + verifyRecordProgressSucceeded() + + storyProgressController.recordChapterAsInProgressSaved( + profileId, + FRACTIONS_TOPIC_ID, + FRACTIONS_STORY_ID_0, + FRACTIONS_EXPLORATION_ID_0, + fakeOppiaClock.getCurrentTimeMs() + ).toLiveData().observeForever(mockRecordProgressObserver) + testCoroutineDispatchers.runCurrent() + + verifyRecordProgressSucceeded() + verifyChapterPlayStateIsCorrect( + profileId, + FRACTIONS_TOPIC_ID, + FRACTIONS_STORY_ID_0, + FRACTIONS_EXPLORATION_ID_0, + ChapterPlayState.IN_PROGRESS_SAVED + ) + } + + @Test + fun testStoryProgressController_recordChapterAsNotSaved_isSuccessful() { storyProgressController.recordChapterAsInProgressNotSaved( profileId, FRACTIONS_TOPIC_ID, @@ -257,6 +289,38 @@ class StoryProgressControllerTest { ) } + @Test + fun testStoryProgressController_markChapterAsSaved_markChapterAsNotSaved_playStateIsNotSaved() { + storyProgressController.recordChapterAsInProgressSaved( + profileId, + FRACTIONS_TOPIC_ID, + FRACTIONS_STORY_ID_0, + FRACTIONS_EXPLORATION_ID_0, + fakeOppiaClock.getCurrentTimeMs() + ).toLiveData().observeForever(mockRecordProgressObserver) + testCoroutineDispatchers.runCurrent() + + verifyRecordProgressSucceeded() + + storyProgressController.recordChapterAsInProgressNotSaved( + profileId, + FRACTIONS_TOPIC_ID, + FRACTIONS_STORY_ID_0, + FRACTIONS_EXPLORATION_ID_0, + fakeOppiaClock.getCurrentTimeMs() + ).toLiveData().observeForever(mockRecordProgressObserver) + testCoroutineDispatchers.runCurrent() + + verifyRecordProgressSucceeded() + verifyChapterPlayStateIsCorrect( + profileId, + FRACTIONS_TOPIC_ID, + FRACTIONS_STORY_ID_0, + FRACTIONS_EXPLORATION_ID_0, + ChapterPlayState.IN_PROGRESS_NOT_SAVED + ) + } + private fun verifyChapterPlayStateIsCorrect( profileId: ProfileId, topicId: String, diff --git a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt index 616cf565683..fedcff82ca9 100644 --- a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt @@ -132,7 +132,7 @@ class TopicListControllerTest { } @Test - fun testRetrieveTopicList_firstTopic_hasCorrectTopicInfo() { + fun testRetrieveTopicList_firstTopic_hasCrtTopicInfo() { val topicList = retrieveTopicList() val firstTopic = topicList.getTopicSummary(0) assertThat(firstTopic.topicId).isEqualTo(TEST_TOPIC_ID_0) @@ -140,14 +140,14 @@ class TopicListControllerTest { } @Test - fun testRetrieveTopicList_firstTopic_hasCorrectLessonCount() { + fun testRetrieveTopicList_firstTopic_hasCrtLessonCount() { val topicList = retrieveTopicList() val firstTopic = topicList.getTopicSummary(0) assertThat(firstTopic.totalChapterCount).isEqualTo(2) } @Test - fun testRetrieveTopicList_secondTopic_hasCorrectTopicInfo() { + fun testRetrieveTopicList_secondTopic_hasCrtTopicInfo() { val topicList = retrieveTopicList() val secondTopic = topicList.getTopicSummary(1) assertThat(secondTopic.topicId).isEqualTo(TEST_TOPIC_ID_1) @@ -155,14 +155,14 @@ class TopicListControllerTest { } @Test - fun testRetrieveTopicList_secondTopic_hasCorrectLessonCount() { + fun testRetrieveTopicList_secondTopic_hasCrtLessonCount() { val topicList = retrieveTopicList() val secondTopic = topicList.getTopicSummary(1) assertThat(secondTopic.totalChapterCount).isEqualTo(1) } @Test - fun testRetrieveTopicList_fractionsTopic_hasCorrectTopicInfo() { + fun testRetrieveTopicList_fractionsTopic_hasCrtTopicInfo() { val topicList = retrieveTopicList() val fractionsTopic = topicList.getTopicSummary(2) assertThat(fractionsTopic.topicId).isEqualTo(FRACTIONS_TOPIC_ID) @@ -170,14 +170,14 @@ class TopicListControllerTest { } @Test - fun testRetrieveTopicList_fractionsTopic_hasCorrectLessonCount() { + fun testRetrieveTopicList_fractionsTopic_hasCrtLessonCount() { val topicList = retrieveTopicList() val fractionsTopic = topicList.getTopicSummary(2) assertThat(fractionsTopic.totalChapterCount).isEqualTo(2) } @Test - fun testRetrieveTopicList_ratiosTopic_hasCorrectTopicInfo() { + fun testRetrieveTopicList_ratiosTopic_hasCrtTopicInfo() { val topicList = retrieveTopicList() val ratiosTopic = topicList.getTopicSummary(3) assertThat(ratiosTopic.topicId).isEqualTo(RATIOS_TOPIC_ID) @@ -185,7 +185,7 @@ class TopicListControllerTest { } @Test - fun testRetrieveTopicList_ratiosTopic_hasCorrectLessonCount() { + fun testRetrieveTopicList_ratiosTopic_hasCrtLessonCount() { val topicList = retrieveTopicList() val ratiosTopic = topicList.getTopicSummary(3) assertThat(ratiosTopic.totalChapterCount).isEqualTo(4) @@ -207,7 +207,7 @@ class TopicListControllerTest { } @Test - fun testRetrievePromotedActivityList_defaultLesson_hasCorrectInfo() { + fun testRetrievePromotedActivityList_defaultLesson_hasCrtInfo() { topicListController.getPromotedActivityList(profileId0).toLiveData() .observeForever(mockPromotedActivityListObserver) testCoroutineDispatchers.runCurrent() @@ -216,8 +216,8 @@ class TopicListControllerTest { } @Test - fun testGetPromotedActivityList_markRecentlyPlayedFracStory0Exp0_ongoingStoryListIsCorrect() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + fun testGetPromotedActivityList_markFracStory0Exp0StartedNotCompleted_ongoingStoryListIsCrt() { + storyProgressTestHelper.markStartedNotCompletedFractionsStory0Exp0( profileId0, timestampOlderThanOneWeek = false ) @@ -231,11 +231,79 @@ class TopicListControllerTest { } @Test - fun testGetPromotedStoryList_markChapDoneFracStory0Exp0_ongoingStoryListIsCorrect() { + fun testGetPromotedActivityList_markFracStory0Exp0InProgressSaved_ongoingStoryListIsCrt() { + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( + profileId0, + timestampOlderThanOneWeek = false + ) + + val promotedActivityList = retrievePromotedActivityList() + assertThat(promotedActivityList.promotedStoryList.recentlyPlayedStoryCount) + .isEqualTo(1) + verifyOngoingStoryAsFractionStory0Exploration0( + promotedActivityList.promotedStoryList.recentlyPlayedStoryList[0] + ) + } + + @Test + fun testGetPromotedActivityList_markRecentlyPlayedFracStory0Exp0_ongoingStoryListIsCrt() { + storyProgressTestHelper.markInProgressNotSavedFractionsStory0Exp0( + profileId0, + timestampOlderThanOneWeek = false + ) + + val promotedActivityList = retrievePromotedActivityList() + assertThat(promotedActivityList.promotedStoryList.recentlyPlayedStoryCount) + .isEqualTo(1) + verifyOngoingStoryAsFractionStory0Exploration0( + promotedActivityList.promotedStoryList.recentlyPlayedStoryList[0] + ) + } + + @Test + fun testGetPromotedStoryList_markChapDoneFracStory0Exp0_ongoingStoryListIsCrt() { + storyProgressTestHelper.markCompletedFractionsStory0Exp0( + profileId0, + timestampOlderThanOneWeek = false + ) + + val promotedActivityList = retrievePromotedActivityList() + assertThat(promotedActivityList.promotedStoryList.recentlyPlayedStoryCount) + .isEqualTo(1) + verifyOngoingStoryAsFractionStory0Exploration1( + promotedActivityList.promotedStoryList.recentlyPlayedStoryList[0] + ) + } + + @Test + fun testGetStoryList_markChapDoneFracStory0Exp0_fracStory0Exp1Started_ongoingStoryListCrt() { + storyProgressTestHelper.markCompletedFractionsStory0Exp0( + profileId0, + timestampOlderThanOneWeek = false + ) + storyProgressTestHelper.markStartedNotCompletedFractionsStory0Exp1( + profileId0, + timestampOlderThanOneWeek = false + ) + + val promotedActivityList = retrievePromotedActivityList() + assertThat(promotedActivityList.promotedStoryList.recentlyPlayedStoryCount) + .isEqualTo(1) + verifyOngoingStoryAsFractionStory0Exploration1( + promotedActivityList.promotedStoryList.recentlyPlayedStoryList[0] + ) + } + + @Test + fun testGetStoryList_markChapDoneFracStory0Exp0_fracStory0Exp1ProgSaved_ongoingStoryListCrt() { storyProgressTestHelper.markCompletedFractionsStory0Exp0( profileId0, timestampOlderThanOneWeek = false ) + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp1( + profileId0, + timestampOlderThanOneWeek = false + ) val promotedActivityList = retrievePromotedActivityList() assertThat(promotedActivityList.promotedStoryList.recentlyPlayedStoryCount) @@ -246,12 +314,12 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_markChapDoneFracStory0Exp0_playedFracStory0Exp1_ongoingStoryListCorrect() { + fun testGetStoryList_markChapDoneFracStory0Exp0_FracStory0Exp1ProgNotSaved_ongoingStoryListCrt() { storyProgressTestHelper.markCompletedFractionsStory0Exp0( profileId0, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp1( + storyProgressTestHelper.markInProgressNotSavedFractionsStory0Exp1( profileId0, timestampOlderThanOneWeek = false ) @@ -265,7 +333,7 @@ class TopicListControllerTest { } @Test - fun testGetPromotedStoryList_markAllChapsDoneInFractions_suggestedStoryListIsCorrect() { + fun testGetPromotedStoryList_markAllChapsDoneInFractions_suggestedStoryListIsCrt() { storyProgressTestHelper.markCompletedFractionsStory0( profileId0, timestampOlderThanOneWeek = false @@ -285,12 +353,12 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_markRecentPlayedFirstChapInAllStoriesInRatios_ongoingStoryListIsCorrect() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + fun testGetStoryList_markRecentPlayedFirstChapInAllStoriesInRatios_ongoingStoryListIsCrt() { + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId0, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory1Exp0( + storyProgressTestHelper.markInProgressNotSavedRatiosStory1Exp0( profileId0, timestampOlderThanOneWeek = false ) @@ -307,7 +375,7 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_markExp0DoneAndExp2InRatios_promotedStoryListIsCorrect() { + fun testGetStoryList_markExp0DoneAndExp2InRatios_promotedStoryListIsCrt() { storyProgressTestHelper.markCompletedRatiosStory0( profileId0, timestampOlderThanOneWeek = false @@ -322,7 +390,7 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_markStoryDoneOfRatiosAndFirstTestTopic_suggestedStoryListIsCorrect() { + fun testGetStoryList_markStoryDoneOfRatiosAndFirstTestTopic_suggestedStoryListIsCrt() { storyProgressTestHelper.markCompletedTestTopic0Story0( profileId0, timestampOlderThanOneWeek = false @@ -342,7 +410,7 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_noTopicProgress_defaultSuggestedStoryListIsCorrect() { + fun testGetStoryList_noTopicProgress_defaultSuggestedStoryListIsCrt() { val promotedActivityList = retrievePromotedActivityList() assertThat(promotedActivityList.promotedStoryList.recentlyPlayedStoryCount) @@ -360,8 +428,8 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_markRecentlyPlayedFirstTestTopic_suggestedStoryListIsCorrect() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + fun testGetStoryList_markRecentlyPlayedFirstTestTopic_suggestedStoryListIsCrt() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( profileId0, timestampOlderThanOneWeek = false ) @@ -384,7 +452,7 @@ class TopicListControllerTest { } @Test - fun testRetrievePromotedActivityList_markAllChapDoneInAllTopics_comingSoonTopicListIsCorrect() { + fun testRetrievePromotedActivityList_markAllChapDoneInAllTopics_comingSoonTopicListIsCrt() { storyProgressTestHelper.markAllTopicsAsCompleted( profileId0, timestampOlderThanOneWeek = false @@ -413,7 +481,7 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_markAllChapDoneInSecondTestTopic_comingSoonTopicListIsCorrect() { + fun testGetStoryList_markAllChapDoneInSecondTestTopic_comingSoonTopicListIsCrt() { storyProgressTestHelper.markCompletedTestTopic1Story0( profileId0, timestampOlderThanOneWeek = false @@ -432,7 +500,7 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_markFirstExpOfEveryStoryDoneWithinLastSevenDays_ongoingListIsCorrect() { + fun testGetStoryList_markFirstExpOfEveryStoryDoneWithinLastSevenDays_ongoingListIsCrt() { storyProgressTestHelper.markCompletedFractionsStory0Exp0( profileId0, timestampOlderThanOneWeek = false @@ -461,7 +529,7 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_markFirstExpOfEveryStoryDoneWithinLastMonth_ongoingOlderListIsCorrect() { + fun testGetStoryList_markFirstExpOfEveryStoryDoneWithinLastMonth_ongoingOlderListIsCrt() { storyProgressTestHelper.markCompletedFractionsStory0Exp0( profileId0, timestampOlderThanOneWeek = true @@ -490,8 +558,8 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_markRecentlyPlayedForFirstTestTopic_ongoingStoryListIsCorrect() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + fun testGetStoryList_markRecentlyPlayedForFirstTestTopic_ongoingStoryListIsCrt() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( profileId0, timestampOlderThanOneWeek = false ) @@ -505,7 +573,7 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_markOneStoryDoneForFirstTestTopic_suggestedStoryListIsCorrect() { + fun testGetStoryList_markOneStoryDoneForFirstTestTopic_suggestedStoryListIsCrt() { storyProgressTestHelper.markCompletedTestTopic0Story0( profileId0, timestampOlderThanOneWeek = false @@ -521,12 +589,12 @@ class TopicListControllerTest { } @Test - fun testGetStoryList_markOneStoryDoneAndPlayNextStoryOfRatiosTopic_ongoingListIsCorrect() { + fun testGetStoryList_markOneStoryDoneAndPlayNextStoryOfRatiosTopic_ongoingListIsCrt() { storyProgressTestHelper.markCompletedRatiosStory0( profileId0, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory1Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory1Exp0( profileId0, timestampOlderThanOneWeek = false ) @@ -548,11 +616,11 @@ class TopicListControllerTest { profileId0, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedRatiosStory1Exp0( + storyProgressTestHelper.markInProgressSavedRatiosStory1Exp0( profileId0, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId0, timestampOlderThanOneWeek = false ) @@ -574,7 +642,7 @@ class TopicListControllerTest { } @Test - fun testRetrieveStoryList_markFirstExpOfEveryStoryDoneWithinLastMonth_ongoingListIsCorrect() { + fun testRetrieveStoryList_markFirstExpOfEveryStoryDoneWithinLastMonth_ongoingListIsCrt() { storyProgressTestHelper.markCompletedFractionsStory0Exp0( profileId0, timestampOlderThanOneWeek = true diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index cc911ded54f..4c6ff58befb 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -160,6 +160,8 @@ message Solution { SubtitledHtml explanation = 4; // To check if the solution was revealed or not by the learner. bool solution_is_revealed = 5; + // To check if the un-revealed solution was visible to the learner or not. + bool unrevealed_solution_is_visible = 6; } // Structure for a Correct answer in Solution @@ -181,6 +183,8 @@ message Hint { bool hint_is_revealed = 2; // The current state that is display to the user. State state = 3; + // To check if hint is visible but not yet revealed by the user. + bool unrevealed_hint_is_visible = 4; } // Structure for a single outcome @@ -236,6 +240,9 @@ message EphemeralState { // The current status of checkpointing the exploration. CheckpointState checkpoint_state = 7; + + // The state of hints for the current exploration. + HintState hint_state = 8; } // Corresponds to an exploration state that hasn't yet had a correct answer filled in. @@ -320,7 +327,7 @@ message HelpIndex { // or solution hasn't yet been triggered, or because there are none to trigger). oneof index_type { // Indicates this help index corresponds to the index of a hint within the hint list of a state. - int32 hint_index = 1; + HintIndex hint_index = 1; // Indicates this help index corresponds to the solution of a state. The boolean value here has // no importance and is always 'true'. @@ -335,6 +342,18 @@ message HelpIndex { } } +message HintIndex { + int32 index = 1; + bool is_hint_revealed = 2; +} + +message HintState { + HelpIndex help_index = 1; + int32 hint_sequence_number = 4; + int32 tracked_answer_count = 5; + bool is_hint_visible_in_latest_state = 6; +} + // Different states in which exploration checkpoint can exist. enum CheckpointState { // The state of checkpoint is unknown. diff --git a/model/src/main/proto/exploration_checkpoint.proto b/model/src/main/proto/exploration_checkpoint.proto index 6756fac3428..67e2aedaae4 100644 --- a/model/src/main/proto/exploration_checkpoint.proto +++ b/model/src/main/proto/exploration_checkpoint.proto @@ -30,22 +30,17 @@ message ExplorationCheckpoint { // The index of the current selected state from the deck of states. int32 state_index = 4; - // Stores -1 if no hint for the pending top state has been revealed otherwise it stores the index - // of the hint that the learner has revealed. - int32 hint_index = 5; - - // Stores true if the solution to the pending top state has been revealed by the learner otherwise - // it stores false. - bool solution_is_revealed = 6; + // The state of the hints and solution when the checkpoint was saved. + HelpIndex help_index = 5; // The title of the exploration whose checkpoint is saved. - string exploration_title = 7; + string exploration_title = 6; // Stores the version of the exploration which is compatible with the saved checkpoint. - int32 exploration_version = 8; + int32 exploration_version = 7; // The timestamp in milliseconds of when the checkpoint was saved for the first time. - int64 timestamp_of_first_checkpoint = 9; + int64 timestamp_of_first_checkpoint = 8; } // Corresponds to exploration states that have been completed. diff --git a/testing/src/main/java/org/oppia/android/testing/story/StoryProgressTestHelper.kt b/testing/src/main/java/org/oppia/android/testing/story/StoryProgressTestHelper.kt index 506071b6963..88ca390746f 100644 --- a/testing/src/main/java/org/oppia/android/testing/story/StoryProgressTestHelper.kt +++ b/testing/src/main/java/org/oppia/android/testing/story/StoryProgressTestHelper.kt @@ -308,16 +308,16 @@ class StoryProgressTestHelper @Inject constructor( /* Test topic partial completion methods. */ /** - * Marks the first chapter of test topic 0 story 0 as recently played. Note that this may require + * Marks the first chapter of test topic 0 story 0 as started not completed. Note that this may require * completing prerequisite chapters before the chapter can be marked as a prerequisite. See * [markCompletedTestTopic0Story0Exp0] for specifics on the parameters passed to this method, and * any other nuances. */ - fun markRecentlyPlayedTestTopic0Story0Exp0( + fun markStartedNotCompletedTestTopic0Story0Exp0( profileId: ProfileId, timestampOlderThanOneWeek: Boolean ) { - recordRecentlyPlayedChapter( + recordChapterAsStartedNotCompleted( profileId, TEST_TOPIC_ID_0, TEST_STORY_ID_0, @@ -327,16 +327,54 @@ class StoryProgressTestHelper @Inject constructor( } /** - * Marks the second chapter of test topic 0 story 0 as recently played. For specifics on - * parameters and nuances, see: [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the first chapter of test topic 0 story 0 as in progress saved. Note that this may require + * completing prerequisite chapters before the chapter can be marked as a prerequisite. See + * [markCompletedTestTopic0Story0Exp0] for specifics on the parameters passed to this method, and + * any other nuances. + */ + fun markInProgressSavedTestTopic0Story0Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsInProgressSaved( + profileId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the first chapter of test topic 0 story 0 as in progress not saved. Note that this may require + * completing prerequisite chapters before the chapter can be marked as a prerequisite. See + * [markCompletedTestTopic0Story0Exp0] for specifics on the parameters passed to this method, and + * any other nuances. + */ + fun markInProgressNotSavedTestTopic0Story0Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsInProgressNotSaved( + profileId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_2, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the second chapter of test topic 0 story 0 as started not completed. For specifics on + * parameters and nuances, see: [markStartedNotCompletedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedTestTopic0Story0Exp1( + fun markStartedNotCompletedTestTopic0Story0Exp1( profileId: ProfileId, timestampOlderThanOneWeek: Boolean ) { // Must complete the previous chapter first. markCompletedTestTopic0Story0Exp0(profileId, timestampOlderThanOneWeek) - recordRecentlyPlayedChapter( + recordChapterAsStartedNotCompleted( profileId, TEST_TOPIC_ID_0, TEST_STORY_ID_0, @@ -346,14 +384,52 @@ class StoryProgressTestHelper @Inject constructor( } /** - * Marks the only chapter of test topic 1 story 2 as recently played. For specifics on parameters - * and nuances, see: [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the second chapter of test topic 0 story 0 as in progress saved. For specifics on + * parameters and nuances, see: [markInProgressSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedTestTopic1Story2Exp0( + fun markInProgressSavedTestTopic0Story0Exp1( profileId: ProfileId, timestampOlderThanOneWeek: Boolean ) { - recordRecentlyPlayedChapter( + // Must complete the previous chapter first. + markCompletedTestTopic0Story0Exp0(profileId, timestampOlderThanOneWeek) + recordChapterAsInProgressSaved( + profileId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_5, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the second chapter of test topic 0 story 0 as in progress not saved. For specifics on + * parameters and nuances, see: [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedTestTopic0Story0Exp1( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + // Must complete the previous chapter first. + markCompletedTestTopic0Story0Exp0(profileId, timestampOlderThanOneWeek) + recordChapterAsInProgressNotSaved( + profileId, + TEST_TOPIC_ID_0, + TEST_STORY_ID_0, + TEST_EXPLORATION_ID_5, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the only chapter of test topic 1 story 2 as started not completed. For specifics on parameters + * and nuances, see: [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedTestTopic1Story2Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsStartedNotCompleted( profileId, TEST_TOPIC_ID_1, TEST_STORY_ID_2, @@ -363,53 +439,188 @@ class StoryProgressTestHelper @Inject constructor( } /** - * Marks test topic 0's story 0 as recently played. For specifics on parameters and nuances, see: - * [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the only chapter of test topic 1 story 2 as in progress saved. For specifics on parameters + * and nuances, see: [markInProgressSavedTestTopic0Story0Exp0]. + */ + fun markInProgressSavedTestTopic1Story2Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsInProgressSaved( + profileId, + TEST_TOPIC_ID_1, + TEST_STORY_ID_2, + TEST_EXPLORATION_ID_4, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the only chapter of test topic 1 story 2 as in progress not saved. For specifics on parameters + * and nuances, see: [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedTestTopic1Story2Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsInProgressNotSaved( + profileId, + TEST_TOPIC_ID_1, + TEST_STORY_ID_2, + TEST_EXPLORATION_ID_4, + timestampOlderThanOneWeek + ) + } + + /** + * Marks test topic 0's story 0 as started not completed. For specifics on parameters and nuances, see: + * [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedTestTopic0Story0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + markStartedNotCompletedTestTopic0Story0Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks test topic 0's story 0 as in progress saved. For specifics on parameters and nuances, see: + * [markInProgressSavedTestTopic0Story0Exp0]. + */ + fun markInProgressSavedTestTopic0Story0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + markInProgressSavedTestTopic0Story0Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks test topic 0's story 0 as in progress not saved. For specifics on parameters and nuances, see: + * [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedTestTopic0Story0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + markInProgressNotSavedTestTopic0Story0Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks test topic 1's story 2 as started not completed. For specifics on parameters and nuances, see: + * [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedTestTopic1Story0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + markStartedNotCompletedTestTopic1Story2Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks test topic 1's story 2 as in progress saved. For specifics on parameters and nuances, see: + * [markInProgressSavedTestTopic0Story0Exp0]. + */ + fun markInProgressSavedTestTopic1Story0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + markInProgressSavedTestTopic1Story2Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks test topic 1's story 2 as in progress not saved. For specifics on parameters and nuances, see: + * [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedTestTopic1Story0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + markInProgressNotSavedTestTopic1Story2Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks test topic 0 as started not completed. For specifics on parameters and nuances, see: + * [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedTestTopic0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markStartedNotCompletedTestTopic0Story0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks test topic 0 as in progress saved. For specifics on parameters and nuances, see: + * [markInProgressSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedTestTopic0Story0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - markRecentlyPlayedTestTopic0Story0Exp0(profileId, timestampOlderThanOneWeek) + fun markInProgressSavedTestTopic0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressSavedTestTopic0Story0(profileId, timestampOlderThanOneWeek) } /** - * Marks test topic 1's story 2 as recently played. For specifics on parameters and nuances, see: - * [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks test topic 0 as in progress not saved. For specifics on parameters and nuances, see: + * [markInProgressNotSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedTestTopic1Story0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - markRecentlyPlayedTestTopic1Story2Exp0(profileId, timestampOlderThanOneWeek) + fun markInProgressNotSavedTestTopic0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressNotSavedTestTopic0Story0(profileId, timestampOlderThanOneWeek) } /** - * Marks test topic 0 as recently played. For specifics on parameters and nuances, see: - * [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks test topic 1 as started not completed. For specifics on parameters and nuances, see: + * [markStartedNotCompletedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedTestTopic0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - markRecentlyPlayedTestTopic0Story0(profileId, timestampOlderThanOneWeek) + fun markStartedNotCompletedTestTopic1(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markStartedNotCompletedTestTopic1Story0(profileId, timestampOlderThanOneWeek) } /** - * Marks test topic 1 as recently played. For specifics on parameters and nuances, see: - * [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks test topic 1 as in progress saved. For specifics on parameters and nuances, see: + * [markInProgressSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedTestTopic1(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - markRecentlyPlayedTestTopic1Story0(profileId, timestampOlderThanOneWeek) + fun markInProgressSavedTestTopic1(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressSavedTestTopic1Story0(profileId, timestampOlderThanOneWeek) } /** - * Marks all test topics as recently played. See [markCompletedTestTopic0Story0Exp0] for specifics + * Marks test topic 1 as in progress not saved. For specifics on parameters and nuances, see: + * [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedTestTopic1(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressNotSavedTestTopic1Story0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks all test topics as started not completed. See [markCompletedTestTopic0Story0Exp0] for specifics + * on the parameters passed to this method, and any other nuances. + */ + fun markStartedNotCompletedTestTopics(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markStartedNotCompletedTestTopic0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks all test topics as in progress saved. See [markCompletedTestTopic0Story0Exp0] for specifics + * on the parameters passed to this method, and any other nuances. + */ + fun markInProgressSavedTestTopics(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressSavedTestTopic0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks all test topics as in progress not saved. See [markCompletedTestTopic0Story0Exp0] for specifics * on the parameters passed to this method, and any other nuances. */ - fun markRecentlyPlayedTestTopics(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - markRecentlyPlayedTestTopic0(profileId, timestampOlderThanOneWeek) + fun markInProgressNotSavedTestTopics(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressNotSavedTestTopic0(profileId, timestampOlderThanOneWeek) } /* Ratios partial completion methods. */ /** - * Marks the first chapter of ratios story 0 as recently played. For specifics on parameters and - * nuances, see: [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the first chapter of ratios story 0 as started not completed. For specifics on parameters and + * nuances, see: [markStartedNotCompletedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedRatiosStory0Exp0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - recordRecentlyPlayedChapter( + fun markStartedNotCompletedRatiosStory0Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsStartedNotCompleted( profileId, RATIOS_TOPIC_ID, RATIOS_STORY_ID_0, @@ -419,13 +630,50 @@ class StoryProgressTestHelper @Inject constructor( } /** - * Marks the second chapter of ratios story 0 as recently played. For specifics on parameters and - * nuances, see: [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the first chapter of ratios story 0 as in progress saved. For specifics on parameters and + * nuances, see: [markInProgressSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedRatiosStory0Exp1(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + fun markInProgressSavedRatiosStory0Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsInProgressSaved( + profileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, + RATIOS_EXPLORATION_ID_0, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the first chapter of ratios story 0 as in progress not saved. For specifics on parameters and + * nuances, see: [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedRatiosStory0Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsInProgressNotSaved( + profileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, + RATIOS_EXPLORATION_ID_0, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the second chapter of ratios story 0 as started not completed. For specifics on parameters and + * nuances, see: [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedRatiosStory0Exp1( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { // Must complete the previous chapters first. markCompletedRatiosStory0Exp0(profileId, timestampOlderThanOneWeek) - recordRecentlyPlayedChapter( + recordChapterAsStartedNotCompleted( profileId, RATIOS_TOPIC_ID, RATIOS_STORY_ID_0, @@ -435,11 +683,52 @@ class StoryProgressTestHelper @Inject constructor( } /** - * Marks the first chapter of ratios story 1 as recently played. For specifics on parameters and - * nuances, see: [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the second chapter of ratios story 0 as in progress saved. For specifics on parameters and + * nuances, see: [markInProgressSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedRatiosStory1Exp0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - recordRecentlyPlayedChapter( + fun markInProgressSavedRatiosStory0Exp1( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + // Must complete the previous chapters first. + markCompletedRatiosStory0Exp0(profileId, timestampOlderThanOneWeek) + recordChapterAsInProgressSaved( + profileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, + RATIOS_EXPLORATION_ID_1, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the second chapter of ratios story 0 as in progress not saved. For specifics on parameters and + * nuances, see: [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedRatiosStory0Exp1( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + // Must complete the previous chapters first. + markCompletedRatiosStory0Exp0(profileId, timestampOlderThanOneWeek) + recordChapterAsInProgressNotSaved( + profileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, + RATIOS_EXPLORATION_ID_1, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the first chapter of ratios story 1 as started not completed. For specifics on parameters and + * nuances, see: [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedRatiosStory1Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsStartedNotCompleted( profileId, RATIOS_TOPIC_ID, RATIOS_STORY_ID_1, @@ -449,13 +738,50 @@ class StoryProgressTestHelper @Inject constructor( } /** - * Marks the second chapter of ratios story 1 as recently played. For specifics on parameters and - * nuances, see: [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the first chapter of ratios story 1 as in progress saved. For specifics on parameters and + * nuances, see: [markInProgressSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedRatiosStory1Exp1(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + fun markInProgressSavedRatiosStory1Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsInProgressSaved( + profileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_1, + RATIOS_EXPLORATION_ID_2, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the first chapter of ratios story 1 as in progress not saved. For specifics on parameters and + * nuances, see: [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedRatiosStory1Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsInProgressNotSaved( + profileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_1, + RATIOS_EXPLORATION_ID_2, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the second chapter of ratios story 1 as started not completed. For specifics on parameters and + * nuances, see: [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedRatiosStory1Exp1( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { // Must complete the previous chapters first. markCompletedRatiosStory1Exp0(profileId, timestampOlderThanOneWeek) - recordRecentlyPlayedChapter( + recordChapterAsStartedNotCompleted( profileId, RATIOS_TOPIC_ID, RATIOS_STORY_ID_1, @@ -465,40 +791,149 @@ class StoryProgressTestHelper @Inject constructor( } /** - * Marks ratios story 0 as recently played. For specifics on parameters and nuances, see: - * [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the second chapter of ratios story 1 as in progress saved. For specifics on parameters and + * nuances, see: [markInProgressSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedRatiosStory0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - markRecentlyPlayedRatiosStory0Exp0(profileId, timestampOlderThanOneWeek) + fun markInProgressSavedRatiosStory1Exp1( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + // Must complete the previous chapters first. + markCompletedRatiosStory1Exp0(profileId, timestampOlderThanOneWeek) + recordChapterAsInProgressSaved( + profileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_1, + RATIOS_EXPLORATION_ID_3, + timestampOlderThanOneWeek + ) } /** - * Marks ratios story 1 as recently played. For specifics on parameters and nuances, see: - * [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the second chapter of ratios story 1 as in progress not saved. For specifics on parameters and + * nuances, see: [markInProgressNotSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedRatiosStory1(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - markRecentlyPlayedRatiosStory1Exp0(profileId, timestampOlderThanOneWeek) + fun markInProgressNotSavedRatiosStory1Exp1( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + // Must complete the previous chapters first. + markCompletedRatiosStory1Exp0(profileId, timestampOlderThanOneWeek) + recordChapterAsInProgressNotSaved( + profileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_1, + RATIOS_EXPLORATION_ID_3, + timestampOlderThanOneWeek + ) } /** - * Marks the ratios topic as recently played. For specifics on parameters and nuances, see: - * [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks ratios story 0 as started not completed. For specifics on parameters and nuances, see: + * [markStartedNotCompletedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedRatios(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - markRecentlyPlayedRatiosStory0(profileId, timestampOlderThanOneWeek) + fun markStartedNotCompletedRatiosStory0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + markStartedNotCompletedRatiosStory0Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks ratios story 0 as in progress saved. For specifics on parameters and nuances, see: + * [markInProgressSavedTestTopic0Story0Exp0]. + */ + fun markInProgressSavedRatiosStory0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressSavedRatiosStory0Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks ratios story 0 as in progress not saved. For specifics on parameters and nuances, see: + * [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedRatiosStory0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressNotSavedRatiosStory0Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks ratios story 1 as started not completed. For specifics on parameters and nuances, see: + * [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedRatiosStory1( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + markStartedNotCompletedRatiosStory1Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks ratios story 1 as in progress saved. For specifics on parameters and nuances, see: + * [markInProgressSavedTestTopic0Story0Exp0]. + */ + fun markInProgressSavedRatiosStory1(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressSavedRatiosStory1Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks ratios story 1 as in progress not saved. For specifics on parameters and nuances, see: + * [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedRatiosStory1(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressNotSavedRatiosStory1Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks the ratios topic as started not completed. For specifics on parameters and nuances, see: + * [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedRatios(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markStartedNotCompletedRatiosStory0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks the ratios topic as in progress saved. For specifics on parameters and nuances, see: + * [markInProgressSavedTestTopic0Story0Exp0]. + */ + fun markInProgressSavedRatios(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressSavedRatiosStory0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks the ratios topic as in progress not saved. For specifics on parameters and nuances, see: + * [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedRatios(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressNotSavedRatiosStory0(profileId, timestampOlderThanOneWeek) } /* Fractions partial completion methods. */ /** - * Marks the first chapter of fractions story 0 as recently played. For specifics on parameters - * and nuances, see: [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the first chapter of fractions story 0 as started not completed. For specifics on parameters + * and nuances, see: [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedFractionsStory0Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsStartedNotCompleted( + profileId, + FRACTIONS_TOPIC_ID, + FRACTIONS_STORY_ID_0, + FRACTIONS_EXPLORATION_ID_0, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the first chapter of fractions story 0 as in progress saved. For specifics on parameters + * and nuances, see: [markInProgressSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedFractionsStory0Exp0( + fun markInProgressSavedFractionsStory0Exp0( profileId: ProfileId, timestampOlderThanOneWeek: Boolean ) { - recordRecentlyPlayedChapter( + recordChapterAsInProgressSaved( profileId, FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, @@ -508,16 +943,33 @@ class StoryProgressTestHelper @Inject constructor( } /** - * Marks the second chapter of fractions story 0 as recently played. For specifics on parameters - * and nuances, see: [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the first chapter of fractions story 0 as in progress not saved. For specifics on parameters + * and nuances, see: [markInProgressNotSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedFractionsStory0Exp1( + fun markInProgressNotSavedFractionsStory0Exp0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + recordChapterAsInProgressNotSaved( + profileId, + FRACTIONS_TOPIC_ID, + FRACTIONS_STORY_ID_0, + FRACTIONS_EXPLORATION_ID_0, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the second chapter of fractions story 0 as started not completed. For specifics on parameters + * and nuances, see: [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedFractionsStory0Exp1( profileId: ProfileId, timestampOlderThanOneWeek: Boolean ) { // Must complete the previous chapters first. markCompletedFractionsStory0Exp0(profileId, timestampOlderThanOneWeek) - recordRecentlyPlayedChapter( + recordChapterAsStartedNotCompleted( profileId, FRACTIONS_TOPIC_ID, FRACTIONS_STORY_ID_0, @@ -527,19 +979,95 @@ class StoryProgressTestHelper @Inject constructor( } /** - * Marks fractions story 0 as recently played. For specifics on parameters and nuances, see: - * [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the second chapter of fractions story 0 as in progress saved. For specifics on parameters + * and nuances, see: [markInProgressSavedTestTopic0Story0Exp0]. + */ + fun markInProgressSavedFractionsStory0Exp1( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + // Must complete the previous chapters first. + markCompletedFractionsStory0Exp0(profileId, timestampOlderThanOneWeek) + recordChapterAsInProgressSaved( + profileId, + FRACTIONS_TOPIC_ID, + FRACTIONS_STORY_ID_0, + FRACTIONS_EXPLORATION_ID_1, + timestampOlderThanOneWeek + ) + } + + /** + * Marks the second chapter of fractions story 0 as in progress not saved. For specifics on parameters + * and nuances, see: [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedFractionsStory0Exp1( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + // Must complete the previous chapters first. + markCompletedFractionsStory0Exp0(profileId, timestampOlderThanOneWeek) + recordChapterAsInProgressNotSaved( + profileId, + FRACTIONS_TOPIC_ID, + FRACTIONS_STORY_ID_0, + FRACTIONS_EXPLORATION_ID_1, + timestampOlderThanOneWeek + ) + } + + /** + * Marks fractions story 0 as started not completed. For specifics on parameters and nuances, see: + * [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markStartedNotCompletedFractionsStory0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + markStartedNotCompletedFractionsStory0Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks fractions story 0 as in progress saved. For specifics on parameters and nuances, see: + * [markInProgressSavedTestTopic0Story0Exp0]. + */ + fun markInProgressSavedFractionsStory0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressSavedFractionsStory0Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks fractions story 0 as in progress not saved. For specifics on parameters and nuances, see: + * [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedFractionsStory0( + profileId: ProfileId, + timestampOlderThanOneWeek: Boolean + ) { + markInProgressNotSavedFractionsStory0Exp0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks the fractions topic as started not completed. For specifics on parameters and nuances, see: + * [markStartedNotCompletedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedFractionsStory0(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - markRecentlyPlayedFractionsStory0Exp0(profileId, timestampOlderThanOneWeek) + fun markStartedNotCompletedFractions(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markStartedNotCompletedFractionsStory0(profileId, timestampOlderThanOneWeek) } /** - * Marks the fractions topic as recently played. For specifics on parameters and nuances, see: - * [markRecentlyPlayedTestTopic0Story0Exp0]. + * Marks the fractions topic as in progress saved. For specifics on parameters and nuances, see: + * [markInProgressSavedTestTopic0Story0Exp0]. */ - fun markRecentlyPlayedFractions(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - markRecentlyPlayedFractionsStory0(profileId, timestampOlderThanOneWeek) + fun markInProgressSavedFractions(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressSavedFractionsStory0(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks the fractions topic as in progress not saved. For specifics on parameters and nuances, see: + * [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markInProgressNotSavedFractions(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressNotSavedFractionsStory0(profileId, timestampOlderThanOneWeek) } /* Cross-topics functions. */ @@ -556,13 +1084,46 @@ class StoryProgressTestHelper @Inject constructor( /** * Marks all lessons as recently played. For specifics on parameters and nuances, see: - * [markRecentlyPlayedTestTopic0Story0Exp0]. + * [markStartedNotCompletedTestTopic0Story0Exp0]. + */ + fun markAllTopicsAsStartedNotCompleted(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markStartedNotCompletedTestTopic0(profileId, timestampOlderThanOneWeek) + markStartedNotCompletedTestTopic1(profileId, timestampOlderThanOneWeek) + markStartedNotCompletedRatios(profileId, timestampOlderThanOneWeek) + markStartedNotCompletedFractions(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks all lessons as recently played. For specifics on parameters and nuances, see: + * [markInProgressSavedTestTopic0Story0Exp0]. + */ + fun markAllTopicsAsInProgressSaved(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressSavedTestTopic0(profileId, timestampOlderThanOneWeek) + markInProgressSavedTestTopic1(profileId, timestampOlderThanOneWeek) + markInProgressSavedRatios(profileId, timestampOlderThanOneWeek) + markInProgressSavedFractions(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks all lessons as recently played. For specifics on parameters and nuances, see: + * [markInProgressNotSavedTestTopic0Story0Exp0]. + */ + fun markAllTopicsAsInProgressNotSaved(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { + markInProgressNotSavedTestTopic0(profileId, timestampOlderThanOneWeek) + markInProgressNotSavedTestTopic1(profileId, timestampOlderThanOneWeek) + markInProgressNotSavedRatios(profileId, timestampOlderThanOneWeek) + markInProgressNotSavedFractions(profileId, timestampOlderThanOneWeek) + } + + /** + * Marks all lessons as recently played. For specifics on parameters and nuances, see: + * [markInProgressSavedTestTopic0Story0Exp0]. */ fun markAllTopicsAsRecentlyPlayed(profileId: ProfileId, timestampOlderThanOneWeek: Boolean) { - markRecentlyPlayedTestTopic0(profileId, timestampOlderThanOneWeek) - markRecentlyPlayedTestTopic1(profileId, timestampOlderThanOneWeek) - markRecentlyPlayedRatios(profileId, timestampOlderThanOneWeek) - markRecentlyPlayedFractions(profileId, timestampOlderThanOneWeek) + markStartedNotCompletedTestTopic0(profileId, timestampOlderThanOneWeek) + markInProgressSavedTestTopic1(profileId, timestampOlderThanOneWeek) + markInProgressSavedRatios(profileId, timestampOlderThanOneWeek) + markInProgressNotSavedFractions(profileId, timestampOlderThanOneWeek) } private fun recordCompletedChapter( @@ -583,7 +1144,43 @@ class StoryProgressTestHelper @Inject constructor( verifyProviderFinishesWithSuccess(resultProvider) } - private fun recordRecentlyPlayedChapter( + private fun recordChapterAsStartedNotCompleted( + profileId: ProfileId, + topicId: String, + storyId: String, + explorationId: String, + timestampOlderThanOneWeek: Boolean + ) { + primeClockForRecordingProgress() + val resultProvider = storyProgressController.recordChapterAsStartedNotCompleted( + profileId, + topicId, + storyId, + explorationId, + lastPlayedTimestamp = computeTimestamp(timestampOlderThanOneWeek) + ) + verifyProviderFinishesWithSuccess(resultProvider) + } + + private fun recordChapterAsInProgressNotSaved( + profileId: ProfileId, + topicId: String, + storyId: String, + explorationId: String, + timestampOlderThanOneWeek: Boolean + ) { + primeClockForRecordingProgress() + val resultProvider = storyProgressController.recordChapterAsInProgressNotSaved( + profileId, + topicId, + storyId, + explorationId, + lastPlayedTimestamp = computeTimestamp(timestampOlderThanOneWeek) + ) + verifyProviderFinishesWithSuccess(resultProvider) + } + + private fun recordChapterAsInProgressSaved( profileId: ProfileId, topicId: String, storyId: String, @@ -591,7 +1188,7 @@ class StoryProgressTestHelper @Inject constructor( timestampOlderThanOneWeek: Boolean ) { primeClockForRecordingProgress() - val resultProvider = storyProgressController.recordRecentlyPlayedChapter( + val resultProvider = storyProgressController.recordChapterAsInProgressSaved( profileId, topicId, storyId, diff --git a/testing/src/test/java/org/oppia/android/testing/story/StoryProgressTestHelperTest.kt b/testing/src/test/java/org/oppia/android/testing/story/StoryProgressTestHelperTest.kt index 564540a656e..0fa879f21e3 100644 --- a/testing/src/test/java/org/oppia/android/testing/story/StoryProgressTestHelperTest.kt +++ b/testing/src/test/java/org/oppia/android/testing/story/StoryProgressTestHelperTest.kt @@ -549,8 +549,8 @@ class StoryProgressTestHelperTest { /* Test topic chapter started tests. */ @Test - fun testMarkChapterRecentlyPlayed_testTopic0_story0_exp2_chapterIsStarted() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + fun testMarkAsStartedNotCompleted_testTopic0_story0_exp2_chapterIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedTestTopic0Story0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -558,12 +558,62 @@ class StoryProgressTestHelperTest { val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) val story0 = testTopic0.getStory(TEST_STORY_ID_0) val exp2 = story0.getChapter(TEST_EXPLORATION_ID_2) - assertThat(exp2.isStarted()).isTrue() + assertThat(exp2.isStartedNotCompleted()).isTrue() } @Test - fun testMarkChapterRecentlyPlayed_testTopic0_story0_exp2_story0IsNotDone() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + fun testMarkAsInProgressSaved_testTopic0_story0_exp2_chapterIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + val exp2 = story0.getChapter(TEST_EXPLORATION_ID_2) + assertThat(exp2.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkAsInProgressNotSaved_testTopic0_story0_exp2_chapterIsInProgressSaved() { + storyProgressTestHelper.markInProgressNotSavedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + val exp2 = story0.getChapter(TEST_EXPLORATION_ID_2) + assertThat(exp2.isInProgressNotSaved()).isTrue() + } + + @Test + fun testMarkChapterAsStartedNotCompleted_testTopic0_story0_exp2_story0IsNotDone() { + storyProgressTestHelper.markStartedNotCompletedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressSaved_testTopic0_story0_exp2_story0IsNotDone() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_testTopic0_story0_exp2_story0IsNotDone() { + storyProgressTestHelper.markInProgressNotSavedTestTopic0Story0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -574,8 +624,34 @@ class StoryProgressTestHelperTest { } @Test - fun testMarkChapterRecentlyPlayed_testTopic0_story0_exp5_chapterIsStarted() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp1( + fun testMarkChapterAsStartedNotCompleted_testTopic0_story0_exp5_chapterIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedTestTopic0Story0Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + val exp5 = story0.getChapter(TEST_EXPLORATION_ID_5) + assertThat(exp5.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkInProgressSaved_testTopic0_story0_exp5_chapterIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + val exp5 = story0.getChapter(TEST_EXPLORATION_ID_5) + assertThat(exp5.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_testTopic0_story0_exp5_chapterIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedTestTopic0Story0Exp1( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -583,12 +659,36 @@ class StoryProgressTestHelperTest { val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) val story0 = testTopic0.getStory(TEST_STORY_ID_0) val exp5 = story0.getChapter(TEST_EXPLORATION_ID_5) - assertThat(exp5.isStarted()).isTrue() + assertThat(exp5.isInProgressNotSaved()).isTrue() + } + + @Test + fun markStartedNotCompletedForTestTopic0Story0Exp5() { + storyProgressTestHelper.markStartedNotCompletedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() + } + + @Test + fun markInProgressSavedForTestTopic0Story0Exp5() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() } @Test - fun markRecentlyPlayedForTestTopic0Story0Exp5() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + fun markInProgressNotSavedForTestTopic0Story0Exp5() { + storyProgressTestHelper.markInProgressNotSavedTestTopic0Story0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -599,8 +699,34 @@ class StoryProgressTestHelperTest { } @Test - fun testMarkChapterRecentlyPlayed_testTopic1_story2_exp4_chapterIsStarted() { - storyProgressTestHelper.markRecentlyPlayedTestTopic1Story2Exp0( + fun testMarkChapterAsStartedNotCompleted_testTopic1_story2_exp4_chapterIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedTestTopic1Story2Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + val story2 = testTopic1.getStory(TEST_STORY_ID_2) + val exp4 = story2.getChapter(TEST_EXPLORATION_ID_4) + assertThat(exp4.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressSaved_testTopic1_story2_exp4_chapterIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedTestTopic1Story2Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + val story2 = testTopic1.getStory(TEST_STORY_ID_2) + val exp4 = story2.getChapter(TEST_EXPLORATION_ID_4) + assertThat(exp4.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_testTopic1_story2_exp4_chapterIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedTestTopic1Story2Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -608,12 +734,36 @@ class StoryProgressTestHelperTest { val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) val story2 = testTopic1.getStory(TEST_STORY_ID_2) val exp4 = story2.getChapter(TEST_EXPLORATION_ID_4) - assertThat(exp4.isStarted()).isTrue() + assertThat(exp4.isInProgressNotSaved()).isTrue() + } + + @Test + fun testMarkChapterAsStartedNotCompleted_testTopic1_story2_exp4_story2IsNotDone() { + storyProgressTestHelper.markStartedNotCompletedTestTopic1Story2Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + val story2 = testTopic1.getStory(TEST_STORY_ID_2) + assertThat(story2.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressSaved_testTopic1_story2_exp4_story2IsNotDone() { + storyProgressTestHelper.markInProgressSavedTestTopic1Story2Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + val story2 = testTopic1.getStory(TEST_STORY_ID_2) + assertThat(story2.isCompleted()).isFalse() } @Test - fun testMarkChapterRecentlyPlayed_testTopic1_story2_exp4_story2IsNotDone() { - storyProgressTestHelper.markRecentlyPlayedTestTopic1Story2Exp0( + fun testMarkChapterAsInProgressNotSaved_testTopic1_story2_exp4_story2IsNotDone() { + storyProgressTestHelper.markInProgressNotSavedTestTopic1Story2Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -626,20 +776,66 @@ class StoryProgressTestHelperTest { /* Test topic/story started tests. */ @Test - fun testMarkStoryRecentlyPlayed_testTopic0_story0_storyIsStarted() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0( + fun testMarkStoryAsStartedNotCompleted_testTopic0_story0_storyIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedTestTopic0Story0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + assertThat(story0.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkStoryAsInProgressSaved_testTopic0_story0_storyIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + assertThat(story0.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkStoryAsInProgressNotSaved_testTopic0_story0_storyIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedTestTopic0Story0( profileId = profileId0, timestampOlderThanOneWeek = false ) val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) val story0 = testTopic0.getStory(TEST_STORY_ID_0) - assertThat(story0.isStarted()).isTrue() + assertThat(story0.isInProgressNotSaved()).isTrue() + } + + @Test + fun testMarkStoryAsStartedNotCompleted_testTopic0_story0_topicIsNotDone() { + storyProgressTestHelper.markStartedNotCompletedTestTopic0Story0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + assertThat(testTopic0.isCompleted()).isFalse() + } + + @Test + fun testMarkStoryAsInProgressSaved_testTopic0_story0_topicIsNotDone() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + assertThat(testTopic0.isCompleted()).isFalse() } @Test - fun testMarkStoryRecentlyPlayed_testTopic0_story0_topicIsNotDone() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0( + fun testMarkStoryAsInProgressNotSaved_testTopic0_story0_topicIsNotDone() { + storyProgressTestHelper.markInProgressNotSavedTestTopic0Story0( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -649,20 +845,66 @@ class StoryProgressTestHelperTest { } @Test - fun testMarkStoryRecentlyPlayed_testTopic1_story2_storyIsStarted() { - storyProgressTestHelper.markRecentlyPlayedTestTopic1Story0( + fun testMarkStoryAsStartedNotCompleted_testTopic1_story2_storyIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedTestTopic1Story0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + val story2 = testTopic1.getStory(TEST_STORY_ID_2) + assertThat(story2.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkStoryAsInProgressSaved_testTopic1_story2_storyIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedTestTopic1Story0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + val story2 = testTopic1.getStory(TEST_STORY_ID_2) + assertThat(story2.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkStoryAsInProgressNotSaved_testTopic1_story2_storyIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedTestTopic1Story0( profileId = profileId0, timestampOlderThanOneWeek = false ) val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) val story2 = testTopic1.getStory(TEST_STORY_ID_2) - assertThat(story2.isStarted()).isTrue() + assertThat(story2.isInProgressNotSaved()).isTrue() + } + + @Test + fun testMarkStoryAsStartedNotCompleted_testTopic1_story2_topicIsNotDone() { + storyProgressTestHelper.markStartedNotCompletedTestTopic1Story0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + assertThat(testTopic1.isCompleted()).isFalse() + } + + @Test + fun testMarkStoryAsInProgressSaved_testTopic1_story2_topicIsNotDone() { + storyProgressTestHelper.markInProgressSavedTestTopic1Story0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + assertThat(testTopic1.isCompleted()).isFalse() } @Test - fun testMarkStoryRecentlyPlayed_testTopic1_story2_topicIsNotDone() { - storyProgressTestHelper.markRecentlyPlayedTestTopic1Story0( + fun testMarkStoryAsInProgressNotSaved_testTopic1_story2_topicIsNotDone() { + storyProgressTestHelper.markInProgressNotSavedTestTopic1Story0( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -672,34 +914,118 @@ class StoryProgressTestHelperTest { } @Test - fun testMarkTopicRecentlyPlayed_testTopic0_topicIsStarted() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0( + fun testMarkTopicAsStartedNotCompleted_testTopic0_topicIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedTestTopic0( profileId = profileId0, timestampOlderThanOneWeek = false ) val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) - assertThat(testTopic0.isStarted()).isTrue() - assertThat(testTopic1.isStarted()).isFalse() + assertThat(testTopic0.isStartedNotCompleted()).isTrue() + assertThat(testTopic1.isStartedNotCompleted()).isFalse() } @Test - fun testMarkTopicRecentlyPlayed_testTopic1_topicIsStarted() { - storyProgressTestHelper.markRecentlyPlayedTestTopic1( + fun testMarkTopicAsInProgressSaved_testTopic0_topicIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedTestTopic0( profileId = profileId0, timestampOlderThanOneWeek = false ) val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) - assertThat(testTopic0.isStarted()).isFalse() - assertThat(testTopic1.isStarted()).isTrue() + assertThat(testTopic0.isInProgressSaved()).isTrue() + assertThat(testTopic1.isInProgressSaved()).isFalse() } @Test - fun testMarkTopicsRecentlyPlayed_testTopics_oneTopicIsStarted() { - storyProgressTestHelper.markRecentlyPlayedTestTopics( + fun testMarkTopicAsInProgressNotSaved_testTopic0_topicIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedTestTopic0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + assertThat(testTopic0.isInProgressNotSaved()).isTrue() + assertThat(testTopic1.isInProgressNotSaved()).isFalse() + } + + @Test + fun testMarkTopicAsStartedNotCompleted_testTopic1_topicIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedTestTopic1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + assertThat(testTopic0.isStartedNotCompleted()).isFalse() + assertThat(testTopic1.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkTopicAsStartedNotCompleted_testTopic1_topicIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedTestTopic1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + assertThat(testTopic0.isInProgressSaved()).isFalse() + assertThat(testTopic1.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkTopicAsInProgressNotSaved_testTopic1_topicIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedTestTopic1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + assertThat(testTopic0.isInProgressNotSaved()).isFalse() + assertThat(testTopic1.isInProgressNotSaved()).isTrue() + } + + @Test + fun testMarkTopicsAsStartedNotCompleted_testTopics_oneTopicIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedTestTopics( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + // At least one of the topics is started. + assertThat(listOf(testTopic0, testTopic1).any { it.isStartedNotCompleted() }).isTrue() + // But neither is completed. + assertThat(testTopic0.isCompleted()).isFalse() + assertThat(testTopic1.isCompleted()).isFalse() + } + + @Test + fun testMarkTopicsAsInProgressSaved_testTopics_oneTopicIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedTestTopics( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + // At least one of the topics is started. + assertThat(listOf(testTopic0, testTopic1).any { it.isInProgressSaved() }).isTrue() + // But neither is completed. + assertThat(testTopic0.isCompleted()).isFalse() + assertThat(testTopic1.isCompleted()).isFalse() + } + + @Test + fun testMarkTopicsAsInProgressNotSaved_testTopics_oneTopicIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedTestTopics( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -707,7 +1033,7 @@ class StoryProgressTestHelperTest { val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) // At least one of the topics is started. - assertThat(listOf(testTopic0, testTopic1).any { it.isStarted() }).isTrue() + assertThat(listOf(testTopic0, testTopic1).any { it.isInProgressNotSaved() }).isTrue() // But neither is completed. assertThat(testTopic0.isCompleted()).isFalse() assertThat(testTopic1.isCompleted()).isFalse() @@ -716,8 +1042,8 @@ class StoryProgressTestHelperTest { /* Ratios chapter started tests. */ @Test - fun testMarkChapterRecentlyPlayed_ratiosTopic_story0_exp0_chapterIsStarted() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + fun testMarkChapterAsStartedNotCompleted_ratiosTopic_story0_exp0_chapterIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -725,243 +1051,733 @@ class StoryProgressTestHelperTest { val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) val exp0 = story0.getChapter(RATIOS_EXPLORATION_ID_0) - assertThat(exp0.isStarted()).isTrue() + assertThat(exp0.isStartedNotCompleted()).isTrue() } @Test - fun testMarkChapterRecentlyPlayed_ratiosTopic_story0_exp0_story0IsNotDone() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp0( + fun testMarkChapterAsInProgressSaved_ratiosTopic_story0_exp0_chapterIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) - assertThat(story0.isCompleted()).isFalse() + val exp0 = story0.getChapter(RATIOS_EXPLORATION_ID_0) + assertThat(exp0.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_ratiosTopic_story0_exp0_chapterIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + val exp0 = story0.getChapter(RATIOS_EXPLORATION_ID_0) + assertThat(exp0.isInProgressNotSaved()).isTrue() + } + + @Test + fun testMarkChapterAsStartedNotCompleted_ratiosTopic_story0_exp0_story0IsNotDone() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressSaved_ratiosTopic_story0_exp0_story0IsNotDone() { + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_ratiosTopic_story0_exp0_story0IsNotDone() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsStartedNotCompleted_ratiosTopic_story0_exp1_chapterIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory0Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + val exp1 = story0.getChapter(RATIOS_EXPLORATION_ID_1) + assertThat(exp1.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressSaved_ratiosTopic_story0_exp1_chapterIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + val exp1 = story0.getChapter(RATIOS_EXPLORATION_ID_1) + assertThat(exp1.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_ratiosTopic_story0_exp1_chapterIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory0Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + val exp1 = story0.getChapter(RATIOS_EXPLORATION_ID_1) + assertThat(exp1.isInProgressNotSaved()).isTrue() + } + + @Test + fun testMarkChapterAsStartedNotCompleted_ratiosTopic_story0_exp1_story0IsNotDone() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory0Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressSaved_ratiosTopic_story0_exp1_story0IsNotDone() { + storyProgressTestHelper.markInProgressSavedRatiosStory0Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_ratiosTopic_story0_exp1_story0IsNotDone() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory0Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsStartedNotCompleted_ratiosTopic_story1_exp2_chapterIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory1Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + val exp2 = story1.getChapter(RATIOS_EXPLORATION_ID_2) + assertThat(exp2.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressSaved_ratiosTopic_story1_exp2_chapterIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedRatiosStory1Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + val exp2 = story1.getChapter(RATIOS_EXPLORATION_ID_2) + assertThat(exp2.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_ratiosTopic_story1_exp2_chapterIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory1Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + val exp2 = story1.getChapter(RATIOS_EXPLORATION_ID_2) + assertThat(exp2.isInProgressNotSaved()).isTrue() + } + + @Test + fun testMarkChapterAsStartedNotCompleted_ratiosTopic_story1_exp2_story1IsNotDone() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory1Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story1.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressSaved_ratiosTopic_story1_exp2_story1IsNotDone() { + storyProgressTestHelper.markInProgressSavedRatiosStory1Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story1.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_ratiosTopic_story1_exp2_story1IsNotDone() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory1Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story1.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsStartedNotCompleted_ratiosTopic_story1_exp3_chapterIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory1Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + val exp3 = story1.getChapter(RATIOS_EXPLORATION_ID_3) + assertThat(exp3.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressSaved_ratiosTopic_story1_exp3_chapterIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedRatiosStory1Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + val exp3 = story1.getChapter(RATIOS_EXPLORATION_ID_3) + assertThat(exp3.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_ratiosTopic_story1_exp3_chapterIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory1Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + val exp3 = story1.getChapter(RATIOS_EXPLORATION_ID_3) + assertThat(exp3.isInProgressNotSaved()).isTrue() + } + + @Test + fun testMarkChapterAsStartedNotCompleted_ratiosTopic_story1_exp3_story1IsNotDone() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory1Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story1.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressSaved_ratiosTopic_story1_exp3_story1IsNotDone() { + storyProgressTestHelper.markInProgressSavedRatiosStory1Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story1.isCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_ratiosTopic_story1_exp3_story1IsNotDone() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory1Exp1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story1.isCompleted()).isFalse() + } + + /* Ratios topic/story started tests. */ + + @Test + fun testMarkStoryAsStartedNotCompleted_ratiosTopic_story0_storyIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story0.isStartedNotCompleted()).isTrue() + assertThat(story1.isStartedNotCompleted()).isFalse() + } + + @Test + fun testMarkStoryAsInProgressSaved_ratiosTopic_story0_storyIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedRatiosStory0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story0.isInProgressSaved()).isTrue() + assertThat(story1.isInProgressSaved()).isFalse() + } + + @Test + fun testMarkStoryAsInProgressNotSaved_ratiosTopic_story0_storyIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story0.isInProgressNotSaved()).isTrue() + assertThat(story1.isInProgressNotSaved()).isFalse() + } + + @Test + fun testMarkStoryAsStartedNotCompleted_ratiosTopic_story0_topicIsNotDone() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + assertThat(ratiosTopic.isCompleted()).isFalse() + } + + @Test + fun testMarkStoryAsInProgressSaved_ratiosTopic_story0_topicIsNotDone() { + storyProgressTestHelper.markInProgressSavedRatiosStory0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + assertThat(ratiosTopic.isCompleted()).isFalse() + } + + @Test + fun testMarkStoryAsInProgressNotSaved_ratiosTopic_story0_topicIsNotDone() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + assertThat(ratiosTopic.isCompleted()).isFalse() + } + + @Test + fun testMarkStoryAsStartedNotCompleted_ratiosTopic_story1_storyIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story0.isStartedNotCompleted()).isFalse() + assertThat(story1.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkStoryAsInProgressSaved_ratiosTopic_story1_storyIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedRatiosStory1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story0.isInProgressSaved()).isFalse() + assertThat(story1.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkStoryAsInProgressNotSaved_ratiosTopic_story1_storyIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) + assertThat(story0.isInProgressNotSaved()).isFalse() + assertThat(story1.isInProgressNotSaved()).isTrue() + } + + @Test + fun testMarkStoryAsStartedNotCompleted_ratiosTopic_story1_topicIsNotDone() { + storyProgressTestHelper.markStartedNotCompletedRatiosStory1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + assertThat(ratiosTopic.isCompleted()).isFalse() + } + + @Test + fun testMarkStoryAsInProgressSaved_ratiosTopic_story1_topicIsNotDone() { + storyProgressTestHelper.markInProgressSavedRatiosStory1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + assertThat(ratiosTopic.isCompleted()).isFalse() + } + + @Test + fun testMarkStoryAsInProgressNotSaved_ratiosTopic_story1_topicIsNotDone() { + storyProgressTestHelper.markInProgressNotSavedRatiosStory1( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + assertThat(ratiosTopic.isCompleted()).isFalse() + } + + @Test + fun testMarkTopicAsStartedNotCompleted_ratiosTopic_topicIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedRatios( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + assertThat(ratiosTopic.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkTopicAsInProgressSaved_ratiosTopic_topicIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedRatios( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + assertThat(ratiosTopic.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkTopicAsInProgressNotSaved_ratiosTopic_topicIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedRatios( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + assertThat(ratiosTopic.isInProgressNotSaved()).isTrue() + } + + /* Fractions topic/story/chapter started tests. */ + + @Test + fun testMarkChapterStartedNotCompleted_fractionsTopic_story0_exp0_chapterIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedFractionsStory0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + val exp0 = story0.getChapter(FRACTIONS_EXPLORATION_ID_0) + assertThat(exp0.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressSaved_fractionsTopic_story0_exp0_chapterIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + val exp0 = story0.getChapter(FRACTIONS_EXPLORATION_ID_0) + assertThat(exp0.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_fractionsTopic_story0_exp0_chapterIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedFractionsStory0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + val exp0 = story0.getChapter(FRACTIONS_EXPLORATION_ID_0) + assertThat(exp0.isInProgressNotSaved()).isTrue() } @Test - fun testMarkChapterRecentlyPlayed_ratiosTopic_story0_exp1_chapterIsStarted() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp1( + fun testMarkChapterAsStartedNotCompleted_fractionsTopic_story0_exp0_story0IsNotDone() { + storyProgressTestHelper.markStartedNotCompletedFractionsStory0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) - val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) - val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) - val exp1 = story0.getChapter(RATIOS_EXPLORATION_ID_1) - assertThat(exp1.isStarted()).isTrue() + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() } @Test - fun testMarkChapterRecentlyPlayed_ratiosTopic_story0_exp1_story0IsNotDone() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory0Exp1( + fun testMarkChapterAsInProgressSaved_fractionsTopic_story0_exp0_story0IsNotDone() { + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) - val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) - val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) assertThat(story0.isCompleted()).isFalse() } @Test - fun testMarkChapterRecentlyPlayed_ratiosTopic_story1_exp2_chapterIsStarted() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory1Exp0( + fun testMarkChapterAsInProgressNotSaved_fractionsTopic_story0_exp0_story0IsNotDone() { + storyProgressTestHelper.markInProgressNotSavedFractionsStory0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) - val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) - val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) - val exp2 = story1.getChapter(RATIOS_EXPLORATION_ID_2) - assertThat(exp2.isStarted()).isTrue() + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() } @Test - fun testMarkChapterRecentlyPlayed_ratiosTopic_story1_exp2_story1IsNotDone() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory1Exp0( + fun testMarkChapterStartedNotCompleted_fractionsTopic_story0_exp1_chapterIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedFractionsStory0Exp1( profileId = profileId0, timestampOlderThanOneWeek = false ) - val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) - val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) - assertThat(story1.isCompleted()).isFalse() + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + val exp1 = story0.getChapter(FRACTIONS_EXPLORATION_ID_1) + assertThat(exp1.isStartedNotCompleted()).isTrue() } @Test - fun testMarkChapterRecentlyPlayed_ratiosTopic_story1_exp3_chapterIsStarted() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory1Exp1( + fun testMarkChapterAsInProgressSaved_fractionsTopic_story0_exp1_chapterIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp1( profileId = profileId0, timestampOlderThanOneWeek = false ) - val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) - val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) - val exp3 = story1.getChapter(RATIOS_EXPLORATION_ID_3) - assertThat(exp3.isStarted()).isTrue() + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + val exp1 = story0.getChapter(FRACTIONS_EXPLORATION_ID_1) + assertThat(exp1.isInProgressSaved()).isTrue() } @Test - fun testMarkChapterRecentlyPlayed_ratiosTopic_story1_exp3_story1IsNotDone() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory1Exp1( + fun testMarkChapterAsInProgressNotSaved_fractionsTopic_story0_exp1_chapterIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedFractionsStory0Exp1( profileId = profileId0, timestampOlderThanOneWeek = false ) - val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) - val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) - assertThat(story1.isCompleted()).isFalse() + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + val exp1 = story0.getChapter(FRACTIONS_EXPLORATION_ID_1) + assertThat(exp1.isInProgressNotSaved()).isTrue() } - /* Ratios topic/story started tests. */ - @Test - fun testMarkStoryRecentlyPlayed_ratiosTopic_story0_storyIsStarted() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory0( + fun testMarkChapterAsStartedNotCompleted_fractionsTopic_story0_exp1_story0IsNotDone() { + storyProgressTestHelper.markStartedNotCompletedFractionsStory0Exp1( profileId = profileId0, timestampOlderThanOneWeek = false ) - val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) - val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) - val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) - assertThat(story0.isStarted()).isTrue() - assertThat(story1.isStarted()).isFalse() + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() } @Test - fun testMarkStoryRecentlyPlayed_ratiosTopic_story0_topicIsNotDone() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory0( + fun testMarkChapterAsInProgressSaved_fractionsTopic_story0_exp1_story0IsNotDone() { + storyProgressTestHelper.markInProgressSavedFractionsStory0Exp1( profileId = profileId0, timestampOlderThanOneWeek = false ) - val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) - assertThat(ratiosTopic.isCompleted()).isFalse() + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() } @Test - fun testMarkStoryRecentlyPlayed_ratiosTopic_story1_storyIsStarted() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory1( + fun testMarkChapterAsInProgressNotSaved_fractionsTopic_story0_exp1_story0IsNotDone() { + storyProgressTestHelper.markInProgressNotSavedFractionsStory0Exp1( profileId = profileId0, timestampOlderThanOneWeek = false ) - val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) - val story0 = ratiosTopic.getStory(RATIOS_STORY_ID_0) - val story1 = ratiosTopic.getStory(RATIOS_STORY_ID_1) - assertThat(story0.isStarted()).isFalse() - assertThat(story1.isStarted()).isTrue() + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + assertThat(story0.isCompleted()).isFalse() } @Test - fun testMarkStoryRecentlyPlayed_ratiosTopic_story1_topicIsNotDone() { - storyProgressTestHelper.markRecentlyPlayedRatiosStory1( + fun testMarkStoryAsStartedNotCompleted_fractionsTopic_story0_storyIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedFractionsStory0( profileId = profileId0, timestampOlderThanOneWeek = false ) - val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) - assertThat(ratiosTopic.isCompleted()).isFalse() + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + assertThat(story0.isStartedNotCompleted()).isTrue() } @Test - fun testMarkTopicRecentlyPlayed_ratiosTopic_topicIsStarted() { - storyProgressTestHelper.markRecentlyPlayedRatios( + fun testMarkStoryAsInProgressSaved_fractionsTopic_story0_storyIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedFractionsStory0( profileId = profileId0, timestampOlderThanOneWeek = false ) - val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) - assertThat(ratiosTopic.isStarted()).isTrue() + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) + assertThat(story0.isInProgressSaved()).isTrue() } - /* Fractions topic/story/chapter started tests. */ - @Test - fun testMarkChapterRecentlyPlayed_fractionsTopic_story0_exp0_chapterIsStarted() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + fun testMarkStoryAsInProgressNotSaved_fractionsTopic_story0_storyIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedFractionsStory0( profileId = profileId0, timestampOlderThanOneWeek = false ) val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) - val exp0 = story0.getChapter(FRACTIONS_EXPLORATION_ID_0) - assertThat(exp0.isStarted()).isTrue() + assertThat(story0.isInProgressNotSaved()).isTrue() } @Test - fun testMarkChapterRecentlyPlayed_fractionsTopic_story0_exp0_story0IsNotDone() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp0( + fun testMarkStoryAsStartedNotCompleted_fractionsTopic_story0_topicIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedFractionsStory0( profileId = profileId0, timestampOlderThanOneWeek = false ) val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) - val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) - assertThat(story0.isCompleted()).isFalse() + assertThat(fractionsTopic.isStartedNotCompleted()).isTrue() } @Test - fun testMarkChapterRecentlyPlayed_fractionsTopic_story0_exp1_chapterIsStarted() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp1( + fun testMarkStoryAsInProgressSaved_fractionsTopic_story0_topicIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedFractionsStory0( profileId = profileId0, timestampOlderThanOneWeek = false ) val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) - val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) - val exp1 = story0.getChapter(FRACTIONS_EXPLORATION_ID_1) - assertThat(exp1.isStarted()).isTrue() + assertThat(fractionsTopic.isInProgressSaved()).isTrue() } @Test - fun testMarkChapterRecentlyPlayed_fractionsTopic_story0_exp1_story0IsNotDone() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0Exp1( + fun testMarkStoryAsInProgressNotSaved_fractionsTopic_story0_topicIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedFractionsStory0( profileId = profileId0, timestampOlderThanOneWeek = false ) val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) - val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) - assertThat(story0.isCompleted()).isFalse() + assertThat(fractionsTopic.isInProgressNotSaved()).isTrue() } @Test - fun testMarkStoryRecentlyPlayed_fractionsTopic_story0_storyIsStarted() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0( + fun testMarkTopicAsStartedNotCompleted_fractionsTopic_topicIsStartedNotCompleted() { + storyProgressTestHelper.markStartedNotCompletedFractions( profileId = profileId0, timestampOlderThanOneWeek = false ) val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) - val story0 = fractionsTopic.getStory(FRACTIONS_STORY_ID_0) - assertThat(story0.isStarted()).isTrue() + assertThat(fractionsTopic.isStartedNotCompleted()).isTrue() } @Test - fun testMarkStoryRecentlyPlayed_fractionsTopic_story0_topicIsStarted() { - storyProgressTestHelper.markRecentlyPlayedFractionsStory0( + fun testMarkTopicAsInProgressSaved_fractionsTopic_topicIsInProgressSaved() { + storyProgressTestHelper.markInProgressSavedFractions( profileId = profileId0, timestampOlderThanOneWeek = false ) val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) - assertThat(fractionsTopic.isStarted()).isTrue() + assertThat(fractionsTopic.isInProgressSaved()).isTrue() } @Test - fun testMarkTopicRecentlyPlayed_fractionsTopic_topicIsStarted() { - storyProgressTestHelper.markRecentlyPlayedFractions( + fun testMarkTopicAsInProgressNotSaved_fractionsTopic_topicIsInProgressNotSaved() { + storyProgressTestHelper.markInProgressNotSavedFractions( profileId = profileId0, timestampOlderThanOneWeek = false ) val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) - assertThat(fractionsTopic.isStarted()).isTrue() + assertThat(fractionsTopic.isInProgressNotSaved()).isTrue() } /* Specific state & cross-topic tests. */ @@ -980,7 +1796,61 @@ class StoryProgressTestHelperTest { } @Test - fun testMarkAllTopicsRecentlyPlayed_allTopicsAreStarted() { + fun testMarkAllTopicsAsStartedNotCompleted_allTopicsAreStarted() { + storyProgressTestHelper.markAllTopicsAsStartedNotCompleted( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + + assertThat(testTopic0.isStartedNotCompleted()).isTrue() + assertThat(testTopic1.isStartedNotCompleted()).isTrue() + assertThat(ratiosTopic.isStartedNotCompleted()).isTrue() + assertThat(fractionsTopic.isStartedNotCompleted()).isTrue() + } + + @Test + fun testMarkAllTopicsAsInProgressSaved_allTopicsAreInProgressSaved() { + storyProgressTestHelper.markAllTopicsAsInProgressSaved( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + + assertThat(testTopic0.isInProgressSaved()).isTrue() + assertThat(testTopic1.isInProgressSaved()).isTrue() + assertThat(ratiosTopic.isInProgressSaved()).isTrue() + assertThat(fractionsTopic.isInProgressSaved()).isTrue() + } + + @Test + fun testMarkAllTopicsAsInProgressNotSaved_allTopicsAreInProgressNotSaved() { + storyProgressTestHelper.markAllTopicsAsInProgressNotSaved( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val testTopic1 = getTopic(profileId0, TEST_TOPIC_ID_1) + val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) + val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) + + assertThat(testTopic0.isInProgressNotSaved()).isTrue() + assertThat(testTopic1.isInProgressNotSaved()).isTrue() + assertThat(ratiosTopic.isInProgressNotSaved()).isTrue() + assertThat(fractionsTopic.isInProgressNotSaved()).isTrue() + } + + @Test + fun testMarkAllTopicsAsRecentlyPlayed_allTopicsAreCorrectlyMarked() { storyProgressTestHelper.markAllTopicsAsRecentlyPlayed( profileId = profileId0, timestampOlderThanOneWeek = false @@ -991,10 +1861,10 @@ class StoryProgressTestHelperTest { val ratiosTopic = getTopic(profileId0, RATIOS_TOPIC_ID) val fractionsTopic = getTopic(profileId0, FRACTIONS_TOPIC_ID) - assertThat(testTopic0.isStarted()).isTrue() - assertThat(testTopic1.isStarted()).isTrue() - assertThat(ratiosTopic.isStarted()).isTrue() - assertThat(fractionsTopic.isStarted()).isTrue() + assertThat(testTopic0.isStartedNotCompleted()).isTrue() + assertThat(testTopic1.isInProgressSaved()).isTrue() + assertThat(ratiosTopic.isInProgressSaved()).isTrue() + assertThat(fractionsTopic.isInProgressNotSaved()).isTrue() } @Test @@ -1016,15 +1886,83 @@ class StoryProgressTestHelperTest { } @Test - fun testRecentlyPlayedChapter_thenMarkedDone_chapterIsCompleted() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + fun testStartedNotCompletedChapter_thenMarkedDone_chapterIsCompleted() { + storyProgressTestHelper.markStartedNotCompletedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + storyProgressTestHelper.markCompletedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + val exp2 = story0.getChapter(TEST_EXPLORATION_ID_2) + assertThat(exp2.isCompleted()).isTrue() + } + + @Test + fun testInProgressSavedChapter_thenMarkedDone_chapterIsCompleted() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + storyProgressTestHelper.markCompletedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + val exp2 = story0.getChapter(TEST_EXPLORATION_ID_2) + assertThat(exp2.isCompleted()).isTrue() + } + + @Test + fun testInProgressNotSavedChapter_thenMarkedDone_chapterIsCompleted() { + storyProgressTestHelper.markInProgressNotSavedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + storyProgressTestHelper.markCompletedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + val exp2 = story0.getChapter(TEST_EXPLORATION_ID_2) + assertThat(exp2.isCompleted()).isTrue() + } + + @Test + fun testMarkChapterDone_thenStartedNotCompleted_chapterIsCompleted() { + storyProgressTestHelper.markCompletedTestTopic0Story0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) + storyProgressTestHelper.markStartedNotCompletedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + val exp2 = story0.getChapter(TEST_EXPLORATION_ID_2) + assertThat(exp2.isCompleted()).isTrue() + } + + @Test + fun testMarkChapterDone_thenInProgressSaved_chapterIsCompleted() { storyProgressTestHelper.markCompletedTestTopic0Story0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) val testTopic0 = getTopic(profileId0, TEST_TOPIC_ID_0) val story0 = testTopic0.getStory(TEST_STORY_ID_0) @@ -1033,12 +1971,12 @@ class StoryProgressTestHelperTest { } @Test - fun testMarkChapterDone_thenRecentlyPlayed_chapterIsCompleted() { + fun testMarkChapterDone_thenInProgressNotSaved_chapterIsCompleted() { storyProgressTestHelper.markCompletedTestTopic0Story0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + storyProgressTestHelper.markInProgressNotSavedTestTopic0Story0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -1084,8 +2022,42 @@ class StoryProgressTestHelperTest { } @Test - fun testMarkChapterRecentlyPlayed_newerThanWeek_timestampNewerThanWeek() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + fun testMarkChapterAsStartedNotCompleted_newerThanWeek_timestampNewerThanWeek() { + storyProgressTestHelper.markStartedNotCompletedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val progressDatabase = getTopicProgressDatabase(profileId0) + val testTopic0Progress = progressDatabase.getTopicProgress(TEST_TOPIC_ID_0) + val story0Progress = testTopic0Progress.getStoryProgress(TEST_STORY_ID_0) + val exp2Progress = story0Progress.getChapterProgress(TEST_EXPLORATION_ID_2) + + val currentTime = fakeOppiaClock.getCurrentTimeMs() + val timeSincePlayed = currentTime - exp2Progress.lastPlayedTimestamp + assertThat(timeSincePlayed).isAtMost(TimeUnit.DAYS.toMillis(7)) + } + + @Test + fun testMarkChapterAsInProgressSaved_newerThanWeek_timestampNewerThanWeek() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val progressDatabase = getTopicProgressDatabase(profileId0) + val testTopic0Progress = progressDatabase.getTopicProgress(TEST_TOPIC_ID_0) + val story0Progress = testTopic0Progress.getStoryProgress(TEST_STORY_ID_0) + val exp2Progress = story0Progress.getChapterProgress(TEST_EXPLORATION_ID_2) + + val currentTime = fakeOppiaClock.getCurrentTimeMs() + val timeSincePlayed = currentTime - exp2Progress.lastPlayedTimestamp + assertThat(timeSincePlayed).isAtMost(TimeUnit.DAYS.toMillis(7)) + } + + @Test + fun testMarkChapterAsInProgressNotSaved_newerThanWeek_timestampNewerThanWeek() { + storyProgressTestHelper.markInProgressNotSavedTestTopic0Story0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -1101,8 +2073,42 @@ class StoryProgressTestHelperTest { } @Test - fun testMarkChapterRecentlyPlayed_olderThanWeek_timestampOlderThanWeek() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + fun testMarkChapterAsStartedNotCompleted_olderThanWeek_timestampOlderThanWeek() { + storyProgressTestHelper.markStartedNotCompletedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = true + ) + + val progressDatabase = getTopicProgressDatabase(profileId0) + val testTopic0Progress = progressDatabase.getTopicProgress(TEST_TOPIC_ID_0) + val story0Progress = testTopic0Progress.getStoryProgress(TEST_STORY_ID_0) + val exp2Progress = story0Progress.getChapterProgress(TEST_EXPLORATION_ID_2) + + val currentTime = fakeOppiaClock.getCurrentTimeMs() + val timeSincePlayed = currentTime - exp2Progress.lastPlayedTimestamp + assertThat(timeSincePlayed).isAtLeast(TimeUnit.DAYS.toMillis(7)) + } + + @Test + fun testMarkChapterInProgressSaved_olderThanWeek_timestampOlderThanWeek() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = true + ) + + val progressDatabase = getTopicProgressDatabase(profileId0) + val testTopic0Progress = progressDatabase.getTopicProgress(TEST_TOPIC_ID_0) + val story0Progress = testTopic0Progress.getStoryProgress(TEST_STORY_ID_0) + val exp2Progress = story0Progress.getChapterProgress(TEST_EXPLORATION_ID_2) + + val currentTime = fakeOppiaClock.getCurrentTimeMs() + val timeSincePlayed = currentTime - exp2Progress.lastPlayedTimestamp + assertThat(timeSincePlayed).isAtLeast(TimeUnit.DAYS.toMillis(7)) + } + + @Test + fun testMarkChapterInProgressNotSaved_olderThanWeek_timestampOlderThanWeek() { + storyProgressTestHelper.markInProgressNotSavedTestTopic0Story0Exp0( profileId = profileId0, timestampOlderThanOneWeek = true ) @@ -1131,8 +2137,21 @@ class StoryProgressTestHelperTest { } @Test - fun testMarkChapterRecentlyPlayed_oneOneProfile_notStartedOnOtherProfile() { - storyProgressTestHelper.markRecentlyPlayedTestTopic0Story0Exp0( + fun testMarkChapterAsStartedNotCompleted_oneOneProfile_notStartedOnOtherProfile() { + storyProgressTestHelper.markStartedNotCompletedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId1, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + val exp2 = story0.getChapter(TEST_EXPLORATION_ID_2) + assertThat(exp2.isStartedNotCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressSaved_oneOneProfile_notStartedOnOtherProfile() { + storyProgressTestHelper.markInProgressSavedTestTopic0Story0Exp0( profileId = profileId0, timestampOlderThanOneWeek = false ) @@ -1140,7 +2159,20 @@ class StoryProgressTestHelperTest { val testTopic0 = getTopic(profileId1, TEST_TOPIC_ID_0) val story0 = testTopic0.getStory(TEST_STORY_ID_0) val exp2 = story0.getChapter(TEST_EXPLORATION_ID_2) - assertThat(exp2.isStarted()).isFalse() + assertThat(exp2.isStartedNotCompleted()).isFalse() + } + + @Test + fun testMarkChapterAsInProgressNotSaved_oneOneProfile_notStartedOnOtherProfile() { + storyProgressTestHelper.markInProgressNotSavedTestTopic0Story0Exp0( + profileId = profileId0, + timestampOlderThanOneWeek = false + ) + + val testTopic0 = getTopic(profileId1, TEST_TOPIC_ID_0) + val story0 = testTopic0.getStory(TEST_STORY_ID_0) + val exp2 = story0.getChapter(TEST_EXPLORATION_ID_2) + assertThat(exp2.isStartedNotCompleted()).isFalse() } private fun getTopic(profileId: ProfileId, topicId: String): Topic { @@ -1155,7 +2187,11 @@ class StoryProgressTestHelperTest { private fun Topic.isNotStarted(): Boolean = storyList.all { it.isNotStarted() } - private fun Topic.isStarted(): Boolean = storyList.any { it.isStarted() } + private fun Topic.isStartedNotCompleted(): Boolean = storyList.any { it.isStartedNotCompleted() } + + private fun Topic.isInProgressSaved(): Boolean = storyList.any { it.isInProgressSaved() } + + private fun Topic.isInProgressNotSaved(): Boolean = storyList.any { it.isInProgressNotSaved() } private fun Topic.isPartiallyCompleted(): Boolean = storyList.any { it.isCompleted() } @@ -1167,7 +2203,13 @@ class StoryProgressTestHelperTest { private fun StorySummary.isNotStarted(): Boolean = chapterList.all { it.isNotStarted() } - private fun StorySummary.isStarted(): Boolean = chapterList.any { it.isStarted() } + private fun StorySummary.isStartedNotCompleted(): Boolean = + chapterList.any { it.isStartedNotCompleted() } + + private fun StorySummary.isInProgressSaved(): Boolean = chapterList.any { it.isInProgressSaved() } + + private fun StorySummary.isInProgressNotSaved(): Boolean = + chapterList.any { it.isInProgressNotSaved() } private fun StorySummary.isCompleted(): Boolean = chapterList.all { it.isCompleted() } @@ -1176,9 +2218,15 @@ class StoryProgressTestHelperTest { ChapterPlayState.NOT_STARTED, ChapterPlayState.NOT_PLAYABLE_MISSING_PREREQUISITES ) - private fun ChapterSummary.isStarted(): Boolean = + private fun ChapterSummary.isStartedNotCompleted(): Boolean = chapterPlayState == ChapterPlayState.STARTED_NOT_COMPLETED + private fun ChapterSummary.isInProgressSaved(): Boolean = + chapterPlayState == ChapterPlayState.IN_PROGRESS_SAVED + + private fun ChapterSummary.isInProgressNotSaved(): Boolean = + chapterPlayState == ChapterPlayState.IN_PROGRESS_NOT_SAVED + private fun ChapterSummary.isCompleted(): Boolean = chapterPlayState == ChapterPlayState.COMPLETED private fun getTopicProgressDatabase(profileId: ProfileId): TopicProgressDatabase {