summaryrefslogtreecommitdiffstats
path: root/mobile/android/android-components/components/support/test-libstate
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
commitd8bbc7858622b6d9c278469aab701ca0b609cddf (patch)
treeeff41dc61d9f714852212739e6b3738b82a2af87 /mobile/android/android-components/components/support/test-libstate
parentReleasing progress-linux version 125.0.3-1~progress7.99u1. (diff)
downloadfirefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz
firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/android-components/components/support/test-libstate')
-rw-r--r--mobile/android/android-components/components/support/test-libstate/README.md19
-rw-r--r--mobile/android/android-components/components/support/test-libstate/build.gradle45
-rw-r--r--mobile/android/android-components/components/support/test-libstate/src/main/AndroidManifest.xml4
-rw-r--r--mobile/android/android-components/components/support/test-libstate/src/main/java/mozilla/components/support/test/libstate/ext/Store.kt26
-rw-r--r--mobile/android/android-components/components/support/test-libstate/src/main/java/mozilla/components/support/test/middleware/CaptureActionsMiddleware.kt88
-rw-r--r--mobile/android/android-components/components/support/test-libstate/src/test/java/mozilla/components/support/test/libstate/ext/StoreTest.kt43
6 files changed, 225 insertions, 0 deletions
diff --git a/mobile/android/android-components/components/support/test-libstate/README.md b/mobile/android/android-components/components/support/test-libstate/README.md
new file mode 100644
index 0000000000..2756befdfc
--- /dev/null
+++ b/mobile/android/android-components/components/support/test-libstate/README.md
@@ -0,0 +1,19 @@
+# [Android Components](../../../README.md) > Support > Test
+
+A collection of helpers for testing functionality that relies on the lib-state component in local unit tests (`src/test`).
+
+## Usage
+
+### Setting up the dependency
+
+Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)):
+
+```Groovy
+implementation "org.mozilla.components:support-test-libstate:{latest-version}"
+```
+
+## License
+
+ 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/
diff --git a/mobile/android/android-components/components/support/test-libstate/build.gradle b/mobile/android/android-components/components/support/test-libstate/build.gradle
new file mode 100644
index 0000000000..b67bf66a7a
--- /dev/null
+++ b/mobile/android/android-components/components/support/test-libstate/build.gradle
@@ -0,0 +1,45 @@
+/* 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/. */
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ defaultConfig {
+ minSdkVersion config.minSdkVersion
+ compileSdk config.compileSdkVersion
+ targetSdkVersion config.targetSdkVersion
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt')
+ }
+ }
+
+ lint {
+ // Disabled since this caused issues with Android Gradle Plugin 3.2.1+ (NullPointerException:InvalidPackageDetector)
+ tasks.lint.enabled = false
+
+ lintConfig file("lint.xml")
+ }
+
+ namespace 'mozilla.components.support.test.libstate'
+}
+
+dependencies {
+ implementation ComponentsDependencies.kotlin_coroutines
+ implementation project(':lib-state')
+
+ testImplementation ComponentsDependencies.androidx_core
+ testImplementation ComponentsDependencies.androidx_test_junit
+}
+
+apply from: '../../../android-lint.gradle'
+apply from: '../../../publish.gradle'
+ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description)
+
diff --git a/mobile/android/android-components/components/support/test-libstate/src/main/AndroidManifest.xml b/mobile/android/android-components/components/support/test-libstate/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..41078a7325
--- /dev/null
+++ b/mobile/android/android-components/components/support/test-libstate/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<!-- 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/. -->
+<manifest />
diff --git a/mobile/android/android-components/components/support/test-libstate/src/main/java/mozilla/components/support/test/libstate/ext/Store.kt b/mobile/android/android-components/components/support/test-libstate/src/main/java/mozilla/components/support/test/libstate/ext/Store.kt
new file mode 100644
index 0000000000..8620ca6e90
--- /dev/null
+++ b/mobile/android/android-components/components/support/test-libstate/src/main/java/mozilla/components/support/test/libstate/ext/Store.kt
@@ -0,0 +1,26 @@
+/* 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.support.test.libstate.ext
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.runBlocking
+import mozilla.components.lib.state.Action
+import mozilla.components.lib.state.State
+import mozilla.components.lib.state.Store
+
+/**
+ * Blocks and returns once all dispatched actions have been processed
+ * i.e. the reducers have run and all observers have been notified of
+ * state changes.
+ */
+fun <S : State, A : Action> Store<S, A>.waitUntilIdle() {
+ val scopeField = Store::class.java.getDeclaredField("scope")
+ scopeField.isAccessible = true
+ val scope = scopeField.get(this) as CoroutineScope
+ runBlocking {
+ scope.coroutineContext[Job]?.children?.forEach { it.join() }
+ }
+}
diff --git a/mobile/android/android-components/components/support/test-libstate/src/main/java/mozilla/components/support/test/middleware/CaptureActionsMiddleware.kt b/mobile/android/android-components/components/support/test-libstate/src/main/java/mozilla/components/support/test/middleware/CaptureActionsMiddleware.kt
new file mode 100644
index 0000000000..55670446d0
--- /dev/null
+++ b/mobile/android/android-components/components/support/test-libstate/src/main/java/mozilla/components/support/test/middleware/CaptureActionsMiddleware.kt
@@ -0,0 +1,88 @@
+/* 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.support.test.middleware
+
+import mozilla.components.lib.state.Action
+import mozilla.components.lib.state.Middleware
+import mozilla.components.lib.state.MiddlewareContext
+import mozilla.components.lib.state.State
+import kotlin.reflect.KClass
+
+/**
+ * A [Middleware] implementation for unit tests that want to inspect actions dispatched on a `Store`
+ */
+class CaptureActionsMiddleware<S : State, A : Action> : Middleware<S, A> {
+ private val capturedActions = mutableListOf<A>()
+
+ @Synchronized
+ override fun invoke(context: MiddlewareContext<S, A>, next: (A) -> Unit, action: A) {
+ capturedActions.add(action)
+ next(action)
+ }
+
+ /**
+ * Returns the first action of type [clazz] that was dispatched on the store. Throws
+ * [AssertionError] if no such action was dispatched.
+ */
+ @Synchronized
+ @Suppress("UNCHECKED_CAST")
+ fun <X : A> findFirstAction(clazz: KClass<X>): X {
+ return capturedActions.firstOrNull { it.javaClass == clazz.java } as? X
+ ?: throw AssertionError("No action of type $clazz found")
+ }
+
+ /**
+ * Returns the last action of type [clazz] that was dispatched on the store. Throws
+ * [AssertionError] if no such action was dispatched.
+ */
+ @Synchronized
+ @Suppress("UNCHECKED_CAST")
+ fun <X : A> findLastAction(clazz: KClass<X>): X {
+ return capturedActions.lastOrNull { it.javaClass == clazz.java } as? X
+ ?: throw AssertionError("No action of type $clazz found")
+ }
+
+ /**
+ * Asserts that an action of type [clazz] was dispatched and optionally executes a given [block]
+ * with the first action of type [clazz] that was dispatched on the store. Throws [AssertionError]
+ * if no such action was dispatched.
+ */
+ @Synchronized
+ fun <X : A> assertFirstAction(clazz: KClass<X>, block: (X) -> Unit = {}) {
+ val action = findFirstAction(clazz)
+ block(action)
+ }
+
+ /**
+ * Executes the given [block] with the last action of type [clazz] that was dispatched on the
+ * store. Throws [AssertionError] if no such action was dispatched.
+ */
+ @Synchronized
+ fun <X : A> assertLastAction(clazz: KClass<X>, block: (X) -> Unit) {
+ val action = findLastAction(clazz)
+ block(action)
+ }
+
+ /**
+ * Asserts that no action of type [clazz] was dispatched. Throws [AssertionError] if a matching
+ * action was found.
+ */
+ @Synchronized
+ fun <X : A> assertNotDispatched(clazz: KClass<X>) {
+ if (!capturedActions.none { it.javaClass == clazz.java }) {
+ throw AssertionError("Action of type $clazz was dispatched: ${findFirstAction(clazz)}")
+ }
+ }
+
+ /**
+ * Resets the remembered list of actions.
+ *
+ * Usually this is called between test runs to avoid verifying actions of a previous test methods.
+ */
+ @Synchronized
+ fun reset() {
+ capturedActions.clear()
+ }
+}
diff --git a/mobile/android/android-components/components/support/test-libstate/src/test/java/mozilla/components/support/test/libstate/ext/StoreTest.kt b/mobile/android/android-components/components/support/test-libstate/src/test/java/mozilla/components/support/test/libstate/ext/StoreTest.kt
new file mode 100644
index 0000000000..516206215e
--- /dev/null
+++ b/mobile/android/android-components/components/support/test-libstate/src/test/java/mozilla/components/support/test/libstate/ext/StoreTest.kt
@@ -0,0 +1,43 @@
+/* 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.support.test.libstate.ext
+
+import mozilla.components.lib.state.Action
+import mozilla.components.lib.state.State
+import mozilla.components.lib.state.Store
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class StoreTest {
+
+ @Test
+ fun `waitUntilIdle blocks and returns once reducers were executed`() {
+ val store = Store(
+ TestState(counter = 23),
+ ::reducer,
+ )
+
+ store.dispatch(TestAction.IncrementAction)
+ store.waitUntilIdle()
+ assertEquals(24, store.state.counter)
+
+ store.dispatch(TestAction.DecrementAction)
+ store.dispatch(TestAction.DecrementAction)
+ store.waitUntilIdle()
+ assertEquals(22, store.state.counter)
+ }
+}
+
+fun reducer(state: TestState, action: TestAction): TestState = when (action) {
+ is TestAction.IncrementAction -> state.copy(counter = state.counter + 1)
+ is TestAction.DecrementAction -> state.copy(counter = state.counter - 1)
+}
+
+data class TestState(val counter: Int) : State
+
+sealed class TestAction : Action {
+ object IncrementAction : TestAction()
+ object DecrementAction : TestAction()
+}