-
Notifications
You must be signed in to change notification settings - Fork 527
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix #124: Introduce basic stub and tests for AnswerClassificationCont…
…roller. (#187)
- Loading branch information
1 parent
4404f71
commit 4e870d0
Showing
2 changed files
with
147 additions
and
0 deletions.
There are no files selected for viewing
26 changes: 26 additions & 0 deletions
26
domain/src/main/java/org/oppia/domain/classify/AnswerClassificationController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package org.oppia.domain.classify | ||
|
||
import org.oppia.app.model.Interaction | ||
import org.oppia.app.model.InteractionObject | ||
import org.oppia.app.model.Outcome | ||
import javax.inject.Inject | ||
|
||
// TODO(#59): Restrict the visibility of this class to only other controllers. | ||
/** | ||
* Controller responsible for classifying user answers to a specific outcome based on Oppia's interaction rule engine. | ||
* This controller is not meant to be interacted with directly by the UI. Instead, UIs wanting to submit answers should | ||
* do so via various progress controllers, like [StoryProgressController]. | ||
* | ||
* This controller should only be interacted with via background threads. | ||
*/ | ||
class AnswerClassificationController @Inject constructor() { | ||
/** | ||
* Classifies the specified answer in the context of the specified [Interaction] and returns the [Outcome] that best | ||
* matches the learner's answer. | ||
*/ | ||
internal fun classify(interaction: Interaction, answer: InteractionObject): Outcome { | ||
// Assume only the default outcome is returned currently since this stubbed implementation is not actually used by | ||
// downstream stubbed progress controllers. | ||
return interaction.defaultOutcome | ||
} | ||
} |
121 changes: 121 additions & 0 deletions
121
domain/src/test/java/org/oppia/domain/classify/AnswerClassificationControllerTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package org.oppia.domain.classify | ||
|
||
import android.app.Application | ||
import android.content.Context | ||
import androidx.test.core.app.ApplicationProvider | ||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import com.google.common.truth.Truth.assertThat | ||
import dagger.BindsInstance | ||
import dagger.Component | ||
import dagger.Module | ||
import dagger.Provides | ||
import org.junit.Before | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import org.oppia.app.model.AnswerGroup | ||
import org.oppia.app.model.Interaction | ||
import org.oppia.app.model.InteractionObject | ||
import org.oppia.app.model.Outcome | ||
import org.oppia.app.model.SubtitledHtml | ||
import org.robolectric.annotation.Config | ||
import javax.inject.Inject | ||
import javax.inject.Singleton | ||
|
||
/** Tests for [AnswerClassificationController]. */ | ||
@RunWith(AndroidJUnit4::class) | ||
@Config(manifest = Config.NONE) | ||
class AnswerClassificationControllerTest { | ||
private val ARBITRARY_SAMPLE_ANSWER = InteractionObject.newBuilder().setNormalizedString("Some value").build() | ||
|
||
private val OUTCOME_0 = Outcome.newBuilder() | ||
.setDestStateName("First state") | ||
.setFeedback(SubtitledHtml.newBuilder().setContentId("content_id_0").setHtml("Feedback 1")) | ||
.build() | ||
private val OUTCOME_1 = Outcome.newBuilder() | ||
.setDestStateName("Second state") | ||
.setFeedback(SubtitledHtml.newBuilder().setContentId("content_id_1").setHtml("Feedback 2")) | ||
.build() | ||
private val OUTCOME_2 = Outcome.newBuilder() | ||
.setDestStateName("Third state") | ||
.setFeedback(SubtitledHtml.newBuilder().setContentId("content_id_2").setHtml("Feedback 3")) | ||
.build() | ||
|
||
@Inject | ||
lateinit var answerClassificationController: AnswerClassificationController | ||
|
||
@Before | ||
fun setUp() { | ||
setUpTestApplicationComponent() | ||
} | ||
|
||
@Test | ||
fun testClassify_testInteraction_withOnlyDefaultOutcome_returnsDefaultOutcome() { | ||
val interaction = Interaction.newBuilder() | ||
.setDefaultOutcome(OUTCOME_0) | ||
.build() | ||
|
||
val outcome = answerClassificationController.classify(interaction, ARBITRARY_SAMPLE_ANSWER) | ||
|
||
assertThat(outcome).isEqualTo(OUTCOME_0) | ||
} | ||
|
||
@Test | ||
fun testClassify_testInteraction_withMultipleDefaultOutcomes_returnsDefaultOutcome() { | ||
val interaction = Interaction.newBuilder() | ||
.setDefaultOutcome(OUTCOME_1) | ||
.addAnswerGroups(AnswerGroup.newBuilder().setOutcome(OUTCOME_2)) | ||
.build() | ||
|
||
val outcome = answerClassificationController.classify(interaction, ARBITRARY_SAMPLE_ANSWER) | ||
|
||
assertThat(outcome).isEqualTo(OUTCOME_1) | ||
} | ||
|
||
@Test | ||
fun testClassify_afterPreviousInteraction_returnsDefaultOutcomeOfSecondInteraction() { | ||
val interaction1 = Interaction.newBuilder() | ||
.setDefaultOutcome(OUTCOME_1) | ||
.addAnswerGroups(AnswerGroup.newBuilder().setOutcome(OUTCOME_0)) | ||
.build() | ||
val interaction2 = Interaction.newBuilder() | ||
.setDefaultOutcome(OUTCOME_2) | ||
.build() | ||
answerClassificationController.classify(interaction1, ARBITRARY_SAMPLE_ANSWER) | ||
|
||
val outcome = answerClassificationController.classify(interaction2, ARBITRARY_SAMPLE_ANSWER) | ||
|
||
assertThat(outcome).isEqualTo(OUTCOME_2) | ||
} | ||
|
||
private fun setUpTestApplicationComponent() { | ||
DaggerAnswerClassificationControllerTest_TestApplicationComponent.builder() | ||
.setApplication(ApplicationProvider.getApplicationContext()) | ||
.build() | ||
.inject(this) | ||
} | ||
|
||
// TODO(#89): Move this to a common test application component. | ||
@Module | ||
class TestModule { | ||
@Provides | ||
@Singleton | ||
fun provideContext(application: Application): Context { | ||
return application | ||
} | ||
} | ||
|
||
// TODO(#89): Move this to a common test application component. | ||
@Singleton | ||
@Component(modules = [TestModule::class]) | ||
interface TestApplicationComponent { | ||
@Component.Builder | ||
interface Builder { | ||
@BindsInstance | ||
fun setApplication(application: Application): Builder | ||
|
||
fun build(): TestApplicationComponent | ||
} | ||
|
||
fun inject(answerClassificationControllerTest: AnswerClassificationControllerTest) | ||
} | ||
} |