1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.woopi.ant.taskdefs.junit;
18
19 import java.io.BufferedOutputStream;
20 import java.io.File;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.OutputStream;
24 import java.io.OutputStreamWriter;
25 import java.io.PrintWriter;
26 import java.io.FileWriter;
27 import java.io.BufferedWriter;
28 import java.util.Enumeration;
29 import java.util.Vector;
30 import java.util.Hashtable;
31 import java.util.HashSet;
32 import java.util.Map;
33 import java.util.TreeMap;
34 import java.util.StringTokenizer;
35 import java.text.DecimalFormat;
36 import javax.xml.parsers.DocumentBuilder;
37 import javax.xml.parsers.DocumentBuilderFactory;
38 import org.apache.tools.ant.BuildException;
39 import org.apache.tools.ant.DirectoryScanner;
40 import org.apache.tools.ant.Project;
41 import org.apache.tools.ant.Task;
42 import org.apache.tools.ant.types.FileSet;
43 import org.apache.tools.ant.util.DOMElementWriter;
44 import org.apache.tools.ant.util.StringUtils;
45 import org.w3c.dom.Document;
46 import org.w3c.dom.Element;
47 import org.w3c.dom.Node;
48 import org.w3c.dom.NodeList;
49 import org.w3c.dom.NamedNodeMap;
50 import org.xml.sax.SAXException;
51
52 import org.apache.velocity.VelocityContext;
53 import org.apache.velocity.Template;
54 import org.apache.velocity.app.Velocity;
55 import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
56
57
58 /***
59 * Aggregates all <junit> XML formatter testsuite data under
60 * a specific directory and transforms the results via XSLT.
61 * It is not particulary clean but
62 * should be helpful while I am thinking about another technique.
63 *
64 * <p> The main problem is due to the fact that a JVM can be forked for a testcase
65 * thus making it impossible to aggregate all testcases since the listener is
66 * (obviously) in the forked JVM. A solution could be to write a
67 * TestListener that will receive events from the TestRunner via sockets. This
68 * is IMHO the simplest way to do it to avoid this file hacking thing.
69 *
70 *
71 * @ant.task name="junitreport" category="testing"
72 */
73 public class JunitVelocityReportTask extends Task implements XMLConstants {
74
75 /*** the list of all filesets, that should contains the xml to aggregate */
76 protected Vector filesets = new Vector();
77
78 /*** the name of the result file */
79 protected String toFile;
80
81 /*** the directory to write the file to */
82 protected File toDir;
83
84 protected Vector transformers = new Vector();
85
86 /*** The default directory: <tt>.</tt>. It is resolved from the project directory */
87 public static final String DEFAULT_DIR = ".";
88
89 /*** the default file name: <tt>TESTS-TestSuites.xml</tt> */
90 public static final String DEFAULT_FILENAME = "TESTS-TestSuites.xml";
91
92 public DecimalFormat dnf = new DecimalFormat("##0.##");
93
94 public JunitVelocityReportTask() {
95 super();
96 try {
97 Velocity.setProperty("resource.loader","class");
98 Velocity.setProperty("class.resource.loader.class",
99 "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
100 Velocity.init();
101 Velocity.setProperty("resource.loader","class");
102 Velocity.setProperty("class.resource.loader.class",
103 "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
104 } catch (Exception e) {e.printStackTrace();}
105 }
106
107
108 /***
109 * Generate a report based on the document created by the merge.
110 */
111 public AggregateTransformer createReport() {
112 AggregateTransformer transformer = new AggregateTransformer(this);
113 transformers.addElement(transformer);
114 return transformer;
115 }
116
117 /***
118 * Set the name of the aggregegated results file. It must be relative
119 * from the <tt>todir</tt> attribute. If not set it will use {@link #DEFAULT_FILENAME}
120 * @param value the name of the file.
121 * @see #setTodir(File)
122 */
123 public void setTofile(String value) {
124 toFile = value;
125 }
126
127 /***
128 * Set the destination directory where the results should be written. If not
129 * set if will use {@link #DEFAULT_DIR}. When given a relative directory
130 * it will resolve it from the project directory.
131 * @param value the directory where to write the results, absolute or
132 * relative.
133 */
134 public void setTodir(File value) {
135 toDir = value;
136 }
137
138 /***
139 * Add a new fileset containing the XML results to aggregate
140 * @param fs the new fileset of xml results.
141 */
142 public void addFileSet(FileSet fs) {
143 filesets.addElement(fs);
144 }
145
146 /***
147 * Aggregate all testsuites into a single document and write it to the
148 * specified directory and file.
149 * @throws BuildException thrown if there is a serious error while writing
150 * the document.
151 */
152 public void execute() throws BuildException {
153 Element rootElement = createDocument();
154 File destFile = getDestinationFile();
155
156 try {
157 writeDOMTree(rootElement.getOwnerDocument(), destFile);
158 } catch (IOException e) {
159 throw new BuildException("Unable to write test aggregate to '" + destFile + "'", e);
160 }
161
162 generateVelocityReport(rootElement);
163
164
165
166
167
168
169
170
171
172
173 }
174
175 /***
176 * the rootElement should contain the node testsuits
177 */
178 public void generateVelocityReport(Element rootElement) {
179
180
181
182
183
184
185
186 generateStylesheet(rootElement);
187 generateIndex(rootElement);
188 generateOverviewFrame(rootElement);
189 generateOverviewSummary(rootElement);
190 generateAllClassesFrame(rootElement);
191 generateTestClassInfos(rootElement);
192 generatePackageSummaryFiles(rootElement);
193 generatePackageFrames(rootElement);
194 }
195
196 /***
197 * just applicable to testsuites Node
198 */
199 public int getIntegerSumCount(Element rootElement,String attributeName) {
200 int result = 0;
201 NodeList testsuiteNodes = rootElement.getElementsByTagName(TESTSUITE);
202 for (int i=0;i<testsuiteNodes.getLength();i++) {
203 Element testsuiteNode = (Element)testsuiteNodes.item(i);
204 NamedNodeMap attributeMap =testsuiteNode.getAttributes();
205 Node testAttribute = attributeMap.getNamedItem(attributeName);
206 try {result += Integer.parseInt(testAttribute.getNodeValue()); } catch (Exception e) {}
207 }
208 return result;
209 }
210
211 /***
212 * just applicable to testsuites Node
213 */
214 public double getDoubleSumCount(Element rootElement,String attributeName) {
215 double result = 0.0;
216 NodeList testsuiteNodes = rootElement.getElementsByTagName(TESTSUITE);
217 for (int i=0;i<testsuiteNodes.getLength();i++) {
218 Element testsuiteNode = (Element)testsuiteNodes.item(i);
219 NamedNodeMap attributeMap =testsuiteNode.getAttributes();
220 Node testAttribute = attributeMap.getNamedItem(attributeName);
221 try {result += Double.parseDouble(testAttribute.getNodeValue()); } catch (Exception e) {}
222 }
223 return result;
224 }
225
226 public String getSummaryStylesheetClass(int errorCount, int failureCount) {
227 String result = "Pass";
228 if (errorCount > 0) result = "Error";
229 else if (failureCount > 0) result = "Failure";
230 return result;
231 }
232
233 /***
234 * just applicable to testsuites Node
235 */
236 public Map[] getPackagesMap(Element rootElement) {
237 Map[] result = null;
238 HashSet resultSet = new HashSet();
239 String noPackage = "<none>";
240 NodeList testsuiteNodes = rootElement.getElementsByTagName(TESTSUITE);
241 for (int i=0;i<testsuiteNodes.getLength();i++) {
242 Element testsuiteNode = (Element)testsuiteNodes.item(i);
243 NamedNodeMap attributeMap =testsuiteNode.getAttributes();
244 Node packageAttribute = attributeMap.getNamedItem(ATTR_PACKAGE);
245 Hashtable temp = new Hashtable();
246 String packageName = packageAttribute.getNodeValue();
247 if (packageName == null || packageName.trim().equals("")) {
248 packageName = noPackage;
249 }
250 temp.put("package",packageName);
251 temp.put("packagePath",packageName.replace(".","/"));
252 resultSet.add(temp);
253 }
254 result = new Hashtable[resultSet.size()];
255 result = (Map[])resultSet.toArray(result);
256 return result;
257 }
258
259 /***
260 * just applicable to testsuites Node
261 */
262 public Map[] getClassesMap(Element rootElement) {
263 Map[] result = null;
264 HashSet resultSet = new HashSet();
265 String noPackage = "<none>";
266 NodeList testsuiteNodes = rootElement.getElementsByTagName(TESTSUITE);
267 for (int i=0;i<testsuiteNodes.getLength();i++) {
268 Element testsuiteNode = (Element)testsuiteNodes.item(i);
269 NamedNodeMap attributeMap =testsuiteNode.getAttributes();
270 Node packageAttribute = attributeMap.getNamedItem(ATTR_PACKAGE);
271 Node nameAttribute = attributeMap.getNamedItem(ATTR_NAME);
272 Hashtable temp = new Hashtable();
273 String packageName = packageAttribute.getNodeValue();
274 String className = nameAttribute.getNodeValue();
275 if (packageName == null || packageName.trim().equals("")) {
276 packageName = noPackage;
277 }
278 temp.put("className",className);
279 temp.put("packagePath",packageName.replace(".","/"));
280 resultSet.add(temp);
281 }
282 result = new Hashtable[resultSet.size()];
283 result = (Map[])resultSet.toArray(result);
284 return result;
285 }
286
287 /***
288 * just applicable to testsuites Node
289 */
290 public Map[] getClassesMap(Element rootElement, String packageNameP) {
291 Map[] result = null;
292 TreeMap resultMap = new TreeMap();
293 String noPackage = "<none>";
294 NodeList testsuiteNodes = rootElement.getElementsByTagName(TESTSUITE);
295 for (int i=0;i<testsuiteNodes.getLength();i++) {
296 Element testsuiteNode = (Element)testsuiteNodes.item(i);
297 NamedNodeMap attributeMap =testsuiteNode.getAttributes();
298 Node packageAttribute = attributeMap.getNamedItem(ATTR_PACKAGE);
299 String packageName = packageAttribute.getNodeValue();
300 if (packageName.equals(packageNameP)) {
301 Node nameAttribute = attributeMap.getNamedItem(ATTR_NAME);
302 Hashtable temp = new Hashtable();
303 String className = nameAttribute.getNodeValue();
304 if (packageName == null || packageName.trim().equals("")) {
305 packageName = noPackage;
306 }
307 temp.put("className",className);
308 temp.put("packagePath",packageName.replace(".","/"));
309 resultMap.put(className,temp);
310 }
311 }
312 result = new Hashtable[resultMap.size()];
313 result = (Map[])resultMap.values().toArray(result);
314 return result;
315 }
316
317
318 /***
319 * just applicable to testsuites Node
320 */
321 public Map[] getPackageStatisticsMap(Element rootElement) {
322 Map[] result = null;
323 TreeMap resultMap = new TreeMap();
324 String noPackage = "<none>";
325 NodeList testsuiteNodes = rootElement.getElementsByTagName(TESTSUITE);
326 for (int i=0;i<testsuiteNodes.getLength();i++) {
327 Element testsuiteNode = (Element)testsuiteNodes.item(i);
328 NamedNodeMap attributeMap =testsuiteNode.getAttributes();
329 Node packageAttribute = attributeMap.getNamedItem(ATTR_PACKAGE);
330 Node nameAttribute = attributeMap.getNamedItem(ATTR_NAME);
331 Node testsAttribute = attributeMap.getNamedItem("tests");
332 Node errorAttribute = attributeMap.getNamedItem(ATTR_ERRORS);
333 Node failureAttribute = attributeMap.getNamedItem(ATTR_FAILURES);
334 Node timeAttribute = attributeMap.getNamedItem(ATTR_TIME);
335 int tests = 0;
336 int errors = 0;
337 int failures = 0;
338 double time = 0.0;
339 try {tests += Integer.parseInt(testsAttribute.getNodeValue()); } catch (Exception e) {}
340 try {errors += Integer.parseInt(errorAttribute.getNodeValue()); } catch (Exception e) {}
341 try {failures += Integer.parseInt(failureAttribute.getNodeValue()); } catch (Exception e) {}
342 try {time += Double.parseDouble(timeAttribute.getNodeValue()); } catch (Exception e) {}
343 String packageName = packageAttribute.getNodeValue();
344 String className = nameAttribute.getNodeValue();
345 if (packageName == null || packageName.trim().equals("")) {
346 packageName = noPackage;
347 }
348 String stylesheet = "Pass";
349 if (errors > 0) stylesheet = "Error";
350 else if (failures > 0) stylesheet = "Failure";
351 Map temp = new Hashtable();
352 if (resultMap.containsKey(packageName)) {
353 temp = (Map)resultMap.get(packageName);
354 temp.put("tests",(Integer)temp.get("tests")+tests);
355 temp.put("errors",(Integer)temp.get("errors")+errors);
356 temp.put("failures",(Integer)temp.get("failures")+failures);
357 temp.put("stylesheet", stylesheet);
358 temp.put("time",(Double)temp.get("time")+time);
359 } else {
360 temp.put("tests",tests);
361 temp.put("errors",errors);
362 temp.put("failures",failures);
363 temp.put("time",time);
364 temp.put("packageName",packageName);
365 temp.put("stylesheet", stylesheet);
366 temp.put("packagePath",packageName.replace(".","/"));
367 resultMap.put(packageName,temp);
368 }
369 }
370 result = new Hashtable[resultMap.size()];
371 result = (Map[])resultMap.values().toArray(result);
372 for (int i=0;i<result.length;i++) {
373 result[i].put("time",getTimeFormat((Double)result[i].get("time")));
374 }
375 return result;
376 }
377
378 /***
379 * just applicable to testsuites Node
380 */
381 public Map[] getSpecialPackageStatisticsMap(Element rootElement, String packageNameP) {
382 Map[] result = null;
383 TreeMap resultMap = new TreeMap();
384 String noPackage = "<none>";
385 NodeList testsuiteNodes = rootElement.getElementsByTagName(TESTSUITE);
386 for (int i=0;i<testsuiteNodes.getLength();i++) {
387 Element testsuiteNode = (Element)testsuiteNodes.item(i);
388 NamedNodeMap attributeMap =testsuiteNode.getAttributes();
389 Node packageAttribute = attributeMap.getNamedItem(ATTR_PACKAGE);
390 Node nameAttribute = attributeMap.getNamedItem(ATTR_NAME);
391 Node testsAttribute = attributeMap.getNamedItem("tests");
392 Node errorAttribute = attributeMap.getNamedItem(ATTR_ERRORS);
393 Node failureAttribute = attributeMap.getNamedItem(ATTR_FAILURES);
394 Node timeAttribute = attributeMap.getNamedItem(ATTR_TIME);
395 String packageName = packageAttribute.getNodeValue();
396 String className = nameAttribute.getNodeValue();
397 if (packageName.equals(packageNameP)) {
398 int tests = 0;
399 int errors = 0;
400 int failures = 0;
401 double time = 0.0;
402 try {tests += Integer.parseInt(testsAttribute.getNodeValue()); } catch (Exception e) {}
403 try {errors += Integer.parseInt(errorAttribute.getNodeValue()); } catch (Exception e) {}
404 try {failures += Integer.parseInt(failureAttribute.getNodeValue()); } catch (Exception e) {}
405 try {time += Double.parseDouble(timeAttribute.getNodeValue()); } catch (Exception e) {}
406 if (packageName == null || packageName.trim().equals("")) {
407 packageName = noPackage;
408 }
409 String stylesheet = "Pass";
410 if (errors > 0) stylesheet = "Error";
411 else if (failures > 0) stylesheet = "Failure";
412 Map temp = new Hashtable();
413 if (resultMap.containsKey(className)) {
414 temp = (Map)resultMap.get(className);
415 temp.put("tests",(Integer)temp.get("tests")+tests);
416 temp.put("errors",(Integer)temp.get("errors")+errors);
417 temp.put("failures",(Integer)temp.get("failures")+failures);
418 temp.put("time",(Double)temp.get("time")+time);
419 } else {
420 temp.put("className",className);
421 temp.put("tests" ,tests);
422 temp.put("errors" ,errors);
423 temp.put("failures" ,failures);
424 temp.put("time" ,time);
425 temp.put("packageName",packageName);
426 temp.put("stylesheet", stylesheet);
427 temp.put("packagePath",packageName.replace(".","/"));
428 resultMap.put(className,temp);
429 }
430 }
431 }
432 result = new Hashtable[resultMap.size()];
433 result = (Map[])resultMap.values().toArray(result);
434 for (int i=0;i<result.length;i++) {
435 result[i].put("time",getTimeFormat((Double)result[i].get("time")));
436 }
437 return result;
438 }
439
440 public void generateOverviewSummary(Element rootElement) {
441 try {
442 int testCount = getIntegerSumCount(rootElement,"tests");
443 int errorCount = getIntegerSumCount(rootElement,"errors");
444 int failureCount = getIntegerSumCount(rootElement,"failures");
445 double timeCount = getDoubleSumCount(rootElement,"time");
446 int successRate = (testCount - failureCount - errorCount) * 100 / testCount;
447 String summaryStylesheetClass = getSummaryStylesheetClass(errorCount, failureCount);
448 VelocityContext context = new VelocityContext();
449 context.put("testCount", ""+testCount);
450 context.put("errorCount", ""+errorCount);
451 context.put("failureCount", ""+failureCount);
452 context.put("timeCount", getTimeFormat(timeCount));
453 context.put("successRate", successRate);
454 context.put("summaryStylesheetClass",summaryStylesheetClass);
455 Map[] packages = getPackageStatisticsMap(rootElement);
456 context.put("packages",packages);
457 Template template = Velocity.getTemplate("org/woopi/ant/taskdefs/junit/vm/overview-summary.vm");
458 File baseDir = new File(toDir+"/html");
459 if (!baseDir.exists()) baseDir.mkdirs();
460 FileWriter fw = new FileWriter(baseDir+"/overview-summary.html");
461 BufferedWriter writer = new BufferedWriter(fw);
462 template.merge(context , writer);
463 writer.close();
464 } catch (Exception e) {
465 e.printStackTrace();
466 }
467 }
468
469 public void generateStylesheet(Element rootElement) {
470 try {
471 VelocityContext context = new VelocityContext();
472 Template template = Velocity.getTemplate("org/woopi/ant/taskdefs/junit/vm/stylesheet.vm");
473 File baseDir = new File(toDir+"/html");
474 if (!baseDir.exists()) baseDir.mkdirs();
475 FileWriter fw = new FileWriter(baseDir+"/stylesheet.css");
476 BufferedWriter writer = new BufferedWriter(fw);
477 template.merge(context , writer);
478 writer.close();
479 } catch (Exception e) {
480 e.printStackTrace();
481 }
482 }
483
484 public void generateIndex(Element rootElement) {
485 try {
486 VelocityContext context = new VelocityContext();
487 Template template = Velocity.getTemplate("org/woopi/ant/taskdefs/junit/vm/index.vm");
488 File baseDir = new File(toDir+"/html");
489 if (!baseDir.exists()) baseDir.mkdirs();
490 FileWriter fw = new FileWriter(baseDir+"/index.html");
491 BufferedWriter writer = new BufferedWriter(fw);
492 template.merge(context , writer);
493 writer.close();
494 } catch (Exception e) {
495 e.printStackTrace();
496 }
497 }
498
499 public void generateOverviewFrame(Element rootElement) {
500 try {
501 VelocityContext context = new VelocityContext();
502 Integer maxPackages = new Integer(2);
503 Map[] packages = getPackagesMap(rootElement);
504 context.put("packages",packages);
505 Template template = Velocity.getTemplate("org/woopi/ant/taskdefs/junit/vm/overview-frame.vm");
506 File baseDir = new File(toDir+"/html");
507 if (!baseDir.exists()) baseDir.mkdirs();
508 FileWriter fw = new FileWriter(baseDir+"/overview-frame.html");
509 BufferedWriter writer = new BufferedWriter(fw);
510 template.merge(context , writer);
511 writer.close();
512 } catch (Exception e) {
513 e.printStackTrace();
514 }
515 }
516
517
518 public void generateAllClassesFrame(Element rootElement) {
519 try {
520 VelocityContext context = new VelocityContext();
521 Integer maxPackages = new Integer(2);
522 Map[] classes = getClassesMap(rootElement);
523 context.put("classes",classes);
524 Template template = Velocity.getTemplate("org/woopi/ant/taskdefs/junit/vm/allclasses-frame.vm");
525 File baseDir = new File(toDir+"/html");
526 if (!baseDir.exists()) baseDir.mkdirs();
527 FileWriter fw = new FileWriter(baseDir+"/allclasses-frame.html");
528 BufferedWriter writer = new BufferedWriter(fw);
529 template.merge(context , writer);
530 writer.close();
531 } catch (Exception e) {
532 e.printStackTrace();
533 }
534 }
535
536 public void generatePackageFrames(Element rootElement) {
537 try {
538 String noPackage = "<none>";
539 Map[] pkgStats = getPackageStatisticsMap(rootElement);
540 for (int i=0;i<pkgStats.length;i++) {
541 Map statMap = pkgStats[i];
542 String packageName = (String)statMap.get("packageName");
543 Map[] classes = getClassesMap(rootElement, packageName);
544 StringTokenizer st = new StringTokenizer(packageName, ".");
545 int packageNameParts = st.countTokens();
546 if(packageName.equals(noPackage))
547 packageNameParts = 0;
548 String stylesheetUrl = "stylesheet.css";
549 for(int k = 0; k < packageNameParts; k++)
550 stylesheetUrl = (new StringBuilder()).append("../")
551 .append(stylesheetUrl).toString();
552
553 VelocityContext context = new VelocityContext();
554 Integer maxPackages = new Integer(2);
555 context.put("packageName",packageName);
556 context.put("stylesheetUrl",stylesheetUrl);
557 context.put("classes",classes);
558
559 Template template = Velocity.getTemplate("org/woopi/ant/taskdefs/junit/vm/package-frame.vm");
560 String packagePath = packageName.replace(".", "/");
561
562 String baseDirString = (new StringBuilder()).append(toDir)
563 .append("/html").toString();
564 if(!packageName.equals(noPackage))
565 baseDirString = (new StringBuilder()).append(baseDirString)
566 .append("/").append(packagePath).toString();
567 File baseDir = new File(baseDirString);
568 if(!baseDir.exists())
569 baseDir.mkdirs();
570 FileWriter fw = new FileWriter(baseDir+"/package-frame.html");
571 BufferedWriter writer = new BufferedWriter(fw);
572 template.merge(context , writer);
573 writer.close();
574 }
575 } catch (Exception e) {
576 e.printStackTrace();
577 }
578 }
579
580 public Map[] getTestClassProperties(Element testSuiteElement) {
581 Map[] result = null;
582 TreeMap resultMap = new TreeMap();
583 String noPackage = "<none>";
584 NodeList propsNodes = testSuiteElement.getElementsByTagName(PROPERTIES);
585 if (propsNodes.getLength() >= 1) {
586 NodeList propNodes = ((Element)propsNodes.item(0)).getElementsByTagName(PROPERTY);
587 for (int i=0;i<propNodes.getLength();i++) {
588 NamedNodeMap attributeMap = propNodes.item(i).getAttributes();
589 Node valueAttribute = attributeMap.getNamedItem(ATTR_VALUE);
590 Node nameAttribute = attributeMap.getNamedItem(ATTR_NAME);
591 String name = nameAttribute.getNodeValue();
592 String value = valueAttribute.getNodeValue();
593 Hashtable temp = new Hashtable();
594 temp.put("propertyName",name);
595 temp.put("propertyValue",jsEscape(value));
596 resultMap.put(name,temp);
597 }
598
599
600
601
602
603
604 }
605 result = new Hashtable[resultMap.size()];
606 result = (Map[])resultMap.values().toArray(result);
607 return result;
608 }
609
610 public String jsEscape(String inString) {
611 String result = "";
612 result = inString.replace("//","////");
613 return result;
614 }
615
616 public Map[] getTestCaseInfos(Element testSuiteElement) {
617 Map[] result = null;
618 TreeMap resultMap = new TreeMap();
619 String noPackage = "<none>";
620 NodeList testCaseNodes = testSuiteElement.getElementsByTagName(TESTCASE);
621 NodeList sysOutNodes = testSuiteElement.getElementsByTagName(SYSTEM_OUT);
622 NodeList sysErrNodes = testSuiteElement.getElementsByTagName(SYSTEM_ERR);
623 String hasSystemErr = "false";
624 if (sysErrNodes.getLength() > 0) hasSystemErr = "true";
625 String hasSystemOut = "false";
626 if (sysOutNodes.getLength() > 0) hasSystemOut = "true";
627
628 for (int i=0;i<testCaseNodes.getLength();i++) {
629 Element testCaseNode = (Element)testCaseNodes.item(i);
630 NodeList testCaseErrors = testCaseNode.getElementsByTagName(ERROR);
631 NodeList testCaseFailures = testCaseNode.getElementsByTagName(FAILURE);
632 NodeList testCaseJavadoc = testCaseNode.getElementsByTagName(JAVADOC);
633 String status = "SUCCESS";
634 int errors = testCaseErrors.getLength();
635 int failures = testCaseFailures.getLength();
636 String typeMessage = "";
637 String typeCode = "";
638 if (errors > 0) {
639 typeMessage = testCaseErrors.item(0).getAttributes().getNamedItem(ATTR_MESSAGE).getNodeValue();
640 typeCode = testCaseErrors.item(0).getChildNodes().item(0).getNodeValue();
641 status = "ERROR";
642 } else if (failures > 0) {
643 typeMessage = testCaseFailures.item(0).getAttributes().getNamedItem(ATTR_MESSAGE).getNodeValue();
644 typeCode = testCaseFailures.item(0).getChildNodes().item(0).getNodeValue();
645 status = "FAILURE";
646 }
647
648 String javadoc = "";
649 if (testCaseJavadoc.getLength() > 0) {
650 javadoc = testCaseJavadoc.item(0).getChildNodes().item(0).getNodeValue();
651 }
652 NamedNodeMap attributeMap = testCaseNode.getAttributes();
653 Node classNameAttribute = attributeMap.getNamedItem(ATTR_CLASSNAME);
654 String className = classNameAttribute.getNodeValue();
655 Node nameAttribute = attributeMap.getNamedItem(ATTR_NAME);
656 String testCaseName = nameAttribute.getNodeValue();
657 Node timeAttribute = attributeMap.getNamedItem(ATTR_TIME);
658 double time = 0.0;
659 try {time += Double.parseDouble(timeAttribute.getNodeValue()); } catch (Exception e) {}
660
661 String stylesheet = "TableRowColor";
662 if (errors > 0) stylesheet = "Error";
663 else if (failures > 0) stylesheet = "Failure";
664
665 String mapKeyString = className+"/"+testCaseName;
666 Map temp = new Hashtable();
667 if (resultMap.containsKey(mapKeyString)) {
668 temp = (Map)resultMap.get(mapKeyString);
669 temp.put("testCaseName",testCaseName);
670 temp.put("typeMessage",typeMessage);
671 temp.put("className",className);
672 temp.put("typeCode",typeCode);
673 temp.put("time",getTimeFormat(time));
674 temp.put("status",status);
675 temp.put("javadoc",javadoc);
676 temp.put("stylesheet", stylesheet);
677 } else {
678 temp.put("hasSystemErr",hasSystemErr);
679 temp.put("hasSystemOut",hasSystemOut);
680 temp.put("testCaseName",testCaseName);
681 temp.put("className",className);
682 temp.put("typeMessage",typeMessage);
683 temp.put("typeCode",typeCode);
684 temp.put("status",status);
685 temp.put("time",getTimeFormat(time));
686 temp.put("stylesheet", stylesheet);
687 temp.put("javadoc",javadoc);
688 resultMap.put(mapKeyString,temp);
689 }
690 }
691 result = new Hashtable[resultMap.size()];
692 result = (Map[])resultMap.values().toArray(result);
693 for (int i=0;i<result.length;i++){
694 Map tempMap = result[i];
695 tempMap.put("ankerName","anker"+i);
696 tempMap.put("indexNo",""+i);
697 result[i] = tempMap;
698 }
699 return result;
700 }
701
702 public String getTimeFormat(double time) {
703 String result = "";
704 try {
705 result = dnf.format(time);
706 } catch (Exception e) {}
707 return result;
708 }
709
710 public void generateTestClassInfo(Element testCaseElement)
711 {
712 try
713 {
714 String noPackage = "<none>";
715 NamedNodeMap attributeMap = testCaseElement.getAttributes();
716 NodeList sysOutNodes = testCaseElement.getElementsByTagName(SYSTEM_OUT);
717 String hasSystemOut = "false";
718 String systemOut = "";
719 String systemErr = "";
720 if (sysOutNodes.getLength() > 0) {
721 NodeList outChilds = sysOutNodes.item(0).getChildNodes();
722 if (outChilds.getLength() > 0) {
723 systemOut = outChilds.item(0).getNodeValue();
724 }
725 }
726 if (!systemOut.trim().equals("")) {
727 hasSystemOut = "true";
728 }
729 NodeList sysErrNodes = testCaseElement.getElementsByTagName(SYSTEM_ERR);
730 String hasSystemErr = "false";
731 if (sysErrNodes.getLength() > 0) {
732 NodeList errChilds = sysErrNodes.item(0).getChildNodes();
733 if (errChilds.getLength() > 0) {
734 systemErr = errChilds.item(0).getNodeValue();
735 }
736 }
737 if (!systemErr.trim().equals("")) {
738 hasSystemErr = "true";
739 }
740
741 Node packageAttribute = attributeMap.getNamedItem(ATTR_PACKAGE);
742 Node nameAttribute = attributeMap.getNamedItem(ATTR_NAME);
743 Node testsAttribute = attributeMap.getNamedItem(ATTR_TESTS);
744 Node errorAttribute = attributeMap.getNamedItem(ATTR_ERRORS);
745 Node failureAttribute = attributeMap.getNamedItem(ATTR_FAILURES);
746 Node timeAttribute = attributeMap.getNamedItem(ATTR_TIME);
747 String packageName = packageAttribute.getNodeValue();
748 String className = nameAttribute.getNodeValue();
749 if(packageName == null || packageName.trim().equals(""))
750 packageName = noPackage;
751 String packagePath = packageName.replace(".", "/");
752 String fqclassname = (new StringBuilder()).append(packageName).append(".").append(className).toString();
753 int tests = 0;
754 int errors = 0;
755 int failures = 0;
756 double time = 0.0D;
757 try
758 {
759 tests += Integer.parseInt(testsAttribute.getNodeValue());
760 }
761 catch(Exception e) { }
762 try
763 {
764 errors += Integer.parseInt(errorAttribute.getNodeValue());
765 }
766 catch(Exception e) { }
767 try
768 {
769 failures += Integer.parseInt(failureAttribute.getNodeValue());
770 }
771 catch(Exception e) { }
772 try
773 {
774 time += Double.parseDouble(timeAttribute.getNodeValue());
775 }
776 catch(Exception e) { }
777 Map testCases[] = getTestCaseInfos(testCaseElement);
778 Map properties[] = getTestClassProperties(testCaseElement);
779 String stylesheet = getSummaryStylesheetClass(errors, failures);
780 VelocityContext context = new VelocityContext();
781 StringTokenizer st = new StringTokenizer(packageName, ".");
782 int packageNameParts = st.countTokens();
783 if(packageName.equals(noPackage))
784 packageNameParts = 0;
785 String stylesheetUrl = "stylesheet.css";
786 for(int i = 0; i < packageNameParts; i++)
787 stylesheetUrl = (new StringBuilder()).append("../").append(stylesheetUrl).toString();
788
789 context.put("fqclassname", fqclassname);
790 context.put("className", className);
791 context.put("hasSystemOut", hasSystemOut);
792 context.put("hasSystemErr", hasSystemErr);
793 context.put("packageName", packageName);
794 context.put("testsCount", (new StringBuilder()).append("").append(tests).toString());
795 context.put("errorCount", (new StringBuilder()).append("").append(errors).toString());
796 context.put("failureCount", (new StringBuilder()).append("").append(failures).toString());
797 context.put("time", (new StringBuilder()).append("").append(getTimeFormat(time)).toString());
798 context.put("stylesheet", stylesheet);
799 context.put("stylesheetUrl", stylesheetUrl);
800 context.put("testCases", testCases);
801 context.put("properties", properties);
802 Template template = Velocity.getTemplate("org/woopi/ant/taskdefs/junit/vm/testclass.vm");
803 String baseDirString = (new StringBuilder()).append(toDir).append("/html").toString();
804 if(!packageName.equals(noPackage))
805 baseDirString = (new StringBuilder()).append(baseDirString).append("/").append(packagePath).toString();
806 File baseDir = new File(baseDirString);
807 if(!baseDir.exists())
808 baseDir.mkdirs();
809 FileWriter fw = new FileWriter((new StringBuilder()).append(baseDir).append("/").append(className).append(".html").toString());
810 BufferedWriter writer = new BufferedWriter(fw);
811 template.merge(context, writer);
812 writer.close();
813 }
814 catch(Exception e)
815 {
816 e.printStackTrace();
817 }
818 }
819
820 public void generateTestClassSystemOut(Element testCaseElement) {
821 try {
822 String noPackage = "<none>";
823
824 NamedNodeMap attributeMap =testCaseElement.getAttributes();
825 Node packageAttribute = attributeMap.getNamedItem(ATTR_PACKAGE);
826 Node nameAttribute = attributeMap.getNamedItem(ATTR_NAME);
827 NodeList sysOutNodes = testCaseElement.getElementsByTagName(SYSTEM_OUT);
828 if (sysOutNodes.getLength() > 0) {
829 NodeList outChilds = sysOutNodes.item(0).getChildNodes();
830 if (outChilds.getLength() > 0) {
831 String systemOut = outChilds.item(0).getNodeValue();
832 if (!systemOut.trim().equals("")) {
833
834 String packageName = packageAttribute.getNodeValue();
835 String className = nameAttribute.getNodeValue();
836 if (packageName == null || packageName.trim().equals("")) {
837 packageName = noPackage;
838 }
839 String packagePath = packageName.replace(".","/");
840 String fqclassname = packageName+"."+className;
841
842 VelocityContext context = new VelocityContext();
843 StringTokenizer st = new StringTokenizer(packageName,".");
844 int packageNameParts = st.countTokens();
845 if (packageName.equals(noPackage)) packageNameParts = 0;
846 context.put("fqclassname", fqclassname);
847 context.put("systemOut", systemOut);
848
849 Template template = Velocity.getTemplate("org/woopi/ant/taskdefs/junit/vm/systemout.vm");
850 String baseDirString = toDir+"/html";
851 if (!packageName.equals(noPackage)) {
852 baseDirString += "/"+packagePath;
853 }
854 File baseDir = new File(baseDirString);
855 if (!baseDir.exists()) baseDir.mkdirs();
856 FileWriter fw = new FileWriter(baseDir+"/"+className+"-out.txt");
857 BufferedWriter writer = new BufferedWriter(fw);
858 template.merge(context , writer);
859 writer.close();
860 }
861 }
862 }
863 } catch (Exception e) {
864 e.printStackTrace();
865 }
866 }
867
868 public void generateTestClassSystemErr(Element testCaseElement) {
869 try {
870 String noPackage = "<none>";
871
872 NamedNodeMap attributeMap =testCaseElement.getAttributes();
873 Node packageAttribute = attributeMap.getNamedItem(ATTR_PACKAGE);
874 Node nameAttribute = attributeMap.getNamedItem(ATTR_NAME);
875 NodeList sysErrNodes = testCaseElement.getElementsByTagName(SYSTEM_ERR);
876 if (sysErrNodes.getLength() > 0) {
877 NodeList errChilds = sysErrNodes.item(0).getChildNodes();
878 if (errChilds.getLength() > 0) {
879 String systemErr = errChilds.item(0).getNodeValue();
880 if (!systemErr.trim().equals("")) {
881 String packageName = packageAttribute.getNodeValue();
882 String className = nameAttribute.getNodeValue();
883 if (packageName == null || packageName.trim().equals("")) {
884 packageName = noPackage;
885 }
886 String packagePath = packageName.replace(".","/");
887 String fqclassname = packageName+"."+className;
888
889 VelocityContext context = new VelocityContext();
890 StringTokenizer st = new StringTokenizer(packageName,".");
891 int packageNameParts = st.countTokens();
892 if (packageName.equals(noPackage)) packageNameParts = 0;
893 context.put("fqclassname", fqclassname);
894 context.put("systemErr", systemErr);
895
896 Template template = Velocity.getTemplate("org/woopi/ant/taskdefs/junit/vm/systemerr.vm");
897 String baseDirString = toDir+"/html";
898 if (!packageName.equals(noPackage)) {
899 baseDirString += "/"+packagePath;
900 }
901 File baseDir = new File(baseDirString);
902 if (!baseDir.exists()) baseDir.mkdirs();
903 FileWriter fw = new FileWriter(baseDir+"/"+className+"-err.txt");
904 BufferedWriter writer = new BufferedWriter(fw);
905 template.merge(context , writer);
906 writer.close();
907 }
908 }
909 }
910 } catch (Exception e) {
911 e.printStackTrace();
912 }
913 }
914
915 public void generateTestClassInfos(Element rootElement) {
916 String noPackage = "<none>";
917 NodeList testsuiteNodes = rootElement.getElementsByTagName(TESTSUITE);
918 for (int i=0;i<testsuiteNodes.getLength();i++) {
919 Element testsuiteNode = (Element)testsuiteNodes.item(i);
920 generateTestClassInfo(testsuiteNode);
921 generateTestClassSystemOut(testsuiteNode);
922 generateTestClassSystemErr(testsuiteNode);
923 }
924 }
925
926 public void generatePackageSummaryFiles(Element rootElement) {
927 try {
928 String noPackage = "<none>";
929
930 NodeList testsuiteNodes = rootElement.getElementsByTagName(TESTSUITE);
931 for (int i=0;i<testsuiteNodes.getLength();i++) {
932 Element testuiteNode = (Element)testsuiteNodes.item(i);
933 NamedNodeMap attributeMap =testuiteNode.getAttributes();
934 Node packageAttribute = attributeMap.getNamedItem(ATTR_PACKAGE);
935 Node nameAttribute = attributeMap.getNamedItem(ATTR_NAME);
936
937
938 String packageName = packageAttribute.getNodeValue();
939 String className = nameAttribute.getNodeValue();
940 String packagePath = "";
941 if (packageName == null || packageName.trim().equals("")) {
942 packageName = noPackage;
943 } else {
944 packagePath = packageName.replace(".","/");
945 }
946
947 String fqclassname = packageName+"."+className;
948
949 VelocityContext context = new VelocityContext();
950 StringTokenizer st = new StringTokenizer(packageName,".");
951 int packageNameParts = st.countTokens();
952 if (packageName.equals(noPackage)) packageNameParts = 0;
953 String stylesheetUrl = "stylesheet.css";
954 for(int k = 0; k < packageNameParts; k++)
955 stylesheetUrl = (new StringBuilder()).append("../").append(stylesheetUrl).toString();
956
957 context.put("packageName", packageName);
958 context.put("stylesheetUrl", stylesheetUrl);
959 context.put("className", className);
960 context.put("fqclassname", fqclassname);
961 context.put("packagePath", packagePath);
962
963 Map[] classes = getSpecialPackageStatisticsMap(rootElement, packageName);
964 context.put("classes",classes);
965
966 Template template = Velocity.getTemplate("org/woopi/ant/taskdefs/junit/vm/package-summary.vm");
967 String baseDirString = toDir+"/html";
968 if (!packageName.equals(noPackage)) {
969 baseDirString += "/"+packagePath;
970 }
971 File baseDir = new File(baseDirString);
972 if (!baseDir.exists()) baseDir.mkdirs();
973 FileWriter fw = new FileWriter(baseDir+"/package-summary.html");
974 BufferedWriter writer = new BufferedWriter(fw);
975 template.merge(context , writer);
976 writer.close();
977 }
978 } catch (Exception e) {
979 e.printStackTrace();
980 }
981 }
982
983 /***
984 * Get the full destination file where to write the result. It is made of
985 * the <tt>todir</tt> and <tt>tofile</tt> attributes.
986 * @return the destination file where should be written the result file.
987 */
988 protected File getDestinationFile() {
989 if (toFile == null) {
990 toFile = DEFAULT_FILENAME;
991 }
992 if (toDir == null) {
993 toDir = getProject().resolveFile(DEFAULT_DIR);
994 }
995 return new File(toDir, toFile);
996 }
997
998 /***
999 * Get all <code>.xml</code> files in the fileset.
1000 *
1001 * @return all files in the fileset that end with a '.xml'.
1002 */
1003 protected File[] getFiles() {
1004 Vector v = new Vector();
1005 final int size = filesets.size();
1006 for (int i = 0; i < size; i++) {
1007 FileSet fs = (FileSet) filesets.elementAt(i);
1008 DirectoryScanner ds = fs.getDirectoryScanner(getProject());
1009 ds.scan();
1010 String[] f = ds.getIncludedFiles();
1011 for (int j = 0; j < f.length; j++) {
1012 String pathname = f[j];
1013 if (pathname.endsWith(".xml")) {
1014 File file = new File(ds.getBasedir(), pathname);
1015 file = getProject().resolveFile(file.getPath());
1016 v.addElement(file);
1017 }
1018 }
1019 }
1020
1021 File[] files = new File[v.size()];
1022 v.copyInto(files);
1023 return files;
1024 }
1025
1026
1027
1028 /***
1029 * Write the DOM tree to a file.
1030 * @param doc the XML document to dump to disk.
1031 * @param file the filename to write the document to. Should obviouslly be a .xml file.
1032 * @throws IOException thrown if there is an error while writing the content.
1033 */
1034 protected void writeDOMTree(Document doc, File file) throws IOException {
1035 OutputStream out = null;
1036 PrintWriter wri = null;
1037 try {
1038 out = new BufferedOutputStream(new FileOutputStream(file));
1039 wri = new PrintWriter(new OutputStreamWriter(out, "UTF8"));
1040 wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
1041 (new DOMElementWriter()).write(doc.getDocumentElement(), wri, 0, " ");
1042 wri.flush();
1043
1044 if (wri.checkError()) {
1045 throw new IOException("Error while writing DOM content");
1046 }
1047 } finally {
1048 if (wri != null) {
1049 wri.close();
1050 out = null;
1051 }
1052 if (out != null) {
1053 out.close();
1054 }
1055 }
1056 }
1057
1058 /***
1059 * <p> Create a DOM tree.
1060 * Has 'testsuites' as firstchild and aggregates all
1061 * testsuite results that exists in the base directory.
1062 * @return the root element of DOM tree that aggregates all testsuites.
1063 */
1064 protected Element createDocument() {
1065
1066 DocumentBuilder builder = getDocumentBuilder();
1067 Document doc = builder.newDocument();
1068 Element rootElement = doc.createElement(TESTSUITES);
1069 doc.appendChild(rootElement);
1070
1071
1072 File[] files = getFiles();
1073 for (int i = 0; i < files.length; i++) {
1074 try {
1075 log("Parsing file: '" + files[i] + "'", Project.MSG_VERBOSE);
1076
1077
1078
1079 Document testsuiteDoc
1080 = builder.parse("file:///" + files[i].getAbsolutePath());
1081 Element elem = testsuiteDoc.getDocumentElement();
1082
1083 if (TESTSUITE.equals(elem.getNodeName())) {
1084 addTestSuite(rootElement, elem);
1085 } else {
1086
1087 log("the file " + files[i]
1088 + " is not a valid testsuite XML document",
1089 Project.MSG_WARN);
1090 }
1091 } catch (SAXException e) {
1092
1093
1094 log("The file " + files[i] + " is not a valid XML document. "
1095 + "It is possibly corrupted.", Project.MSG_WARN);
1096 log(StringUtils.getStackTrace(e), Project.MSG_DEBUG);
1097 } catch (IOException e) {
1098 log("Error while accessing file " + files[i] + ": "
1099 + e.getMessage(), Project.MSG_ERR);
1100 }
1101 }
1102 return rootElement;
1103 }
1104
1105 /***
1106 * <p> Add a new testsuite node to the document.
1107 * The main difference is that it
1108 * split the previous fully qualified name into a package and a name.
1109 * <p> For example: <tt>org.apache.Whatever</tt> will be split into
1110 * <tt>org.apache</tt> and <tt>Whatever</tt>.
1111 * @param root the root element to which the <tt>testsuite</tt> node should
1112 * be appended.
1113 * @param testsuite the element to append to the given root. It will slightly
1114 * modify the original node to change the name attribute and add
1115 * a package one.
1116 */
1117 protected void addTestSuite(Element root, Element testsuite) {
1118 String fullclassname = testsuite.getAttribute(ATTR_NAME);
1119 int pos = fullclassname.lastIndexOf('.');
1120
1121
1122 String pkgName = (pos == -1) ? "" : fullclassname.substring(0, pos);
1123 String classname = (pos == -1) ? fullclassname : fullclassname.substring(pos + 1);
1124 Element copy = (Element) DOMUtil.importNode(root, testsuite);
1125
1126
1127 copy.setAttribute(ATTR_NAME, classname);
1128 copy.setAttribute(ATTR_PACKAGE, pkgName);
1129 }
1130
1131 /***
1132 * Create a new document builder. Will issue an <tt>ExceptionInitializerError</tt>
1133 * if something is going wrong. It is fatal anyway.
1134 * @todo factorize this somewhere else. It is duplicated code.
1135 * @return a new document builder to create a DOM
1136 */
1137 private static DocumentBuilder getDocumentBuilder() {
1138 try {
1139 return DocumentBuilderFactory.newInstance().newDocumentBuilder();
1140 } catch (Exception exc) {
1141 throw new ExceptionInInitializerError(exc);
1142 }
1143 }
1144
1145 }