diff --git a/spring-statemachine-samples/showcase/src/main/java/demo/showcase/Application.java b/spring-statemachine-samples/showcase/src/main/java/demo/showcase/Application.java index 86d24b07..82adb5bb 100644 --- a/spring-statemachine-samples/showcase/src/main/java/demo/showcase/Application.java +++ b/spring-statemachine-samples/showcase/src/main/java/demo/showcase/Application.java @@ -37,6 +37,7 @@ public class Application { .parent(States.S1) .initial(States.S11) .state(States.S11) + .state(States.S12) .and() .withStates() .parent(States.S0) @@ -96,7 +97,10 @@ public class Application { .and() .withExternal() .source(States.S21).target(States.S21).event(Events.H) - .guard(fooGuard()); + .guard(fooGuard()) + .and() + .withExternal() + .source(States.S11).target(States.S12).event(Events.I); } @@ -115,13 +119,13 @@ public class Application { //tag::snippetB[] public static enum States { - S0, S1, S11, S2, S21, S211 + S0, S1, S11, S12, S2, S21, S211 } //end::snippetB[] //tag::snippetC[] public static enum Events { - A, B, C, D, E, F, G, H + A, B, C, D, E, F, G, H, I } //end::snippetC[] diff --git a/spring-statemachine-samples/showcase/src/main/resources/statechartmodel.txt b/spring-statemachine-samples/showcase/src/main/resources/statechartmodel.txt index c0da1297..aa572b6d 100644 --- a/spring-statemachine-samples/showcase/src/main/resources/statechartmodel.txt +++ b/spring-statemachine-samples/showcase/src/main/resources/statechartmodel.txt @@ -5,10 +5,10 @@ | exit/ | | +-------------------------+ +--------------------------------------------+ | | *-->| S1 | | S2 | | -| +-------------------------+ C +--------------------------------------------+ | -| | entry/ | | entry/ H | | -| D | exit/ |----->| exit/ +-------------------+ | | -|<<---------| | | | H[foo.equals(1)]; | | | +| +-------------------------+ +--------------------------------------------+ | +| | entry/ | C | entry/ H | | +| D | exit/ |----->| exit/ +-------------------+ | | +|<----------| | | | H[foo.equals(1)]; | | | | | +---------------+ | C | +------------------------------+ | | | | | *-->| S11 | |<-----| *-->| S21 |<--+ | | | | +---------------+ | | +------------------------------+ | | @@ -18,14 +18,17 @@ | A[foo 1]; | B | | | | | *-->| s211 | | | | | |---->| | | | F | +--------------+ | | | | | | | |-------------------->| entry/ | | G | | -| | | | | | G | | exit/ |----------------->| -| | | |------------------------>| | | | | -| | | | | | | B | | | E | | -| | | | | | |------>| |<-----------------| -| | | | | | | | | | | | -| | | | | | | D | | | | | -| | | | | | |<------| | | | | -| | | | | | | +--------------+ | | | +| | +--| | | | G | | exit/ |----------------->| +| | | | |------------------------>| | | | | +| | | +---------------+ | | | B | | | E | | +| | | | | |------>| |<-----------------| +| | | +---------------+ | | | | | | | | +| | I| | S12 | | | | D | | | | | +| | | +---------------+ | | |<------| | | | | +| | | | entry/ | | | | +--------------+ | | | +| | | | exit/ | | | | | | | +| | | | | | | | | | | +| | +->| | | | | | | | | | +---------------+ | | +------------------------------+ | | | | | | | | | +-------------------------+ +--------------------------------------------+ | diff --git a/spring-statemachine-samples/showcase/src/test/java/demo/showcase/ShowcaseTests.java b/spring-statemachine-samples/showcase/src/test/java/demo/showcase/ShowcaseTests.java index e2a53907..ed561965 100644 --- a/spring-statemachine-samples/showcase/src/test/java/demo/showcase/ShowcaseTests.java +++ b/spring-statemachine-samples/showcase/src/test/java/demo/showcase/ShowcaseTests.java @@ -1,11 +1,26 @@ package demo.showcase; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + import org.junit.After; +import org.junit.Before; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; import org.springframework.statemachine.EnumStateMachine; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.StateMachineSystemConstants; +import org.springframework.statemachine.listener.StateMachineListener; +import org.springframework.statemachine.listener.StateMachineListenerAdapter; +import org.springframework.statemachine.state.State; import demo.CommonConfiguration; import demo.showcase.Application.Events; @@ -15,27 +30,161 @@ public class ShowcaseTests { private AnnotationConfigApplicationContext context; - @SuppressWarnings("resource") + private StateMachine machine; + + private TestListener listener; + @Test - public void testApplication() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.register(CommonConfiguration.class, Application.class); - context.refresh(); - @SuppressWarnings("unchecked") - StateMachine machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); - - machine.start(); + public void testInitialState() throws Exception { + listener.stateChangedLatch.await(1, TimeUnit.SECONDS); + listener.stateEnteredLatch.await(1, TimeUnit.SECONDS); + assertThat(machine.getState().getIds(), contains(States.S0, States.S1, States.S11)); + assertThat(listener.statesEntered.size(), is(3)); + assertThat(listener.statesEntered.get(0).getId(), is(States.S0)); + assertThat(listener.statesEntered.get(1).getId(), is(States.S1)); + assertThat(listener.statesEntered.get(2).getId(), is(States.S11)); + assertThat(listener.statesExited.size(), is(0)); + } + @Test + public void testA() throws Exception { + listener.reset(1, 2, 2); machine.sendEvent(Events.A); + listener.stateChangedLatch.await(1, TimeUnit.SECONDS); + listener.stateEnteredLatch.await(1, TimeUnit.SECONDS); + listener.stateExitedLatch.await(1, TimeUnit.SECONDS); + assertThat(machine.getState().getIds(), contains(States.S0, States.S1, States.S11)); + assertThat(listener.statesEntered.size(), is(2)); + assertThat(listener.statesEntered.get(0).getId(), is(States.S1)); + assertThat(listener.statesEntered.get(1).getId(), is(States.S11)); + assertThat(listener.statesExited.size(), is(2)); + assertThat(listener.statesExited.get(0).getId(), is(States.S11)); + assertThat(listener.statesExited.get(1).getId(), is(States.S1)); + } + @Test + public void testC() throws Exception { + listener.reset(1, 3, 0); + machine.sendEvent(Events.C); + listener.stateChangedLatch.await(1, TimeUnit.SECONDS); + listener.stateEnteredLatch.await(1, TimeUnit.SECONDS); + assertThat(machine.getState().getIds(), contains(States.S0, States.S2, States.S21, States.S211)); + assertThat(listener.statesEntered.size(), is(3)); + assertThat(listener.statesEntered.get(0).getId(), is(States.S2)); + assertThat(listener.statesEntered.get(1).getId(), is(States.S21)); + assertThat(listener.statesEntered.get(2).getId(), is(States.S211)); + } + + @Test + public void testCC() throws Exception { + listener.reset(1, 3, 0); + machine.sendEvent(Events.C); + listener.stateChangedLatch.await(1, TimeUnit.SECONDS); + assertThat(machine.getState().getIds(), contains(States.S0, States.S2, States.S21, States.S211)); + listener.reset(1, 2, 0); + machine.sendEvent(Events.C); + listener.stateChangedLatch.await(1, TimeUnit.SECONDS); + listener.stateEnteredLatch.await(1, TimeUnit.SECONDS); + assertThat(machine.getState().getIds(), contains(States.S0, States.S1, States.S11)); + assertThat(listener.statesEntered.size(), is(2)); + assertThat(listener.statesEntered.get(0).getId(), is(States.S1)); + assertThat(listener.statesEntered.get(1).getId(), is(States.S11)); + } + + @Test + public void testCD() throws Exception { + listener.reset(1, 3, 0); + machine.sendEvent(Events.C); + listener.stateChangedLatch.await(1, TimeUnit.SECONDS); + listener.reset(1, 2, 0); + machine.sendEvent(Events.D); + listener.stateChangedLatch.await(1, TimeUnit.SECONDS); + listener.stateEnteredLatch.await(1, TimeUnit.SECONDS); + assertThat(machine.getState().getIds(), contains(States.S0, States.S2, States.S21, States.S211)); + assertThat(listener.statesEntered.size(), is(2)); + assertThat(listener.statesEntered.get(0).getId(), is(States.S21)); + assertThat(listener.statesEntered.get(1).getId(), is(States.S211)); + } + + @Test + public void testI() throws Exception { + listener.reset(1, 1, 1); + machine.sendEvent(Events.I); + listener.stateChangedLatch.await(1, TimeUnit.SECONDS); + listener.stateEnteredLatch.await(1, TimeUnit.SECONDS); + listener.stateExitedLatch.await(1, TimeUnit.SECONDS); + assertThat(machine.getState().getIds(), contains(States.S0, States.S1, States.S12)); + assertThat(listener.statesEntered.size(), is(1)); + assertThat(listener.statesEntered.get(0).getId(), is(States.S12)); + assertThat(listener.statesExited.size(), is(1)); + assertThat(listener.statesExited.get(0).getId(), is(States.S11)); + } + + static class Config { + + @Autowired + private StateMachine machine; + + @Bean + public StateMachineListener stateMachineListener() { + TestListener listener = new TestListener(); + machine.addStateListener(listener); + return listener; + } + } + + static class TestListener extends StateMachineListenerAdapter { + + volatile CountDownLatch stateChangedLatch = new CountDownLatch(1); + volatile CountDownLatch stateEnteredLatch = new CountDownLatch(3); + volatile CountDownLatch stateExitedLatch = new CountDownLatch(0); + List> statesEntered = new ArrayList>(); + List> statesExited = new ArrayList>(); + + @Override + public void stateChanged(State from, State to) { + stateChangedLatch.countDown(); + } + + @Override + public void stateEntered(State state) { + statesEntered.add(state); + stateEnteredLatch.countDown(); + } + + @Override + public void stateExited(State state) { + statesExited.add(state); + stateExitedLatch.countDown(); + } + + public void reset(int c1, int c2, int c3) { + stateChangedLatch = new CountDownLatch(c1); + stateEnteredLatch = new CountDownLatch(c2); + stateExitedLatch = new CountDownLatch(c3); + statesEntered.clear(); + statesExited.clear(); + } + + } + + @SuppressWarnings("unchecked") + @Before + public void setup() { + context = new AnnotationConfigApplicationContext(); + context.register(CommonConfiguration.class, Application.class, Config.class); + context.refresh(); + machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class); + listener = context.getBean(TestListener.class); + machine.start(); } @After public void clean() { - if (context != null) { - context.close(); - context = null; - } + machine.stop(); + context.close(); + context = null; + machine = null; } }