Skip to content

Commit

Permalink
nit fixes and added more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
MaskedCarrot committed Aug 16, 2021
1 parent 4aeb1b8 commit de49a31
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,10 @@ import org.oppia.android.util.logging.LogLevel
import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode
import java.io.FileNotFoundException
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
import org.oppia.android.app.model.HelpIndex

// For context:
// https://github.com/oppia/oppia/blob/37285a/extensions/interactions/Continue/directives/oppia-interactive-continue.directive.ts.
Expand Down Expand Up @@ -1127,6 +1129,233 @@ class ExplorationProgressControllerTest {
assertThat(updatedState.state.interaction.solution.solutionIsRevealed).isTrue()
}

@Test
fun testHintsAndSolution_noHintVisible_checkHelpIndexIsCorrect() {
subscribeToCurrentStateToAllowExplorationToLoad()
playExploration(
profileId.internalId,
TEST_TOPIC_ID_0,
TEST_STORY_ID_0,
TEST_EXPLORATION_ID_2,
shouldSavePartialProgress = true
)
playThroughPrototypeState1AndMoveToNextState()

// Verify that the helpIndex.IndexTypeCase is equal to INDEX_TYPE_NOT_SET because no hint
// is visible yet.
verify(mockCurrentStateLiveDataObserver, atLeastOnce())
.onChanged(currentStateResultCaptor.capture())
assertThat(currentStateResultCaptor.value.isSuccess()).isTrue()
val currentState = currentStateResultCaptor.value.getOrThrow()
assertThat(currentState.helpIndex.indexTypeCase)
.isEqualTo(HelpIndex.IndexTypeCase.INDEXTYPE_NOT_SET)
}

@Test
fun testHintsAndSolution_wait60Seconds_unrevealedHintIsVisible_checkHelpIndexIsCorrect() {
subscribeToCurrentStateToAllowExplorationToLoad()
playExploration(
profileId.internalId,
TEST_TOPIC_ID_0,
TEST_STORY_ID_0,
TEST_EXPLORATION_ID_2,
shouldSavePartialProgress = true
)
playThroughPrototypeState1AndMoveToNextState()
// Make the first hint visible by submitting two wrong answers.
testCoroutineDispatchers.advanceTimeBy(TimeUnit.SECONDS.toMillis(60))
testCoroutineDispatchers.runCurrent()

// Verify that the helpIndex.IndexTypeCase is equal AVAILABLE_NEXT_HINT_HINT_INDEX because a new
// unrevealed hint is visible.
verify(mockCurrentStateLiveDataObserver, atLeastOnce())
.onChanged(currentStateResultCaptor.capture())
assertThat(currentStateResultCaptor.value.isSuccess()).isTrue()
val currentState = currentStateResultCaptor.value.getOrThrow()
assertThat(currentState.state.interaction.hintList[0].hintIsRevealed).isFalse()
assertThat(currentState.helpIndex.indexTypeCase)
.isEqualTo(HelpIndex.IndexTypeCase.AVAILABLE_NEXT_HINT_INDEX)
assertThat(currentState.helpIndex.availableNextHintIndex).isEqualTo(0)
}

@Test
fun testHintsAndSolution_submitTwoWrongAnswers_unrevealedHintIsVisible_checkHelpIndexIsCorrect() {
subscribeToCurrentStateToAllowExplorationToLoad()
playExploration(
profileId.internalId,
TEST_TOPIC_ID_0,
TEST_STORY_ID_0,
TEST_EXPLORATION_ID_2,
shouldSavePartialProgress = true
)
playThroughPrototypeState1AndMoveToNextState()
// Make the first hint visible by submitting two wrong answers.
submitWrongAnswerForPrototypeState2()
submitWrongAnswerForPrototypeState2()

// Verify that the helpIndex.IndexTypeCase is equal AVAILABLE_NEXT_HINT_HINT_INDEX because a new
// unrevealed hint is visible.
verify(mockCurrentStateLiveDataObserver, atLeastOnce())
.onChanged(currentStateResultCaptor.capture())
assertThat(currentStateResultCaptor.value.isSuccess()).isTrue()
val currentState = currentStateResultCaptor.value.getOrThrow()
assertThat(currentState.state.interaction.hintList[0].hintIsRevealed).isFalse()
assertThat(currentState.helpIndex.indexTypeCase)
.isEqualTo(HelpIndex.IndexTypeCase.AVAILABLE_NEXT_HINT_INDEX)
assertThat(currentState.helpIndex.availableNextHintIndex).isEqualTo(0)
}

