• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 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  *      http://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 @file:OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class)
18 
19 package com.android.test.tracing.coroutines
20 
21 import android.platform.test.annotations.EnableFlags
22 import com.android.app.tracing.coroutines.asyncTraced
23 import com.android.app.tracing.coroutines.flow.collectLatestTraced
24 import com.android.app.tracing.coroutines.flow.collectTraced
25 import com.android.app.tracing.coroutines.flow.filterTraced
26 import com.android.app.tracing.coroutines.flow.mapTraced
27 import com.android.app.tracing.coroutines.flow.transformTraced
28 import com.android.app.tracing.coroutines.launchTraced
29 import com.android.app.tracing.coroutines.withContextTraced
30 import com.android.systemui.Flags.FLAG_COROUTINE_TRACING
31 import kotlin.coroutines.EmptyCoroutineContext
32 import kotlinx.coroutines.CoroutineScope
33 import kotlinx.coroutines.DelicateCoroutinesApi
34 import kotlinx.coroutines.ExperimentalCoroutinesApi
35 import kotlinx.coroutines.cancelChildren
36 import kotlinx.coroutines.delay
37 import kotlinx.coroutines.flow.Flow
38 import kotlinx.coroutines.flow.FlowCollector
39 import kotlinx.coroutines.flow.SharingStarted
40 import kotlinx.coroutines.flow.flow
41 import kotlinx.coroutines.flow.shareIn
42 import kotlinx.coroutines.job
43 import kotlinx.coroutines.withContext
44 import org.junit.Assert.assertEquals
45 import org.junit.Test
46 
47 /** Tests behavior of default names using reflection */
48 @EnableFlags(FLAG_COROUTINE_TRACING)
49 class DefaultNamingTest : TestBase() {
50 
51     @Test
52     fun collectTraced1() {
53         val coldFlow =
54             flow {
55                     expect(2, "1^main", "collect:DefaultNamingTest\$collectTraced1$1$1")
56                     emit(21) // 21 * 2 = 42
57                     expect(6, "1^main", "collect:DefaultNamingTest\$collectTraced1$1$1")
58                 }
59                 .mapTraced("2x") {
60                     expect(3, "1^main", "collect:DefaultNamingTest\$collectTraced1$1$1", "2x")
61                     it * 2 // 42
62                 }
63                 .filterTraced("mod2") {
64                     expect(4, "1^main", "collect:DefaultNamingTest\$collectTraced1$1$1", "mod2")
65                     it % 2 == 0 // true
66                 }
67         runTest(finalEvent = 7) {
68             expect(1, "1^main")
69             coldFlow.collectTraced {
70                 assertEquals(42, it) // 21 * 2 = 42
71                 expect(
72                     5,
73                     "1^main",
74                     "collect:DefaultNamingTest\$collectTraced1$1$1",
75                     "emit:DefaultNamingTest\$collectTraced1$1$1",
76                 )
77             }
78             expect(7, "1^main")
79         }
80     }
81 
82     @Test
83     fun collectTraced2() {
84         val coldFlow =
85             flow {
86                     expect(
87                         2,
88                         "1^main:1^",
89                         "collect:collectLatest:DefaultNamingTest\$collectTraced2$1$1",
90                     ) // child scope used by `collectLatest {}`
91                     emit(1) // should not get used by collectLatest {}
92                     expect(
93                         6,
94                         "1^main:1^",
95                         "collect:collectLatest:DefaultNamingTest\$collectTraced2$1$1",
96                     )
97                     emit(21) // 21 * 2 = 42
98                     expect(
99                         10,
100                         "1^main:1^",
101                         "collect:collectLatest:DefaultNamingTest\$collectTraced2$1$1",
102                     )
103                 }
104                 .filterTraced("mod2") {
105                     expect(
106                         listOf(3, 7),
107                         "1^main:1^",
108                         "collect:collectLatest:DefaultNamingTest\$collectTraced2$1$1",
109                         "mod2",
110                     )
111                     it % 2 == 1 // true
112                 }
113                 .mapTraced("2x") {
114                     expect(
115                         listOf(4, 8),
116                         "1^main:1^",
117                         "collect:collectLatest:DefaultNamingTest\$collectTraced2$1$1",
118                         "2x",
119                     )
120                     it * 2 // 42
121                 }
122         runTest(finalEvent = 12) {
123             expect(1, "1^main") // top-level scope
124             coldFlow.collectLatestTraced {
125                 expectEvent(listOf(5, 9))
126                 delay(50)
127                 assertEquals(42, it) // 21 * 2 = 42
128                 expect(11, "1^main:1^:2^", "DefaultNamingTest\$collectTraced2$1$1")
129             }
130             expect(12, "1^main")
131         }
132     }
133 
134     @Test
135     fun collectTraced3() =
136         runTest(finalEvent = 8) {
137             expect(1, "1^main") // top-level scope
138 
139             val sharedFlow =
140                 flow {
141                         expect(2, "1^main:1^")
142                         delay(1)
143                         emit(22)
144                         expect(3, "1^main:1^")
145                         delay(1)
146                         emit(32)
147                         expect(4, "1^main:1^")
148                         delay(1)
149                         emit(42)
150                         expect(5, "1^main:1^")
151                     } // there is no API for passing a custom context to the new shared flow, so we
152                     // can't pass our custom child name using `CoroutineTraceName()`
153                     .shareIn(this, SharingStarted.Eagerly, 4)
154 
155             launchTraced("AAAA") {
156                 sharedFlow.collectLatestTraced {
157                     delay(10)
158                     expect(6, "1^main:2^AAAA:1^:3^", "DefaultNamingTest\$collectTraced3$1$1$1")
159                 }
160             }
161             launchTraced("BBBB") {
162                 sharedFlow.collectLatestTraced {
163                     delay(40)
164                     assertEquals(42, it)
165                     expect(7, "1^main:3^BBBB:1^:3^", "DefaultNamingTest\$collectTraced3$1$2$1")
166                 }
167             }
168 
169             delay(70)
170             expect(8, "1^main")
171             coroutineContext.job.cancelChildren()
172         }
173 
174     @Test
175     fun collectTraced4() =
176         runTest(finalEvent = 5) {
177             expect(1, "1^main")
178             flow {
179                     expect(2, "1^main", "collect:DefaultNamingTest\$collectTraced4$1$2")
180                     emit(42)
181                     expect(4, "1^main", "collect:DefaultNamingTest\$collectTraced4$1$2")
182                 }
183                 .collectTraced {
184                     assertEquals(42, it)
185                     expect(
186                         3,
187                         "1^main",
188                         "collect:DefaultNamingTest\$collectTraced4$1$2",
189                         "emit:DefaultNamingTest\$collectTraced4$1$2",
190                     )
191                 }
192             expect(5, "1^main")
193         }
194 
195     @Test
196     fun collectTraced5_localFun() {
197         fun localFun(value: Int) {
198             assertEquals(42, value)
199             expect(
200                 3,
201                 "1^main",
202                 "collect:DefaultNamingTest\$collectTraced5_localFun$1$2",
203                 "emit:DefaultNamingTest\$collectTraced5_localFun$1$2",
204             )
205         }
206         return runTest(finalEvent = 5) {
207             expect(1, "1^main")
208             flow {
209                     expect(2, "1^main", "collect:DefaultNamingTest\$collectTraced5_localFun$1$2")
210                     emit(42)
211                     expect(4, "1^main", "collect:DefaultNamingTest\$collectTraced5_localFun$1$2")
212                 }
213                 .collectTraced(::localFun)
214             expect(5, "1^main")
215         }
216     }
217 
218     fun memberFun(value: Int) {
219         assertEquals(42, value)
220         expect(
221             3,
222             "1^main",
223             "collect:DefaultNamingTest\$collectTraced6_memberFun$1$2",
224             "emit:DefaultNamingTest\$collectTraced6_memberFun$1$2",
225         )
226     }
227 
228     @Test
229     fun collectTraced6_memberFun() =
230         runTest(finalEvent = 5) {
231             expect(1, "1^main")
232             flow {
233                     expect(2, "1^main", "collect:DefaultNamingTest\$collectTraced6_memberFun$1$2")
234                     emit(42)
235                     expect(4, "1^main", "collect:DefaultNamingTest\$collectTraced6_memberFun$1$2")
236                 }
237                 .collectTraced(::memberFun)
238             expect(5, "1^main")
239         }
240 
241     @Test
242     fun collectTraced7_topLevelFun() =
243         runTest(finalEvent = 4) {
244             expect(1, "1^main")
245             flow {
246                     expect(2, "1^main", "collect:DefaultNamingTest\$collectTraced7_topLevelFun$1$2")
247                     emit(42)
248                     expect(3, "1^main", "collect:DefaultNamingTest\$collectTraced7_topLevelFun$1$2")
249                 }
250                 .collectTraced(::topLevelFun)
251             expect(4, "1^main")
252         }
253 
254     @Test
255     fun collectTraced8_localFlowObject() =
256         runTest(finalEvent = 5) {
257             expect(1, "1^main")
258             val flowObj =
259                 object : Flow<Int> {
260                     override suspend fun collect(collector: FlowCollector<Int>) {
261                         expect(
262                             2,
263                             "1^main",
264                             "collect:DefaultNamingTest\$collectTraced8_localFlowObject$1$1",
265                         )
266                         collector.emit(42)
267                         expect(
268                             4,
269                             "1^main",
270                             "collect:DefaultNamingTest\$collectTraced8_localFlowObject$1$1",
271                         )
272                     }
273                 }
274             flowObj.collectTraced {
275                 assertEquals(42, it)
276                 expect(
277                     3,
278                     "1^main",
279                     "collect:DefaultNamingTest\$collectTraced8_localFlowObject$1$1",
280                     "emit:DefaultNamingTest\$collectTraced8_localFlowObject$1$1",
281                 )
282             }
283             expect(5, "1^main")
284         }
285 
286     @Test
287     fun collectTraced9_flowObjectWithClassName() =
288         runTest(finalEvent = 5) {
289             expect(1, "1^main")
290             FlowWithName(this@DefaultNamingTest).collectTraced {
291                 assertEquals(42, it)
292                 expect(
293                     3,
294                     "1^main",
295                     "collect:DefaultNamingTest\$collectTraced9_flowObjectWithClassName$1$1",
296                     "emit:DefaultNamingTest\$collectTraced9_flowObjectWithClassName$1$1",
297                 )
298             }
299             expect(5, "1^main")
300         }
301 
302     @Test
303     fun collectTraced10_flowCollectorObjectWithClassName() =
304         runTest(finalEvent = 5) {
305             expect(1, "1^main")
306             flow {
307                     expect(2, "1^main", "collect:FlowCollectorWithName")
308                     emit(42)
309                     expect(4, "1^main", "collect:FlowCollectorWithName")
310                 }
311                 .collectTraced(FlowCollectorWithName(this@DefaultNamingTest))
312             expect(5, "1^main")
313         }
314 
315     @Test
316     fun collectTraced11_transform() =
317         runTest(finalEvent = 8) {
318             expect(1, "1^main")
319             flow {
320                     expect(2, "1^main", "collect:COLLECT")
321                     emit(42)
322                     expect(7, "1^main", "collect:COLLECT")
323                 }
324                 .transformTraced("TRANSFORM") {
325                     expect(3, "1^main", "collect:COLLECT", "TRANSFORM")
326                     emit(it)
327                     emit(it * 2)
328                     emit(it * 4)
329                 }
330                 .collectTraced("COLLECT") {
331                     expect(
332                         listOf(4, 5, 6),
333                         "1^main",
334                         "collect:COLLECT",
335                         "TRANSFORM",
336                         "emit:COLLECT",
337                     )
338                 }
339             expect(8, "1^main")
340         }
341 
342     @Test
343     fun collectTraced12_badTransform() =
344         runTest(
345             finalEvent = 2,
346             isExpectedException = { e ->
347                 e is java.lang.IllegalStateException &&
348                     (e.message?.startsWith("Flow invariant is violated") ?: false)
349             },
350             block = {
351                 val thread1 = bgThread1
352                 expect(1, "1^main")
353                 flow {
354                         expect(2, "1^main", "collect:COLLECT")
355                         emit(42)
356                     }
357                     .transformTraced("TRANSFORM") {
358                         // throws IllegalStateException:
359                         withContext(thread1) { emit(it * 2) } // <-- Flow invariant is violated
360                     }
361                     .collectTraced("COLLECT") {}
362             },
363         )
364 
365     @Test
366     fun coroutineBuilder_defaultNames() {
367         val localFun: suspend CoroutineScope.() -> Unit = {
368             expectAny(
369                 arrayOf("1^main:4^DefaultNamingTest\$coroutineBuilder_defaultNames\$localFun$1"),
370                 arrayOf("1^main", "DefaultNamingTest\$coroutineBuilder_defaultNames\$localFun$1"),
371                 arrayOf("1^main:2^DefaultNamingTest\$coroutineBuilder_defaultNames\$localFun$1"),
372             )
373         }
374         runTest(totalEvents = 6) {
375             launchTraced { expect("1^main:1^DefaultNamingTest\$coroutineBuilder_defaultNames$1$1") }
376                 .join()
377             launchTraced(block = localFun).join()
378             asyncTraced { expect("1^main:3^DefaultNamingTest\$coroutineBuilder_defaultNames$1$2") }
379                 .await()
380             asyncTraced(block = localFun).await()
381             withContextTraced(context = EmptyCoroutineContext) {
382                 expect("1^main", "DefaultNamingTest\$coroutineBuilder_defaultNames$1$3")
383             }
384             withContextTraced(context = EmptyCoroutineContext, block = localFun)
385         }
386     }
387 }
388 
topLevelFunnull389 fun topLevelFun(value: Int) {
390     assertEquals(42, value)
391 }
392 
393 class FlowWithName(private val test: TestBase) : Flow<Int> {
collectnull394     override suspend fun collect(collector: FlowCollector<Int>) {
395         test.expect(
396             2,
397             "1^main",
398             "collect:DefaultNamingTest\$collectTraced9_flowObjectWithClassName$1$1",
399         )
400         collector.emit(42)
401         test.expect(
402             4,
403             "1^main",
404             "collect:DefaultNamingTest\$collectTraced9_flowObjectWithClassName$1$1",
405         )
406     }
407 }
408 
409 class FlowCollectorWithName(private val test: TestBase) : FlowCollector<Int> {
emitnull410     override suspend fun emit(value: Int) {
411         assertEquals(42, value)
412         test.expect(3, "1^main", "collect:FlowCollectorWithName", "emit:FlowCollectorWithName")
413     }
414 }
415