diff --git a/.github/workflows/build-workflow.yml b/.github/workflows/build-workflow.yml index 112574ae..e5a1aeba 100644 --- a/.github/workflows/build-workflow.yml +++ b/.github/workflows/build-workflow.yml @@ -32,6 +32,8 @@ jobs: run: ./gradlew :formula-android:testRelease - name: Run Formula Android Instrumentation Tests run: ./gradlew :formula-android-tests:testRelease + - name: Run Formula Test Module tests + run: ./gradlew :formula-test:test - name: Run Formula Lint Tests run: ./gradlew :formula-lint:build - name: Generate Jacoco Report diff --git a/formula-test/src/main/java/com/instacart/formula/test/TestFormula.kt b/formula-test/src/main/java/com/instacart/formula/test/TestFormula.kt index c865866d..89e241af 100644 --- a/formula-test/src/main/java/com/instacart/formula/test/TestFormula.kt +++ b/formula-test/src/main/java/com/instacart/formula/test/TestFormula.kt @@ -4,7 +4,7 @@ import com.instacart.formula.Action import com.instacart.formula.Evaluation import com.instacart.formula.Formula import com.instacart.formula.Snapshot -import java.lang.IllegalStateException +import java.util.concurrent.atomic.AtomicLong /** * Test formula is used to provide a fake formula implementation. It allows you to [send][output] @@ -30,15 +30,19 @@ abstract class TestFormula : } data class State( + val uniqueIdentifier: Long, val key: Any?, val output: Output ) data class Value( + val key: Any?, val input: Input, val onNewOutput: (Output) -> Unit ) + private val identifierGenerator = AtomicLong(0) + /** * Uses initial input as key (to be decided if its robust enough) */ @@ -47,11 +51,16 @@ abstract class TestFormula : abstract fun initialOutput(): Output override fun initialState(input: Input): State { - return State(key = key(input), output = initialOutput()) + return State( + uniqueIdentifier = identifierGenerator.getAndIncrement(), + key = key(input), + output = initialOutput(), + ) } override fun Snapshot>.evaluate(): Evaluation { - stateMap[state.key] = Value( + stateMap[state.uniqueIdentifier] = Value( + key = state.key, input = input, onNewOutput = context.onEvent { transition(state.copy(output = it)) @@ -62,9 +71,8 @@ abstract class TestFormula : output = state.output, actions = context.actions { Action.onTerminate().onEvent { - transition { - stateMap.remove(state.key) - } + stateMap.remove(state.uniqueIdentifier) + none() } } ) @@ -81,9 +89,7 @@ abstract class TestFormula : } fun output(key: Any?, output: Output) { - val instance = requireNotNull(stateMap[key]) { - "Formula is not running" - } + val instance = getByKey(key) instance.onNewOutput(output) } @@ -101,9 +107,7 @@ abstract class TestFormula : * Performs an interaction on the current [Input] passed by the parent. */ fun input(key: Any?, interact: Input.() -> Unit) { - val instance = requireNotNull(stateMap[key]) { - "Formula for $key is not running, there are ${stateMap.keys} running" - } + val instance = getByKey(key) instance.input.interact() } @@ -113,4 +117,11 @@ abstract class TestFormula : throw AssertionError("Expected $expected running formulas, but there were $count instead") } } + + private fun getByKey(key: Any?): Value { + return requireNotNull(stateMap.entries.firstOrNull { it.key == key }?.value) { + val existingKeys = stateMap.entries.map { it.key } + "Formula for $key is not running, there are $existingKeys running" + } + } } \ No newline at end of file