diff --git a/formula/src/main/java/com/instacart/formula/internal/FormulaManagerImpl.kt b/formula/src/main/java/com/instacart/formula/internal/FormulaManagerImpl.kt
index cfa0704d..c880738b 100644
--- a/formula/src/main/java/com/instacart/formula/internal/FormulaManagerImpl.kt
+++ b/formula/src/main/java/com/instacart/formula/internal/FormulaManagerImpl.kt
@@ -279,6 +279,10 @@ internal class FormulaManagerImpl(
* @return True if we need to re-evaluate.
*/
private fun postEvaluation(evaluationId: Long): Boolean {
+ if (isEvaluationNeeded(evaluationId)) {
+ return true
+ }
+
if (handleTransitionQueue(evaluationId)) {
return true
}
diff --git a/formula/src/test/java/com/instacart/formula/FormulaRuntimeTest.kt b/formula/src/test/java/com/instacart/formula/FormulaRuntimeTest.kt
index aaf090a2..2252e52c 100644
--- a/formula/src/test/java/com/instacart/formula/FormulaRuntimeTest.kt
+++ b/formula/src/test/java/com/instacart/formula/FormulaRuntimeTest.kt
@@ -26,11 +26,13 @@ import com.instacart.formula.subjects.EventFormula
import com.instacart.formula.subjects.ExtremelyNestedFormula
import com.instacart.formula.subjects.FromObservableWithInputFormula
import com.instacart.formula.subjects.HasChildFormula
+import com.instacart.formula.subjects.MultiChildIndirectStateChangeRobot
import com.instacart.formula.subjects.InputChangeWhileFormulaRunningRobot
import com.instacart.formula.subjects.KeyUsingListFormula
import com.instacart.formula.subjects.MessageFormula
import com.instacart.formula.subjects.MixingCallbackUseWithKeyUse
import com.instacart.formula.subjects.MultipleChildEvents
+import com.instacart.formula.subjects.NestedCallbackCallRobot
import com.instacart.formula.subjects.NestedChildTransitionAfterNoEvaluationPass
import com.instacart.formula.subjects.NestedKeyFormula
import com.instacart.formula.subjects.NestedTerminationWithInputChanged
@@ -41,6 +43,7 @@ import com.instacart.formula.subjects.OptionalChildFormula
import com.instacart.formula.subjects.OptionalEventCallbackFormula
import com.instacart.formula.subjects.ParallelChildFormulaFiresEventOnStart
import com.instacart.formula.subjects.ParentTransitionOnChildActionStart
+import com.instacart.formula.subjects.ParentUpdateChildAndSelfOnEventRobot
import com.instacart.formula.subjects.PendingActionFormulaTerminatedOnActionInit
import com.instacart.formula.subjects.RemovingTerminateStreamSendsNoMessagesFormula
import com.instacart.formula.subjects.RootFormulaKeyTestSubject
@@ -673,6 +676,35 @@ class FormulaRuntimeTest(val runtime: TestableRuntime, val name: String) {
.assertCurrentValue(2)
}
+ @Test fun `parent updates a child and self in a single action`() {
+ val robot = ParentUpdateChildAndSelfOnEventRobot(runtime)
+ robot.start()
+ robot.subject.output { onAction() }
+ robot.subject.output {
+ assertThat(childValue).isEqualTo(1)
+ assertThat(parentValue).isEqualTo(3)
+ }
+ }
+
+ @Test
+ fun `formula calls an event listener from a transition`() {
+ val robot = NestedCallbackCallRobot(runtime)
+ robot.start()
+ robot.subject.output { onAction() }
+ robot.subject.output { assertThat(value).isEqualTo(1) }
+ }
+
+ @Test
+ fun `formula calls own event listener which starts multiple transitions`() {
+ val robot = MultiChildIndirectStateChangeRobot(runtime)
+ robot.start()
+ robot.subject.output { onAction() }
+ robot.subject.output {
+ assertThat(childValue).isEqualTo(2)
+ assertThat(parentValue).isEqualTo(3)
+ }
+ }
+
@Test
fun `action runAgain`() {
val inspector = CountingInspector()
diff --git a/formula/src/test/java/com/instacart/formula/subjects/MultiChildIndirectStateChangeRobot.kt b/formula/src/test/java/com/instacart/formula/subjects/MultiChildIndirectStateChangeRobot.kt
new file mode 100644
index 00000000..b35c9cc6
--- /dev/null
+++ b/formula/src/test/java/com/instacart/formula/subjects/MultiChildIndirectStateChangeRobot.kt
@@ -0,0 +1,120 @@
+package com.instacart.formula.subjects
+
+import com.instacart.formula.Evaluation
+import com.instacart.formula.Formula
+import com.instacart.formula.Snapshot
+import com.instacart.formula.rxjava3.RxAction
+import com.instacart.formula.test.TestableRuntime
+import io.reactivex.rxjava3.core.Observable
+
+class MultiChildIndirectStateChangeRobot(runtime: TestableRuntime) {
+ val subject = runtime.test(Parent())
+
+ fun start() = apply {
+ subject.input(Unit)
+ }
+
+ class Child : Formula() {
+ data class Input(
+ val preAction: () -> Unit = {},
+ )
+
+ data class State(
+ val actionId: Int = 0,
+ val value: Int = 0,
+ )
+
+ data class Output(
+ val value: Int,
+ val onChildAction: () -> Unit,
+ )
+
+ override fun initialState(input: Input): State = State()
+
+ override fun Snapshot.evaluate(): Evaluation