Skip to content

Commit

Permalink
add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Nek-12 committed Feb 11, 2024
1 parent 8e2c0c7 commit 1cca8ec
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ internal class StoreImpl<S : MVIState, I : MVIIntent, A : MVIAction>(
checkNotNull(launchJob.getAndSet(null)) { "Store is closed but was not started" }
onStop(it)
}
) pipeline@{
) {
check(launchJob.getAndSet(coroutineContext.job) == null) { "Store is already started" }
launch intents@{
coroutineScope {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,16 @@ import pro.respawn.flowmvi.util.asUnconfined
import pro.respawn.flowmvi.util.idle
import pro.respawn.flowmvi.util.testStore

// TODO:
// parent store plugin
// while subscribed plugin: job cancelled, multiple subs, single sub
// subscriber manager
// subscriber count is correct
// subscriber count decrements correctly
// await subscribers
// job manager
class StorePluginTest : FreeSpec({
asUnconfined()
// TODO:
// action: emit, action()
// intent: emit, action()
// all store plugin events are invoked
// subscriber count is correct
// subscriber count decrements correctly
// disallow restart plugin
// parent store plugin
// cache plugin
// while subscribed plugin: job cancelled, multiple subs, single sub
"given test store" - {
"and recover plugin that throws".config(

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,42 @@
package pro.respawn.flowmvi.test.plugin

import io.kotest.assertions.assertSoftly
import io.kotest.assertions.throwables.shouldThrowAny
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.collections.shouldContain
import io.kotest.matchers.shouldBe
import pro.respawn.flowmvi.plugins.UndoRedo
import pro.respawn.flowmvi.plugins.undoRedoPlugin
import pro.respawn.flowmvi.util.TestAction
import pro.respawn.flowmvi.util.TestIntent
import pro.respawn.flowmvi.util.TestState
import pro.respawn.flowmvi.util.asUnconfined
import pro.respawn.flowmvi.util.idle

private fun UndoRedo.shouldBeEmpty() {
queueSize shouldBe 0
canUndo shouldBe false
canRedo shouldBe false
index.value shouldBe -1
}

class UndoRedoPluginTest : FreeSpec({
asUnconfined()
"Given undo/redo" - {
val plugin = UndoRedo(10)
var counter = 0
suspend fun redo() = plugin.invoke(true, redo = { ++counter }, undo = { --counter })
suspend fun run() = plugin.invoke(true, redo = { ++counter }, undo = { --counter })
"and when queue is empty" - {
"then undo throws" {
shouldThrowAny {
plugin.undo(true)
}
}
}
"and when redo is invoked" - {
"then block is executed" {
with(plugin) {
redo()
run()
counter shouldBe 1
isQueueEmpty shouldBe false
index.value shouldBe 0
Expand All @@ -42,7 +62,7 @@ class UndoRedoPluginTest : FreeSpec({
plugin.reset()
counter = 0
val reps = 5
repeat(5) { redo() }
repeat(5) { run() }
"then queue size matches intent count" {
plugin.index.value shouldBe reps - 1
counter shouldBe 5
Expand All @@ -56,13 +76,41 @@ class UndoRedoPluginTest : FreeSpec({
plugin.queueSize shouldBe 5
}
"then making another action replaces the redo queue" {
redo()
run()
idle()
assertSoftly {
counter shouldBe 4
plugin.queueSize shouldBe 5 - 2 + 1
plugin.index.value shouldBe 4 - 1
}
}
"then undone action can be redone" {
plugin.undo(false)
counter shouldBe 3
plugin.redo(true)
counter shouldBe 4
}
}
"and undo/redo is installed as a plugin" - {
undoRedoPlugin<TestState, TestIntent, TestAction>(plugin, resetOnException = true).test(TestState.Some) {
"and an exception is thrown" - {
val e = IllegalArgumentException()
onException(e)
"then queue is cleared" {
timeTravel.exceptions shouldContain e
counter shouldBe 4
plugin.shouldBeEmpty()
}
}
"and store is closed" - {
run()
counter shouldBe 5
onStop(null)
"then queue is cleared" {
plugin.shouldBeEmpty()
}
}
}
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@file:Suppress("UnnecessaryVariable")

package pro.respawn.flowmvi.test.store

import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.equals.shouldBeEqual
import io.kotest.matchers.equals.shouldNotBeEqual
import pro.respawn.flowmvi.util.testStore

class StoreComparisonTest : FreeSpec({
"Given two stores without names" - {
val a = testStore { name = null }
val b = testStore { name = null }
"then stores are distinct" {
a shouldNotBeEqual b
}
}
"given two stores with different names" - {
val a = testStore { name = "a" }
val b = testStore { name = "b" }
"then stores are distinct" {
a shouldNotBeEqual b
}
}
"given same store without a name" - {
val a = testStore { name = null }
val b = a
"then store is equal to itself" {
a shouldBeEqual b
}
}
"given different stores with the same name" - {
val a = testStore { name = "a" }
val b = testStore { name = "a" }
"then stores are equal" {
a shouldBeEqual b
}
"then stores are equal to themselves" {
a shouldBeEqual a
}
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import app.cash.turbine.test
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.launch
import pro.respawn.flowmvi.api.DelicateStoreApi
import pro.respawn.flowmvi.dsl.intent
import pro.respawn.flowmvi.plugins.recover
import pro.respawn.flowmvi.plugins.reduce
Expand All @@ -14,6 +15,7 @@ import pro.respawn.flowmvi.util.asUnconfined
import pro.respawn.flowmvi.util.testStore
import pro.respawn.flowmvi.util.testTimeTravel

@OptIn(DelicateStoreApi::class)
class StoreEventsTest : FreeSpec({
asUnconfined()
val plugin = testTimeTravel()
Expand All @@ -24,7 +26,7 @@ class StoreEventsTest : FreeSpec({
val store = testStore(plugin)
"then intents result in actions" {
store.subscribeAndTest {
intent { action(TestAction.Some) }
intent { send(TestAction.Some) } // use async api
actions.test {
awaitItem() shouldBe TestAction.Some
}
Expand All @@ -33,7 +35,9 @@ class StoreEventsTest : FreeSpec({
}
"and reducer that changes states" - {
val newState = TestState.SomeData(1)
val store = testStore(plugin)
val store = testStore(plugin) {
parallelIntents = true // smoke-test parallel intents as well
}
"then intents result in state change" {
store.subscribeAndTest {
states.test {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class StoreStatesTest : FreeSpec({
}
"then state is never updated by another intent" {
store.subscribeAndTest {
send(blockingIntent)
emit(blockingIntent)
intent {
updateState {
TestState.SomeData(1)
Expand Down

0 comments on commit 1cca8ec

Please sign in to comment.