FlowDelegateUsageDetectorTest.kt
3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
@file:Suppress("UnstableApiUsage", "NewObjectEquality")
package io.livekit.lint
import com.android.tools.lint.checks.infrastructure.TestFile
import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import org.junit.Test
class FlowDelegateUsageDetectorTest {
@Test
fun normalFlowAccess() {
lint()
.allowMissingSdk()
.files(
flowAccess(),
kotlin(
"""
package foo
import io.livekit.android.util.FlowObservable
import io.livekit.android.util.flow
import io.livekit.android.util.flowDelegate
class Example {
@field:FlowObservable
val value: Int by flowDelegate(0)
fun foo() {
::value.flow
return
}
}"""
).indented()
)
.issues(FlowDelegateUsageDetector.ISSUE)
.run()
.expectClean()
}
@Test
fun nonAnnotatedFlowAccess() {
lint()
.allowMissingSdk()
.files(
flowAccess(),
kotlin(
"""
package foo
import io.livekit.android.util.FlowObservable
import io.livekit.android.util.flow
import io.livekit.android.util.flowDelegate
class Example {
val value: Int by flowDelegate(0)
fun foo() {
this::value.flow
return
}
}"""
).indented()
)
.issues(FlowDelegateUsageDetector.ISSUE)
.run()
.expectErrorCount(1)
}
}
fun flowAccess(): TestFile {
return kotlin(
"""
package io.livekit.android.util
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
internal val <T> KProperty0<T>.delegate: Any?
get() { getDelegate() }
@Suppress("UNCHECKED_CAST")
val <T> KProperty0<T>.flow: StateFlow<T>
get() = delegate as StateFlow<T>
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class FlowObservable
class MutableStateFlowDelegate<T>
internal constructor(
private val flow: MutableStateFlow<T>,
private val onSetValue: ((newValue: T, oldValue: T) -> Unit)? = null
) : MutableStateFlow<T> by flow {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return flow.value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val oldValue = flow.value
flow.value = value
onSetValue?.invoke(value, oldValue)
}
}
public fun <T> flowDelegate(
initialValue: T,
onSetValue: ((newValue: T, oldValue: T) -> Unit)? = null
): MutableStateFlowDelegate<T> {
return MutableStateFlowDelegate(MutableStateFlow(initialValue), onSetValue)
}
interface StateFlow<out T> {
val value: T
}
class MutableStateFlow<T>(override var value: T) : StateFlow<T>
"""
).indented()
}