001    /*
002     * Copyright 1999-2001,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    // java imports
020    import java.io.IOException;
021    import java.io.PrintWriter;
022    import java.io.StringWriter;
023    // latka imports
024    import org.apache.commons.latka.event.LatkaEventListener;
025    import org.apache.commons.latka.event.ReportMessageEvent;
026    import org.apache.commons.latka.event.RequestErrorEvent;
027    import org.apache.commons.latka.event.RequestEvent;
028    import org.apache.commons.latka.event.RequestFailedEvent;
029    import org.apache.commons.latka.event.SuiteEvent;
030    import org.apache.commons.latka.http.Request;
031    // dom4j imports
032    import org.dom4j.Document;
033    import org.dom4j.DocumentFactory;
034    import org.dom4j.Element;
035    import org.dom4j.io.OutputFormat;
036    import org.dom4j.io.XMLWriter;
037    
038    /**
039     * This LatkaEventListener will generate an XML report
040     * of all tests run during an Latka XML suite.  XMLReporter
041     * instances are NOT reusable for multiple invocations
042     * of the {@link 
043     * org.apache.commons.latka.Latka#runTests(org.apache.commons.latka.Suite,
044     * org.apache.commons.latka.event.LatkaEventInfo) 
045     * Latka#runTests(Suite, LatkaEventInfo)} method.
046     *
047     * @author Morgan Delagrange
048     * @author dIon Gillard
049     * @version $Id: XMLReporter.java 155424 2005-02-26 13:09:29Z dirkv $
050     */
051    public class XMLReporter extends AbstractReporter implements LatkaEventListener {
052        /** dom4j document produced as output */
053        protected Document _doc         = null;
054        /** top level element of dom4j document, a <report> element*/
055        protected Element  _rootElement = null;
056        /** used for output as tests run */                   
057        protected PrintWriter _printWriter = new PrintWriter(System.out);
058    
059        protected boolean _didSuiteSucceed = true;
060    
061        protected DocumentFactory _factory = new DocumentFactory();
062    
063        /**
064         * Create an XML Reporter, initialising document property, as a new empty
065         * report with an unsuccessful suite
066         */
067        public XMLReporter() {
068            _rootElement = _factory.createElement("report");
069            //defaults to false, until a SuiteSuccess event occurs
070            _doc = _factory.createDocument(_rootElement);
071            _rootElement.addAttribute("suiteSuccess", "false");
072        }
073    
074        /**
075         * Returns the XML Document produced by this listener
076         *
077         * @return dom4j representation of the test report
078         */
079        public Document getDocument() {
080            return _doc;
081        }
082    
083        /**
084         * Returns the test report converted from dom4j to a
085         * text string.
086         *
087         * @return the test report as an XML string
088         * @throws IOException if the XML formatter cannot convert the dom4j object
089         *                     to text
090         */
091        public String getDocumentAsString() throws IOException {
092            // get the xml string from the listener
093            StringWriter writer = new StringWriter();
094            XMLWriter outputter = new XMLWriter(writer, new OutputFormat("  ", true));
095          
096            outputter.write(getDocument());
097    
098            String xmlDocument = writer.toString();
099    
100            return xmlDocument;
101        }
102    
103        /**
104         * @see AbstractReporter#requestError(RequestEvent) 
105         * @param event the event detailing the request in error
106         */
107        public void requestError(RequestEvent event) {
108            _didSuiteSucceed = false;
109    
110            _printWriter.print("E");
111            _printWriter.flush();
112    
113            RequestErrorEvent errorEvent = (RequestErrorEvent) event;
114            // <root><request><requestError><label>errortext</label>
115            // </requestError></request></root>
116            Element label = _factory.createElement("label")
117                                .addText(errorEvent.getError().toString());
118            Element requestError = _factory.createElement("requestError");
119            requestError.add(label);
120    
121            Element requestElement = createRequestElement(event);
122            requestElement.add(requestError);
123            _rootElement.add(requestElement);
124    
125        }
126    
127        /**
128         * @see AbstractReporter#requestSucceeded(RequestEvent)
129         * @param event the event detailing the request that succeeded
130         */
131        public void requestSucceeded(RequestEvent event) {
132    
133            _printWriter.print(".");
134            _printWriter.flush();
135        
136            Element requestElement = createRequestElement(event);
137            requestElement.add(_factory.createElement("requestSuccess"));
138            _rootElement.add(requestElement);
139        }
140    
141        /**
142         * @see AbstractReporter#requestFailed(RequestEvent)
143         * @param event the event detailing the request that failed
144         */
145        public void requestFailed(RequestEvent event) {
146            _didSuiteSucceed = false;
147    
148            _printWriter.print("F");
149            _printWriter.flush();
150        
151            RequestFailedEvent failedEvent = (RequestFailedEvent) event;
152    
153            // <root><request><requestFailure responseId="xxx"><label>XXX</label>
154            // </requestFailure></request></root>
155            Element label = _factory.createElement("label")
156                                .addText(failedEvent.getValidationException()
157                                                       .getMessage()
158                                                       .toString());
159            Element requestFailure = _factory.createElement("requestFailure")
160                                        .addAttribute("responseId",
161                                                      event.getResponse().toString()
162                                                     );
163            requestFailure.add(label);
164    
165            Element requestElement = createRequestElement(event);
166            requestElement.add(requestFailure);
167            _rootElement.add(requestElement);
168      }
169    
170        /**
171         * @see AbstractReporter#requestSkipped(RequestEvent)
172         * @param event the event detailing the request that was skipped
173         */
174        public void requestSkipped(RequestEvent event) {
175            _didSuiteSucceed = false;
176    
177            _printWriter.print("S");
178            _printWriter.flush();
179        
180            Request request = event.getRequest();
181            // <root><request><requestSkipped/></request></root>
182            Element requestElement = createRequestElement(event);
183            requestElement.add(_factory.createElement("requestSkipped"));
184            _rootElement.add(requestElement);
185    
186        }
187    
188        /**
189         * Add the {@link ReportMessageEvent#getMessage() message} of the provided
190         * event to the XML document as a &lt;reportMessage&gt; element with text
191         *
192         * @param event a {@link ReportMessageEvent}
193         */
194        public void reportMessage(ReportMessageEvent event) {
195            Element messageElement = _factory.createElement("reportMessage")
196              .addAttribute("message", event.getMessage());
197            _rootElement.add(messageElement);
198        }
199    
200        /**
201         * @see AbstractReporter#suiteCompleted(SuiteEvent)
202         * @param event the event detailing the completing Suite
203         */
204        public void suiteCompleted(SuiteEvent event) {
205            _rootElement.addAttribute("suiteSuccess",
206                String.valueOf(_didSuiteSucceed));
207        }
208    
209        /**
210         * Utility method that converts a RequestEvent object to its
211         * corresponding element in the dom4j object.
212         *
213         * @param event the request event sent to the listener
214         * @return the dom4j Element equivalent
215         */
216        protected Element createRequestElement(RequestEvent event) {
217            // the request object
218            Request request = event.getRequest();
219    
220            Element requestElement = _factory.createElement("request")
221                .addAttribute("requestTime",
222                              String.valueOf(request.getRequestTiming()))
223                .addAttribute("url", request.getURL().toString());
224    
225            String label = request.getLabel();
226            if (label != null) {
227                requestElement.addAttribute("label", label);
228            }
229    
230            return requestElement;
231        }
232    
233        /**
234         * During the execution, any diagnostic ouput will be sent
235         * to the stream designated here.  If no PrintWriter
236         * is set, output will be sent to standard out.
237         * 
238         * @param writer PrintWriter that will receive output generated during
239         *               the test
240         */
241        public void setPrintWriter(PrintWriter writer) {
242            _printWriter = writer;
243        }
244    
245    }