package org.squirrelframework.foundation.issues; import org.junit.*; import org.squirrelframework.foundation.component.SquirrelPostProcessorProvider; import org.squirrelframework.foundation.fsm.*; import org.squirrelframework.foundation.fsm.annotation.State; import org.squirrelframework.foundation.fsm.annotation.States; import org.squirrelframework.foundation.fsm.annotation.Transit; import org.squirrelframework.foundation.fsm.annotation.Transitions; import org.squirrelframework.foundation.fsm.impl.AbstractStateMachine; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; public class Issue33 { public enum HState { A, A1, A2, A2a, A2a1, A2a2, B } public enum HEvent { A2B, B2A, A12A2, A2a12A2a2 } @States({ @State(name="A", entryCallMethod="enterA", exitCallMethod="leftA", historyType = HistoryType.DEEP), @State(parent="A", name="A1", entryCallMethod="enterA1", exitCallMethod="leftA1", initialState = true), @State(parent="A", name="A2", entryCallMethod="enterA2", exitCallMethod="leftA2"), @State(parent="A2", name="A2a", entryCallMethod="enterA2a", exitCallMethod="leftA2a", initialState = true), @State(parent="A2a", name="A2a1", entryCallMethod="enterA2a1", exitCallMethod="leftA2a1", initialState = true), @State(parent = "A2a", name="A2a2", entryCallMethod="enterA2a2", exitCallMethod="leftA2a2"), @State(name="B", entryCallMethod="enterB", exitCallMethod="leftB") }) @Transitions({ @Transit(from="A1", to="A2", on="A12A2", callMethod="transitA12A2"), @Transit(from="A2a1", to="A2a2", on="A2a12A2a2", callMethod="transitA2a12A2a2"), @Transit(from="A", to="B", on="A2B", callMethod="transitA2B"), @Transit(from="B", to="A", on="B2A", callMethod="transitB2A") }) static class HierarchicalStateMachine extends AbstractStateMachine<HierarchicalStateMachine, HState, HEvent, Integer> { private StringBuilder logger = new StringBuilder(); public void entryA(HState from, HState to, HEvent event, Integer context) { logger.append("entryA"); } public void exitA(HState from, HState to, HEvent event, Integer context) { logger.append("exitA"); } public void transitA2a12A2a2(HState from, HState to, HEvent event, Integer context) { logger.append("transitA2a12A2a2"); } public void transitA12A2(HState from, HState to, HEvent event, Integer context) { logger.append("transitA12A2"); } public void entryA1(HState from, HState to, HEvent event, Integer context) { logger.append("entryA1"); } public void exitA1(HState from, HState to, HEvent event, Integer context) { logger.append("exitA1"); } public void entryA2(HState from, HState to, HEvent event, Integer context) { logger.append("entryA2"); } public void exitA2(HState from, HState to, HEvent event, Integer context) { logger.append("exitA2"); } public void entryA2a(HState from, HState to, HEvent event, Integer context) { logger.append("entryA2a"); } public void exitA2a(HState from, HState to, HEvent event, Integer context) { logger.append("exitA2a"); } public void entryB(HState from, HState to, HEvent event, Integer context) { logger.append("entryB"); } public void exitB(HState from, HState to, HEvent event, Integer context) { logger.append("exitB"); } public void enterA2a1(HState from, HState to, HEvent event, Integer context) { logger.append("enterA2a1"); } public void leftA2a1(HState from, HState to, HEvent event, Integer context) { logger.append("leftA2a1"); } public void enterA2a2(HState from, HState to, HEvent event, Integer context) { logger.append("enterA2a2"); } public void leftA2a2(HState from, HState to, HEvent event, Integer context) { logger.append("leftA2a2"); } public void transitA2B(HState from, HState to, HEvent event, Integer context) { logger.append("transitA2B"); } public void transitB2A(HState from, HState to, HEvent event, Integer context) { logger.append("transitB2A"); } @Override protected void beforeActionInvoked(HState from, HState to, HEvent event, Integer context) { addOptionalDot(); } private void addOptionalDot() { if (logger.length() > 0) { logger.append('.'); } } public String consumeLog() { final String result = logger.toString(); logger = new StringBuilder(); return result; } } HierarchicalStateMachine stateMachine; StateMachineLogger fsmLogger; @BeforeClass public static void beforeTest() { } @AfterClass public static void afterTest() { ConverterProvider.INSTANCE.clearRegistry(); SquirrelPostProcessorProvider.getInstance().clearRegistry(); } @After public void teardown() { if(stateMachine.getStatus()!= StateMachineStatus.TERMINATED) stateMachine.terminate(null); System.out.println("-------------------------------------------------"); } @Before public void setup() { StateMachineBuilder<HierarchicalStateMachine, HState, HEvent, Integer> builder = StateMachineBuilderFactory.create(HierarchicalStateMachine.class, HState.class, HEvent.class, Integer.class, new Class<?>[0]); stateMachine = builder.newStateMachine(HState.A, StateMachineConfiguration.create().enableDebugMode(true), new Object[0]); } @Test public void testBasicHierarchicalState() { stateMachine.start(); assertThat(stateMachine.consumeLog(), is(equalTo("entryA..entryA1"))); assertThat(stateMachine.getCurrentState(), is(equalTo(HState.A1))); stateMachine.fire(HEvent.A12A2); assertThat(stateMachine.consumeLog(), is(equalTo("exitA1.transitA12A2..entryA2..entryA2a.enterA2a1"))); assertThat(stateMachine.getCurrentState(), is(equalTo(HState.A2a1))); stateMachine.fire(HEvent.A2a12A2a2); assertThat(stateMachine.consumeLog(), is(equalTo("leftA2a1.transitA2a12A2a2.enterA2a2"))); assertThat(stateMachine.getCurrentState(), is(equalTo(HState.A2a2))); stateMachine.fire(HEvent.A2B); assertThat(stateMachine.consumeLog(), is(equalTo("leftA2a2..exitA2a..exitA2..exitA.transitA2B..entryB"))); assertThat(stateMachine.getCurrentState(), is(equalTo(HState.B))); stateMachine.fire(HEvent.B2A); assertThat(stateMachine.consumeLog(), is(equalTo("exitB.transitB2A..entryA..entryA2..entryA2a.enterA2a2"))); assertThat(stateMachine.getCurrentState(), is(equalTo(HState.A2a2))); } }