1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.statemachine;
21
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertTrue;
24
25 import java.util.LinkedList;
26
27 import org.apache.mina.statemachine.annotation.Transition;
28 import org.apache.mina.statemachine.annotation.Transitions;
29 import org.apache.mina.statemachine.annotation.OnEntry;
30 import org.apache.mina.statemachine.annotation.OnExit;
31 import org.apache.mina.statemachine.context.StateContext;
32 import org.apache.mina.statemachine.event.Event;
33 import org.apache.mina.statemachine.transition.MethodSelfTransition;
34 import org.apache.mina.statemachine.transition.MethodTransition;
35 import org.junit.Test;
36
37
38
39
40
41
42 public class StateMachineProxyBuilderTest {
43 @Test
44 public void testReentrantStateMachine() throws Exception {
45 ReentrantStateMachineHandler handler = new ReentrantStateMachineHandler();
46
47 State s1 = new State("s1");
48 State s2 = new State("s2");
49 State s3 = new State("s3");
50
51 s1.addTransition(new MethodTransition("call1", s2, handler));
52 s2.addTransition(new MethodTransition("call2", s3, handler));
53 s3.addTransition(new MethodTransition("call3", handler));
54
55 StateMachine sm = new StateMachine(new State[] { s1, s2, s3 }, "s1");
56 Reentrant reentrant = new StateMachineProxyBuilder().create(Reentrant.class, sm);
57 reentrant.call1(reentrant);
58 assertTrue(handler.finished);
59 }
60
61 @Test
62 public void testTapeDeckStateMachine() throws Exception {
63 TapeDeckStateMachineHandler handler = new TapeDeckStateMachineHandler();
64
65 State parent = new State("parent");
66 State s1 = new State("s1", parent);
67 State s2 = new State("s2", parent);
68 State s3 = new State("s3", parent);
69 State s4 = new State("s4", parent);
70 State s5 = new State("s5", parent);
71
72 parent.addTransition(new MethodTransition("*", "error", handler));
73 s1.addTransition(new MethodTransition("insert", s2, "inserted", handler));
74 s2.addTransition(new MethodTransition("start", s3, "playing", handler));
75 s3.addTransition(new MethodTransition("stop", s4, "stopped", handler));
76 s3.addTransition(new MethodTransition("pause", s5, "paused", handler));
77 s4.addTransition(new MethodTransition("eject", s1, "ejected", handler));
78 s5.addTransition(new MethodTransition("pause", s3, "playing", handler));
79
80 s2.addOnEntrySelfTransaction(new MethodSelfTransition("onEntryS2", handler));
81 s2.addOnExitSelfTransaction(new MethodSelfTransition("onExitS2", handler));
82
83 s3.addOnEntrySelfTransaction(new MethodSelfTransition("onEntryS3", handler));
84 s3.addOnExitSelfTransaction(new MethodSelfTransition("onExitS3", handler));
85
86 s4.addOnEntrySelfTransaction(new MethodSelfTransition("onEntryS4", handler));
87 s4.addOnExitSelfTransaction(new MethodSelfTransition("onExitS4", handler));
88
89 StateMachine sm = new StateMachine(new State[] { s1, s2, s3, s4, s5 }, "s1");
90 TapeDeck player = new StateMachineProxyBuilder().create(TapeDeck.class, sm);
91 player.insert("Kings of convenience - Riot on an empty street");
92 player.start();
93 player.pause();
94 player.pause();
95 player.eject();
96 player.stop();
97 player.eject();
98
99 LinkedList<String> messages = handler.messages;
100 assertEquals("Tape 'Kings of convenience - Riot on an empty street' inserted", messages.removeFirst());
101 assertEquals("S2 entered", messages.removeFirst());
102 assertEquals("Playing", messages.removeFirst());
103 assertEquals("S2 exited", messages.removeFirst());
104 assertEquals("S3 entered with stateContext", messages.removeFirst());
105 assertEquals("Paused", messages.removeFirst());
106 assertEquals("S3 exited with stateContext", messages.removeFirst());
107 assertEquals("Playing", messages.removeFirst());
108 assertEquals("S3 entered with stateContext", messages.removeFirst());
109 assertEquals("Error: Cannot eject at this time", messages.removeFirst());
110 assertEquals("Stopped", messages.removeFirst());
111 assertEquals("S3 exited with stateContext", messages.removeFirst());
112 assertEquals("S4 entered with stateContext and state", messages.removeFirst());
113 assertEquals("Tape ejected", messages.removeFirst());
114 assertEquals("S4 exited with stateContext and state", messages.removeFirst());
115
116 assertTrue(messages.isEmpty());
117 }
118
119 @Test
120 public void testTapeDeckStateMachineAnnotations() throws Exception {
121 TapeDeckStateMachineHandler handler = new TapeDeckStateMachineHandler();
122
123 StateMachine sm = StateMachineFactory.getInstance(Transition.class).create(TapeDeckStateMachineHandler.S1,
124 handler);
125
126 TapeDeck player = new StateMachineProxyBuilder().create(TapeDeck.class, sm);
127 player.insert("Kings of convenience - Riot on an empty street");
128 player.start();
129 player.pause();
130 player.pause();
131 player.eject();
132 player.stop();
133 player.eject();
134
135 LinkedList<String> messages = handler.messages;
136 assertEquals("Tape 'Kings of convenience - Riot on an empty street' inserted", messages.removeFirst());
137 assertEquals("S2 entered", messages.removeFirst());
138 assertEquals("Playing", messages.removeFirst());
139 assertEquals("S2 exited", messages.removeFirst());
140 assertEquals("S3 entered with stateContext", messages.removeFirst());
141 assertEquals("Paused", messages.removeFirst());
142 assertEquals("S3 exited with stateContext", messages.removeFirst());
143 assertEquals("Playing", messages.removeFirst());
144 assertEquals("S3 entered with stateContext", messages.removeFirst());
145 assertEquals("Error: Cannot eject at this time", messages.removeFirst());
146 assertEquals("Stopped", messages.removeFirst());
147 assertEquals("S3 exited with stateContext", messages.removeFirst());
148 assertEquals("S4 entered with stateContext and state", messages.removeFirst());
149 assertEquals("Tape ejected", messages.removeFirst());
150 assertEquals("S4 exited with stateContext and state", messages.removeFirst());
151
152 assertTrue(messages.isEmpty());
153 }
154
155 public interface Reentrant {
156 void call1(Reentrant proxy);
157
158 void call2(Reentrant proxy);
159
160 void call3(Reentrant proxy);
161 }
162
163 public static class ReentrantStateMachineHandler {
164 private boolean finished = false;
165
166 public void call1(Reentrant proxy) {
167 proxy.call2(proxy);
168 }
169
170 public void call2(Reentrant proxy) {
171 proxy.call3(proxy);
172 }
173
174 public void call3(Reentrant proxy) {
175 finished = true;
176 }
177 }
178
179 public interface TapeDeck {
180 void insert(String name);
181
182 void eject();
183
184 void start();
185
186 void pause();
187
188 void stop();
189 }
190
191 public static class TapeDeckStateMachineHandler {
192 @org.apache.mina.statemachine.annotation.State
193 public static final String PARENT = "parent";
194
195 @org.apache.mina.statemachine.annotation.State(PARENT)
196 public static final String S1 = "s1";
197
198 @org.apache.mina.statemachine.annotation.State(PARENT)
199 public static final String S2 = "s2";
200
201 @org.apache.mina.statemachine.annotation.State(PARENT)
202 public static final String S3 = "s3";
203
204 @org.apache.mina.statemachine.annotation.State(PARENT)
205 public static final String S4 = "s4";
206
207 @org.apache.mina.statemachine.annotation.State(PARENT)
208 public static final String S5 = "s5";
209
210 private LinkedList<String> messages = new LinkedList<String>();
211
212 @OnEntry(S2)
213 public void onEntryS2() {
214 messages.add("S2 entered");
215 }
216
217 @OnExit(S2)
218 public void onExitS2() {
219 messages.add("S2 exited");
220 }
221
222 @OnEntry(S3)
223 public void onEntryS3(StateContext stateContext) {
224 messages.add("S3 entered with stateContext");
225 }
226
227 @OnExit(S3)
228 public void onExitS3(StateContext stateContext) {
229 messages.add("S3 exited with stateContext");
230 }
231
232 @OnEntry(S4)
233 public void onEntryS4(StateContext stateContext, State state) {
234 messages.add("S4 entered with stateContext and state");
235 }
236
237 @OnExit(S4)
238 public void onExitS4(StateContext stateContext, State state) {
239 messages.add("S4 exited with stateContext and state");
240 }
241
242 @Transition(on = "insert", in = "s1", next = "s2")
243 public void inserted(String name) {
244 messages.add("Tape '" + name + "' inserted");
245 }
246
247 @Transition(on = "eject", in = "s4", next = "s1")
248 public void ejected() {
249 messages.add("Tape ejected");
250 }
251
252 @Transitions({ @Transition(on = "start", in = "s2", next = "s3"),
253 @Transition(on = "pause", in = "s5", next = "s3") })
254 public void playing() {
255 messages.add("Playing");
256 }
257
258 @Transition(on = "pause", in = "s3", next = "s5")
259 public void paused() {
260 messages.add("Paused");
261 }
262
263 @Transition(on = "stop", in = "s3", next = "s4")
264 public void stopped() {
265 messages.add("Stopped");
266 }
267
268 @Transition(on = "*", in = "parent")
269 public void error(Event event) {
270 messages.add("Error: Cannot " + event.getId() + " at this time");
271 }
272 }
273 }