@Test
fun testHintsAndSolution_revealedHintIsVisible_checkHelpIndexIsCorrect() {
subscribeToCurrentStateToAllowExplorationToLoad()
playExploration(
profileId.internalId,
TEST_TOPIC_ID_0,
TEST_STORY_ID_0,
TEST_EXPLORATION_ID_2,
shouldSavePartialProgress = true
)
playThroughPrototypeState1AndMoveToNextState()
submitWrongAnswerForPrototypeState2()
submitWrongAnswerForPrototypeState2()

val result = explorationProgressController.submitHintIsRevealed(
hintIsRevealed = true,
hintIndex = 0,
)
result.observeForever(mockAsyncHintObserver)
testCoroutineDispatchers.runCurrent()

// Verify that the helpIndex.IndexTypeCase is equal LATEST_REVEALED_HINT_INDEX because a new
// revealed hint is visible.
verify(mockCurrentStateLiveDataObserver, atLeastOnce())
.onChanged(currentStateResultCaptor.capture())
assertThat(currentStateResultCaptor.value.isSuccess()).isTrue()
val currentState = currentStateResultCaptor.value.getOrThrow()
assertThat(currentState.state.interaction.hintList[0].hintIsRevealed).isTrue()
assertThat(currentState.state.interaction.solution.solutionIsRevealed).isFalse()
assertThat(currentState.helpIndex.indexTypeCase)
.isEqualTo(HelpIndex.IndexTypeCase.LATEST_REVEALED_HINT_INDEX)
assertThat(currentState.helpIndex.latestRevealedHintIndex).isEqualTo(0)
}

@Test
fun testHintsAndSolution_allHintsVisible_wait30Seconds_solutionVisible_checkHelpIndexIsCorrect() {
subscribeToCurrentStateToAllowExplorationToLoad()
playExploration(
profileId.internalId,
TEST_TOPIC_ID_0,
TEST_STORY_ID_0,
TEST_EXPLORATION_ID_2,
shouldSavePartialProgress = true
)
playThroughPrototypeState1AndMoveToNextState()
submitWrongAnswerForPrototypeState2()
submitWrongAnswerForPrototypeState2()

val result = explorationProgressController.submitHintIsRevealed(
hintIsRevealed = true,
hintIndex = 0,
)
result.observeForever(mockAsyncHintObserver)
testCoroutineDispatchers.runCurrent()

// The solution should be visible after 30 seconds of the last hint being reveled.
testCoroutineDispatchers.advanceTimeBy(TimeUnit.SECONDS.toMillis(30))
testCoroutineDispatchers.runCurrent()

// Verify that the helpIndex.IndexTypeCase is equal SHOW_SOLUTION because unrevealed solution is
// visible.
verify(mockCurrentStateLiveDataObserver, atLeastOnce())
.onChanged(currentStateResultCaptor.capture())
assertThat(currentStateResultCaptor.value.isSuccess()).isTrue()
val currentState = currentStateResultCaptor.value.getOrThrow()
assertThat(currentState.state.interaction.hintList[0].hintIsRevealed).isTrue()
assertThat(currentState.state.interaction.solution.solutionIsRevealed).isFalse()
assertThat(currentState.helpIndex.indexTypeCase)
.isEqualTo(HelpIndex.IndexTypeCase.SHOW_SOLUTION)
}

@Test
fun testHintAndSol_hintsVisible_submitWrongAns_wait10Seconds_solVisible_checkHelpIndexIsCorrect() {
subscribeToCurrentStateToAllowExplorationToLoad()
playExploration(
profileId.internalId,
TEST_TOPIC_ID_0,
TEST_STORY_ID_0,
TEST_EXPLORATION_ID_2,
shouldSavePartialProgress = true
)
playThroughPrototypeState1AndMoveToNextState()
submitWrongAnswerForPrototypeState2()
submitWrongAnswerForPrototypeState2()

val result = explorationProgressController.submitHintIsRevealed(
hintIsRevealed = true,
hintIndex = 0,
)
result.observeForever(mockAsyncHintObserver)
testCoroutineDispatchers.runCurrent()

submitWrongAnswerForPrototypeState2()
// The solution should be visible after 10 seconds becuase one wrong answer was submitted.
testCoroutineDispatchers.advanceTimeBy(TimeUnit.SECONDS.toMillis(10))
testCoroutineDispatchers.runCurrent()

// Verify that the helpIndex.IndexTypeCase is equal SHOW_SOLUTION because unrevealed solution is
// visible.
verify(mockCurrentStateLiveDataObserver, atLeastOnce())
.onChanged(currentStateResultCaptor.capture())
assertThat(currentStateResultCaptor.value.isSuccess()).isTrue()
val currentState = currentStateResultCaptor.value.getOrThrow()
assertThat(currentState.state.interaction.hintList[0].hintIsRevealed).isTrue()
assertThat(currentState.state.interaction.solution.solutionIsRevealed).isFalse()
assertThat(currentState.helpIndex.indexTypeCase)
.isEqualTo(HelpIndex.IndexTypeCase.SHOW_SOLUTION)
}


