1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.statemachine;
21  
22  import java.util.LinkedList;
23  
24  import junit.framework.TestCase;
25  
26  import org.apache.mina.statemachine.annotation.Transition;
27  import org.apache.mina.statemachine.annotation.Transitions;
28  import org.apache.mina.statemachine.event.Event;
29  import org.apache.mina.statemachine.transition.MethodTransition;
30  
31  /**
32   * Tests {@link StateMachineProxyBuilder}.
33   *
34   * @author The Apache MINA Project (dev@mina.apache.org)
35   */
36  public class StateMachineProxyBuilderTest extends TestCase {
37  
38      public void testReentrantStateMachine() throws Exception {
39          ReentrantStateMachineHandler handler = new ReentrantStateMachineHandler();
40  
41          State s1 = new State("s1");
42          State s2 = new State("s2");
43          State s3 = new State("s3");
44  
45          s1.addTransition(new MethodTransition("call1", s2, handler));
46          s2.addTransition(new MethodTransition("call2", s3, handler));
47          s3.addTransition(new MethodTransition("call3", handler));
48  
49          StateMachine sm = new StateMachine(new State[] { s1, s2, s3 }, "s1");
50          Reentrant reentrant = new StateMachineProxyBuilder().create(Reentrant.class, sm);
51          reentrant.call1(reentrant);
52          assertTrue(handler.finished);
53      }
54      
55      public void testTapeDeckStateMachine() throws Exception {
56          TapeDeckStateMachineHandler handler = new TapeDeckStateMachineHandler();
57  
58          State parent = new State("parent");
59          State s1 = new State("s1", parent);
60          State s2 = new State("s2", parent);
61          State s3 = new State("s3", parent);
62          State s4 = new State("s4", parent);
63          State s5 = new State("s5", parent);
64  
65          parent.addTransition(new MethodTransition("*", "error", handler));
66          s1.addTransition(new MethodTransition("insert", s2, "inserted", handler));
67          s2.addTransition(new MethodTransition("start", s3, "playing", handler));
68          s3.addTransition(new MethodTransition("stop", s4, "stopped", handler));
69          s3.addTransition(new MethodTransition("pause", s5, "paused", handler));
70          s4.addTransition(new MethodTransition("eject", s1, "ejected", handler));
71          s5.addTransition(new MethodTransition("pause", s3, "playing", handler));
72  
73          StateMachine sm = new StateMachine(new State[] { s1, s2, s3, s4, s5 }, "s1");
74          TapeDeck player = new StateMachineProxyBuilder().create(TapeDeck.class, sm);
75          player.insert("Kings of convenience - Riot on an empty street");
76          player.start();
77          player.pause();
78          player.pause();
79          player.eject();
80          player.stop();
81          player.eject();
82  
83          LinkedList<String> messages = handler.messages;
84          assertEquals("Tape 'Kings of convenience - Riot on an empty street' inserted", messages.removeFirst());
85          assertEquals("Playing", messages.removeFirst());
86          assertEquals("Paused", messages.removeFirst());
87          assertEquals("Playing", messages.removeFirst());
88          assertEquals("Error: Cannot eject at this time", messages.removeFirst());
89          assertEquals("Stopped", messages.removeFirst());
90          assertEquals("Tape ejected", messages.removeFirst());
91          assertTrue(messages.isEmpty());
92      }
93      
94      public void testTapeDeckStateMachineAnnotations() throws Exception {
95          TapeDeckStateMachineHandler handler = new TapeDeckStateMachineHandler();
96  
97          StateMachine sm = StateMachineFactory.getInstance(Transition.class).create(TapeDeckStateMachineHandler.S1, handler);
98  
99          TapeDeck player = new StateMachineProxyBuilder().create(TapeDeck.class, sm);
100         player.insert("Kings of convenience - Riot on an empty street");
101         player.start();
102         player.pause();
103         player.pause();
104         player.eject();
105         player.stop();
106         player.eject();
107 
108         LinkedList<String> messages = handler.messages;
109         assertEquals("Tape 'Kings of convenience - Riot on an empty street' inserted", messages.removeFirst());
110         assertEquals("Playing", messages.removeFirst());
111         assertEquals("Paused", messages.removeFirst());
112         assertEquals("Playing", messages.removeFirst());
113         assertEquals("Error: Cannot eject at this time", messages.removeFirst());
114         assertEquals("Stopped", messages.removeFirst());
115         assertEquals("Tape ejected", messages.removeFirst());
116         assertTrue(messages.isEmpty());
117     }
118     
119     public interface Reentrant {
120         void call1(Reentrant proxy);
121         void call2(Reentrant proxy);
122         void call3(Reentrant proxy);
123     }
124 
125     public static class ReentrantStateMachineHandler {
126         private boolean finished = false;
127 
128         public void call1(Reentrant proxy) {
129             proxy.call2(proxy);
130         }
131 
132         public void call2(Reentrant proxy) {
133             proxy.call3(proxy);
134         }
135 
136         public void call3(Reentrant proxy) {
137             finished = true;
138         }
139     }
140 
141     public interface TapeDeck {
142         void insert(String name);
143         void eject();
144         void start();
145         void pause();
146         void stop();
147     }
148     
149     public static class TapeDeckStateMachineHandler {
150         @org.apache.mina.statemachine.annotation.State public static final String PARENT = "parent";
151         @org.apache.mina.statemachine.annotation.State(PARENT) public static final String S1 = "s1";
152         @org.apache.mina.statemachine.annotation.State(PARENT) public static final String S2 = "s2";
153         @org.apache.mina.statemachine.annotation.State(PARENT) public static final String S3 = "s3";
154         @org.apache.mina.statemachine.annotation.State(PARENT) public static final String S4 = "s4";
155         @org.apache.mina.statemachine.annotation.State(PARENT) public static final String S5 = "s5";
156         
157         private LinkedList<String> messages = new LinkedList<String>();
158         
159         @Transition(on = "insert", in = "s1", next = "s2")
160         public void inserted(String name) {
161             messages.add("Tape '" + name + "' inserted");
162         }
163 
164         @Transition(on = "eject", in = "s4", next = "s1")
165         public void ejected() {
166             messages.add("Tape ejected");
167         }
168         
169         @Transitions({@Transition( on = "start", in = "s2", next = "s3" ),
170                    @Transition( on = "pause", in = "s5", next = "s3" )})
171         public void playing() {
172             messages.add("Playing");
173         }
174         
175         @Transition(on = "pause", in = "s3", next = "s5")
176         public void paused() {
177             messages.add("Paused");
178         }
179 
180         @Transition(on = "stop", in = "s3", next = "s4")
181         public void stopped() {
182             messages.add("Stopped");
183         }
184 
185         @Transition(on = "*", in = "parent")
186         public void error(Event event) {
187             messages.add("Error: Cannot " + event.getId() + " at this time");
188         }
189     }
190 }