mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-11-22 16:23:50 +00:00
complete rework of states
This commit is contained in:
parent
a1854b68d8
commit
4901a8844c
@ -16,5 +16,19 @@ kotlin {
|
|||||||
api project(":micro_utils.common.compose")
|
api project(":micro_utils.common.compose")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// androidUnitTest {
|
||||||
|
// dependencies {
|
||||||
|
// implementation libs.ui.test.junit4
|
||||||
|
// implementation libs.ui.test.manifest
|
||||||
|
// implementation libs.android.compose.material3
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// jvmTest {
|
||||||
|
// dependencies {
|
||||||
|
// implementation libs.ui.test.junit4
|
||||||
|
// implementation libs.ui.test.manifest
|
||||||
|
// implementation libs.android.compose.material3
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
<manifest/>
|
|
@ -1,46 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.coroutines.compose
|
|
||||||
|
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This type works like [MutableState], [kotlinx.coroutines.flow.StateFlow] and [kotlinx.coroutines.flow.MutableSharedFlow].
|
|
||||||
* Based on [SpecialMutableStateFlow]
|
|
||||||
*/
|
|
||||||
@Deprecated("Will be removed soon")
|
|
||||||
class FlowState<T>(
|
|
||||||
initial: T,
|
|
||||||
internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
) : MutableState<T>,
|
|
||||||
SpecialMutableStateFlow<T>(initial, internalScope) {
|
|
||||||
private var internalValue: T = initial
|
|
||||||
override var value: T
|
|
||||||
get() = internalValue
|
|
||||||
set(value) {
|
|
||||||
internalValue = value
|
|
||||||
tryEmit(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onChangeWithoutSync(value: T) {
|
|
||||||
internalValue = value
|
|
||||||
super.onChangeWithoutSync(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun component1(): T = value
|
|
||||||
|
|
||||||
override fun component2(): (T) -> Unit = { tryEmit(it) }
|
|
||||||
|
|
||||||
override fun tryEmit(value: T): Boolean {
|
|
||||||
internalValue = value
|
|
||||||
return super.tryEmit(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun emit(value: T) {
|
|
||||||
internalValue = value
|
|
||||||
super.emit(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//fun <T> MutableState<T>.asFlowState(scope: CoroutineScope = CoroutineScope(Dispatchers.Main)) = FlowState(this, scope)
|
|
24
coroutines/compose/src/jvmTest/kotlin/FlowStateTests.kt
Normal file
24
coroutines/compose/src/jvmTest/kotlin/FlowStateTests.kt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import androidx.compose.material.Button
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.ui.test.*
|
||||||
|
import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
class FlowStateTests {
|
||||||
|
@OptIn(ExperimentalTestApi::class)
|
||||||
|
@Test
|
||||||
|
@TestOnly
|
||||||
|
fun simpleTest() = runComposeUiTest {
|
||||||
|
val flowState = SpecialMutableStateFlow(0)
|
||||||
|
setContent {
|
||||||
|
Button({ flowState.value++ }) { Text("Click") }
|
||||||
|
Text(flowState.collectAsState().value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
onNodeWithText(0.toString()).assertExists()
|
||||||
|
onNodeWithText("Click").performClick()
|
||||||
|
onNodeWithText(1.toString()).assertExists()
|
||||||
|
}
|
||||||
|
}
|
@ -18,75 +18,49 @@ import kotlin.coroutines.CoroutineContext
|
|||||||
* each active subscriber
|
* each active subscriber
|
||||||
*/
|
*/
|
||||||
open class SpecialMutableStateFlow<T>(
|
open class SpecialMutableStateFlow<T>(
|
||||||
initialValue: T,
|
initialValue: T
|
||||||
internalScope: CoroutineScope
|
|
||||||
) : MutableStateFlow<T>, FlowCollector<T>, MutableSharedFlow<T> {
|
) : MutableStateFlow<T>, FlowCollector<T>, MutableSharedFlow<T> {
|
||||||
@OptIn(InternalCoroutinesApi::class)
|
@OptIn(InternalCoroutinesApi::class)
|
||||||
private val syncObject = SynchronizedObject()
|
private val syncObject = SynchronizedObject()
|
||||||
protected val internalSharedFlow: MutableSharedFlow<T> = MutableSharedFlow(
|
protected val sharingFlow: MutableSharedFlow<T> = MutableSharedFlow(
|
||||||
replay = 0,
|
|
||||||
extraBufferCapacity = 2,
|
|
||||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
|
||||||
)
|
|
||||||
protected val publicSharedFlow: MutableSharedFlow<T> = MutableSharedFlow(
|
|
||||||
replay = 1,
|
replay = 1,
|
||||||
extraBufferCapacity = 1,
|
extraBufferCapacity = 1,
|
||||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||||
)
|
)
|
||||||
|
|
||||||
protected var _value: T = initialValue
|
@OptIn(InternalCoroutinesApi::class)
|
||||||
override var value: T
|
override var value: T = initialValue
|
||||||
get() = _value
|
|
||||||
set(value) {
|
set(value) {
|
||||||
internalSharedFlow.tryEmit(value)
|
synchronized(syncObject) {
|
||||||
|
if (field != value) {
|
||||||
|
field = value
|
||||||
|
sharingFlow.tryEmit(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
protected val job = internalSharedFlow.subscribe(internalScope) {
|
|
||||||
doOnChangeAction(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val replayCache: List<T>
|
override val replayCache: List<T>
|
||||||
get() = publicSharedFlow.replayCache
|
get() = sharingFlow.replayCache
|
||||||
override val subscriptionCount: StateFlow<Int>
|
override val subscriptionCount: StateFlow<Int>
|
||||||
get() = publicSharedFlow.subscriptionCount
|
get() = sharingFlow.subscriptionCount
|
||||||
|
|
||||||
constructor(
|
|
||||||
initialValue: T,
|
|
||||||
internalContext: CoroutineContext = Dispatchers.Default
|
|
||||||
) : this(initialValue, CoroutineScope(internalContext))
|
|
||||||
|
|
||||||
@OptIn(InternalCoroutinesApi::class)
|
|
||||||
override fun compareAndSet(expect: T, update: T): Boolean {
|
override fun compareAndSet(expect: T, update: T): Boolean {
|
||||||
return synchronized(syncObject) {
|
if (expect == value) {
|
||||||
if (expect == _value && update != _value) {
|
value = update
|
||||||
doOnChangeAction(update)
|
|
||||||
}
|
|
||||||
expect == _value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun onChangeWithoutSync(value: T) {
|
|
||||||
_value = value
|
|
||||||
publicSharedFlow.tryEmit(value)
|
|
||||||
}
|
|
||||||
@OptIn(InternalCoroutinesApi::class)
|
|
||||||
protected open fun doOnChangeAction(value: T) {
|
|
||||||
synchronized(syncObject) {
|
|
||||||
if (_value != value) {
|
|
||||||
onChangeWithoutSync(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return expect == value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
@ExperimentalCoroutinesApi
|
||||||
override fun resetReplayCache() = publicSharedFlow.resetReplayCache()
|
override fun resetReplayCache() = sharingFlow.resetReplayCache()
|
||||||
|
|
||||||
override fun tryEmit(value: T): Boolean {
|
override fun tryEmit(value: T): Boolean {
|
||||||
return internalSharedFlow.tryEmit(value)
|
return compareAndSet(this.value, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun emit(value: T) {
|
override suspend fun emit(value: T) {
|
||||||
internalSharedFlow.emit(value)
|
compareAndSet(this.value, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun collect(collector: FlowCollector<T>) = publicSharedFlow.collect(collector)
|
override suspend fun collect(collector: FlowCollector<T>) = sharingFlow.collect(collector)
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,33 @@
|
|||||||
import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
|
import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
|
||||||
|
import dev.inmo.micro_utils.coroutines.asDeferred
|
||||||
import dev.inmo.micro_utils.coroutines.subscribe
|
import dev.inmo.micro_utils.coroutines.subscribe
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class SpecialMutableStateFlowTests {
|
class SpecialMutableStateFlowTests {
|
||||||
@Test
|
@Test
|
||||||
fun simpleTest() {
|
fun simpleTest() = runTest {
|
||||||
val specialMutableStateFlow = SpecialMutableStateFlow(0)
|
val specialMutableStateFlow = SpecialMutableStateFlow(0)
|
||||||
runTest {
|
specialMutableStateFlow.value = 1
|
||||||
specialMutableStateFlow.value = 1
|
specialMutableStateFlow.first { it == 1 }
|
||||||
specialMutableStateFlow.first { it == 1 }
|
|
||||||
}
|
|
||||||
assertEquals(1, specialMutableStateFlow.value)
|
assertEquals(1, specialMutableStateFlow.value)
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
fun specialTest() {
|
fun specialTest() = runTest {
|
||||||
val specialMutableStateFlow = SpecialMutableStateFlow(0)
|
val specialMutableStateFlow = SpecialMutableStateFlow(0)
|
||||||
runTest {
|
lateinit var subscriberJob: Job
|
||||||
lateinit var subscriberJob: Job
|
subscriberJob = specialMutableStateFlow.subscribe(this) {
|
||||||
subscriberJob = specialMutableStateFlow.subscribe(this) {
|
when (it) {
|
||||||
when (it) {
|
1 -> specialMutableStateFlow.value = 2
|
||||||
1 -> specialMutableStateFlow.value = 2
|
2 -> subscriberJob.cancel()
|
||||||
2 -> subscriberJob.cancel()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
specialMutableStateFlow.value = 1
|
|
||||||
subscriberJob.join()
|
|
||||||
}
|
}
|
||||||
|
specialMutableStateFlow.value = 1
|
||||||
|
subscriberJob.join()
|
||||||
assertEquals(2, specialMutableStateFlow.value)
|
assertEquals(2, specialMutableStateFlow.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -28,8 +28,8 @@ kotlin {
|
|||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin('stdlib')
|
implementation kotlin('stdlib')
|
||||||
api libs.kt.serialization
|
|
||||||
implementation compose.runtime
|
implementation compose.runtime
|
||||||
|
api libs.kt.serialization
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commonTest {
|
commonTest {
|
||||||
@ -47,6 +47,7 @@ kotlin {
|
|||||||
jvmTest {
|
jvmTest {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin('test-junit')
|
implementation kotlin('test-junit')
|
||||||
|
implementation compose.uiTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
@ -61,6 +62,14 @@ kotlin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
androidUnitTest {
|
androidUnitTest {
|
||||||
|
dependencies {
|
||||||
|
implementation kotlin('test-junit')
|
||||||
|
implementation libs.android.test.junit
|
||||||
|
implementation libs.android.espresso
|
||||||
|
implementation compose.uiTest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
androidInstrumentedTest {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin('test-junit')
|
implementation kotlin('test-junit')
|
||||||
implementation libs.android.test.junit
|
implementation libs.android.test.junit
|
||||||
|
Loading…
Reference in New Issue
Block a user