Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/src/main/kotlin/org/stypox/dicio/skills/joke/JokeOutput.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,14 @@ sealed interface JokeOutput : SkillOutput {
val ENDS_WITH_PUNCTUATION_REGEX = ".*\\p{Punct}$".toRegex()
}
}

class Failed : JokeOutput {
override fun getSpeechOutput(ctx: SkillContext): String =
ctx.getString(R.string.skill_joke_failed)

@Composable
override fun GraphicalOutput(ctx: SkillContext) {
Body(text = getSpeechOutput(ctx))
}
}
}
5 changes: 2 additions & 3 deletions app/src/main/kotlin/org/stypox/dicio/skills/joke/JokeSkill.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ import org.stypox.dicio.util.LocaleUtils
class JokeSkill(correspondingSkillInfo: SkillInfo, data: StandardRecognizerData<Joke>)
: StandardRecognizerSkill<Joke>(correspondingSkillInfo, data) {
override suspend fun generateOutput(ctx: SkillContext, inputData: Joke): SkillOutput {
// we can use !! because the JokeInfo would have declared this skill unavailable
// if the current locale was not among the supported ones
val locale = LocaleUtils.resolveSupportedLocale(ctx.locale, JOKE_SUPPORTED_LOCALES)!!
val locale = LocaleUtils.resolveSupportedLocale(ctx.locale, JOKE_SUPPORTED_LOCALES)
?: return JokeOutput.Failed()

if (locale == "en") {
val joke: JSONObject = ConnectionUtils.getPageJson(RANDOM_JOKE_URL_EN)
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
<string name="skill_sentence_example_media">Next song</string>
<string name="skill_name_joke">Joke</string>
<string name="skill_sentence_example_joke">Tell me a joke</string>
<string name="skill_joke_failed">Sorry, jokes are not available for this language</string>
<string name="skill_name_calculator">Calculator</string>
<string name="skill_sentence_example_calculator">What is five times four minus a million?</string>
<string name="skill_name_navigation">Navigation</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.stypox.dicio.skills.joke

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.types.shouldBeInstanceOf
import kotlinx.coroutines.runBlocking
import org.dicio.skill.context.SkillContext
import org.dicio.skill.context.SpeechOutputDevice
import org.dicio.skill.skill.SkillOutput
import org.dicio.numbers.ParserFormatter
import org.dicio.skill.standard.util.MatchHelper
import org.stypox.dicio.sentences.Sentences
import java.util.Locale
import android.content.Context

/**
* Regression test for https://github.com/DicioTeam/dicio-android/pull/412
*
* When the user changes language, there is a race condition where JokeSkill.generateOutput()
* can be called with a locale not in JOKE_SUPPORTED_LOCALES. Previously this caused a NPE
* due to !! on resolveSupportedLocale(). The fix returns JokeOutput.Failed instead.
*/
class JokeSkillNpeTest : StringSpec({

"generateOutput returns Failed when ctx locale is not in JOKE_SUPPORTED_LOCALES" {
val data = Sentences.Joke["en"]!!
val skill = JokeSkill(JokeInfo, data)

val ctx = object : SkillContext {
override val locale: Locale = Locale.ITALIAN
override val android: Context get() = throw NotImplementedError()
override val sentencesLanguage: String = "it"
override val parserFormatter: ParserFormatter? = null
override val speechOutputDevice: SpeechOutputDevice get() = throw NotImplementedError()
override val previousOutput: SkillOutput? = null
override val standardMatchHelper: MatchHelper? = null
}

val result = runBlocking {
skill.generateOutput(ctx, Sentences.Joke.Command)
}
result.shouldBeInstanceOf<JokeOutput.Failed>()
}

"generateOutput returns Failed when locale is Turkish" {
val data = Sentences.Joke["en"]!!
val skill = JokeSkill(JokeInfo, data)

val ctx = object : SkillContext {
override val locale: Locale = Locale("tr")
override val android: Context get() = throw NotImplementedError()
override val sentencesLanguage: String = "tr"
override val parserFormatter: ParserFormatter? = null
override val speechOutputDevice: SpeechOutputDevice get() = throw NotImplementedError()
override val previousOutput: SkillOutput? = null
override val standardMatchHelper: MatchHelper? = null
}

val result = runBlocking {
skill.generateOutput(ctx, Sentences.Joke.Command)
}
result.shouldBeInstanceOf<JokeOutput.Failed>()
}
})