Skip to content

Commit 49c2f42

Browse files
refactor: standardize conjugate label language checks using ScribeLan… (#690)
* refactor: standardize conjugate label language checks using ScribeLanguage enum (#426) * refactor: extract duplicated language alias list into shared constant, add edge-case tests (#426)
1 parent 4b1b5bc commit 49c2f42

4 files changed

Lines changed: 327 additions & 8 deletions

File tree

app/src/keyboards/java/be/scri/helpers/ui/KeyboardUIManager.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import be.scri.helpers.english.ENInterfaceVariables.ALREADY_PLURAL_MSG
4040
import be.scri.helpers.getCategoryIconRes
4141
import be.scri.helpers.getRecentEmojis
4242
import be.scri.helpers.parseRawEmojiSpecsFile
43+
import be.scri.models.ScribeLanguage
4344
import be.scri.models.ScribeState
4445
import be.scri.services.GeneralKeyboardIME
4546
import be.scri.views.KeyboardView
@@ -418,11 +419,12 @@ class KeyboardUIManager(
418419
languageOutput?.get(title)?.toList() ?: listOf("", "", "", "")
419420
}
420421

422+
val scribeLanguage = ScribeLanguage.fromDisplayName(language)
421423
val layoutResId =
422424
when {
423425
isSubSelection -> R.layout.conjugate_grid_2x1
424-
language == "English" && forms.size <= 4 -> R.layout.conjugate_grid_2x2
425-
language in listOf("Russian", "Swedish") && forms.size <= 4 -> R.layout.conjugate_grid_2x2
426+
scribeLanguage == ScribeLanguage.ENGLISH && forms.size <= 4 -> R.layout.conjugate_grid_2x2
427+
scribeLanguage in listOf(ScribeLanguage.RUSSIAN, ScribeLanguage.SWEDISH) && forms.size <= 4 -> R.layout.conjugate_grid_2x2
426428
forms.size > 4 -> R.layout.conjugate_grid_3x2
427429
else -> R.layout.conjugate_grid_2x2
428430
}

app/src/main/java/be/scri/ui/screens/ConjugateViewModel.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import androidx.lifecycle.viewModelScope
1010
import be.scri.helpers.DatabaseFileManager
1111
import be.scri.helpers.data.getInfinitiveColumnName
1212
import be.scri.helpers.data.tableExists
13+
import be.scri.models.ScribeLanguage
1314
import kotlinx.coroutines.Dispatchers
1415
import kotlinx.coroutines.flow.MutableStateFlow
1516
import kotlinx.coroutines.flow.SharingStarted
@@ -35,6 +36,10 @@ data class ConjugateSearchResult(
3536
class ConjugateViewModel(
3637
application: Application,
3738
) : AndroidViewModel(application) {
39+
companion object {
40+
private val SUPPORTED_ALIASES = ScribeLanguage.entries.map { it.isoCode }
41+
}
42+
3843
private val prefs = application.getSharedPreferences("scribe_conjugate_search_prefs", Context.MODE_PRIVATE)
3944

4045
private val _searchQuery = MutableStateFlow("")
@@ -67,13 +72,11 @@ class ConjugateViewModel(
6772
/**
6873
* Returns a list of language aliases that have been downloaded (i.e. conjugate database exists).
6974
*/
70-
fun getDownloadedLanguages(): List<String> {
71-
val aliases = listOf("EN", "FR", "DE", "IT", "PT", "RU", "ES", "SV")
72-
return aliases.filter { alias ->
75+
fun getDownloadedLanguages(): List<String> =
76+
SUPPORTED_ALIASES.filter { alias ->
7377
val dbName = "${alias}ConjugateData.sqlite"
7478
getApplication<Application>().getDatabasePath(dbName).exists()
7579
}
76-
}
7780

7881
/**
7982
* Formats the list of downloaded languages into a user-friendly display string.
@@ -115,9 +118,8 @@ class ConjugateViewModel(
115118
viewModelScope.launch(Dispatchers.IO) {
116119
val results = mutableListOf<ConjugateSearchResult>()
117120
val fileManager = DatabaseFileManager(getApplication())
118-
val aliases = listOf("EN", "FR", "DE", "IT", "PT", "RU", "ES", "SV")
119121

120-
for (alias in aliases) {
122+
for (alias in SUPPORTED_ALIASES) {
121123
val db = fileManager.getConjugateDatabase(alias) ?: continue
122124

123125
try {

app/src/test/kotlin/be/scri/ui/screens/ConjugateViewModelTest.kt

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.content.SharedPreferences
88
import io.mockk.every
99
import io.mockk.mockk
1010
import io.mockk.verify
11+
import java.io.File
1112
import kotlin.test.BeforeTest
1213
import kotlin.test.Test
1314
import kotlin.test.assertEquals
@@ -98,4 +99,137 @@ class ConjugateViewModelTest {
9899
assertEquals(ConjugateSearchResult("essere", "IT"), list[0])
99100
assertEquals(ConjugateSearchResult("parler", "FR"), list[1])
100101
}
102+
103+
@Test
104+
fun getDownloadedLanguages_AllDatabasesExist_ReturnsAllEightAliasesInExpectedOrder() {
105+
// Arrange
106+
val expectedAliases = listOf("EN", "FR", "DE", "IT", "PT", "RU", "ES", "SV")
107+
every { application.getDatabasePath(any()) } answers {
108+
val file = mockk<File>()
109+
every { file.exists() } returns true
110+
file
111+
}
112+
val viewModel = ConjugateViewModel(application)
113+
114+
// Act
115+
val actualLanguages = viewModel.getDownloadedLanguages()
116+
117+
// Assert
118+
assertEquals(expectedAliases, actualLanguages)
119+
assertEquals(8, actualLanguages.size)
120+
}
121+
122+
@Test
123+
fun getDownloadedLanguages_PartialDatabasesExist_ReturnsMatchingAliases() {
124+
// Arrange
125+
every { application.getDatabasePath(any()) } answers {
126+
val dbName = firstArg<String>()
127+
val file = mockk<File>()
128+
every { file.exists() } returns (dbName == "ENConjugateData.sqlite" || dbName == "ESConjugateData.sqlite")
129+
file
130+
}
131+
val viewModel = ConjugateViewModel(application)
132+
133+
// Act
134+
val actualLanguages = viewModel.getDownloadedLanguages()
135+
136+
// Assert
137+
assertEquals(listOf("EN", "ES"), actualLanguages)
138+
}
139+
140+
@Test
141+
fun getDownloadedLanguages_NoDatabasesExist_ReturnsEmptyList() {
142+
// Arrange
143+
every { application.getDatabasePath(any()) } answers {
144+
val file = mockk<File>()
145+
every { file.exists() } returns false
146+
file
147+
}
148+
val viewModel = ConjugateViewModel(application)
149+
150+
// Act
151+
val actualLanguages = viewModel.getDownloadedLanguages()
152+
153+
// Assert
154+
assertTrue(actualLanguages.isEmpty())
155+
}
156+
157+
@Test
158+
fun getDownloadedLanguages_UnrecognizedDatabasesPresent_OnlyReturnsSupportedAliases() {
159+
// Arrange
160+
every { application.getDatabasePath(any()) } answers {
161+
val dbName = firstArg<String>()
162+
val file = mockk<File>()
163+
every { file.exists() } returns (
164+
dbName == "ENConjugateData.sqlite" ||
165+
dbName == "XYZConjugateData.sqlite" ||
166+
dbName == "UNKNOWNConjugateData.sqlite"
167+
)
168+
file
169+
}
170+
val viewModel = ConjugateViewModel(application)
171+
172+
// Act
173+
val actualLanguages = viewModel.getDownloadedLanguages()
174+
175+
// Assert
176+
assertEquals(listOf("EN"), actualLanguages)
177+
}
178+
179+
@Test
180+
fun getDownloadedLanguagesFormatted_FormatsCorrectlyForVariousCounts() {
181+
// Arrange
182+
val viewModel = ConjugateViewModel(application)
183+
184+
// 0 languages
185+
every { application.getDatabasePath(any()) } answers {
186+
val file = mockk<File>()
187+
every { file.exists() } returns false
188+
file
189+
}
190+
assertEquals("", viewModel.getDownloadedLanguagesFormatted())
191+
192+
// 1 language
193+
every { application.getDatabasePath(any()) } answers {
194+
val dbName = firstArg<String>()
195+
val file = mockk<File>()
196+
every { file.exists() } returns (dbName == "ENConjugateData.sqlite")
197+
file
198+
}
199+
assertEquals("English", viewModel.getDownloadedLanguagesFormatted())
200+
201+
// 2 languages
202+
every { application.getDatabasePath(any()) } answers {
203+
val dbName = firstArg<String>()
204+
val file = mockk<File>()
205+
every { file.exists() } returns (dbName == "ENConjugateData.sqlite" || dbName == "FRConjugateData.sqlite")
206+
file
207+
}
208+
assertEquals("English and Français", viewModel.getDownloadedLanguagesFormatted())
209+
210+
// 3 languages
211+
every { application.getDatabasePath(any()) } answers {
212+
val dbName = firstArg<String>()
213+
val file = mockk<File>()
214+
every { file.exists() } returns (
215+
dbName == "ENConjugateData.sqlite" ||
216+
dbName == "FRConjugateData.sqlite" ||
217+
dbName == "DEConjugateData.sqlite"
218+
)
219+
file
220+
}
221+
assertEquals("English, Français and Deutsch", viewModel.getDownloadedLanguagesFormatted())
222+
}
223+
224+
@Test
225+
fun testLoadRecentlyConjugatedWithUnrecognizedLanguageAlias() {
226+
every { sharedPreferences.getString("recently_conjugated_list", null) } returns "parler,XYZ;mangiare,INVALID_ALIAS;speak,EN"
227+
228+
val testViewModel = ConjugateViewModel(application)
229+
val list = testViewModel.recentlyConjugated.value
230+
assertEquals(3, list.size)
231+
assertEquals(ConjugateSearchResult("parler", "XYZ"), list[0])
232+
assertEquals(ConjugateSearchResult("mangiare", "INVALID_ALIAS"), list[1])
233+
assertEquals(ConjugateSearchResult("speak", "EN"), list[2])
234+
}
101235
}

0 commit comments

Comments
 (0)