View Javadoc

1   /*
2       Jameleon - An automation testing tool..
3       Copyright (C) 2003-2006 Christian W. Hargraves (engrean@hotmail.com)
4   
5       This library is free software; you can redistribute it and/or
6       modify it under the terms of the GNU Lesser General Public
7       License as published by the Free Software Foundation; either
8       version 2.1 of the License, or (at your option) any later version.
9   
10      This library is distributed in the hope that it will be useful,
11      but WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13      Lesser General Public License for more details.
14  
15      You should have received a copy of the GNU Lesser General Public
16      License along with this library; if not, write to the Free Software
17      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */
19  package net.sf.jameleon;
20  
21  import net.sf.jameleon.event.TestCaseEventHandler;
22  import net.sf.jameleon.event.TestCaseListener;
23  import net.sf.jameleon.event.TestRunEventHandler;
24  import net.sf.jameleon.exception.JameleonException;
25  import net.sf.jameleon.reporting.ReporterUtils;
26  import net.sf.jameleon.reporting.ResultsReporter;
27  import net.sf.jameleon.util.Configurator;
28  import net.sf.jameleon.util.JameleonDefaultValues;
29  import net.sf.jameleon.util.JameleonUtility;
30  import org.apache.commons.jelly.JellyContext;
31  import org.apache.commons.jelly.JellyException;
32  import org.apache.commons.jelly.XMLOutput;
33  import org.apache.log4j.LogManager;
34  import org.apache.log4j.Logger;
35  
36  import java.io.File;
37  import java.io.FileNotFoundException;
38  import java.io.FileWriter;
39  import java.io.IOException;
40  import java.util.*;
41  
42  /***
43   * Executes a Jameleon Test Case.
44   */
45  public class ExecuteTestCase {
46  
47      /***
48       * A list of files to execute
49       */
50      protected final List files = new LinkedList();
51      /***
52       * A list of files to execute
53       */
54      protected final List testCaseListeners = new LinkedList();
55      /***
56       * A set of key-value variables to be set in the context
57       */
58      protected Map contextVars;
59      /***
60       * Print out stack trace of each failed Jameleon Test Case
61       * to stdout.
62       */
63      protected boolean debug = false;
64      /***
65       * Print the name of the script being executed to stdout
66       */
67      protected boolean printFooter = true;
68      /***
69       * The # of milliseconds to wait between each script execution
70       */
71      protected long waitTimeBetweenScripts = 0;
72      /***
73       * A line of dashes
74       */
75      protected static final String DASH = "\n-------------------------------------------------------------\n";
76      /***
77       * A line of underscores
78       */
79      protected static final String US = "\n_______________________________________________________________\n";
80      protected final Logger log = Logger.getLogger(ExecuteTestCase.class.getName());
81      private ResultsReporter reporter;
82  
83      protected final static TestCaseTagLibrary tagLibrary = new TestCaseTagLibrary();
84      /***
85       * The jameleon.conf configuration name for a list of desired TestCaseListeners to be registered.
86       */
87      public static final String TEST_CASE_LISTENERS = "TestCaseListeners";
88  
89      public ExecuteTestCase(){
90          contextVars = new HashMap();
91      }
92      
93      public ExecuteTestCase(boolean debug){
94          this();
95          this.debug = debug;
96      }
97  
98      /***
99       * Read in desired TestCaseListeners defined in jameleon.conf and register them
100      * in the TestCaseEventHandler.
101      */
102     public void registerEventListeners(){
103         registerEventListeners(true);
104     }
105 
106     public ResultsReporter getResultsReporter(){
107         return reporter;
108     }
109     
110     /***
111      * Read in desired TestCaseListeners defined in jameleon.conf and register them
112      * in the TestCaseEventHandler.
113      * @param useCurrentThread if set to true, then use the current thread's classloader
114      */
115     public void registerEventListeners(boolean useCurrentThread){
116         //Add the ResultsReporter as test case and test run listener
117         beginTestRun();
118         String[] tcListeners = Configurator.getInstance().getValueAsArray(TEST_CASE_LISTENERS);
119         TestCaseListener tcListener;
120         Class c;
121         for (int i = 0; tcListeners != null && i < tcListeners.length; i++) {
122             try{
123                 //This is a hack to get this working under both Ant and the GUI.
124                 //When not running in a separate VM for Ant, it prefers the Class.forName().
125                 if (useCurrentThread) {
126                     c = Thread.currentThread().getContextClassLoader().loadClass(tcListeners[i]);
127                 }else{
128                     c = Class.forName(tcListeners[i]);
129                 }
130                 tcListener = (TestCaseListener)c.newInstance();
131                 //Store it for later removal
132                 registerEventListener(tcListener);
133             }catch(Exception e){
134                 e.printStackTrace();
135                 System.err.println("Could not register TestCaseListeners: "+e.getMessage());
136             }
137         }
138     }
139 
140     /***
141      * Register a new TestCaseListener
142      * in the TestCaseEventHandler.
143      * @param tcListener The TestCaseListener to register
144      */
145     public void registerEventListener(TestCaseListener tcListener){
146         if (tcListener != null) {
147             TestCaseEventHandler tcHandler = TestCaseEventHandler.getInstance();
148             testCaseListeners.add(tcListener);
149             tcHandler.addTestCaseListener(tcListener);
150         }
151     }
152 
153     /***
154      * Remove all TestCaseListeners registered via the registerEventListeners method.
155      */
156     public void deregisterEventListeners(){
157         endTestRun();
158         TestCaseListener listener;
159         for (Iterator it = testCaseListeners.iterator(); it.hasNext(); ) {
160             listener = (TestCaseListener)it.next();
161             TestCaseEventHandler.getInstance().removeTestCaseListener(listener);
162         }
163     }
164 
165     public void setPrintFooter(boolean printFooter){
166         this.printFooter = printFooter;
167     }
168 
169     public void setWaitTimeBetweenScripts(long waitTimeBetweenScripts){
170         this.waitTimeBetweenScripts = waitTimeBetweenScripts;
171     }
172 
173     public long getWaitTimeBetweenScripts(){
174         return waitTimeBetweenScripts;
175     }
176 
177     /***
178      * Begins the test run. This creates a new html reporter which in turn ends up
179      * generating another results file.
180      */
181     public void beginTestRun(){
182         Calendar startTime = Calendar.getInstance();
183         try{
184             File resultsDir = ReporterUtils.getResultsDir();
185             File resultsFile = new File(ResultsReporter.getResultsDir(resultsDir, startTime),
186                     "TestResults.html");
187             File testRunSummaryFile = new File(resultsDir, JameleonDefaultValues.TEST_RUN_SUMMARY_FILE_NAME);
188             JameleonUtility.createDirStructure(resultsFile.getParentFile());
189             reporter = ResultsReporter.getInstance();
190             reporter.getHtmlTestRunReporter().setWriter(new FileWriter(resultsFile));
191             reporter.getHtmlTestRunSummaryReporter().setPrintHeader(testRunSummaryFile.length() == 0);
192             
193             reporter.getHtmlTestRunSummaryReporter().setWriter(new FileWriter(testRunSummaryFile, true));
194         }catch(IOException ioe){
195             throw new JameleonException("Could not configure Jameleon to write out results file", ioe);
196         }
197         TestCaseEventHandler.getInstance().addTestCaseListener(reporter);
198         TestRunEventHandler.getInstance().addTestRunListener(reporter);
199         TestRunEventHandler.getInstance().beginTestRun(startTime);
200     }
201 
202     /***
203      * Ends the test run.
204      */
205     public void endTestRun(){
206         TestRunEventHandler.getInstance().endTestRun(Calendar.getInstance());
207         TestCaseEventHandler.getInstance().removeTestCaseListener(reporter);
208         TestRunEventHandler.getInstance().removeTestRunListener(reporter);
209         try {
210             reporter.getHtmlTestRunReporter().getWriter().close();
211         } catch (IOException e) {
212             System.err.println("Error closing test results summary writer");
213             e.printStackTrace();
214         }
215     }
216 
217     /*
218      * Runs through a list of files or directories (args[]),
219      * executing each one.
220      */
221     public static void main (String args[]) {
222         StringBuffer errMsg = new StringBuffer();
223         ExecuteTestCase exec = new ExecuteTestCase();
224         exec.registerEventListeners();
225         boolean printFooterStatic = true;
226         int startingPoint = 0;
227         if (args.length > 0 && args[0].equalsIgnoreCase("false")) {
228             printFooterStatic = false;
229             startingPoint = 1;
230         }
231         if (args.length > startingPoint) {
232             File f = null;
233             for (int i = startingPoint; i < args.length; i++) {
234                 f  = new File(args[i]);
235                 if (f.exists()) {
236                     exec.addFile(f);
237                 }
238             }
239         }
240         errMsg.append(exec.executeFiles());
241         try{
242             if ( errMsg.length() > 0 ) {
243                 System.out.println("The following test cases failed:"+errMsg.toString());
244                 System.out.println("");
245                 System.out.println("");
246                 throw new JameleonException("See 'TestResults.xml' and 'TestResults.html' for a more detailed reason (stack trace) as why the test case(s) failed.");
247             }
248         }finally{
249             exec.deregisterEventListeners();
250             if (printFooterStatic) {
251                 closeAllLogs();
252             }
253         }
254     }
255 
256     /***
257      * This is a workaround to the fact the shutdown is not called in log4j.
258      */
259     public static void closeAllLogs(){
260         LogManager.getLoggerRepository().shutdown();
261     }
262 
263     /***
264      * Runs through all files in the given file or directory
265      * and executes each file as a Jameleon test script.
266      * @param f The file to execute
267      * @return the error message if one exists
268      */
269     public String execute(File f) {
270         StringBuffer errMsg = new StringBuffer();
271         if (f.isDirectory()) {
272             File[] filesA = f.listFiles();
273             for (int i = 0; i < filesA.length; i++) {
274                 if (filesA[i].isDirectory()) {
275                     errMsg.append(execute(filesA[i]));
276                 }else if(filesA[i].isFile() && filesA[i].getName().endsWith(".xml")){
277                     errMsg.append(executeJellyScript(filesA[i]));
278                 }else{
279                     System.out.println("Skipping "+filesA[i].getAbsolutePath());
280                 }
281             }
282         }else if(f.isFile() && f.getName().endsWith(".xml")){
283             errMsg.append(executeJellyScript(f));
284         }else{
285             System.out.println("Skipping "+f.getAbsolutePath());
286         }
287         return errMsg.toString();
288     }
289 
290     public String executeFiles(){
291         StringBuffer errMsg = new StringBuffer();
292         Iterator it = getFiles().iterator();
293         while (it.hasNext()) {
294             File f = (File)it.next();
295             errMsg.append(execute(f));
296             if (it.hasNext()) {
297                 delay();
298             }
299         }
300         return errMsg.toString();
301     }
302 
303     /***
304      * Executes the given file as a Jameleon test script and returns the error message if the test failed.
305      * Only a single file should be passed to this method.
306      * @param file - the file to execute.
307      * @return a String representing the error(s), if any, that occured
308      */
309     public String executeJellyScript(File file){
310         String errMsg = "";
311         JellyContext context = null;
312         try{
313             context = runScript(file);
314         }catch(FileNotFoundException fnfe){
315             errMsg = "Could not execute \""+file.getPath() + "\"! File NOT FOUND!";
316             return errMsg;
317         }catch(Exception je){
318             if ( debug ) {
319                 je.printStackTrace();
320             }
321             errMsg = US + file + DASH + "CAUSE:\n" + je.getMessage();
322         }finally{
323             if (context != null) {
324                 context.getVariables().clear();
325                 context.clear();
326                 context = null;
327             }
328         }
329         return errMsg;
330     }
331 
332     public JellyContext runScript(File file) throws JellyException, FileNotFoundException{
333         if (!file.exists()) {
334             throw new FileNotFoundException(file.getPath() +" not found!");
335         }
336         XMLOutput out = XMLOutput.createDummyXMLOutput();
337         JellyContext context = new JellyContext();
338         //This seems to cause jelly to max out on memory if set.
339         //context.setCacheTags(false);
340         String fileName = file.getPath();
341         JellyContext scriptContext = null;
342         try{
343             context.registerTagLibrary("jelly:jameleon", tagLibrary);
344             setContextVariables(context);
345             scriptContext = context.runScript(file, out);
346         }finally{
347             try{
348                 out.close();
349             }catch(IOException ioe){
350                 ioe.printStackTrace();
351             }
352             out = null;
353         }
354         return scriptContext;
355     }
356 
357     protected void setContextVariables(JellyContext context){
358         if (contextVars.size() > 0) {
359             context.setVariables(contextVars);
360         }
361     }
362 
363     /***
364      * Adds a file or directory to the list of files to be executed by this instance.
365      * @param f The file to add
366      */
367     public void addFile(File f){
368         files.add(f);
369     }
370 
371     /***
372      * @return a List of files and/or directories to be executed by this instance.
373      */
374     public List getFiles(){
375         return files;
376     }
377 
378     public Map getContextVars(){
379         return contextVars;
380     }
381 
382 
383     protected void delay(){
384         if (waitTimeBetweenScripts > 0) {
385             synchronized (this){ 
386                 try {
387                     this.wait(waitTimeBetweenScripts); 
388                 } catch (InterruptedException e) {
389                     e.printStackTrace(); 
390                 }
391             } 
392         }
393     }
394 
395 }
396