1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.scxml2;
18
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileOutputStream;
22 import java.io.ObjectInputStream;
23 import java.io.ObjectOutputStream;
24 import java.io.Reader;
25 import java.io.StringReader;
26 import java.net.URL;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.List;
30 import java.util.Set;
31
32 import javax.xml.parsers.DocumentBuilderFactory;
33
34 import org.apache.commons.scxml2.env.SimpleDispatcher;
35 import org.apache.commons.scxml2.env.Tracer;
36 import org.apache.commons.scxml2.io.SCXMLReader;
37 import org.apache.commons.scxml2.io.SCXMLReader.Configuration;
38 import org.apache.commons.scxml2.model.CustomAction;
39 import org.apache.commons.scxml2.model.EnterableState;
40 import org.apache.commons.scxml2.model.SCXML;
41 import org.apache.commons.scxml2.model.TransitionTarget;
42 import org.junit.Assert;
43 import org.w3c.dom.Document;
44 import org.xml.sax.InputSource;
45
46
47
48 public class SCXMLTestHelper {
49
50
51
52
53
54
55 public static final String SERIALIZATION_DIR = "target/serialization";
56 public static final String SERIALIZATION_FILE_PREFIX = SERIALIZATION_DIR + "/scxml";
57 public static final String SERIALIZATION_FILE_SUFFIX = ".ser";
58
59
60 private static int sequence=0;
61
62 private synchronized static String getSequenceNumber() {
63 return Integer.toString(++sequence);
64 }
65
66 public static URL getResource(String name) {
67 return SCXMLTestHelper.class.getClassLoader().getResource(name);
68 }
69
70 public static SCXML parse(final String scxmlResource) throws Exception {
71 Assert.assertNotNull(scxmlResource);
72 return parse(getResource(scxmlResource), null);
73 }
74
75 public static SCXML parse(final URL url) throws Exception {
76 return parse(url, null);
77 }
78
79 public static SCXML parse(final String scxmlResource, final List<CustomAction> customActions) throws Exception {
80 Assert.assertNotNull(scxmlResource);
81 return parse(getResource(scxmlResource), customActions);
82 }
83
84 public static SCXML parse(final URL url, final List<CustomAction> customActions) throws Exception {
85 Assert.assertNotNull(url);
86 Configuration configuration = new Configuration(null, null, customActions);
87 SCXML scxml = SCXMLReader.read(url, configuration);
88 Assert.assertNotNull(scxml);
89 SCXML roundtrip = testModelSerializability(scxml);
90 return roundtrip;
91 }
92
93 public static SCXML parse(final Reader scxmlReader, final List<CustomAction> customActions) throws Exception {
94 Assert.assertNotNull(scxmlReader);
95 Configuration configuration = new Configuration(null, null, customActions);
96 SCXML scxml = SCXMLReader.read(scxmlReader, configuration);
97 Assert.assertNotNull(scxml);
98 SCXML roundtrip = testModelSerializability(scxml);
99 return roundtrip;
100 }
101
102 public static SCXMLExecutor getExecutor(final URL url) throws Exception {
103 return getExecutor(parse(url), null);
104 }
105
106 public static SCXMLExecutor getExecutor(final String scxmlResource) throws Exception {
107 return getExecutor(parse(scxmlResource), null);
108 }
109
110 public static SCXMLExecutor getExecutor(final SCXML scxml) throws Exception {
111 return getExecutor(scxml, null);
112 }
113
114 public static SCXMLExecutor getExecutor(final URL url, final Evaluator evaluator) throws Exception {
115 return getExecutor(parse(url), evaluator);
116 }
117
118 public static SCXMLExecutor getExecutor(final SCXML scxml, final Evaluator evaluator) throws Exception {
119 return getExecutor(scxml, evaluator, new SimpleDispatcher());
120 }
121
122 public static SCXMLExecutor getExecutor(final SCXML scxml, final Evaluator evaluator, final EventDispatcher eventDispatcher) throws Exception {
123 Tracer trc = new Tracer();
124 SCXMLExecutor exec = new SCXMLExecutor(evaluator, eventDispatcher, trc);
125 exec.setStateMachine(scxml);
126 exec.addListener(scxml, trc);
127 return exec;
128 }
129
130 public static TransitionTarget lookupTransitionTarget(SCXMLExecutor exec, String id) {
131 return exec.getStateMachine().getTargets().get(id);
132 }
133
134 public static Context lookupContext(SCXMLExecutor exec, String id) {
135 TransitionTarget tt = lookupTransitionTarget(exec, id);
136 if (tt == null || !(tt instanceof EnterableState)) {
137 return null;
138 }
139 return exec.getSCInstance().lookupContext((EnterableState)tt);
140 }
141
142 public static void assertState(SCXMLExecutor exec, String expectedStateId) throws Exception {
143 Set<EnterableState> currentStates = exec.getStatus().getStates();
144 Assert.assertEquals("Expected 1 simple (leaf) state with id '"
145 + expectedStateId + "' but found " + currentStates.size() + " states instead.",
146 1, currentStates.size());
147 Assert.assertEquals(expectedStateId, currentStates.iterator().
148 next().getId());
149 }
150
151 public static Set<EnterableState> fireEvent(SCXMLExecutor exec, String name) throws Exception {
152 return fireEvent(exec, name, null);
153 }
154
155 public static Set<EnterableState> fireEvent(SCXMLExecutor exec, String name, Object payload) throws Exception {
156 TriggerEvent[] evts = {new TriggerEvent(name, TriggerEvent.SIGNAL_EVENT, payload)};
157 exec.triggerEvents(evts);
158 return exec.getStatus().getStates();
159 }
160
161 public static Set<EnterableState> fireEvent(SCXMLExecutor exec, TriggerEvent te) throws Exception {
162 exec.triggerEvent(te);
163 return exec.getStatus().getStates();
164 }
165
166 public static Set<EnterableState> fireEvents(SCXMLExecutor exec, TriggerEvent[] evts) throws Exception {
167 exec.triggerEvents(evts);
168 return exec.getStatus().getStates();
169 }
170
171 public static void assertPostTriggerState(SCXMLExecutor exec,
172 String triggerEventName, String expectedStateId) throws Exception {
173 assertPostTriggerState(exec, triggerEventName, null, expectedStateId);
174 }
175
176 public static void assertPostTriggerState(SCXMLExecutor exec,
177 String triggerEventName, Object payload, String expectedStateId) throws Exception {
178 assertPostTriggerState(exec, new TriggerEvent(triggerEventName,
179 TriggerEvent.SIGNAL_EVENT, payload), expectedStateId);
180 }
181
182 public static void assertPostTriggerStates(SCXMLExecutor exec,
183 String triggerEventName, String[] expectedStateIds) throws Exception {
184 assertPostTriggerStates(exec, triggerEventName, null, expectedStateIds);
185 }
186
187 public static void assertPostTriggerStates(SCXMLExecutor exec,
188 String triggerEventName, Object payload, String[] expectedStateIds) throws Exception {
189 assertPostTriggerStates(exec, new TriggerEvent(triggerEventName,
190 TriggerEvent.SIGNAL_EVENT, payload), expectedStateIds);
191 }
192
193 public static void assertPostTriggerState(SCXMLExecutor exec,
194 TriggerEvent triggerEvent, String expectedStateId) throws Exception {
195 Set<EnterableState> currentStates = fireEvent(exec, triggerEvent);
196 Assert.assertEquals("Expected 1 simple (leaf) state with id '"
197 + expectedStateId + "' on firing event " + triggerEvent
198 + " but found " + currentStates.size() + " states instead.",
199 1, currentStates.size());
200 Assert.assertEquals(expectedStateId, currentStates.iterator().
201 next().getId());
202 }
203
204 public static void assertPostTriggerStates(SCXMLExecutor exec,
205 TriggerEvent triggerEvent, String[] expectedStateIds) throws Exception {
206 if (expectedStateIds == null || expectedStateIds.length == 0) {
207 Assert.fail("Must specify an array of one or more "
208 + "expected state IDs");
209 }
210 Set<EnterableState> currentStates = fireEvent(exec, triggerEvent);
211 int n = expectedStateIds.length;
212 Assert.assertEquals("Expected " + n + " simple (leaf) state(s) "
213 + " on firing event " + triggerEvent + " but found "
214 + currentStates.size() + " states instead.",
215 n, currentStates.size());
216 List<String> expectedStateIdList = new ArrayList<String>(Arrays.asList(expectedStateIds));
217 for (TransitionTarget tt : currentStates) {
218 String stateId = tt.getId();
219 if(!expectedStateIdList.remove(stateId)) {
220 Assert.fail("Expected state with id '" + stateId
221 + "' in current states on firing event "
222 + triggerEvent);
223 }
224 }
225 Assert.assertEquals("More states in current configuration than those"
226 + "specified in the expected state ids '" + expectedStateIds
227 + "'", 0, expectedStateIdList.size());
228 }
229
230 public static SCXML testModelSerializability(final SCXML scxml) throws Exception {
231 File fileDir = new File(SERIALIZATION_DIR);
232 if (!fileDir.exists()) {
233 fileDir.mkdirs();
234 }
235 String filename = SERIALIZATION_FILE_PREFIX
236 + getSequenceNumber() + SERIALIZATION_FILE_SUFFIX;
237 SCXML roundtrip = null;
238 ObjectOutputStream out =
239 new ObjectOutputStream(new FileOutputStream(filename));
240 out.writeObject(scxml);
241 out.close();
242 ObjectInputStream in =
243 new ObjectInputStream(new FileInputStream(filename));
244 roundtrip = (SCXML) in.readObject();
245 in.close();
246 return roundtrip;
247 }
248
249 public static SCXMLExecutor testInstanceSerializability(final SCXMLExecutor exec) throws Exception {
250 File fileDir = new File(SERIALIZATION_DIR);
251 if (!fileDir.exists()) {
252 fileDir.mkdirs();
253 }
254 String filename = SERIALIZATION_FILE_PREFIX
255 + getSequenceNumber() + SERIALIZATION_FILE_SUFFIX;
256 ObjectOutputStream out =
257 new ObjectOutputStream(new FileOutputStream(filename));
258 out.writeObject(exec.detachInstance());
259 out.close();
260 ObjectInputStream in =
261 new ObjectInputStream(new FileInputStream(filename));
262 exec.attachInstance((SCInstance) in.readObject());
263 in.close();
264 return exec;
265 }
266
267
268
269
270
271
272
273 public static Document stringToXMLDocument(final String xml) {
274 try {
275 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
276 dbf.setNamespaceAware(true);
277 return dbf.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
278 } catch (Exception e) {
279 throw new RuntimeException("Exception parsing String to Node:\n" + xml);
280 }
281 }
282
283
284
285
286 private SCXMLTestHelper() {
287 super();
288 }
289 }