Skip to content

Commit 2ace10e

Browse files
authored
[❄️flakes] Another round of flakes fixing (#5969)
* Another round of flakes fixing * keep using watch() * use proper dispatcher * do not break delays
1 parent 365e471 commit 2ace10e

File tree

3 files changed

+96
-73
lines changed

3 files changed

+96
-73
lines changed

tests/integration-tests/src/commonTest/kotlin/test/FetchPolicyTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ import com.apollographql.apollo3.integration.normalizer.CharacterNameByIdQuery
3131
import com.apollographql.apollo3.integration.normalizer.HeroNameQuery
3232
import com.apollographql.apollo3.interceptor.ApolloInterceptor
3333
import com.apollographql.apollo3.interceptor.ApolloInterceptorChain
34+
import com.apollographql.apollo3.testing.assertNoElement
35+
import com.apollographql.apollo3.testing.awaitElement
36+
import com.apollographql.apollo3.testing.internal.runTest
3437
import com.apollographql.mockserver.MockServer
3538
import com.apollographql.mockserver.awaitRequest
3639
import com.apollographql.mockserver.enqueueError
3740
import com.apollographql.mockserver.enqueueString
38-
import com.apollographql.apollo3.testing.assertNoElement
39-
import com.apollographql.apollo3.testing.awaitElement
40-
import com.apollographql.apollo3.testing.internal.runTest
4141
import kotlinx.coroutines.channels.Channel
4242
import kotlinx.coroutines.delay
4343
import kotlinx.coroutines.flow.Flow
@@ -66,7 +66,7 @@ class FetchPolicyTest {
6666
apolloClient = ApolloClient.Builder().serverUrl(mockServer.url()).store(store = store).build()
6767
}
6868

69-
private suspend fun tearDown() {
69+
private fun tearDown() {
7070
mockServer.close()
7171
apolloClient.close()
7272
}

tests/integration-tests/src/commonTest/kotlin/test/WatcherErrorHandlingTest.kt

Lines changed: 50 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package test
22

33
import IdCacheKeyGenerator
4+
import app.cash.turbine.test
45
import com.apollographql.apollo3.ApolloClient
56
import com.apollographql.apollo3.api.ApolloResponse
67
import com.apollographql.apollo3.cache.normalized.ApolloStore
@@ -14,12 +15,11 @@ import com.apollographql.apollo3.exception.ApolloHttpException
1415
import com.apollographql.apollo3.exception.CacheMissException
1516
import com.apollographql.apollo3.integration.normalizer.EpisodeHeroNameQuery
1617
import com.apollographql.apollo3.integration.normalizer.type.Episode
18+
import com.apollographql.apollo3.testing.awaitElement
19+
import com.apollographql.apollo3.testing.internal.runTest
1720
import com.apollographql.mockserver.MockServer
1821
import com.apollographql.mockserver.enqueueError
1922
import com.apollographql.mockserver.enqueueString
20-
import com.apollographql.apollo3.testing.awaitElement
21-
import com.apollographql.apollo3.testing.internal.runTest
22-
import kotlinx.coroutines.Job
2323
import kotlinx.coroutines.channels.Channel
2424
import kotlinx.coroutines.flow.catch
2525
import kotlinx.coroutines.flow.first
@@ -41,66 +41,61 @@ class WatcherErrorHandlingTest {
4141
apolloClient = ApolloClient.Builder().serverUrl(mockServer.url()).store(store).build()
4242
}
4343

44-
private suspend fun tearDown() {
44+
private fun tearDown() {
4545
mockServer.close()
4646
}
4747

48+
/**
49+
* watch() should behave just like toFlow() in the absence of cache writes
50+
*/
4851
@Test
4952
fun fetchEmitsAllErrors() = runTest(before = { setUp() }, after = { tearDown() }) {
50-
val channel = Channel<EpisodeHeroNameQuery.Data?>()
51-
val jobs = mutableListOf<Job>()
52-
53-
jobs += launch {
54-
mockServer.enqueueError(statusCode = 500)
55-
apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE))
56-
.fetchPolicy(FetchPolicy.CacheFirst)
57-
.watch()
58-
.collect {
59-
channel.send(it.data)
60-
}
61-
}
62-
63-
jobs += launch {
64-
apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE))
65-
.fetchPolicy(FetchPolicy.CacheOnly)
66-
.watch()
67-
.collect {
68-
channel.send(it.data)
69-
}
70-
}
71-
72-
jobs += launch {
73-
mockServer.enqueueError(statusCode = 500)
74-
apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE))
75-
.fetchPolicy(FetchPolicy.NetworkFirst)
76-
.watch()
77-
.collect {
78-
channel.send(it.data)
79-
}
80-
}
53+
mockServer.enqueueError(statusCode = 500)
54+
apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE))
55+
.fetchPolicy(FetchPolicy.CacheFirst)
56+
.watch()
57+
.test {
58+
assertIs<CacheMissException>(awaitItem().exception)
59+
assertIs<ApolloHttpException>(awaitItem().exception)
60+
cancelAndIgnoreRemainingEvents()
61+
}
62+
63+
apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE))
64+
.fetchPolicy(FetchPolicy.CacheOnly)
65+
.watch()
66+
.test {
67+
assertIs<CacheMissException>(awaitItem().exception)
68+
cancelAndIgnoreRemainingEvents()
69+
}
8170

