Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d0eba995 authored by Lucas Silva's avatar Lucas Silva
Browse files

Add helper to convert a kotlin flow to a Condition

This extension function makes it easier to convert a kotlin flow to a
Condition for interoperability between the repository pattern which
utilizes flows and the Monitor framework.

Bug: 277762120
Test: atest ConditionExtensionsTest
Change-Id: I1e0b137ff87f489eaaa94799df48fcb86e882358
parent d2d746ce
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
package com.android.systemui.shared.condition

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch

/** Converts a boolean flow to a [Condition] object which can be used with a [Monitor] */
@JvmOverloads
fun Flow<Boolean>.toCondition(scope: CoroutineScope, initialValue: Boolean? = null): Condition {
    return object : Condition(initialValue, false) {
        var job: Job? = null

        override fun start() {
            job = scope.launch { collect { updateCondition(it) } }
        }

        override fun stop() {
            job?.cancel()
            job = null
        }
    }
}
+135 −0
Original line number Diff line number Diff line
package com.android.systemui.shared.condition

import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidTestingRunner::class)
class ConditionExtensionsTest : SysuiTestCase() {
    private lateinit var testScope: TestScope

    @Before
    fun setUp() {
        testScope = TestScope(StandardTestDispatcher())
    }

    @Test
    fun flowInitiallyTrue() =
        testScope.runTest {
            val flow = flowOf(true)
            val condition = flow.toCondition(this)

            runCurrent()
            assertThat(condition.isConditionSet).isFalse()

            condition.start()
            runCurrent()
            assertThat(condition.isConditionSet).isTrue()
            assertThat(condition.isConditionMet).isTrue()
        }

    @Test
    fun flowInitiallyFalse() =
        testScope.runTest {
            val flow = flowOf(false)
            val condition = flow.toCondition(this)

            runCurrent()
            assertThat(condition.isConditionSet).isFalse()

            condition.start()
            runCurrent()
            assertThat(condition.isConditionSet).isTrue()
            assertThat(condition.isConditionMet).isFalse()
        }

    @Test
    fun emptyFlowWithNoInitialValue() =
        testScope.runTest {
            val flow = emptyFlow<Boolean>()
            val condition = flow.toCondition(this)
            condition.start()

            runCurrent()
            assertThat(condition.isConditionSet).isFalse()
            assertThat(condition.isConditionMet).isFalse()
        }

    @Test
    fun emptyFlowWithInitialValueOfTrue() =
        testScope.runTest {
            val flow = emptyFlow<Boolean>()
            val condition = flow.toCondition(scope = this, initialValue = true)
            condition.start()

            runCurrent()
            assertThat(condition.isConditionSet).isTrue()
            assertThat(condition.isConditionMet).isTrue()
        }

    @Test
    fun emptyFlowWithInitialValueOfFalse() =
        testScope.runTest {
            val flow = emptyFlow<Boolean>()
            val condition = flow.toCondition(scope = this, initialValue = false)
            condition.start()

            runCurrent()
            assertThat(condition.isConditionSet).isTrue()
            assertThat(condition.isConditionMet).isFalse()
        }

    @Test
    fun conditionUpdatesWhenFlowEmitsNewValue() =
        testScope.runTest {
            val flow = MutableStateFlow(false)
            val condition = flow.toCondition(this)
            condition.start()

            runCurrent()
            assertThat(condition.isConditionSet).isTrue()
            assertThat(condition.isConditionMet).isFalse()

            flow.value = true
            runCurrent()
            assertThat(condition.isConditionMet).isTrue()

            flow.value = false
            runCurrent()
            assertThat(condition.isConditionMet).isFalse()

            condition.stop()
        }

    @Test
    fun stoppingConditionUnsubscribesFromFlow() =
        testScope.runTest {
            val flow = MutableSharedFlow<Boolean>()
            val condition = flow.toCondition(this)
            runCurrent()
            assertThat(flow.subscriptionCount.value).isEqualTo(0)

            condition.start()
            runCurrent()
            assertThat(flow.subscriptionCount.value).isEqualTo(1)

            condition.stop()
            runCurrent()
            assertThat(flow.subscriptionCount.value).isEqualTo(0)
        }
}