tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit d5ae708c037d3cc3d98e439806c9fefc4266954f
parent dd1af534d51e5a4baf0c40ffddd2356b989e6be8
Author: pollymce <pmceldowney@mozilla.com>
Date:   Thu,  8 Jan 2026 09:52:14 +0000

Bug 1988984 - introduce StateFlow to Store. r=android-reviewers,sfamisa

Differential Revision: https://phabricator.services.mozilla.com/D278109

Diffstat:
Mmobile/android/android-components/components/lib/state/src/main/java/mozilla/components/lib/state/Store.kt | 18++++++++++++++----
Amobile/android/android-components/components/lib/state/src/test/java/mozilla/components/lib/state/StoreStateFlowTest.kt | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 84 insertions(+), 4 deletions(-)

diff --git a/mobile/android/android-components/components/lib/state/src/main/java/mozilla/components/lib/state/Store.kt b/mobile/android/android-components/components/lib/state/src/main/java/mozilla/components/lib/state/Store.kt @@ -6,6 +6,9 @@ package mozilla.components.lib.state import androidx.annotation.CheckResult import androidx.annotation.VisibleForTesting +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import java.lang.ref.WeakReference import java.util.Collections import java.util.concurrent.ConcurrentHashMap @@ -30,13 +33,20 @@ open class Store<S : State, A : Action>( @VisibleForTesting internal val subscriptions = Collections.newSetFromMap(ConcurrentHashMap<Subscription<S, A>, Boolean>()) - @Volatile private var currentState = initialState + private val mutableStateFlow = MutableStateFlow(initialState) /** * The current [State]. */ val state: S - get() = currentState + get() = mutableStateFlow.value + + /** + * An observable flow which will emit the store state as it updates. + * + * @return the current state as a [StateFlow] + */ + val stateFlow: StateFlow<S> = mutableStateFlow.asStateFlow() /** * Registers an [Observer] function that will be invoked whenever the [State] changes. @@ -74,8 +84,8 @@ open class Store<S : State, A : Action>( if (reducerChain == null) { var chain: (A) -> Unit = { action -> val newState = reducer(state, action) - if (newState != currentState) { - currentState = newState + if (newState != mutableStateFlow.value) { + mutableStateFlow.value = newState subscriptions.forEach { subscription -> subscription.dispatch(newState) } } } diff --git a/mobile/android/android-components/components/lib/state/src/test/java/mozilla/components/lib/state/StoreStateFlowTest.kt b/mobile/android/android-components/components/lib/state/src/test/java/mozilla/components/lib/state/StoreStateFlowTest.kt @@ -0,0 +1,70 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package mozilla.components.lib.state + +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class StoreStateFlowTest { + + @Test + fun `initial state is set in store`() { + val store = Store( + TestState(counter = 4), + ::reducer, + ) + assertEquals(4, store.state.counter) + } + + @Test + fun `can increment state in store`() { + val store = Store( + TestState(counter = 0), + ::reducer, + ) + assertEquals(0, store.state.counter) + store.dispatch(TestAction.IncrementAction) + assertEquals(1, store.state.counter) + } + + @Test + fun `stateflow exposes incrementing state`() { + val store = Store( + TestState(counter = 0), + ::reducer, + ) + assertEquals(0, store.stateFlow.value.counter) + store.dispatch(TestAction.IncrementAction) + store.dispatch(TestAction.IncrementAction) + assertEquals(2, store.stateFlow.value.counter) + } + + @Test + fun `can collect from stateflow`() = runTest { + val store = Store( + TestState(counter = 0), + ::reducer, + ) + var counter = 0 + assertEquals(0, counter) + backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) { + store.stateFlow.collect { + counter = it.counter + } + } + store.dispatch(TestAction.IncrementAction) + store.dispatch(TestAction.IncrementAction) + store.dispatch(TestAction.IncrementAction) + testScheduler.advanceUntilIdle() + assertEquals(3, counter) + } +}