1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.scxml;
18  
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.NotSerializableException;
24  import java.io.ObjectInputStream;
25  import java.io.ObjectOutputStream;
26  import java.net.URL;
27  import java.util.List;
28  import java.util.Set;
29  
30  import junit.framework.Assert;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.commons.scxml.env.SimpleDispatcher;
35  import org.apache.commons.scxml.env.Tracer;
36  import org.apache.commons.scxml.env.jexl.JexlContext;
37  import org.apache.commons.scxml.env.jexl.JexlEvaluator;
38  import org.apache.commons.scxml.io.SCXMLDigester;
39  import org.apache.commons.scxml.model.SCXML;
40  import org.apache.commons.scxml.model.TransitionTarget;
41  import org.xml.sax.ErrorHandler;
42  /***
43   * Helper methods for running SCXML unit tests.
44   */
45  public class SCXMLTestHelper {
46  
47      /***
48       * Serialized Commons SCXML object model temporary store.
49       * Assumes the default build artifacts are generated in the
50       * "target" directory (so it can be removed via a clean build).
51       */
52      public static final String SERIALIZATION_DIR = "target/serialization";
53      public static final String SERIALIZATION_FILE_PREFIX =
54          SERIALIZATION_DIR + "/scxml";
55      public static final String SERIALIZATION_FILE_SUFFIX = ".ser";
56  
57      public static SCXML digest(final URL url) {
58          return digest(url, null, null);
59      }
60  
61      public static SCXML digest(final URL url, final List customActions) {
62          return digest(url, null, customActions);
63      }
64  
65      public static SCXML digest(final URL url, final ErrorHandler errHandler) {
66          return digest(url, errHandler, null);
67      }
68  
69      public static SCXML digest(final URL url, final ErrorHandler errHandler,
70              final List customActions) {
71          Assert.assertNotNull(url);
72          // SAX ErrorHandler may be null
73          SCXML scxml = null;
74          try {
75              scxml = SCXMLDigester.digest(url, errHandler, customActions);
76          } catch (Exception e) {
77              Log log = LogFactory.getLog(SCXMLTestHelper.class);
78              log.error(e.getMessage(), e);
79              Assert.fail(e.getMessage());
80          }
81          Assert.assertNotNull(scxml);
82          SCXML roundtrip = testModelSerializability(scxml);
83          return roundtrip;
84      }
85  
86      public static SCXMLExecutor getExecutor(final URL url) {
87          SCXML scxml = digest(url);
88          Evaluator evaluator = new JexlEvaluator();
89          return getExecutor(evaluator, scxml);
90      }
91  
92      public static SCXMLExecutor getExecutor(final URL url,
93              final Evaluator evaluator) {
94          SCXML scxml = digest(url);
95          return getExecutor(evaluator, scxml);
96      }
97  
98      public static SCXMLExecutor getExecutor(final URL url,
99              final ErrorHandler errHandler) {
100         SCXML scxml = digest(url, errHandler);
101         Evaluator evaluator = new JexlEvaluator();
102         return getExecutor(evaluator, scxml);
103     }
104 
105     public static SCXMLExecutor getExecutor(SCXML scxml) {
106         return getExecutor(scxml, null);
107     }
108 
109     public static SCXMLExecutor getExecutor(SCXML scxml,
110             SCXMLSemantics semantics) {
111         Context context = new JexlContext();
112         Evaluator evaluator = new JexlEvaluator();
113         EventDispatcher ed = new SimpleDispatcher();
114         Tracer trc = new Tracer();
115         return getExecutor(context, evaluator, scxml, ed, trc, semantics);
116     }
117 
118     public static SCXMLExecutor getExecutor(Evaluator evaluator, SCXML scxml) {
119         EventDispatcher ed = new SimpleDispatcher();
120         Tracer trc = new Tracer();
121         Context context = new JexlContext();
122         return getExecutor(context, evaluator, scxml, ed, trc);
123     }
124 
125     public static SCXMLExecutor getExecutor(final URL url, final Context ctx,
126             final Evaluator evaluator) {
127         SCXML scxml = digest(url);
128         EventDispatcher ed = new SimpleDispatcher();
129         Tracer trc = new Tracer();
130         return getExecutor(ctx, evaluator, scxml, ed, trc);
131     }
132 
133     public static SCXMLExecutor getExecutor(final SCXML scxml,
134             final Context ctx, final Evaluator evaluator) {
135         EventDispatcher ed = new SimpleDispatcher();
136         Tracer trc = new Tracer();
137         return getExecutor(ctx, evaluator, scxml, ed, trc);
138     }
139 
140     public static SCXMLExecutor getExecutor(Context context,
141             Evaluator evaluator, SCXML scxml, EventDispatcher ed, Tracer trc) {
142         return getExecutor(context, evaluator, scxml, ed, trc, null);
143     }
144 
145     public static SCXMLExecutor getExecutor(Context context,
146             Evaluator evaluator, SCXML scxml, EventDispatcher ed,
147             Tracer trc, SCXMLSemantics semantics) {
148         Assert.assertNotNull(evaluator);
149         Assert.assertNotNull(context);
150         Assert.assertNotNull(scxml);
151         Assert.assertNotNull(ed);
152         Assert.assertNotNull(trc);
153         SCXMLExecutor exec = null;
154         try {
155             if (semantics == null) {
156                 exec = new SCXMLExecutor(evaluator, ed, trc);
157             } else {
158                 exec = new SCXMLExecutor(evaluator, ed, trc, semantics);
159             }
160             exec.addListener(scxml, trc);
161             exec.setRootContext(context);
162             exec.setSuperStep(true);
163             exec.setStateMachine(scxml);
164             exec.go();
165         } catch (Exception e) {
166             Log log = LogFactory.getLog(SCXMLTestHelper.class);
167             log.error(e.getMessage(), e);
168             Assert.fail(e.getMessage());
169         }
170         Assert.assertNotNull(exec);
171         return exec;
172     }
173 
174     public static TransitionTarget lookupTransitionTarget(SCXMLExecutor exec,
175             String id) {
176         return (TransitionTarget) exec.getStateMachine().getTargets().get(id);
177     }
178 
179     public static Context lookupContext(SCXMLExecutor exec,
180             TransitionTarget tt) {
181         return exec.getSCInstance().lookupContext(tt);
182     }
183 
184     public static Context lookupContext(SCXMLExecutor exec,
185             String id) {
186         TransitionTarget tt = lookupTransitionTarget(exec, id);
187         if (tt == null) {
188             return null;
189         }
190         return exec.getSCInstance().lookupContext(tt);
191     }
192 
193     public static Set fireEvent(SCXMLExecutor exec, String name) {
194         TriggerEvent[] evts = {new TriggerEvent(name,
195                 TriggerEvent.SIGNAL_EVENT, null)};
196         try {
197             exec.triggerEvents(evts);
198         } catch (Exception e) {
199             Log log = LogFactory.getLog(SCXMLTestHelper.class);
200             log.error(e.getMessage(), e);
201             Assert.fail(e.getMessage());
202         }
203         return exec.getCurrentStatus().getStates();
204     }
205 
206     public static Set fireEvent(SCXMLExecutor exec, TriggerEvent te) {
207         TriggerEvent[] evts = { te };
208         try {
209             exec.triggerEvents(evts);
210         } catch (Exception e) {
211             Log log = LogFactory.getLog(SCXMLTestHelper.class);
212             log.error(e.getMessage(), e);
213             Assert.fail(e.getMessage());
214         }
215         return exec.getCurrentStatus().getStates();
216     }
217 
218     public static Set fireEvents(SCXMLExecutor exec, TriggerEvent[] evts) {
219         try {
220             exec.triggerEvents(evts);
221         } catch (Exception e) {
222             Log log = LogFactory.getLog(SCXMLTestHelper.class);
223             log.error(e.getMessage(), e);
224             Assert.fail(e.getMessage());
225         }
226         return exec.getCurrentStatus().getStates();
227     }
228 
229     public static SCXML testModelSerializability(final SCXML scxml) {
230         File fileDir = new File(SERIALIZATION_DIR);
231         if (!fileDir.exists() && !fileDir.mkdir()) {
232             return null;
233         }
234         String filename = SERIALIZATION_FILE_PREFIX
235             + System.currentTimeMillis() + SERIALIZATION_FILE_SUFFIX;
236         SCXML roundtrip = null;
237         try {
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         } catch (NotSerializableException nse) {
247             // <data> nodes failed serialization
248             System.err.println("SERIALIZATION ERROR: The DOM implementation"
249                 + " in use is not serializable");
250             return scxml;
251         } catch(IOException ex) {
252             ex.printStackTrace();
253         } catch(ClassNotFoundException ex) {
254             ex.printStackTrace();
255         }
256         return roundtrip;
257     }
258 
259     public static SCXMLExecutor testExecutorSerializability(final SCXMLExecutor exec) {
260         File fileDir = new File(SERIALIZATION_DIR);
261         if (!fileDir.exists() && !fileDir.mkdir()) {
262             return null;
263         }
264         String filename = SERIALIZATION_FILE_PREFIX
265             + System.currentTimeMillis() + SERIALIZATION_FILE_SUFFIX;
266         SCXMLExecutor roundtrip = null;
267         try {
268             ObjectOutputStream out =
269                 new ObjectOutputStream(new FileOutputStream(filename));
270             out.writeObject(exec);
271             out.close();
272             ObjectInputStream in =
273                 new ObjectInputStream(new FileInputStream(filename));
274             roundtrip = (SCXMLExecutor) in.readObject();
275             in.close();
276         } catch (NotSerializableException nse) {
277             // <data> nodes failed serialization, test cases do not add
278             // other non-serializable context data
279             System.err.println("SERIALIZATION ERROR: The DOM implementation"
280                 + " in use is not serializable");
281             return exec;
282         } catch(IOException ex) {
283             ex.printStackTrace();
284         } catch(ClassNotFoundException ex) {
285             ex.printStackTrace();
286         }
287         return roundtrip;
288     }
289 
290     /***
291      * Discourage instantiation.
292      */
293     private SCXMLTestHelper() {
294         super();
295     }
296 
297 }
298