1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package vogar; 18 19import java.io.File; 20import java.io.FileOutputStream; 21import java.io.IOException; 22import java.text.SimpleDateFormat; 23import java.util.ArrayList; 24import java.util.Collection; 25import java.util.Date; 26import java.util.LinkedHashMap; 27import java.util.List; 28import java.util.Map; 29import java.util.TimeZone; 30import org.kxml2.io.KXmlSerializer; 31 32 33/** 34 * Writes JUnit results to a series of XML files in a format consistent with 35 * Ant's XMLJUnitResultFormatter. 36 * 37 * <p>Unlike Ant's formatter, this class does not report the execution time of 38 * tests. 39 * 40 * TODO: unify this and com.google.coretests.XmlReportPrinter 41 */ 42public class XmlReportPrinter { 43 44 /** the XML namespace */ 45 private static final String ns = null; 46 47 private final File directory; 48 private final ExpectationStore expectationStore; 49 private final Date date; 50 51 public XmlReportPrinter(File directory, ExpectationStore expectationStore, Date date) { 52 this.directory = directory; 53 this.expectationStore = expectationStore; 54 this.date = date; 55 } 56 57 /** 58 * Returns true if this XML Report printer can be used to emit XML. 59 */ 60 public boolean isReady() { 61 return directory != null; 62 } 63 64 private String getGMTTimestamp() { 65 SimpleDateFormat dateFormat = new SimpleDateFormat(XmlReportConstants.DATEFORMAT); 66 TimeZone gmt = TimeZone.getTimeZone("GMT"); 67 dateFormat.setTimeZone(gmt); 68 dateFormat.setLenient(true); 69 return dateFormat.format(date); 70 } 71 72 /** 73 * Populates the directory with the report data from the completed tests. 74 */ 75 public int generateReports(Collection<Outcome> results) { 76 Map<String, Suite> suites = testsToSuites(results); 77 78 String timestamp = getGMTTimestamp(); 79 80 for (Suite suite : suites.values()) { 81 String fileName = "TEST-" + suite.name + ".xml"; 82 suite.printReport(timestamp, fileName); 83 } 84 85 return suites.size(); 86 } 87 88 private Map<String, Suite> testsToSuites(Collection<Outcome> outcomes) { 89 Map<String, Suite> result = new LinkedHashMap<String, Suite>(); 90 for (Outcome outcome : outcomes) { 91 if (outcome.getResult() == Result.UNSUPPORTED) { 92 continue; 93 } 94 95 String suiteName = outcome.getSuiteName(); 96 Suite suite = result.get(suiteName); 97 if (suite == null) { 98 suite = new Suite(suiteName); 99 result.put(suiteName, suite); 100 } 101 102 suite.outcomes.add(outcome); 103 104 Expectation expectation = expectationStore.get(outcome); 105 if (!expectation.matches(outcome)) { 106 if (outcome.getResult() == Result.EXEC_FAILED) { 107 suite.failuresCount++; 108 } else { 109 suite.errorsCount++; 110 } 111 } 112 } 113 return result; 114 } 115 116 class Suite { 117 private final String name; 118 private final List<Outcome> outcomes = new ArrayList<Outcome>(); 119 private int failuresCount; 120 private int errorsCount; 121 122 Suite(String name) { 123 this.name = name; 124 } 125 126 private void print(KXmlSerializer serializer, String timestamp) throws IOException { 127 serializer.startTag(ns, XmlReportConstants.TESTSUITE); 128 serializer.attribute(ns, XmlReportConstants.ATTR_NAME, name); 129 serializer.attribute(ns, XmlReportConstants.ATTR_TESTS, Integer.toString(outcomes.size())); 130 serializer.attribute(ns, XmlReportConstants.ATTR_FAILURES, Integer.toString(failuresCount)); 131 serializer.attribute(ns, XmlReportConstants.ATTR_ERRORS, Integer.toString(errorsCount)); 132 serializer.attribute(ns, XmlReportConstants.ATTR_TIME, "0"); 133 serializer.attribute(ns, XmlReportConstants.TIMESTAMP, timestamp); 134 serializer.attribute(ns, XmlReportConstants.HOSTNAME, "localhost"); 135 serializer.startTag(ns, XmlReportConstants.PROPERTIES); 136 serializer.endTag(ns, XmlReportConstants.PROPERTIES); 137 138 for (Outcome outcome : outcomes) { 139 print(serializer, outcome); 140 } 141 142 serializer.endTag(ns, XmlReportConstants.TESTSUITE); 143 } 144 145 private void print(KXmlSerializer serializer, Outcome outcome) throws IOException { 146 serializer.startTag(ns, XmlReportConstants.TESTCASE); 147 serializer.attribute(ns, XmlReportConstants.ATTR_NAME, outcome.getTestName()); 148 serializer.attribute(ns, XmlReportConstants.ATTR_CLASSNAME, outcome.getSuiteName()); 149 serializer.attribute(ns, XmlReportConstants.ATTR_TIME, "0"); 150 151 Expectation expectation = expectationStore.get(outcome); 152 if (!expectation.matches(outcome)) { 153 String result; 154 switch (outcome.getResult()) { 155 case EXEC_FAILED: 156 result = XmlReportConstants.FAILURE; 157 break; 158 case SUCCESS: 159 result = XmlReportConstants.SUCCESS; 160 break; 161 default: 162 result = XmlReportConstants.ERROR; 163 break; 164 } 165 serializer.startTag(ns, result); 166 serializer.attribute(ns, XmlReportConstants.ATTR_TYPE, outcome.getResult().toString()); 167 String text = outcome.getOutput(); 168 serializer.text(text); 169 serializer.endTag(ns, result); 170 } 171 172 serializer.endTag(ns, XmlReportConstants.TESTCASE); 173 } 174 175 void printReport(String timestamp, String fileName) { 176 FileOutputStream stream = null; 177 try { 178 stream = new FileOutputStream(new File(directory, fileName)); 179 180 KXmlSerializer serializer = new KXmlSerializer(); 181 serializer.setOutput(stream, "UTF-8"); 182 serializer.startDocument("UTF-8", null); 183 serializer.setFeature( 184 "http://xmlpull.org/v1/doc/features.html#indent-output", true); 185 print(serializer, timestamp); 186 serializer.endDocument(); 187 } catch (IOException e) { 188 throw new RuntimeException(e); 189 } finally { 190 if (stream != null) { 191 try { 192 stream.close(); 193 } catch (IOException ignored) { 194 } 195 } 196 } 197 } 198 } 199} 200