17850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com/* 27850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Copyright (C) 2009 The Android Open Source Project 37850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * 47850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Licensed under the Apache License, Version 2.0 (the "License"); 57850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * you may not use this file except in compliance with the License. 67850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * You may obtain a copy of the License at 77850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * 87850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * http://www.apache.org/licenses/LICENSE-2.0 97850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * 107850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Unless required by applicable law or agreed to in writing, software 117850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * distributed under the License is distributed on an "AS IS" BASIS, 127850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * See the License for the specific language governing permissions and 147850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * limitations under the License. 157850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 167850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 177850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.compackage vogar; 187850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 197850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.io.File; 207850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.io.FileOutputStream; 217850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.io.IOException; 227850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.text.SimpleDateFormat; 237850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.util.ArrayList; 247850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.util.Collection; 257850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.util.Date; 267850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.util.LinkedHashMap; 277850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.util.List; 287850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.util.Map; 297850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.util.TimeZone; 307850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport org.kxml2.io.KXmlSerializer; 317850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 327850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 337850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com/** 347850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Writes JUnit results to a series of XML files in a format consistent with 357850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Ant's XMLJUnitResultFormatter. 367850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * 377850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * <p>Unlike Ant's formatter, this class does not report the execution time of 387850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * tests. 397850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * 407850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * TODO: unify this and com.google.coretests.XmlReportPrinter 417850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 427850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.compublic class XmlReportPrinter { 437850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 447850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** the XML namespace */ 457850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com private static final String ns = null; 467850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 47f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com private final File directory; 48f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com private final ExpectationStore expectationStore; 49f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com private final Date date; 50d9085944c8b6d18628266347c59545abae03ab9cjessewilson@google.com 51f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com public XmlReportPrinter(File directory, ExpectationStore expectationStore, Date date) { 52f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com this.directory = directory; 53f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com this.expectationStore = expectationStore; 54f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com this.date = date; 55f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com } 56d9085944c8b6d18628266347c59545abae03ab9cjessewilson@google.com 578918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com /** 58d9085944c8b6d18628266347c59545abae03ab9cjessewilson@google.com * Returns true if this XML Report printer can be used to emit XML. 598918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com */ 60d9085944c8b6d18628266347c59545abae03ab9cjessewilson@google.com public boolean isReady() { 61d9085944c8b6d18628266347c59545abae03ab9cjessewilson@google.com return directory != null; 62d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 637850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 64d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com private String getGMTTimestamp() { 65d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com SimpleDateFormat dateFormat = new SimpleDateFormat(XmlReportConstants.DATEFORMAT); 66d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com TimeZone gmt = TimeZone.getTimeZone("GMT"); 67d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com dateFormat.setTimeZone(gmt); 68d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com dateFormat.setLenient(true); 69d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com return dateFormat.format(date); 707850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 717850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 727850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** 737850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Populates the directory with the report data from the completed tests. 747850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 757850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com public int generateReports(Collection<Outcome> results) { 767850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com Map<String, Suite> suites = testsToSuites(results); 777850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 78d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com String timestamp = getGMTTimestamp(); 797850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 807850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com for (Suite suite : suites.values()) { 81d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com String fileName = "TEST-" + suite.name + ".xml"; 82d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com suite.printReport(timestamp, fileName); 83d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 847850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 85d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com return suites.size(); 86d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 87d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com 887850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com private Map<String, Suite> testsToSuites(Collection<Outcome> outcomes) { 897850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com Map<String, Suite> result = new LinkedHashMap<String, Suite>(); 907850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com for (Outcome outcome : outcomes) { 917850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com if (outcome.getResult() == Result.UNSUPPORTED) { 927850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com continue; 937850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 947850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 957850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com String suiteName = outcome.getSuiteName(); 967850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com Suite suite = result.get(suiteName); 977850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com if (suite == null) { 987850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com suite = new Suite(suiteName); 997850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com result.put(suiteName, suite); 1007850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1017850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1027850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com suite.outcomes.add(outcome); 1037850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1047850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com Expectation expectation = expectationStore.get(outcome); 1057850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com if (!expectation.matches(outcome)) { 1067850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com if (outcome.getResult() == Result.EXEC_FAILED) { 1077850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com suite.failuresCount++; 1087850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } else { 1097850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com suite.errorsCount++; 1107850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1117850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1127850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1137850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com return result; 1147850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1157850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1167850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com class Suite { 1177850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com private final String name; 1187850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com private final List<Outcome> outcomes = new ArrayList<Outcome>(); 1197850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com private int failuresCount; 1207850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com private int errorsCount; 1217850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1227850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com Suite(String name) { 1237850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com this.name = name; 1247850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1257850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 126d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com private void print(KXmlSerializer serializer, String timestamp) throws IOException { 127d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.startTag(ns, XmlReportConstants.TESTSUITE); 128d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.attribute(ns, XmlReportConstants.ATTR_NAME, name); 129d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.attribute(ns, XmlReportConstants.ATTR_TESTS, Integer.toString(outcomes.size())); 130d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.attribute(ns, XmlReportConstants.ATTR_FAILURES, Integer.toString(failuresCount)); 131d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.attribute(ns, XmlReportConstants.ATTR_ERRORS, Integer.toString(errorsCount)); 132d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.attribute(ns, XmlReportConstants.ATTR_TIME, "0"); 133d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.attribute(ns, XmlReportConstants.TIMESTAMP, timestamp); 134d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.attribute(ns, XmlReportConstants.HOSTNAME, "localhost"); 135d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.startTag(ns, XmlReportConstants.PROPERTIES); 136d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.endTag(ns, XmlReportConstants.PROPERTIES); 1377850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1387850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com for (Outcome outcome : outcomes) { 1397850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com print(serializer, outcome); 1407850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1417850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 142d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.endTag(ns, XmlReportConstants.TESTSUITE); 1437850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1447850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 145d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com private void print(KXmlSerializer serializer, Outcome outcome) throws IOException { 146d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.startTag(ns, XmlReportConstants.TESTCASE); 147d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.attribute(ns, XmlReportConstants.ATTR_NAME, outcome.getTestName()); 148d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.attribute(ns, XmlReportConstants.ATTR_CLASSNAME, outcome.getSuiteName()); 149d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.attribute(ns, XmlReportConstants.ATTR_TIME, "0"); 1507850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1517850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com Expectation expectation = expectationStore.get(outcome); 152f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com if (!expectation.matches(outcome)) { 153d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com String result; 154d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com switch (outcome.getResult()) { 155d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com case EXEC_FAILED: 156d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com result = XmlReportConstants.FAILURE; 157d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com break; 158d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com case SUCCESS: 159d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com result = XmlReportConstants.SUCCESS; 160d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com break; 161d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com default: 162d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com result = XmlReportConstants.ERROR; 163d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com break; 164d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 1657850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com serializer.startTag(ns, result); 166d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.attribute(ns, XmlReportConstants.ATTR_TYPE, outcome.getResult().toString()); 167d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com String text = outcome.getOutput(); 1687850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com serializer.text(text); 1697850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com serializer.endTag(ns, result); 1707850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1717850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 172d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.endTag(ns, XmlReportConstants.TESTCASE); 1737850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1747850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 175d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com void printReport(String timestamp, String fileName) { 176d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com FileOutputStream stream = null; 177d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com try { 178d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com stream = new FileOutputStream(new File(directory, fileName)); 179d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com 180d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com KXmlSerializer serializer = new KXmlSerializer(); 181d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.setOutput(stream, "UTF-8"); 182d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.startDocument("UTF-8", null); 183d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.setFeature( 184d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com "http://xmlpull.org/v1/doc/features.html#indent-output", true); 185d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com print(serializer, timestamp); 186d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com serializer.endDocument(); 187d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } catch (IOException e) { 188d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com throw new RuntimeException(e); 189d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } finally { 190d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com if (stream != null) { 191d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com try { 192d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com stream.close(); 193d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } catch (IOException ignored) { 194d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 195d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 196d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 1977850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1987850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1997850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com} 200