package org.squirrelframework.foundation.fsm;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory;
import org.squirrelframework.foundation.fsm.UntypedAnonymousAction;
import org.squirrelframework.foundation.fsm.UntypedStateMachine;
import org.squirrelframework.foundation.fsm.UntypedStateMachineBuilder;
import org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine;
/**
* Tests two interacting FSMs.
* <p>
* A state change action running on the first FSM fires an event against the
* second FSM. The second FSM's state should change accordingly.
*
* @author Jeremy.Stone
*/
public class CollaboratingStateMachineTest {
/*
* Pair of FSMs. Two states each (A and B) with transitions allowed in
* either direction.
*
* One FSM starts at A; the other at B.
*/
UntypedStateMachine fsmAtoB;
UntypedStateMachine fsmBtoA;
static class Context {
FSMEvent lastEvent;
}
final Context ctxAtoB = new Context();
final Context ctxBtoA = new Context();
enum FSMEvent {
ToA, ToB
}
static class StateMachineSample extends AbstractUntypedStateMachine {
}
/** */
@Test
public void transitionInOneFSMHasActionThatCausesTransitionInAnother() {
createFsmBtoA();
createFsmAtoB();
fsmAtoB.fire(FSMEvent.ToB, ctxAtoB);
// Check final states as well as last events seen by each FSM...
assertEquals(FSMEvent.ToB, ctxAtoB.lastEvent);
assertEquals("B", fsmAtoB.getCurrentState());
assertEquals(FSMEvent.ToA, ctxBtoA.lastEvent);
assertEquals("A", fsmBtoA.getCurrentState());
}
private void createFsmAtoB() {
UntypedStateMachineBuilder fsmAtoB_builder = StateMachineBuilderFactory.create(StateMachineSample.class);
// In transitioning from A to B trigger other FSM (fsmBtoA) to
// transition
// to A...
fsmAtoB_builder.externalTransition().from("A").to("B").on(FSMEvent.ToB)
.perform(storeEventAndFire(fsmBtoA, ctxBtoA, FSMEvent.ToA));
fsmAtoB_builder.externalTransition().from("B").to("A").on(FSMEvent.ToA).perform(storeState());
fsmAtoB = fsmAtoB_builder.newStateMachine("A");
}
private void createFsmBtoA() {
UntypedStateMachineBuilder fsmBtoA_builder = StateMachineBuilderFactory.create(StateMachineSample.class);
fsmBtoA_builder.externalTransition().from("A").to("B").on(FSMEvent.ToB).perform(storeState());
fsmBtoA_builder.externalTransition().from("B").to("A").on(FSMEvent.ToA).perform(storeState());
fsmBtoA = fsmBtoA_builder.newStateMachine("B");
}
private static UntypedAnonymousAction storeEventAndFire(final UntypedStateMachine otherFsm, final Context otherCtx,
final FSMEvent otherEvent) {
return new UntypedAnonymousAction() {
@Override
public void execute(Object from, Object to, Object event, Object context, UntypedStateMachine stateMachine) {
((Context) context).lastEvent = (FSMEvent) event;
otherFsm.fire(otherEvent, otherCtx);
}
};
}
private static UntypedAnonymousAction storeState() {
return new UntypedAnonymousAction() {
@Override
public void execute(Object from, Object to, Object event, Object context, UntypedStateMachine stateMachine) {
((Context) context).lastEvent = (FSMEvent) event;
}
};
}
}