@Test
fun testHintsAndSolution_revealedSolutionIsVisible_checkHelpIndexIsCorrect() {
subscribeToCurrentStateToAllowExplorationToLoad()
playExploration(
profileId.internalId,
TEST_TOPIC_ID_0,
TEST_STORY_ID_0,
TEST_EXPLORATION_ID_2,
shouldSavePartialProgress = true
)
playThroughPrototypeState1AndMoveToNextState()
submitWrongAnswerForPrototypeState2()
submitWrongAnswerForPrototypeState2()

val hintResult = explorationProgressController.submitHintIsRevealed(
hintIsRevealed = true,
hintIndex = 0,
)
hintResult.observeForever(mockAsyncHintObserver)
testCoroutineDispatchers.runCurrent()

// The solution should be visible after 30 seconds of the last hint being reveled.
testCoroutineDispatchers.advanceTimeBy(TimeUnit.SECONDS.toMillis(30))
testCoroutineDispatchers.runCurrent()

val solutionResult = explorationProgressController.submitSolutionIsRevealed()
solutionResult.observeForever(mockAsyncSolutionObserver)
testCoroutineDispatchers.runCurrent()

// Verify that the helpIndex.IndexTypeCase is equal EVERYTHING_IS_REVEALED because a new the
// solution has been revealed.
verify(mockCurrentStateLiveDataObserver, atLeastOnce())
.onChanged(currentStateResultCaptor.capture())
assertThat(currentStateResultCaptor.value.isSuccess()).isTrue()
val currentState = currentStateResultCaptor.value.getOrThrow()
assertThat(currentState.state.interaction.hintList[0].hintIsRevealed).isTrue()
assertThat(currentState.state.interaction.solution.solutionIsRevealed).isTrue()
assertThat(currentState.helpIndex.indexTypeCase)
.isEqualTo(HelpIndex.IndexTypeCase.EVERYTHING_REVEALED)
}

@Test
fun testSubmitAnswer_forTextInput_wrongAnswer_afterAllHintsAreExhausted_showSolution() {
subscribeToCurrentStateToAllowExplorationToLoad()
Expand Down
26 changes: 15 additions & 11 deletions model/src/main/proto/exploration.proto
Original file line number Diff line number Diff line change
Expand Up @@ -333,27 +333,31 @@ message AnswerOutcome {
// to properly account for variable numbers of hints, for cases when only a solution or no solution
// exists, and for when there are no hints or solutions.
message HelpIndex {
// Deprecating HINT_INDEX because it is now split in two, latest_revealed_hint_index and
// available_next_hint_index.
reserved 1;

// This type is uninitialized in cases when no index is currently available (either because a hint
// 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 the next available hint within the hint
// list of a state.
int32 available_next_hint_index = 1;

// Indicates this help index corresponds to the index of the last revealed hint within the hint
// list of a state.
int32 latest_revealed_hint_index = 2;

// Indicates this help index corresponds to the solution of a state. The boolean value here has
// no importance and is always 'true'.
bool show_solution = 3;
bool show_solution = 2;

// Indicates that everything available has been revealed. Note that this is different than the
// case when there are no hints or solutions to trigger, even though the resulting behavior may
// be different. This case specifically indicates that all hints and the solution, if present,
// have been revealed and there's no other help to provide. The boolean value here has no
// importance and is always 'true'.
bool everything_revealed = 4;
bool everything_revealed = 3;

// Indicates this help index corresponds to the index of the next available hint within the hint
// list of a state.
int32 available_next_hint_index = 4;

// Indicates this help index corresponds to the index of the last revealed hint within the hint
// list of a state.
int32 latest_revealed_hint_index = 5;
}
}

Expand All @@ -367,7 +371,7 @@ message HintState {
int32 hint_sequence_number = 2;

// Delay for scheduling a new task to show more help to the learner.
int64 delay_to_show_next_hint_and_solution = 4;
int64 delay_to_show_next_hint_and_solution = 3;
}

// Different states in which exploration checkpoint can exist.
Expand Down
1 change: 1 addition & 0 deletions model/src/main/proto/exploration_checkpoint.proto
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ message ExplorationCheckpoint {
// The timestamp in milliseconds of when the checkpoint was saved for the first time.
int64 timestamp_of_first_checkpoint = 9;

// The saved help index for the exploration.
HelpIndex help_index = 10;
}

Expand Down

0 comments on commit de49a31

Please sign in to comment.