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