1
1
package com.mapbox.navigation.core.internal.utils
2
2
3
+ import android.os.Handler
4
+ import android.os.HandlerThread
5
+ import kotlinx.coroutines.CoroutineScope
3
6
import kotlinx.coroutines.Job
4
7
import kotlinx.coroutines.SupervisorJob
8
+ import kotlinx.coroutines.android.asCoroutineDispatcher
5
9
import kotlinx.coroutines.cancel
6
10
import kotlinx.coroutines.delay
7
11
import kotlinx.coroutines.launch
12
+ import kotlinx.coroutines.runBlocking
8
13
import org.junit.After
14
+ import org.junit.Assert.assertEquals
9
15
import org.junit.Assert.assertFalse
16
+ import org.junit.Assert.assertNotNull
10
17
import org.junit.Assert.assertTrue
11
18
import org.junit.Before
12
19
import org.junit.Test
20
+ import org.junit.runner.RunWith
21
+ import org.robolectric.RobolectricTestRunner
22
+ import kotlin.coroutines.EmptyCoroutineContext
13
23
24
+ @RunWith(RobolectricTestRunner ::class )
14
25
class CoroutineUtilsTest {
15
26
27
+ private lateinit var parentThread: HandlerThread
28
+ private lateinit var parentScope: CoroutineScope
16
29
private lateinit var parentJob: Job
17
30
18
31
@Before
19
32
fun setUp () {
33
+ parentThread = HandlerThread (" parent thread" ).also { it.start() }
20
34
parentJob = SupervisorJob ()
35
+ parentScope = CoroutineScope (
36
+ SupervisorJob () + Handler (parentThread.looper).asCoroutineDispatcher()
37
+ )
21
38
}
22
39
23
40
@After
24
41
fun tearDown () {
42
+ parentScope.cancel()
25
43
parentJob.cancel()
44
+ parentThread.quit()
26
45
}
27
46
28
47
@Test
29
48
fun createChildScope_parentCancellationCancelsChildren () {
30
- val scope1 = CoroutineUtils .createChildScope(parentJob )
31
- val scope2 = CoroutineUtils .createChildScope(parentJob )
49
+ val scope1 = CoroutineUtils .createChildScope(parentScope )
50
+ val scope2 = CoroutineUtils .createChildScope(parentScope )
32
51
33
52
val job1 = scope1.launch {
34
53
delay(5000 )
@@ -40,16 +59,78 @@ class CoroutineUtilsTest {
40
59
assertFalse(job1.isCancelled)
41
60
assertFalse(job2.isCancelled)
42
61
43
- parentJob .cancel()
62
+ parentScope .cancel()
44
63
45
64
assertTrue(job1.isCancelled)
46
65
assertTrue(job2.isCancelled)
47
66
}
48
67
49
68
@Test
50
69
fun createChildScope_childDoesNotCancelOtherChildren () {
51
- val scope1 = CoroutineUtils .createChildScope(parentJob)
52
- val scope2 = CoroutineUtils .createChildScope(parentJob)
70
+ val scope1 = CoroutineUtils .createChildScope(parentScope)
71
+ val scope2 = CoroutineUtils .createChildScope(parentScope)
72
+
73
+ val job1 = scope1.launch {
74
+ delay(5000 )
75
+ }
76
+ val job2 = scope2.launch {
77
+ delay(5000 )
78
+ }
79
+
80
+ assertFalse(job1.isCancelled)
81
+ assertFalse(job2.isCancelled)
82
+
83
+ scope1.cancel()
84
+
85
+ assertTrue(job1.isCancelled)
86
+ assertFalse(job2.isCancelled)
87
+ }
88
+
89
+ @Test
90
+ fun createChildScope_childrenUseParentDispatcher () = runBlocking {
91
+ val childScope = CoroutineUtils .createChildScope(parentScope)
92
+
93
+ var childThread: Long? = null
94
+ var parentThread: Long? = null
95
+ val childJob = childScope.launch {
96
+ childThread = Thread .currentThread().id
97
+ }
98
+ val parentJob = parentScope.launch {
99
+ parentThread = Thread .currentThread().id
100
+ }
101
+
102
+ parentJob.join()
103
+ childJob.join()
104
+
105
+ assertNotNull(childThread)
106
+ assertEquals(parentThread, childThread)
107
+ }
108
+
109
+ @Test
110
+ fun createScope_parentCancellationCancelsChildren () {
111
+ val scope1 = CoroutineUtils .createScope(parentJob, EmptyCoroutineContext )
112
+ val scope2 = CoroutineUtils .createScope(parentJob, EmptyCoroutineContext )
113
+
114
+ val job1 = scope1.launch {
115
+ delay(5000 )
116
+ }
117
+ val job2 = scope2.launch {
118
+ delay(5000 )
119
+ }
120
+
121
+ assertFalse(job1.isCancelled)
122
+ assertFalse(job2.isCancelled)
123
+
124
+ parentJob.cancel()
125
+
126
+ assertTrue(job1.isCancelled)
127
+ assertTrue(job2.isCancelled)
128
+ }
129
+
130
+ @Test
131
+ fun createScope_childDoesNotCancelOtherChildren () {
132
+ val scope1 = CoroutineUtils .createScope(parentJob, EmptyCoroutineContext )
133
+ val scope2 = CoroutineUtils .createScope(parentJob, EmptyCoroutineContext )
53
134
54
135
val job1 = scope1.launch {
55
136
delay(5000 )
@@ -66,4 +147,38 @@ class CoroutineUtilsTest {
66
147
assertTrue(job1.isCancelled)
67
148
assertFalse(job2.isCancelled)
68
149
}
150
+
151
+ @Test
152
+ fun createScope_childrenUseOwnDispatcher () = runBlocking {
153
+ val thread1 = HandlerThread (" thread 1" ).also { it.start() }
154
+ val thread2 = HandlerThread (" thread 2" ).also { it.start() }
155
+ try {
156
+ val childScope1 = CoroutineUtils .createScope(
157
+ parentJob,
158
+ Handler (thread1.looper).asCoroutineDispatcher()
159
+ )
160
+ val childScope2 = CoroutineUtils .createScope(
161
+ parentJob,
162
+ Handler (thread2.looper).asCoroutineDispatcher()
163
+ )
164
+
165
+ var childThread1: Thread ? = null
166
+ var childThread2: Thread ? = null
167
+ val childJob1 = childScope1.launch {
168
+ childThread1 = Thread .currentThread()
169
+ }
170
+ val childJob2 = childScope2.launch {
171
+ childThread2 = Thread .currentThread()
172
+ }
173
+
174
+ childJob1.join()
175
+ childJob2.join()
176
+
177
+ assertEquals(thread1, childThread1)
178
+ assertEquals(thread2, childThread2)
179
+ } finally {
180
+ thread1.quit()
181
+ thread2.quit()
182
+ }
183
+ }
69
184
}
0 commit comments