Skip to content

Commit

Permalink
Fix #124: Introduce basic stub and tests for AnswerClassificationCont…
Browse files Browse the repository at this point in the history
…roller. (#187)
  • Loading branch information
BenHenning authored Sep 27, 2019
1 parent 4404f71 commit 4e870d0
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 0 deletions.
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
}
}
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)
}
}

0 comments on commit 4e870d0

Please sign in to comment.