Initial state wrongly entered with entry point
- Fix case where submachine entry via entrypoint wrongly caused its initial state to get activated before entering transition target leading out from entrypoint. - Fixes #577
This commit is contained in:
@@ -216,6 +216,16 @@ public class StateMachineState<S, E> extends AbstractState<S, E> {
|
||||
getSubmachine().getStateMachineAccessor().doWithRegion(
|
||||
new StateMachineFunction<StateMachineAccess<S, E>>() {
|
||||
|
||||
@Override
|
||||
public void apply(StateMachineAccess<S, E> function) {
|
||||
function.setInitialEnabled(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (immediateDeepParent == null && getSubmachine().getStates().contains(target) && isEntry(target)) {
|
||||
getSubmachine().getStateMachineAccessor().doWithRegion(
|
||||
new StateMachineFunction<StateMachineAccess<S, E>>() {
|
||||
|
||||
@Override
|
||||
public void apply(StateMachineAccess<S, E> function) {
|
||||
function.setInitialEnabled(false);
|
||||
@@ -230,6 +240,10 @@ public class StateMachineState<S, E> extends AbstractState<S, E> {
|
||||
return state.getPseudoState() != null && state.getPseudoState().getKind() == PseudoStateKind.INITIAL;
|
||||
}
|
||||
|
||||
private boolean isEntry(State<S, E> state) {
|
||||
return state.getPseudoState() != null && state.getPseudoState().getKind() == PseudoStateKind.ENTRY;
|
||||
}
|
||||
|
||||
private State<S, E> findDeepParent(Collection<State<S, E>> states, State<S, E> state) {
|
||||
for (State<S, E> s : states) {
|
||||
if (s.getStates().contains(state)) {
|
||||
|
||||
@@ -19,6 +19,9 @@ import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -29,6 +32,7 @@ import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
|
||||
|
||||
public class ExitEntryStateTests extends AbstractStateMachineTests {
|
||||
|
||||
@@ -40,14 +44,38 @@ public class ExitEntryStateTests extends AbstractStateMachineTests {
|
||||
StateMachine<String, String> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, StateMachine.class);
|
||||
assertThat(machine, notNullValue());
|
||||
TestStateEntryExitListener listener = new TestStateEntryExitListener();
|
||||
machine.addStateListener(listener);
|
||||
machine.start();
|
||||
assertThat(machine.getState().getIds(), contains("S1"));
|
||||
listener.reset();
|
||||
machine.sendEvent("ENTRY1");
|
||||
assertThat(machine.getState().getIds(), contains("S2", "S22"));
|
||||
assertThat(listener.exited, contains("S1"));
|
||||
assertThat(listener.entered, contains("S2", "S22"));
|
||||
machine.sendEvent("EXIT1");
|
||||
assertThat(machine.getState().getIds(), contains("S4"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testSimpleEntryToInitial() {
|
||||
context.register(Config1.class);
|
||||
context.refresh();
|
||||
StateMachine<String, String> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, StateMachine.class);
|
||||
assertThat(machine, notNullValue());
|
||||
TestStateEntryExitListener listener = new TestStateEntryExitListener();
|
||||
machine.addStateListener(listener);
|
||||
machine.start();
|
||||
assertThat(machine.getState().getIds(), contains("S1"));
|
||||
listener.reset();
|
||||
machine.sendEvent("ENTRY3");
|
||||
assertThat(machine.getState().getIds(), contains("S2", "S21"));
|
||||
assertThat(listener.exited, contains("S1"));
|
||||
assertThat(listener.entered, contains("S2", "S21"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testMultipleExitsToSameState() {
|
||||
@@ -92,6 +120,7 @@ public class ExitEntryStateTests extends AbstractStateMachineTests {
|
||||
.initial("S21")
|
||||
.entry("S2ENTRY1")
|
||||
.entry("S2ENTRY2")
|
||||
.entry("S2ENTRY3")
|
||||
.exit("S2EXIT1")
|
||||
.exit("S2EXIT2")
|
||||
.state("S22")
|
||||
@@ -117,6 +146,10 @@ public class ExitEntryStateTests extends AbstractStateMachineTests {
|
||||
.source("S1").target("S2ENTRY2")
|
||||
.event("ENTRY2")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("S1").target("S2ENTRY3")
|
||||
.event("ENTRY3")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("S22").target("S2EXIT1")
|
||||
.event("EXIT1")
|
||||
@@ -131,6 +164,9 @@ public class ExitEntryStateTests extends AbstractStateMachineTests {
|
||||
.withEntry()
|
||||
.source("S2ENTRY2").target("S23")
|
||||
.and()
|
||||
.withEntry()
|
||||
.source("S2ENTRY3").target("S21")
|
||||
.and()
|
||||
.withExit()
|
||||
.source("S2EXIT1").target("S4")
|
||||
.and()
|
||||
@@ -197,6 +233,26 @@ public class ExitEntryStateTests extends AbstractStateMachineTests {
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestStateEntryExitListener extends StateMachineListenerAdapter<String, String> {
|
||||
|
||||
List<String> entered = new ArrayList<>();
|
||||
List<String> exited = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void stateEntered(State<String, String> state) {
|
||||
entered.add(state.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateExited(State<String, String> state) {
|
||||
exited.add(state.getId());
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
entered.clear();
|
||||
exited.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AnnotationConfigApplicationContext buildContext() {
|
||||
|
||||
Reference in New Issue
Block a user