feat: Integrate Mokkery and Turbine into KMP testing framework (#4845)

This commit is contained in:
James Rich 2026-03-18 18:33:37 -05:00 committed by GitHub
parent df3a094430
commit dcbbc0823b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
159 changed files with 1860 additions and 2809 deletions

View file

@ -27,7 +27,7 @@ import org.meshtastic.proto.LocalStats
/** Class that handles saving and retrieving [LocalStats] data. */
@Single
class LocalStatsDataSource(@Named("CoreLocalStatsDataStore") private val localStatsStore: DataStore<LocalStats>) {
open class LocalStatsDataSource(@Named("CoreLocalStatsDataStore") private val localStatsStore: DataStore<LocalStats>) {
val localStatsFlow: Flow<LocalStats> =
localStatsStore.data.catch { exception ->
if (exception is IOException) {
@ -38,11 +38,11 @@ class LocalStatsDataSource(@Named("CoreLocalStatsDataStore") private val localSt
}
}
suspend fun setLocalStats(stats: LocalStats) {
open suspend fun setLocalStats(stats: LocalStats) {
localStatsStore.updateData { stats }
}
suspend fun clearLocalStats() {
open suspend fun clearLocalStats() {
localStatsStore.updateData { LocalStats() }
}
}

View file

@ -37,12 +37,12 @@ import org.koin.core.annotation.Single
import org.meshtastic.core.datastore.model.RecentAddress
@Single
class RecentAddressesDataSource(@Named("CorePreferencesDataStore") private val dataStore: DataStore<Preferences>) {
open class RecentAddressesDataSource(@Named("CorePreferencesDataStore") private val dataStore: DataStore<Preferences>) {
private object PreferencesKeys {
val RECENT_IP_ADDRESSES = stringPreferencesKey("recent-ip-addresses")
}
val recentAddresses: Flow<List<RecentAddress>> =
open val recentAddresses: Flow<List<RecentAddress>> =
dataStore.data.map { preferences ->
val jsonString = preferences[PreferencesKeys.RECENT_IP_ADDRESSES]
if (jsonString != null) {
@ -95,20 +95,20 @@ class RecentAddressesDataSource(@Named("CorePreferencesDataStore") private val d
}
}
suspend fun setRecentAddresses(addresses: List<RecentAddress>) {
open suspend fun setRecentAddresses(addresses: List<RecentAddress>) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.RECENT_IP_ADDRESSES] = Json.encodeToString(addresses)
}
}
suspend fun add(address: RecentAddress) {
open suspend fun add(address: RecentAddress) {
val currentAddresses = recentAddresses.first()
val updatedList = mutableListOf(address)
currentAddresses.filterTo(updatedList) { it.address != address.address }
setRecentAddresses(updatedList.take(CACHE_CAPACITY))
}
suspend fun remove(address: String) {
open suspend fun remove(address: String) {
val currentAddresses = recentAddresses.first()
val updatedList = currentAddresses.filter { it.address != address }
setRecentAddresses(updatedList)

View file

@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.annotation.Named
import org.koin.core.annotation.Single
import org.meshtastic.core.common.UiPreferences
const val KEY_APP_INTRO_COMPLETED = "app_intro_completed"
const val KEY_THEME = "theme"
@ -48,70 +49,78 @@ const val KEY_EXCLUDE_MQTT = "exclude-mqtt"
@Single
@Suppress("TooManyFunctions") // One setter per preference field — inherently grows with preferences.
class UiPreferencesDataSource(@Named("CorePreferencesDataStore") private val dataStore: DataStore<Preferences>) {
open class UiPreferencesDataSource(@Named("CorePreferencesDataStore") private val dataStore: DataStore<Preferences>) :
UiPreferences {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
// Start this flow eagerly, so app intro doesn't flash (when disabled) on cold app start.
val appIntroCompleted: StateFlow<Boolean> =
override val appIntroCompleted: StateFlow<Boolean> =
dataStore.prefStateFlow(key = APP_INTRO_COMPLETED, default = false, started = SharingStarted.Eagerly)
// Default value for AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
val theme: StateFlow<Int> = dataStore.prefStateFlow(key = THEME, default = -1)
override val theme: StateFlow<Int> = dataStore.prefStateFlow(key = THEME, default = -1)
/** Persisted language tag (e.g. "de", "pt-BR"). Empty string means system default. */
val locale: StateFlow<String> =
override val locale: StateFlow<String> =
dataStore.prefStateFlow(key = LOCALE, default = "", started = SharingStarted.Eagerly)
fun setLocale(languageTag: String) {
override fun setLocale(languageTag: String) {
dataStore.setPref(key = LOCALE, value = languageTag)
}
val nodeSort: StateFlow<Int> = dataStore.prefStateFlow(key = NODE_SORT, default = -1)
val includeUnknown: StateFlow<Boolean> = dataStore.prefStateFlow(key = INCLUDE_UNKNOWN, default = false)
val excludeInfrastructure: StateFlow<Boolean> =
override val nodeSort: StateFlow<Int> = dataStore.prefStateFlow(key = NODE_SORT, default = -1)
override val includeUnknown: StateFlow<Boolean> = dataStore.prefStateFlow(key = INCLUDE_UNKNOWN, default = false)
override val excludeInfrastructure: StateFlow<Boolean> =
dataStore.prefStateFlow(key = EXCLUDE_INFRASTRUCTURE, default = false)
val onlyOnline: StateFlow<Boolean> = dataStore.prefStateFlow(key = ONLY_ONLINE, default = false)
val onlyDirect: StateFlow<Boolean> = dataStore.prefStateFlow(key = ONLY_DIRECT, default = false)
val showIgnored: StateFlow<Boolean> = dataStore.prefStateFlow(key = SHOW_IGNORED, default = false)
val excludeMqtt: StateFlow<Boolean> = dataStore.prefStateFlow(key = EXCLUDE_MQTT, default = false)
override val onlyOnline: StateFlow<Boolean> = dataStore.prefStateFlow(key = ONLY_ONLINE, default = false)
override val onlyDirect: StateFlow<Boolean> = dataStore.prefStateFlow(key = ONLY_DIRECT, default = false)
override val showIgnored: StateFlow<Boolean> = dataStore.prefStateFlow(key = SHOW_IGNORED, default = false)
override val excludeMqtt: StateFlow<Boolean> = dataStore.prefStateFlow(key = EXCLUDE_MQTT, default = false)
fun setAppIntroCompleted(completed: Boolean) {
override fun setAppIntroCompleted(completed: Boolean) {
dataStore.setPref(key = APP_INTRO_COMPLETED, value = completed)
}
fun setTheme(value: Int) {
override fun setTheme(value: Int) {
dataStore.setPref(key = THEME, value = value)
}
fun setNodeSort(value: Int) {
override fun setNodeSort(value: Int) {
dataStore.setPref(key = NODE_SORT, value = value)
}
fun setIncludeUnknown(value: Boolean) {
override fun setIncludeUnknown(value: Boolean) {
dataStore.setPref(key = INCLUDE_UNKNOWN, value = value)
}
fun setExcludeInfrastructure(value: Boolean) {
override fun setExcludeInfrastructure(value: Boolean) {
dataStore.setPref(key = EXCLUDE_INFRASTRUCTURE, value = value)
}
fun setOnlyOnline(value: Boolean) {
override fun setOnlyOnline(value: Boolean) {
dataStore.setPref(key = ONLY_ONLINE, value = value)
}
fun setOnlyDirect(value: Boolean) {
override fun setOnlyDirect(value: Boolean) {
dataStore.setPref(key = ONLY_DIRECT, value = value)
}
fun setShowIgnored(value: Boolean) {
override fun setShowIgnored(value: Boolean) {
dataStore.setPref(key = SHOW_IGNORED, value = value)
}
fun setExcludeMqtt(value: Boolean) {
override fun setExcludeMqtt(value: Boolean) {
dataStore.setPref(key = EXCLUDE_MQTT, value = value)
}
override fun shouldProvideNodeLocation(nodeNum: Int): StateFlow<Boolean> =
dataStore.prefStateFlow(key = booleanPreferencesKey("provide-location-$nodeNum"), default = false)
override fun setShouldProvideNodeLocation(nodeNum: Int, provide: Boolean) {
dataStore.setPref(key = booleanPreferencesKey("provide-location-$nodeNum"), value = provide)
}
private fun <T : Any> DataStore<Preferences>.prefStateFlow(
key: Preferences.Key<T>,
default: T,