Closed
Description
I was seeing some actions associated to my states not being invoked.
The machine seems to proceed normally, other than for the action being ignored.
Here's a simple self-contained test that shows this behavior (as the comment notes, this succeeds 99% of the time. But if you execute the test in a loop, it is eventually going to fail).
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.statemachine.test.StateMachineTestPlanBuilder;
import java.util.concurrent.atomic.AtomicInteger;
public class SmallStateActionTest {
enum States {
READY,
S1,
END,
}
enum Events {
START,
S1_DONE,
}
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
/**
* This is the simplest test I could come up with that reproduces a state action not being invoked for some reason.
* Possibly some race condition bug.
*
* Executing this test once will very likely succeed.
* Meaning the state machine configured works ok.
*
* If however the test is executed in a loop (in IDEA: Edit Configuration > Repeat: 'Until failure') it will rapidly
* go through a lot of iterations, and eventually fail (takes a 2 to 5 of seconds for me).
*
* @throws Exception
*/
@Test
public void stateActionsTest() throws Exception {
AtomicInteger s1ActionExecuted = new AtomicInteger(0);
StateMachineBuilder.Builder<States, Events> builder;
builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.taskExecutor(new SyncTaskExecutor());
builder.configureStates()
.withStates()
.initial(States.READY)
.end(States.END)
.state(States.S1, context -> {
System.out.println("Executing state action");
s1ActionExecuted.incrementAndGet();
});
builder.configureTransitions()
.withExternal()
.source(States.READY)
.target(States.S1)
.event(Events.START)
.and()
.withExternal()
.source(States.S1)
.target(States.END)
.event(Events.S1_DONE);
StateMachine<States, Events> stateMachine = builder.build();
StateMachineTestPlanBuilder.<States, Events>builder()
.defaultAwaitTime(2)
.stateMachine(stateMachine)
.step()
.sendEvent(Events.START)
.expectStateChanged(2)
.expectState(States.S1)
.and()
.step()
.sendEvent(Events.S1_DONE)
.expectStateChanged(1)
.expectState(States.END)
.expectStateMachineStopped(1)
.and()
.build()
.test();
Assert.assertTrue(stateMachine.isComplete());
Assert.assertEquals(1, s1ActionExecuted.get());
}
}