State exists in regions

- Related to work done in #221 where we got
  into trouble for properly exiting states when
  fork/join is used.
This commit is contained in:
Janne Valkealahti
2016-05-08 08:58:50 +01:00
parent 2eb8788ce3
commit 66295d9ed2
2 changed files with 54 additions and 1 deletions

View File

@@ -84,8 +84,41 @@ public class ForkJoinEntryExitTests extends AbstractBuildTests {
.expectStates("S2", "S210", "S220").and()
.step()
.sendEvent("E2")
.expectStateEntered(1)
.expectStateExited(1)
.expectStates("S2", "S211", "S220").and()
.step()
.sendEvent("E3")
.expectStateEntered(3)
.expectStateEntered(2)
.expectStateExited(4)
.expectStates("S3").and()
.build();
plan.test();
}
@Test
@SuppressWarnings("unchecked")
public void testJoinExitsViaChoices() throws Exception {
context.register(Config2.class);
context.refresh();
StateMachine<String, String> stateMachine = context.getBean(StateMachine.class);
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectState("S1").and()
.step()
.sendEvent("E1")
.expectStates("S2", "S210", "S220").and()
.step()
.sendEvent("E2")
.expectStateEntered(1)
.expectStateExited(1)
.expectStates("S2", "S211", "S220").and()
.step()
.sendEvent("E3")
.expectStateEntered(2)
.expectStateExited(4)
.expectStates("S3").and()
.build();
plan.test();

View File

@@ -45,6 +45,7 @@ import org.springframework.statemachine.region.Region;
import org.springframework.statemachine.state.AbstractState;
import org.springframework.statemachine.state.ForkPseudoState;
import org.springframework.statemachine.state.HistoryPseudoState;
import org.springframework.statemachine.state.JoinPseudoState;
import org.springframework.statemachine.state.PseudoState;
import org.springframework.statemachine.state.PseudoStateContext;
import org.springframework.statemachine.state.PseudoStateKind;
@@ -773,10 +774,18 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
@Override
public void onContext(PseudoStateContext<S, E> context) {
PseudoState<S, E> pseudoState = context.getPseudoState();
if (pseudoState.getKind() == PseudoStateKind.JOIN) {
List<State<S, E>> joins = ((JoinPseudoState<S, E>)context.getPseudoState()).getJoins();
for (State<S, E> join : joins) {
exitFromState(join, null, null, getRelayStateMachine());
}
}
State<S, E> toState = findStateWithPseudoState(pseudoState);
StateContext<S, E> stateContext = buildStateContext(Stage.STATE_EXIT, null, null, getRelayStateMachine());
pseudoState.exit(stateContext);
toState = followLinkedPseudoStates(toState, stateContext);
// should figure out what transition to use as we pass null for now
// which is then expected in exitCurrentState
switchToState(toState, null, null, getRelayStateMachine());
}
});
@@ -953,6 +962,16 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
StateMachine<S, E> submachine = ((AbstractState<S, E>)currentState).getSubmachine();
((AbstractStateMachine<S, E>)submachine).exitCurrentState(state, message, transition, stateMachine);
exitFromState(currentState, message, transition, stateMachine);
} else if (currentState.isOrthogonal()) {
Collection<Region<S,E>> regions = ((AbstractState<S, E>)currentState).getRegions();
for (Region<S,E> r : regions) {
if (r.getStates().contains(state)) {
exitFromState(r.getState(), message, transition, stateMachine);
}
}
if (transition == null) {
exitFromState(currentState, message, transition, stateMachine);
}
} else {
exitFromState(currentState, message, transition, stateMachine);
}
@@ -983,6 +1002,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
} else if (!isSubOfSource && !isSubOfTarget && findDeep == null) {
} else if (!isSubOfSource && !isSubOfTarget && (transition.getSource() == currentState && StateMachineUtils.isSubstate(currentState, transition.getTarget()))) {
} else if (StateMachineUtils.isNormalPseudoState(transition.getTarget())) {
} else if (findDeep != null && findDeep != state && findDeep.getStates().contains(state)) {
} else if (!isSubOfSource && !isSubOfTarget) {
return;
}