package org.squirrelframework.foundation.fsm;
import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.squirrelframework.foundation.exception.SquirrelRuntimeException;
import org.squirrelframework.foundation.exception.TransitionException;
import org.squirrelframework.foundation.fsm.annotation.ContextEvent;
import org.squirrelframework.foundation.fsm.annotation.ContextInsensitive;
import org.squirrelframework.foundation.fsm.annotation.StateMachineParameters;
import org.squirrelframework.foundation.fsm.annotation.Transit;
import org.squirrelframework.foundation.fsm.annotation.Transitions;
import org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine;
public class StateMachineExceptionTest {
@Transitions({
@Transit(from="A", to="B", on="ToB"),
@Transit(from="A", to="C", on="ToC"),
@Transit(from="A", to="D", on="ToD")
})
@StateMachineParameters(stateType=String.class, eventType=String.class, contextType=Void.class)
@ContextInsensitive
static class StateMachineExceptionSample extends AbstractUntypedStateMachine {
protected void transitFromAToBOnToB(String from, String to, String event) {
throw new IllegalArgumentException("This exception can be recovered.");
}
protected void transitFromAToCOnToC(String from, String to, String event) {
throw new UnsupportedOperationException("This exception cannot be recovered.");
}
@Override
protected void afterTransitionCausedException(Object fromState, Object toState, Object event, Object context) {
Throwable targeException = getLastException().getTargetException();
if(targeException instanceof IllegalArgumentException &&
fromState.equals("A") && toState.equals("B") && event.equals("ToB")) {
Assert.assertEquals(targeException.getMessage(), "This exception can be recovered.");
// do some error clean up job here
// ...
// after recovered from this exception, reset the state machine status back to normal
setStatus(StateMachineStatus.IDLE);
} else {
super.afterTransitionCausedException(fromState, toState, event, context);
}
}
}
private StateMachineExceptionSample fsm;
@After
public void teardown() {
}
@Before
public void setup() {
fsm = null;
}
@Test(expected=UnsupportedOperationException.class)
public void testExceptionFail() throws Throwable {
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineExceptionSample.class);
fsm = builder.newUntypedStateMachine("A");
try {
fsm.fire("ToC");
} catch(TransitionException e) {
Assert.assertEquals(fsm.getStatus(), StateMachineStatus.ERROR);
Assert.assertEquals(e.getTargetException().getMessage(), "This exception cannot be recovered.");
throw e.getTargetException();
}
Assert.fail();
}
@Test
public void testExceptionRecovered() {
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineExceptionSample.class);
fsm = builder.newUntypedStateMachine("A");
fsm.fire("ToB");
Assert.assertEquals(fsm.getCurrentState(), "A");
fsm.fire("ToD");
Assert.assertEquals(fsm.getCurrentState(), "D");
if(fsm.getStatus()!=StateMachineStatus.TERMINATED)
fsm.terminate(null);
}
@Transitions({
@Transit(from="A", to="B", on="ToB", type=TransitionType.INTERNAL)
})
@StateMachineParameters(stateType=String.class, eventType=String.class, contextType=Void.class)
@ContextInsensitive
static class StateMachineExceptionSample2 extends AbstractUntypedStateMachine {
}
@Test(expected=IllegalArgumentException.class)
public void testInvalidInternalTransition() {
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineExceptionSample2.class);
fsm = builder.newUntypedStateMachine("A");
}
@Test(expected=IllegalStateException.class)
public void testUpdateBuilderAfterPrepared() {
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineExceptionSample.class);
fsm = builder.newUntypedStateMachine("A");
builder.defineState("Invalid");
}
@Test(expected=IllegalArgumentException.class)
public void testUnexitedInitialState() {
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineExceptionSample.class);
fsm = builder.newUntypedStateMachine("NoSuchState");
}
class MyEvent {}
class MyState {}
@Transitions({
@Transit(from="A", to="B", on="ToB")
})
@StateMachineParameters(stateType=MyState.class, eventType=MyEvent.class, contextType=Void.class)
@ContextInsensitive
static class StateMachineExceptionSample3 extends AbstractUntypedStateMachine {
}
@Test(expected=IllegalStateException.class)
public void testUnregisterStateConverter() {
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineExceptionSample3.class);
builder.newUntypedStateMachine("A");
}
@ContextEvent(finishEvent="FINISH")
static class StateMachineExceptionSample4 extends StateMachineExceptionSample3 {
}
@Test(expected=SquirrelRuntimeException.class)
public void testUnregisterEventConverter() {
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineExceptionSample4.class);
builder.newUntypedStateMachine("A");
}
}