82-
jobs += launch {
83-
mockServer.enqueueError(statusCode = 500)
84-
apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE))
85-
.fetchPolicy(FetchPolicy.NetworkOnly)
86-
.watch()
87-
.collect {
88-
channel.send(it.data)
89-
}
90-
}
71+
mockServer.enqueueError(statusCode = 500)
72+
apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE))
73+
.fetchPolicy(FetchPolicy.NetworkFirst)
74+
.watch()
75+
.test {
76+
assertIs<ApolloHttpException>(awaitItem().exception)
77+
assertIs<CacheMissException>(awaitItem().exception)
78+
cancelAndIgnoreRemainingEvents()
79+
}
9180

92-
jobs += launch {
93-
mockServer.enqueueError(statusCode = 500)
94-
apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE))
95-
.fetchPolicy(FetchPolicy.CacheAndNetwork)
96-
.watch()
97-
.collect {
98-
channel.send(it.data)
99-
}
100-
}
81+
mockServer.enqueueError(statusCode = 500)
82+
apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE))
83+
.fetchPolicy(FetchPolicy.NetworkOnly)
84+
.watch()
85+
.test {
86+
assertIs<ApolloHttpException>(awaitItem().exception)
87+
cancelAndIgnoreRemainingEvents()
88+
}
10189

102-
channel.assertCount(8)
103-
jobs.forEach { it.cancel() }
90+
mockServer.enqueueError(statusCode = 500)
91+
apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE))
92+
.fetchPolicy(FetchPolicy.CacheAndNetwork)
93+
.watch()
94+
.test {
95+
assertIs<CacheMissException>(awaitItem().exception)
96+
assertIs<ApolloHttpException>(awaitItem().exception)
97+
cancelAndIgnoreRemainingEvents()
98+
}
10499
}
105100

106101
@Test

tests/integration-tests/src/commonTest/kotlin/test/WatcherTest.kt

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,30 @@ import com.apollographql.apollo3.integration.normalizer.EpisodeHeroNameWithIdQue
2121
import com.apollographql.apollo3.integration.normalizer.HeroAndFriendsNamesWithIDsQuery
2222
import com.apollographql.apollo3.integration.normalizer.StarshipByIdQuery
2323
import com.apollographql.apollo3.integration.normalizer.type.Episode
24-
import com.apollographql.mockserver.MockResponse
25-
import com.apollographql.mockserver.MockServer
26-
import com.apollographql.mockserver.enqueueString
2724
import com.apollographql.apollo3.testing.QueueTestNetworkTransport
28-
import com.apollographql.apollo3.testing.assertNoElement
29-
import com.apollographql.apollo3.testing.awaitElement
30-
import com.apollographql.apollo3.testing.enqueue
3125
import com.apollographql.apollo3.testing.enqueueTestNetworkError
3226
import com.apollographql.apollo3.testing.enqueueTestResponse
3327
import com.apollographql.apollo3.testing.internal.runTest
28+
import com.apollographql.mockserver.MockResponse
29+
import com.apollographql.mockserver.MockServer
30+
import com.apollographql.mockserver.enqueueString
31+
import kotlinx.coroutines.CoroutineScope
3432
import kotlinx.coroutines.CoroutineStart
33+
import kotlinx.coroutines.Dispatchers
34+
import kotlinx.coroutines.ExperimentalCoroutinesApi
35+
import kotlinx.coroutines.TimeoutCancellationException
3536
import kotlinx.coroutines.cancelAndJoin
3637
import kotlinx.coroutines.channels.Channel
3738
import kotlinx.coroutines.delay
3839
import kotlinx.coroutines.flow.first
3940
import kotlinx.coroutines.launch
41+
import kotlinx.coroutines.withContext
4042
import kotlinx.coroutines.withTimeout
4143
import kotlin.test.Test
4244
import kotlin.test.assertEquals
4345
import kotlin.test.assertIs
4446
import kotlin.test.assertNull
47+
import kotlin.time.Duration.Companion.minutes
4548

