Skip to content

Commit ca2461f

Browse files
authored
Merge pull request #1542 from SimonMarquis/in-memory-datastore
Fix Windows unit test failing because of DataStore threading issue
2 parents beb091d + 3e380b8 commit ca2461f

File tree

12 files changed

+47
-110
lines changed

12 files changed

+47
-110
lines changed

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ dependencies {
114114
kspTest(libs.hilt.compiler)
115115

116116
testImplementation(projects.core.dataTest)
117+
testImplementation(projects.core.datastoreTest)
117118
testImplementation(libs.hilt.android.testing)
118119
testImplementation(projects.sync.syncTest)
119120
testImplementation(libs.kotlin.test)

app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,13 @@ import com.google.samples.apps.nowinandroid.R
3535
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
3636
import com.google.samples.apps.nowinandroid.core.model.data.Topic
3737
import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule
38-
import dagger.hilt.android.testing.BindValue
3938
import dagger.hilt.android.testing.HiltAndroidRule
4039
import dagger.hilt.android.testing.HiltAndroidTest
4140
import kotlinx.coroutines.flow.first
4241
import kotlinx.coroutines.runBlocking
4342
import org.junit.Before
4443
import org.junit.Rule
4544
import org.junit.Test
46-
import org.junit.rules.TemporaryFolder
4745
import javax.inject.Inject
4846
import com.google.samples.apps.nowinandroid.feature.bookmarks.R as BookmarksR
4947
import com.google.samples.apps.nowinandroid.feature.foryou.R as FeatureForyouR
@@ -62,24 +60,16 @@ class NavigationTest {
6260
@get:Rule(order = 0)
6361
val hiltRule = HiltAndroidRule(this)
6462

65-
/**
66-
* Create a temporary folder used to create a Data Store file. This guarantees that
67-
* the file is removed in between each test, preventing a crash.
68-
*/
69-
@BindValue
70-
@get:Rule(order = 1)
71-
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
72-
7363
/**
7464
* Grant [android.Manifest.permission.POST_NOTIFICATIONS] permission.
7565
*/
76-
@get:Rule(order = 2)
66+
@get:Rule(order = 1)
7767
val postNotificationsPermission = GrantPostNotificationsPermissionRule()
7868

7969
/**
8070
* Use the primary activity to initialize the app normally.
8171
*/
82-
@get:Rule(order = 3)
72+
@get:Rule(order = 2)
8373
val composeTestRule = createAndroidComposeRule<MainActivity>()
8474

8575
@Inject

app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/InterestsListDetailScreenTest.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
3131
import com.google.samples.apps.nowinandroid.core.model.data.Topic
3232
import com.google.samples.apps.nowinandroid.ui.interests2pane.InterestsListDetailScreen
3333
import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity
34-
import dagger.hilt.android.testing.BindValue
3534
import dagger.hilt.android.testing.HiltAndroidRule
3635
import dagger.hilt.android.testing.HiltAndroidTest
3736
import dagger.hilt.android.testing.HiltTestApplication
@@ -40,7 +39,6 @@ import kotlinx.coroutines.runBlocking
4039
import org.junit.Before
4140
import org.junit.Rule
4241
import org.junit.Test
43-
import org.junit.rules.TemporaryFolder
4442
import org.junit.runner.RunWith
4543
import org.robolectric.RobolectricTestRunner
4644
import org.robolectric.annotation.Config
@@ -60,11 +58,7 @@ class InterestsListDetailScreenTest {
6058
@get:Rule(order = 0)
6159
val hiltRule = HiltAndroidRule(this)
6260

63-
@BindValue
6461
@get:Rule(order = 1)
65-
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
66-
67-
@get:Rule(order = 2)
6862
val composeTestRule = createAndroidComposeRule<HiltComponentActivity>()
6963

7064
@Inject

app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
3838
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
3939
import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions
4040
import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity
41-
import dagger.hilt.android.testing.BindValue
4241
import dagger.hilt.android.testing.HiltAndroidRule
4342
import dagger.hilt.android.testing.HiltAndroidTest
4443
import dagger.hilt.android.testing.HiltTestApplication
@@ -47,7 +46,6 @@ import kotlinx.coroutines.runBlocking
4746
import org.junit.Before
4847
import org.junit.Rule
4948
import org.junit.Test
50-
import org.junit.rules.TemporaryFolder
5149
import org.junit.runner.RunWith
5250
import org.robolectric.RobolectricTestRunner
5351
import org.robolectric.annotation.Config
@@ -74,18 +72,10 @@ class NiaAppScreenSizesScreenshotTests {
7472
@get:Rule(order = 0)
7573
val hiltRule = HiltAndroidRule(this)
7674

77-
/**
78-
* Create a temporary folder used to create a Data Store file. This guarantees that
79-
* the file is removed in between each test, preventing a crash.
80-
*/
81-
@BindValue
82-
@get:Rule(order = 1)
83-
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
84-
8575
/**
8676
* Use a test activity to set the content on.
8777
*/
88-
@get:Rule(order = 2)
78+
@get:Rule(order = 1)
8979
val composeTestRule = createAndroidComposeRule<HiltComponentActivity>()
9080

9181
@Inject

app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
6969
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
7070
import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions
7171
import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity
72-
import dagger.hilt.android.testing.BindValue
7372
import dagger.hilt.android.testing.HiltAndroidRule
7473
import dagger.hilt.android.testing.HiltAndroidTest
7574
import dagger.hilt.android.testing.HiltTestApplication
@@ -80,7 +79,6 @@ import kotlinx.coroutines.runBlocking
8079
import org.junit.Before
8180
import org.junit.Rule
8281
import org.junit.Test
83-
import org.junit.rules.TemporaryFolder
8482
import org.junit.runner.RunWith
8583
import org.robolectric.RobolectricTestRunner
8684
import org.robolectric.annotation.Config
@@ -107,18 +105,10 @@ class SnackbarInsetsScreenshotTests {
107105
@get:Rule(order = 0)
108106
val hiltRule = HiltAndroidRule(this)
109107

110-
/**
111-
* Create a temporary folder used to create a Data Store file. This guarantees that
112-
* the file is removed in between each test, preventing a crash.
113-
*/
114-
@BindValue
115-
@get:Rule(order = 1)
116-
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
117-
118108
/**
119109
* Use a test activity to set the content on.
120110
*/
121-
@get:Rule(order = 2)
111+
@get:Rule(order = 1)
122112
val composeTestRule = createAndroidComposeRule<HiltComponentActivity>()
123113

124114
@Inject

app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
4242
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
4343
import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions
4444
import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity
45-
import dagger.hilt.android.testing.BindValue
4645
import dagger.hilt.android.testing.HiltAndroidRule
4746
import dagger.hilt.android.testing.HiltAndroidTest
4847
import dagger.hilt.android.testing.HiltTestApplication
@@ -53,7 +52,6 @@ import kotlinx.coroutines.runBlocking
5352
import org.junit.Before
5453
import org.junit.Rule
5554
import org.junit.Test
56-
import org.junit.rules.TemporaryFolder
5755
import org.junit.runner.RunWith
5856
import org.robolectric.RobolectricTestRunner
5957
import org.robolectric.annotation.Config
@@ -80,18 +78,10 @@ class SnackbarScreenshotTests {
8078
@get:Rule(order = 0)
8179
val hiltRule = HiltAndroidRule(this)
8280

83-
/**
84-
* Create a temporary folder used to create a Data Store file. This guarantees that
85-
* the file is removed in between each test, preventing a crash.
86-
*/
87-
@BindValue
88-
@get:Rule(order = 1)
89-
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
90-
9181
/**
9282
* Use a test activity to set the content on.
9383
*/
94-
@get:Rule(order = 2)
84+
@get:Rule(order = 1)
9585
val composeTestRule = createAndroidComposeRule<HiltComponentActivity>()
9686

9787
@Inject

core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsRes
3232
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
3333
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
3434
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
35-
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
35+
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferences
36+
import com.google.samples.apps.nowinandroid.core.datastore.test.InMemoryDataStore
3637
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
3738
import com.google.samples.apps.nowinandroid.core.model.data.Topic
3839
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
@@ -43,9 +44,7 @@ import kotlinx.coroutines.test.TestScope
4344
import kotlinx.coroutines.test.UnconfinedTestDispatcher
4445
import kotlinx.coroutines.test.runTest
4546
import org.junit.Before
46-
import org.junit.Rule
4747
import org.junit.Test
48-
import org.junit.rules.TemporaryFolder
4948
import kotlin.test.assertEquals
5049
import kotlin.test.assertTrue
5150

@@ -67,14 +66,9 @@ class OfflineFirstNewsRepositoryTest {
6766

6867
private lateinit var synchronizer: Synchronizer
6968

70-
@get:Rule
71-
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
72-
7369
@Before
7470
fun setup() {
75-
niaPreferencesDataSource = NiaPreferencesDataSource(
76-
tmpFolder.testUserPreferencesDataStore(testScope.backgroundScope),
77-
)
71+
niaPreferencesDataSource = NiaPreferencesDataSource(InMemoryDataStore(UserPreferences.getDefaultInstance()))
7872
newsResourceDao = TestNewsResourceDao()
7973
topicDao = TestTopicDao()
8074
network = TestNiaNetworkDataSource()

core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,16 @@ import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao
2525
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
2626
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
2727
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
28-
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
28+
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferences
29+
import com.google.samples.apps.nowinandroid.core.datastore.test.InMemoryDataStore
2930
import com.google.samples.apps.nowinandroid.core.model.data.Topic
3031
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
3132
import kotlinx.coroutines.flow.first
3233
import kotlinx.coroutines.test.TestScope
3334
import kotlinx.coroutines.test.UnconfinedTestDispatcher
3435
import kotlinx.coroutines.test.runTest
3536
import org.junit.Before
36-
import org.junit.Rule
3737
import org.junit.Test
38-
import org.junit.rules.TemporaryFolder
3938
import kotlin.test.assertEquals
4039

4140
class OfflineFirstTopicsRepositoryTest {
@@ -52,16 +51,11 @@ class OfflineFirstTopicsRepositoryTest {
5251

5352
private lateinit var synchronizer: Synchronizer
5453

55-
@get:Rule
56-
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
57-
5854
@Before
5955
fun setup() {
6056
topicDao = TestTopicDao()
6157
network = TestNiaNetworkDataSource()
62-
niaPreferences = NiaPreferencesDataSource(
63-
tmpFolder.testUserPreferencesDataStore(testScope.backgroundScope),
64-
)
58+
niaPreferences = NiaPreferencesDataSource(InMemoryDataStore(UserPreferences.getDefaultInstance()))
6559
synchronizer = TestSynchronizer(niaPreferences)
6660

6761
subject = OfflineFirstTopicsRepository(

core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ package com.google.samples.apps.nowinandroid.core.data.repository
1818

1919
import com.google.samples.apps.nowinandroid.core.analytics.NoOpAnalyticsHelper
2020
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
21-
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
21+
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferences
22+
import com.google.samples.apps.nowinandroid.core.datastore.test.InMemoryDataStore
2223
import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig
2324
import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand
2425
import com.google.samples.apps.nowinandroid.core.model.data.UserData
@@ -28,9 +29,7 @@ import kotlinx.coroutines.test.TestScope
2829
import kotlinx.coroutines.test.UnconfinedTestDispatcher
2930
import kotlinx.coroutines.test.runTest
3031
import org.junit.Before
31-
import org.junit.Rule
3232
import org.junit.Test
33-
import org.junit.rules.TemporaryFolder
3433
import kotlin.test.assertEquals
3534
import kotlin.test.assertFalse
3635
import kotlin.test.assertTrue
@@ -45,14 +44,9 @@ class OfflineFirstUserDataRepositoryTest {
4544

4645
private val analyticsHelper = NoOpAnalyticsHelper()
4746

48-
@get:Rule
49-
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
50-
5147
@Before
5248
fun setup() {
53-
niaPreferencesDataSource = NiaPreferencesDataSource(
54-
tmpFolder.testUserPreferencesDataStore(testScope.backgroundScope),
55-
)
49+
niaPreferencesDataSource = NiaPreferencesDataSource(InMemoryDataStore(UserPreferences.getDefaultInstance()))
5650

5751
subject = OfflineFirstUserDataRepository(
5852
niaPreferencesDataSource = niaPreferencesDataSource,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.samples.apps.nowinandroid.core.datastore.test
18+
19+
import androidx.datastore.core.DataStore
20+
import kotlinx.coroutines.flow.MutableStateFlow
21+
import kotlinx.coroutines.flow.updateAndGet
22+
23+
class InMemoryDataStore<T>(initialValue: T) : DataStore<T> {
24+
override val data = MutableStateFlow(initialValue)
25+
override suspend fun updateData(
26+
transform: suspend (it: T) -> T,
27+
) = data.updateAndGet { transform(it) }
28+
}

0 commit comments

Comments
 (0)