diff --git a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt index 9a76a75a1..ba45606df 100644 --- a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt +++ b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt @@ -21,6 +21,7 @@ import androidx.datastore.core.DataStore import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.data.UserData +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import java.io.IOException @@ -30,6 +31,14 @@ class NiaPreferencesDataSource @Inject constructor( private val userPreferences: DataStore, ) { val userData = userPreferences.data + .catch { exception -> + if (exception is IOException) { + Log.e("NiaPreferences", "Error reading user preferences.", exception) + emit(UserPreferences.getDefaultInstance()) + } else { + throw exception + } + } .map { UserData( bookmarkedNewsResources = it.bookmarkedNewsResourceIdsMap.keys, @@ -155,6 +164,14 @@ class NiaPreferencesDataSource @Inject constructor( } suspend fun getChangeListVersions() = userPreferences.data + .catch { exception -> + if (exception is IOException) { + Log.e("NiaPreferences", "Error reading user preferences.", exception) + emit(UserPreferences.getDefaultInstance()) + } else { + throw exception + } + } .map { ChangeListVersions( topicVersion = it.topicChangeListVersion, diff --git a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt index 433bbb5ea..190afea74 100644 --- a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt +++ b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt @@ -16,15 +16,21 @@ package com.google.samples.apps.nowinandroid.core.datastore +import androidx.datastore.core.DataStore +import androidx.datastore.core.IOException import com.google.samples.apps.nowinandroid.core.datastore.test.InMemoryDataStore +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test +import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue +import kotlin.test.fail class NiaPreferencesDataSourceTest { @@ -32,11 +38,30 @@ class NiaPreferencesDataSourceTest { private lateinit var subject: NiaPreferencesDataSource + // A DataStore implementation that throws an IOException when accessed + private val ioExceptionThrowingDataStore = object : DataStore { + override val data: Flow + get() = flow { throw IOException("Failed to read proto") } + + override suspend fun updateData(transform: suspend (t: UserPreferences) -> UserPreferences): UserPreferences { + fail("Not needed for this test") + } + } + @Before fun setup() { subject = NiaPreferencesDataSource(InMemoryDataStore(UserPreferences.getDefaultInstance())) } + @Test + fun userData_emitDefault_whenDataStoreThrowsIOException() = + testScope.runTest { + val dataSource = NiaPreferencesDataSource(ioExceptionThrowingDataStore) + val actualUserData = dataSource.userData.first() + + assertEquals(subject.userData.first(), actualUserData) + } + @Test fun shouldHideOnboardingIsFalseByDefault() = testScope.runTest { assertFalse(subject.userData.first().shouldHideOnboarding) @@ -76,6 +101,15 @@ class NiaPreferencesDataSourceTest { assertFalse(subject.userData.first().shouldHideOnboarding) } + @Test + fun getChangeListVersions_returnsDefault_whenDataStoreThrowsIOException() = + testScope.runTest { + val dataSource = NiaPreferencesDataSource(ioExceptionThrowingDataStore) + val actualResult = dataSource.getChangeListVersions() + + assertEquals(subject.getChangeListVersions(), actualResult) + } + @Test fun shouldUseDynamicColorFalseByDefault() = testScope.runTest { assertFalse(subject.userData.first().useDynamicColor)