4649
class WatcherTest {
4750
private lateinit var apolloClient: ApolloClient
@@ -72,14 +75,24 @@ class WatcherTest {
7275
HeroAndFriendsNamesWithIDsQuery.Friend("1003", "Leia Organa"),
7376
)))
7477

78+
@OptIn(ExperimentalCoroutinesApi::class)
79+
private fun myRunTest(block: suspend CoroutineScope.() -> Unit) {
80+
kotlinx.coroutines.test.runTest(timeout = 10.minutes) {
81+
withContext(Dispatchers.Default.limitedParallelism(1)) {
82+
block()
83+
}
84+
}
85+
}
7586
/**
7687
* Executing the same query out of band should update the watcher
7788
*
7889
* Also, this test checks that the watcher gets control fast enough to subscribe to
7990
* cache changes
8091
*/
8192
@Test
82-
fun sameQueryTriggersWatcher() = runTest(before = { setUp() }) {
93+
fun sameQueryTriggersWatcher() = myRunTest {
94+
setUp()
95+
8396
val query = EpisodeHeroNameQuery(Episode.EMPIRE)
8497
val channel = Channel<EpisodeHeroNameQuery.Data?>()
8598

@@ -191,7 +204,7 @@ class WatcherTest {
191204
apolloClient.enqueueTestResponse(query, episodeHeroNameData)
192205
apolloClient.query(query).fetchPolicy(FetchPolicy.NetworkOnly).execute()
193206

194-
channel.assertNoElement()
207+
channel.assertEmpty()
195208

196209
job.cancel()
197210
}
@@ -256,7 +269,7 @@ class WatcherTest {
256269
.fetchPolicy(FetchPolicy.NetworkOnly)
257270
.execute()
258271

259-
channel.assertNoElement()
272+
channel.assertEmpty()
260273

261274
job.cancel()
262275
}
@@ -322,7 +335,7 @@ class WatcherTest {
322335
}
323336
job.cancelAndJoin()
324337

325-
channel.assertNoElement()
338+
channel.assertEmpty()
326339
}
327340

328341
/**
@@ -373,7 +386,7 @@ class WatcherTest {
373386

374387
// Should see 1 cache miss values
375388
assertIs<CacheMissException>(channel.awaitElement().exception)
376-
channel.assertNoElement()
389+
channel.assertEmpty()
377390

378391
job.cancel()
379392
}
@@ -578,7 +591,7 @@ class WatcherTest {
578591

579592
// 2. Exception from the network (null data)
580593
assertNull(channel.awaitElement())
581-
channel.assertNoElement()
594+
channel.assertEmpty()
582595

583596
// Another newer call updates the cache with "ArTwo"
584597
apolloClient.enqueueTestResponse(query, episodeHeroNameChangedTwoData)
@@ -624,9 +637,24 @@ class WatcherTest {
624637

625638
}
626639

627-
suspend fun <D> Channel<D>.assertCount(count: Int) {
640+
internal suspend fun <D> Channel<D>.assertCount(count: Int) {
628641
repeat(count) {
629642
awaitElement()
630643
}
631-
assertNoElement()
644+
assertEmpty()
645+
}
646+
647+
internal suspend fun <T> Channel<T>.awaitElement(timeoutMillis: Long = 30000) = withTimeout(timeoutMillis) {
648+
receive()
649+
}
650+
651+
internal suspend fun <T> Channel<T>.assertEmpty(timeoutMillis: Long = 300): Unit {
652+
try {
653+
withTimeout(timeoutMillis) {
654+
receive()
655+
}
656+
error("An item was unexpectedly received")
657+
} catch (_: TimeoutCancellationException) {
658+
// nothing
659+
}
632660
}

0 commit comments

Comments
 (0)