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 }