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