/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.buildtests.tck;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.test.StateMachineTestPlan;
import org.springframework.statemachine.test.StateMachineTestPlanBuilder;
/**
* Base tck test class for defining various machine tests
* which different config implementations can implement to
* test that same machine behaviour.
*
* @author Janne Valkealahti
*
*/
public abstract class AbstractTckTests {
protected AnnotationConfigApplicationContext context;
@Before
public void setup() {
cleanInternal();
context = buildContext();
}
@After
public void clean() {
if (context != null) {
context.close();
}
context = null;
}
@SuppressWarnings("unchecked")
protected StateMachine<String, String> getStateMachineFromContext() {
return context.getBean(StateMachine.class);
}
@SuppressWarnings("unchecked")
protected StateMachineFactory<String, String> getStateMachineFactoryFromContext() {
return context.getBean(StateMachineFactory.class);
}
protected void cleanInternal() {
}
protected AnnotationConfigApplicationContext buildContext() {
return null;
}
@Test
public void testSimpleMachine() throws Exception {
StateMachine<String,String> stateMachine = getSimpleMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S1").and()
.step().sendEvent("E1").expectStates("S2").and()
.step().sendEvent("E2").expectStates("S3").and()
.build();
plan.test();
}
/**
* Return state machine for {@link #testSimpleMachine()}.
*
* @return StateMachine for SimpleMachine
*/
protected abstract StateMachine<String, String> getSimpleMachine() throws Exception;
@Test
public void testSimpleSubMachine() throws Exception {
StateMachine<String,String> stateMachine = getSimpleSubMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S1").and()
.step().sendEvent("E1").expectStates("S2", "S21").and()
.step().sendEvent("E2").expectStates("S2", "S22").and()
.step().sendEvent("E3").expectStates("S3").and()
.build();
plan.test();
}
/**
* Return state machine for {@link #testSimpleSubMachine()}.
*
* @return StateMachine for SimpleSubMachine
*/
protected abstract StateMachine<String, String> getSimpleSubMachine() throws Exception;
@Test
public void testShowcaseMachineInitialState() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineA() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("A")
.expectStateChanged(0)
.expectStateEntered(0)
.expectStateExited(0)
.expectStates("S0", "S1", "S11").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineB() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("B")
.expectStateChanged(2)
.expectStateEntered(2)
.expectStateExited(2)
.expectStates("S0", "S1", "S11").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineCHKA() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("C")
.expectStateEntered(3)
.expectStates("S0", "S2", "S21", "S211").and()
.step()
.sendEvent("H")
.expectVariable("foo", 1)
.expectTransition(1)
.expectStates("S0", "S2", "S21", "S211").and()
.step()
.sendEvent("K")
.expectStateEntered(2)
.expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("A")
.expectStateChanged(2)
.expectStateEntered(2)
.expectStateExited(2)
.expectStates("S0", "S1", "S11").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineC() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("C")
.expectStateEntered(3)
.expectStates("S0", "S2", "S21", "S211").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineCK() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("C")
.expectStateEntered(3)
.expectStates("S0", "S2", "S21", "S211").and()
.step()
.sendEvent("K")
.expectStateEntered(2)
.expectStates("S0", "S1", "S11").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineD() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("D")
.expectStateEntered(3)
.expectStates("S0", "S1", "S11").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineCD() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("C")
.expectStateEntered(3)
.expectStates("S0", "S2", "S21", "S211").and()
.step()
.sendEvent("D")
.expectStateEntered(2)
.expectStates("S0", "S2", "S21", "S211").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineI() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("I")
.expectStateEntered(1)
.expectStates("S0", "S1", "S12").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineII() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("I")
.expectStateEntered(1)
.expectStateEntered(1)
.expectStateExited(1)
.expectStates("S0", "S1", "S12").and()
.step()
.sendEvent("I")
.expectStateChanged(2)
.expectStateEntered(3)
.expectStateExited(2)
.expectStates("S0", "S2", "S21", "S212").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineH() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step()
.expectStates("S0", "S1", "S11")
.expectVariable("foo", 0).and()
.step()
.sendEvent("H")
.expectTransition(1)
.expectVariable("foo", 0)
.expectStates("S0", "S1", "S11").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineCH() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step()
.expectStates("S0", "S1", "S11")
.expectVariable("foo", 0).and()
.step()
.sendEvent("C")
.expectStateEntered(3)
.expectStates("S0", "S2", "S21", "S211").and()
.step()
.sendEvent("H")
.expectVariable("foo", 1)
.expectTransition(1)
.expectStates("S0", "S2", "S21", "S211").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineACH() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("A")
.expectStateChanged(0)
.expectStateEntered(0)
.expectStateExited(0)
.expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("C")
.expectStateEntered(3)
.expectStates("S0", "S2", "S21", "S211").and()
.step()
.sendEvent("H")
.expectVariable("foo", 1)
.expectTransition(1)
.expectStates("S0", "S2", "S21", "S211").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineE() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("E")
.expectStateChanged(3)
.expectStateEntered(4)
.expectStateExited(3)
.expectStates("S0", "S2", "S21", "S211").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineF() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("F")
.expectStateChanged(2)
.expectStateEntered(3)
.expectStateExited(2)
.expectStates("S0", "S2", "S21", "S211").and()
.build();
plan.test();
}
@Test
public void testShowcaseMachineG() throws Exception {
StateMachine<String,String> stateMachine = getShowcaseMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("S0", "S1", "S11").and()
.step()
.sendEvent("G")
.expectStateChanged(2)
.expectStateEntered(3)
.expectStateExited(2)
.expectStates("S0", "S2", "S21", "S211").and()
.build();
plan.test();
}
/**
* Return state machine for showcase tests.
*
* @return StateMachine for ShowcaseMachine
*/
protected abstract StateMachine<String, String> getShowcaseMachine() throws Exception;
@Configuration
protected static class ShowcaseMachineBeansConfig {
@Bean
public FooGuard foo0Guard() {
return new FooGuard(0);
}
@Bean
public FooGuard foo1Guard() {
return new FooGuard(1);
}
@Bean
public FooAction fooAction() {
return new FooAction();
}
}
protected static class FooAction implements Action<String, String> {
public FooAction() {
}
@Override
public void execute(StateContext<String, String> context) {
Map<Object, Object> variables = context.getExtendedState().getVariables();
Integer foo = context.getExtendedState().get("foo", Integer.class);
if (foo == null) {
variables.put("foo", 0);
} else if (foo == 0) {
variables.put("foo", 1);
} else if (foo == 1) {
variables.put("foo", 0);
}
}
}
protected static class FooGuard implements Guard<String, String> {
private final int match;
public FooGuard(int match) {
this.match = match;
}
@Override
public boolean evaluate(StateContext<String, String> context) {
Object foo = context.getExtendedState().getVariables().get("foo");
return !(foo == null || !foo.equals(match));
}
}
}