1 /*
2  * Copyright (C) 2022 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:Suppress("OPT_IN_USAGE")
18 
19 package com.android.systemui.coroutines
20 
21 import kotlin.coroutines.CoroutineContext
22 import kotlin.coroutines.EmptyCoroutineContext
23 import kotlin.properties.ReadOnlyProperty
24 import kotlin.reflect.KProperty
25 import kotlinx.coroutines.CoroutineStart
26 import kotlinx.coroutines.Job
27 import kotlinx.coroutines.flow.Flow
28 import kotlinx.coroutines.launch
29 import kotlinx.coroutines.test.TestScope
30 import kotlinx.coroutines.test.runCurrent
31 
32 /**
33  * Collect [flow] in a new [Job] and return a getter for the last collected value.
34  *
35  * ```
36  * fun myTest() = runTest {
37  *   // ...
38  *   val actual by collectLastValue(underTest.flow)
39  *   assertThat(actual).isEqualTo(expected)
40  * }
41  * ```
42  */
collectLastValuenull43 fun <T> TestScope.collectLastValue(
44     flow: Flow<T>,
45     context: CoroutineContext = EmptyCoroutineContext,
46     start: CoroutineStart = CoroutineStart.DEFAULT,
47 ): FlowValue<T?> {
48     val values by
49         collectValues(
50             flow = flow,
51             context = context,
52             start = start,
53         )
54     return FlowValueImpl { values.lastOrNull() }
55 }
56 
57 /**
58  * Collect [flow] in a new [Job] and return a getter for the collection of values collected.
59  *
60  * ```
61  * fun myTest() = runTest {
62  *   // ...
63  *   val values by collectValues(underTest.flow)
64  *   assertThat(values).isEqualTo(listOf(expected1, expected2, ...))
65  * }
66  * ```
67  */
collectValuesnull68 fun <T> TestScope.collectValues(
69     flow: Flow<T>,
70     context: CoroutineContext = EmptyCoroutineContext,
71     start: CoroutineStart = CoroutineStart.DEFAULT,
72 ): FlowValue<List<T>> {
73     val values = mutableListOf<T>()
74     backgroundScope.launch(context, start) { flow.collect(values::add) }
75     return FlowValueImpl {
76         runCurrent()
77         values.toList()
78     }
79 }
80 
81 /** @see collectLastValue */
82 interface FlowValue<T> : ReadOnlyProperty<Any?, T> {
invokenull83     operator fun invoke(): T
84 }
85 
86 private class FlowValueImpl<T>(private val block: () -> T) : FlowValue<T> {
87     override operator fun invoke(): T = block()
88     override fun getValue(thisRef: Any?, property: KProperty<*>): T = invoke()
89 }
90