001: /*
002: * $Id: CanooWebTestReporter.java,v 1.9 2006/04/30 22:25:56 spal Exp $
003: * $Source: /cvsroot/sqlunit/sqlunit/src/net/sourceforge/sqlunit/reporters/CanooWebTestReporter.java,v $
004: * SQLUnit - a test harness for unit testing database stored procedures.
005: * Copyright (C) 2003 The SQLUnit Team
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021: package net.sourceforge.sqlunit.reporters;
022:
023: import net.sourceforge.sqlunit.IReporter;
024:
025: import org.apache.xml.serialize.OutputFormat;
026: import org.apache.xml.serialize.XMLSerializer;
027: import org.w3c.dom.Document;
028: import org.w3c.dom.Element;
029: import org.w3c.dom.Node;
030: import org.w3c.dom.NodeList;
031:
032: import java.io.BufferedWriter;
033: import java.io.File;
034: import java.io.FileOutputStream;
035: import java.io.IOException;
036: import java.io.OutputStreamWriter;
037: import java.io.PrintWriter;
038: import java.io.StringWriter;
039: import java.util.Date;
040: import java.util.Iterator;
041: import java.util.Map;
042:
043: import javax.xml.parsers.DocumentBuilder;
044: import javax.xml.parsers.DocumentBuilderFactory;
045:
046: /**
047: * SQLUnit Reporter that works with the Canoo Web Test framework. Generates
048: * XML that can be converted to output for the Canoo Web Test console.
049: * @author Rob Nielsen (robn@asert.com.au)
050: * @author Paul King (paulk@asert.com.au)
051: * @version $Revision: 1.9 $
052: */
053: public class CanooWebTestReporter implements IReporter {
054:
055: /** the format name to be used in the ant task */
056: public static final String FORMAT_NAME = "canoo";
057:
058: // Constants used in the XML file
059: private static final String ROOT_TAG = "summary";
060:
061: private static final String TEST_TAG = "testresult";
062: private static final String TEST_END_ATT = "endtime";
063: private static final String TEST_START_ATT = "starttime";
064: private static final String TEST_FILE_ATT = "location";
065: private static final String TEST_SUCCESS_ATT = "successful";
066: private static final String TEST_NAME_ATT = "testspecname";
067:
068: private static final String RESULTS_TAG = "results";
069: private static final String CONFIG_TAG = "config";
070:
071: private static final String ECHO_TAG = "echo";
072: private static final String ECHO_TEXT_ATT = "text";
073:
074: private static final String PARAM_TAG = "parameter";
075: private static final String PARAM_NAME_ATT = "name";
076: private static final String PARAM_VALUE_ATT = "value";
077:
078: private static final String STEP_TAG = "step";
079: private static final String DESCRIPTION_TAG = "description";
080: private static final String TASKNAME_TAG = "taskName";
081: private static final String FAILURE_TAG = "failure";
082: private static final String ERROR_TAG = "error";
083: private static final String EXCEPTION_ATT = "exception";
084: private static final String MESSAGE_ATT = "message";
085: private static final String STEP_RESULT_TAG = "result";
086: private static final String STEP_COMPLETED_TAG = "completed";
087: private static final String STEP_COMPLETED_DURATION_ATT = "duration";
088: private static final String STEP_FAILED_TAG = "failed";
089: private static final String STEP_NOTEXECUTED_TAG = "notexecuted";
090:
091: private static final int DEFAULT_LINE_LENGTH = 50;
092: private Element currentStep;
093: private Element currentTestResults;
094: private Element currentTest;
095: private Document doc;
096: private String outputFile;
097:
098: /**
099: * Constructs a new CanooWebTestReporter
100: * @param outputFile the file
101: * @throws Exception if a problem occurs
102: */
103: public CanooWebTestReporter(final String outputFile)
104: throws Exception {
105: this .outputFile = outputFile;
106: File f = new File(outputFile);
107: DocumentBuilder builder = DocumentBuilderFactory.newInstance()
108: .newDocumentBuilder();
109: if (f.exists()) {
110: doc = builder.parse(new File(outputFile));
111: } else {
112: doc = builder.newDocument();
113: }
114: }
115:
116: /**
117: * Returns the format name.
118: * @return the format name.
119: */
120: public final String getName() {
121: return FORMAT_NAME;
122: }
123:
124: /**
125: * Returns true since this reporter runs in the context of a Canoo
126: * Web Test.
127: * @return true.
128: */
129: public final boolean hasContainer() {
130: return true;
131: }
132:
133: /**
134: * Called when a new sqlunit test file is run.
135: * @param name the name of the test being run
136: * @param location the filename of the test file
137: */
138: public final void newTestFile(final String name,
139: final String location) {
140: Element root = null;
141: NodeList nodeList = doc.getElementsByTagName(ROOT_TAG);
142: if (nodeList.getLength() > 0) {
143: root = (Element) nodeList.item(0);
144: } else {
145: if (doc.getChildNodes().getLength() > 0) {
146: throw new IllegalStateException(
147: "Given XML file has an incorrect root element. Expecting "
148: + ROOT_TAG);
149: } else {
150: root = addElement(doc, ROOT_TAG);
151: }
152: }
153: currentTest = addElement(root, TEST_TAG);
154: currentTest.setAttribute(TEST_START_ATT, new Date().toString());
155: currentTest.setAttribute(TEST_FILE_ATT, location);
156: currentTest.setAttribute(TEST_NAME_ATT, name);
157: currentTestResults = addElement(currentTest, RESULTS_TAG);
158: }
159:
160: /**
161: * Called before a database connection is setup.
162: * @param connectionId the id of the connection being attempted,
163: * or null for the default connection
164: */
165: public final void settingUpConnection(final String connectionId) {
166: // :NOTE: no action
167: }
168:
169: /**
170: * Called after the connection is made. The map contains key/value
171: * pairs of configuration parameters for the connection and debug settings.
172: * @param configMap the configuration map
173: */
174: public final void setConfig(final Map configMap) {
175: Element config = addElement(currentTest, CONFIG_TAG);
176: for (Iterator it = configMap.keySet().iterator(); it.hasNext();) {
177: String n = (String) it.next();
178: String v = (String) configMap.get(n);
179: addParameter(config, n, v);
180: }
181: }
182:
183: /**
184: * Called before the set up section of the test.
185: */
186: public final void setUp() {
187: // :NOTE: no action
188: }
189:
190: /**
191: * Called before a test is run.
192: * @param name the name of the test being run
193: * @param testIndex the index of the test being run
194: * @param desc a description of the test being run
195: */
196: public final void runningTest(final String name,
197: final int testIndex, final String desc) {
198: currentStep = addElement(currentTestResults, STEP_TAG);
199: addParameter(currentStep, DESCRIPTION_TAG, desc);
200: addParameter(currentStep, TASKNAME_TAG, "sqlunit:" + name);
201: }
202:
203: /**
204: * Called when a test is completed
205: * @param time the time in milliseconds the test took to run
206: * @param success true if the test succeeded, false otherwise
207: */
208: public final void finishedTest(final long time,
209: final boolean success) {
210: // we may return a null for DDL code such as DROP and CREATE depending
211: // on how the JDBC driver handles it, so if we see this here, then
212: // we dont show it. This is not an ideal situation, we really should
213: // manufacture an empty DOM Node implementation with the text value
214: // of NULL.
215: if (currentStep == null) {
216: return;
217: }
218: Element result = addElement(currentStep, STEP_RESULT_TAG);
219: if (success) {
220: Element completed = addElement(result, STEP_COMPLETED_TAG);
221: completed.setAttribute(STEP_COMPLETED_DURATION_ATT, ""
222: + time);
223: } else {
224: addElement(result, STEP_FAILED_TAG);
225: }
226: }
227:
228: /**
229: * Called when a test is skipped because of an earlier failure.
230: * @param name the name of the test being run
231: * @param testIndex the index of the test being run
232: * @param desc a description of the test being run
233: */
234: public final void skippedTest(final String name,
235: final int testIndex, final String desc) {
236: runningTest(name, testIndex, desc);
237: Element result = addElement(currentStep, STEP_RESULT_TAG);
238: addElement(result, STEP_NOTEXECUTED_TAG);
239: }
240:
241: /**
242: * Called when an exception occured during processing.
243: * @param th the exception that occured
244: * @param isError true if the exception is an error, else false.
245: */
246: public final void addFailure(final Throwable th,
247: final boolean isError) {
248: if (isError) {
249: Element error = addElement(currentTestResults, ERROR_TAG);
250: error.setAttribute(EXCEPTION_ATT, th.getClass().getName());
251: error.setAttribute(MESSAGE_ATT, th.getMessage());
252: StringWriter s = new StringWriter();
253: th.printStackTrace(new PrintWriter(s));
254: error.appendChild(doc.createCDATASection(s.toString()));
255: } else {
256: Element failure = addElement(currentTestResults,
257: FAILURE_TAG);
258: failure.setAttribute(MESSAGE_ATT, th.toString());
259: }
260: }
261:
262: /**
263: * Called when a test has failed and a temporary file is left containing
264: * data.
265: * @param testId the index of the test
266: * @param result the result of the test
267: * @param file the temporary file
268: */
269: public final void tempFile(final int testId, final String result,
270: final String file) {
271: // :NOTE: no action
272: }
273:
274: /**
275: * Called before the tear down section is run
276: */
277: public final void tearDown() {
278: // :NOTE: no action
279: }
280:
281: /**
282: * Called when the test file has been completed.
283: * @param success true if everything completed with no errors,
284: * false otherwise
285: */
286: public final void testFileComplete(final boolean success) {
287: currentTest.setAttribute(TEST_END_ATT, new Date().toString());
288: currentTest.setAttribute(TEST_SUCCESS_ATT, success ? "yes"
289: : "no");
290: writeXML();
291: }
292:
293: /**
294: * Called from within the test from the SQLUnit Echo tag to print
295: * out values from within the test.
296: * @param message the message to echo.
297: */
298: public final void echo(String message) {
299: Element echo = addElement(currentTestResults, ECHO_TAG);
300: echo.setAttribute(ECHO_TEXT_ATT, message);
301: }
302:
303: /**
304: * Writes out the new xml data to the file
305: */
306: private void writeXML() {
307: try {
308: OutputFormat format = new OutputFormat(doc);
309: format.setIndenting(true);
310: format.setEncoding("ISO-8859-1");
311: format.setLineWidth(DEFAULT_LINE_LENGTH);
312: BufferedWriter writer = new BufferedWriter(
313: new OutputStreamWriter(new FileOutputStream(
314: outputFile)));
315: XMLSerializer serializer = new XMLSerializer(writer, format);
316: serializer.asDOMSerializer();
317: serializer.serialize(doc.getDocumentElement());
318: writer.close();
319: } catch (IOException e) {
320: e.printStackTrace();
321: }
322: }
323:
324: /**
325: * Adds an element to the tree
326: * @param parent the parent node
327: * @param name the node to add
328: * @return the Element added to the tree.
329: */
330: private Element addElement(final Node parent, final String name) {
331: Element ret = doc.createElement(name);
332: parent.appendChild(ret);
333: return ret;
334: }
335:
336: /**
337: * Adds a parameter to the tree
338: * @param parent the parent node
339: * @param name the parameter name
340: * @param value the parameter value
341: * @return the parameter element added to the tree.
342: */
343: private Element addParameter(final Node parent, final String name,
344: final String value) {
345: Element param = addElement(parent, PARAM_TAG);
346: param.setAttribute(PARAM_NAME_ATT, name);
347: param.setAttribute(PARAM_VALUE_ATT, value);
348: return param;
349: }
350: }
|