001 /* 002 * Copyright 1999-2002,2004 The Apache Software Foundation. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.apache.commons.latka; 018 019 import java.io.File; 020 import java.io.FileInputStream; 021 import java.io.FileWriter; 022 import java.io.IOException; 023 import java.io.StringReader; 024 import java.io.StringWriter; 025 import java.net.URL; 026 import java.text.SimpleDateFormat; 027 import java.util.Date; 028 import java.util.Iterator; 029 import java.util.Properties; 030 031 import javax.xml.transform.Source; 032 import javax.xml.transform.Transformer; 033 import javax.xml.transform.TransformerException; 034 import javax.xml.transform.TransformerFactory; 035 import javax.xml.transform.stream.StreamResult; 036 import javax.xml.transform.stream.StreamSource; 037 038 import org.apache.commons.jelly.Jelly; 039 import org.apache.commons.jelly.JellyContext; 040 import org.apache.commons.jelly.XMLOutput; 041 042 import org.apache.commons.latka.event.LatkaEventInfo; 043 import org.apache.commons.latka.event.LatkaEventListener; 044 import org.apache.commons.latka.jelly.JellyUtils; 045 046 import org.apache.log4j.Category; 047 048 /** 049 * This is the primary class for executing Latka functional 050 * tests. The main(String aargs[]) class provides a convenient 051 * command-line interface for executing single tests. See the 052 * Latka documentation for details on command-line usage. 053 * There is also a webapp-based Latka interface. 054 * 055 * @see Suite 056 * @see LatkaProperties 057 * 058 * @author Morgan Delagrange 059 * @author <a href="mailto:dion@apache.org">dIon Gillard</a> 060 * 061 * @version $Revision: 155424 $ 062 */ 063 public class Latka { 064 065 /** whether xml parsing is validating or not */ 066 protected boolean _isValidating = true; 067 /** XSL stylesheet url to use when generating XML */ 068 protected URL _reportStylesheetUrl = null; 069 070 /** log4j category for logged output */ 071 protected static final Category _log = 072 Category.getInstance(Latka.class.getName()); 073 074 /** Latka configuration variables */ 075 protected Properties _props = LatkaProperties.getProperties(); 076 077 /** Message displayed to user when incorrect args provided */ 078 private static final String LATKA_USAGE = 079 "\n######################\n" 080 + "Latka usage:\n" 081 + " java org.apache.commons.latka.Latka \"XML_suite_URL\" " 082 + "[\"propfile:file_name\"] [\"prop:prop_name=prop_value\"]\n\n" 083 + "The quotes around properties are REQUIRED.\n" 084 + "######################\n"; 085 086 /** 087 * Execute a single Latka test suite. The Latka event listner 088 * will receive all the events generated during the test. 089 * The Latka packages contain an implmentation of LatkaEventInfo 090 * which can generate an XML report according to a standard 091 * DTD (see documentation). 092 * 093 * @param suite test suite to execute 094 * @param listener target for test events 095 * @exception LatkaException 096 * when any internal error occurs 097 * @see XMLReporter XML-based implementation of LatkaEventListener 098 */ 099 public void runTests(Suite suite, LatkaEventListener listener) 100 throws LatkaException { 101 102 try { 103 104 Jelly jelly = new Jelly(); 105 URL url = suite.getURL(); 106 jelly.setUrl(url); 107 jelly.setValidateXML(_isValidating); 108 jelly.setDefaultNamespaceURI("jelly:org.apache.commons.latka.jelly.LatkaTagLibrary"); 109 110 JellyContext context = buildJellyContext(); 111 JellyUtils.getInstance().setLatkaEventListener(context,listener); 112 113 String exturl = url.toExternalForm(); 114 int lastSlash = exturl.lastIndexOf("/"); 115 String extBase = exturl.substring(0,lastSlash+1); 116 URL baseurl = new URL(extBase); 117 context.setCurrentURL(baseurl); 118 119 // add listener 120 jelly.compileScript().run(context,XMLOutput.createDummyXMLOutput()); 121 122 } catch (Exception e) { 123 throw new LatkaException(e); 124 } 125 } 126 127 protected JellyContext buildJellyContext() { 128 JellyContext context = new JellyContext(); 129 Iterator keys = _props.keySet().iterator(); 130 while (keys.hasNext()) { 131 String key = (String) keys.next(); 132 String value = _props.getProperty(key); 133 context.setVariable(key,value); 134 } 135 136 return context; 137 } 138 139 /** 140 * Set whether or not Latka will validate the XML in 141 * each test. 142 * 143 * @param isValidating whether or not to validate XML 144 */ 145 public void setValidating(boolean isValidating) { 146 _isValidating = isValidating; 147 } 148 149 /** 150 * Set the URL to be used when transforming XML generated by the XMLReporter 151 * @param url a valid URL referencing a stylesheet 152 */ 153 public void setReportStylesheet(URL url) { 154 _reportStylesheetUrl = url; 155 } 156 157 /** 158 * Use this method to log XML generated by the 159 * XMLReporter class. 160 * 161 * @param xml XML to be logged 162 * @throws IOException if the XML could not be written to the filesystem 163 */ 164 protected void logXML(String xml) throws IOException { 165 166 String logProp = _props.getProperty("latka.writeLog"); 167 if ((logProp != null) && (logProp.equals("false"))) { 168 return; 169 } 170 171 File dir = new File("logs"); 172 // try to create logs directory. if it fails, then check to 173 // make sure it is there (may have existed before) and is writeable 174 if (!dir.mkdirs()) { 175 if (!dir.exists ()) { 176 throw new IOException("Unable to create logs directory"); 177 } else if (!dir.canWrite ()) { 178 throw new IOException("Unable to write to logs directory"); 179 } 180 } 181 SimpleDateFormat formatter = new SimpleDateFormat ("yyyyMMdd'-'HHmm"); 182 String dateString = formatter.format(new Date()); 183 File file = new File(dir, dateString + ".xml"); 184 FileWriter fileWriter = new FileWriter(file); 185 fileWriter.write(xml); 186 fileWriter.close(); 187 } 188 189 /** 190 * Transform the XML generated by the XMLReporter using 191 * the default stylesheet. 192 * 193 * @param xml XML generated by XMLReporter 194 * @return transformed report 195 * @throws LatkaException if the XML could not be transformed 196 */ 197 public String transformXML(String xml) throws LatkaException { 198 199 try { 200 StringWriter output = new StringWriter(); 201 202 Source xslSource = null; 203 204 if (_reportStylesheetUrl == null) { 205 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 206 if (loader == null) { 207 // there may not be a context class loader 208 loader = getClass().getClassLoader(); 209 } 210 211 xslSource = new StreamSource( 212 loader.getResourceAsStream( 213 "org.apache.commons.latka.report.xsl") 214 ); 215 } else { 216 xslSource = new StreamSource(_reportStylesheetUrl.toString()); 217 } 218 219 220 Transformer transformer = 221 TransformerFactory.newInstance().newTransformer(xslSource); 222 StreamSource xmlSource = new StreamSource(new StringReader(xml)); 223 StreamResult result = new StreamResult(output); 224 transformer.transform(xmlSource, result); 225 return output.toString(); 226 } catch (TransformerException e) { 227 throw new LatkaException(e); 228 } 229 } 230 231 /** 232 * Processes the command line arguments, executes a single test. 233 * and processes the resulting report. Command line usage is 234 * described in the LATKA_USAGE constant. 235 * 236 * @param args arguments passed into the main(String[]) method 237 * 238 * @throws LatkaException if any problems are encountered during 239 * the execution of the tests 240 */ 241 protected void runCommandLine(String args[]) throws LatkaException { 242 243 if (args.length < 1) { 244 System.out.println(LATKA_USAGE); 245 } 246 247 String urlString = args[0]; 248 249 if (args.length > 1) { 250 251 for (int i = 1; i < args.length; ++i) { 252 String arg = args[i]; 253 if (arg.startsWith("prop:")) { 254 String propName = arg.substring(5, arg.indexOf("=")); 255 String propValue = arg.substring(arg.indexOf("=") + 1); 256 _props.setProperty(propName, propValue); 257 } else if (arg.startsWith("propfile:")) { 258 try { 259 _props.load(new FileInputStream(arg.substring(9))); 260 } catch (IOException e) { 261 System.out.println("Could not load user prop file, uri=" 262 + arg.substring(9)); 263 } 264 } else { 265 throw new IllegalArgumentException(LATKA_USAGE); 266 } 267 } // for 268 } // if 269 270 String xml = null; 271 XMLReporter listener = new XMLReporter(); 272 273 // overkill, all we need is success/failure 274 LatkaEventInfo info = new DefaultLatkaEventInfo(listener); 275 276 try { 277 278 URL url = new URL(urlString); 279 Suite suite = new Suite(url); 280 281 282 runTests(suite, info); 283 284 xml = listener.getDocumentAsString(); 285 logXML(xml); 286 } catch (IOException e) { 287 throw new LatkaException(e); 288 } 289 290 System.out.println(transformXML(xml)); 291 292 if (info.didSuiteSucceed() == false) { 293 // throw an exception, so the process will 294 // return as a failure 295 throw new LatkaException("SUITE FAILED"); 296 } 297 } 298 299 /** 300 * Execute a single test suite via the command line 301 * interface. See the Latka documentation for detailed 302 * usage instructions. The Java process will return 303 * a 0 if all tests succeed and a 1 if any fail 304 * or there is an unrecoverable error in the test 305 * execution. 306 * 307 * @param args arguments containing the test suite location 308 * and any properties or property file references 309 */ 310 public static void main (String args[]) { 311 312 Latka latka = new Latka(); 313 try { 314 latka.runCommandLine(args); 315 } catch (LatkaException e) { 316 e.printStackTrace(); 317 System.exit(1); 318 } 319 320 } 321 }