package org.squirrelframework.foundation.fsm; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.squirrelframework.foundation.fsm.annotation.ContextInsensitive; import org.squirrelframework.foundation.fsm.annotation.State; import org.squirrelframework.foundation.fsm.annotation.StateMachineParameters; 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.AbstractUntypedStateMachine; public class WeightedActionTest { @Transitions({ @Transit(from="A", to="B", on="ToB", callMethod="fromAToB"), @Transit(from="A", to="C", on="ToC", callMethod="fromAToC"), @Transit(from="A", to="D", on="ToD") }) @States({ @State(name="D", entryCallMethod="entryD") // test duplicate extension method definition }) @StateMachineParameters(stateType=String.class, eventType=String.class, contextType=Void.class) @ContextInsensitive static class UntypedStateMachineBase extends AbstractUntypedStateMachine { protected StringBuilder logger = new StringBuilder(); protected void fromAToB(String from, String to, String event) { logger.append("fromAToB"); } protected void transitFromAToBOnToB(String from, String to, String event) { logger.append("transitFromAToBOnToB"); } protected void fromAToC(String from, String to, String event) { logger.append("fromAToC"); } protected void entryD(String from, String to, String event) { logger.append("entryD"); } @Override protected void beforeActionInvoked(Object fromState, Object toState, Object event, Object context) { addOptionalDot(); } private void addOptionalDot() { if (logger.length() > 0) { logger.append('.'); } } public String consumeLog() { final String result = logger.toString(); logger = new StringBuilder(); return result; } } @Transitions({ @Transit(from="A", to="B", on="ToB", callMethod="beforeFromAToB"), @Transit(from="A", to="B", on="ToB", callMethod="afterFromAToB"), @Transit(from="A", to="C", on="ToC", callMethod="goAToC1:+150"), @Transit(from="A", to="C", on="ToC", callMethod="goAToC2:-150"), }) @States({ @State(name="D", entryCallMethod="beforeEntryD"), @State(name="D", entryCallMethod="goEntryD:-150"), }) static class UntypedStateMachineExt extends UntypedStateMachineBase { protected void beforeFromAToB(String from, String to, String event) { logger.append("beforeFromAToB"); } protected void afterFromAToB(String from, String to, String event) { logger.append("afterFromAToB"); } protected void goAToC1(String from, String to, String event) { logger.append("goAToC1"); } protected void goAToC2(String from, String to, String event) { logger.append("goAToC2"); } protected void beforeEntryD(String from, String to, String event) { logger.append("beforeEntryD"); } protected void goEntryD(String from, String to, String event) { for(int i=0; i<10000; ++i) { RandomStringUtils.randomAlphabetic(10); } logger.append("goEntryD"); } } @States({ @State(name="D", entryCallMethod="entryD:+200") // override extension method weight }) static class UntypedStateMachineExt2 extends UntypedStateMachineExt { } private UntypedStateMachineExt fsm; private StateMachineLogger logger; @After public void teardown() { if(fsm.getStatus()!=StateMachineStatus.TERMINATED) fsm.terminate(null); } @Before public void setup() { UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(UntypedStateMachineExt.class); fsm = builder.newUntypedStateMachine("A", StateMachineConfiguration.create().enableDebugMode(true), new Object[0]); } @Test public void testBeforeExtension() { fsm.fire("ToB"); assertThat(fsm.consumeLog(), is(equalTo("beforeFromAToB.fromAToB.transitFromAToBOnToB.afterFromAToB"))); } @Test public void testWeightTransitionAction() { fsm.fire("ToC"); assertThat(fsm.consumeLog(), is(equalTo("goAToC1.fromAToC.goAToC2"))); } @Test public void testWeightStateAction() { fsm.fire("ToD"); assertThat(fsm.consumeLog(), is(equalTo("beforeEntryD.entryD.goEntryD"))); } @Test public void testOverrideWeight() { UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(UntypedStateMachineExt2.class); fsm = builder.newUntypedStateMachine("A"); logger = new StateMachineLogger(fsm); logger.startLogging(); fsm.fire("ToD"); assertThat(fsm.consumeLog(), is(equalTo("entryD.beforeEntryD.goEntryD"))); } @Test public void testIgnoreWeight() { UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(UntypedStateMachineExt2.class); builder.onEntry("D").callMethod("entryD:ignore"); // entryD will not be invoked fsm = builder.newUntypedStateMachine("A"); logger = new StateMachineLogger(fsm); logger.startLogging(); fsm.fire("ToD"); assertThat(fsm.consumeLog(), is(equalTo("beforeEntryD.goEntryD"))); } }