blob: 9abea1b56f61b4eaf1c7619da97891baff1cb2b9 [file] [log] [blame]
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.build.gradle.internal.test.report;
import com.google.common.io.Closeables;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
import org.gradle.api.GradleException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* Custom test reporter based on Gradle's DefaultTestReport
*/
public class TestReport {
private final HtmlReportRenderer htmlRenderer = new HtmlReportRenderer();
private final ReportType reportType;
private final File resultDir;
private final File reportDir;
public TestReport(ReportType reportType, File resultDir, File reportDir) {
this.reportType = reportType;
this.resultDir = resultDir;
this.reportDir = reportDir;
htmlRenderer.requireResource(getClass().getResource("report.js"));
htmlRenderer.requireResource(getClass().getResource("base-style.css"));
htmlRenderer.requireResource(getClass().getResource("style.css"));
}
public CompositeTestResults generateReport() {
AllTestResults model = loadModel();
generateFiles(model);
return model;
}
private AllTestResults loadModel() {
AllTestResults model = new AllTestResults();
if (resultDir.exists()) {
File[] files = resultDir.listFiles();
if (files != null) {
for (File file : files) {
if (file.getName().startsWith("TEST-") && file.getName().endsWith(".xml")) {
mergeFromFile(file, model);
}
}
}
}
return model;
}
private void mergeFromFile(File file, AllTestResults model) {
InputStream inputStream = null;
try {
//noinspection IOResourceOpenedButNotSafelyClosed
inputStream = new FileInputStream(file);
Document document;
try {
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
new InputSource(inputStream));
} finally {
inputStream.close();
}
String deviceName = null;
String projectName = null;
String flavorName = null;
NodeList propertiesList = document.getElementsByTagName("properties");
for (int i = 0; i < propertiesList.getLength(); i++) {
Element properties = (Element) propertiesList.item(i);
XPath xPath = XPathFactory.newInstance().newXPath();
deviceName = xPath.evaluate("property[@name='device']/@value",properties);
projectName = xPath.evaluate("property[@name='project']/@value",properties);
flavorName = xPath.evaluate("property[@name='flavor']/@value",properties);
}
NodeList testCases = document.getElementsByTagName("testcase");
for (int i = 0; i < testCases.getLength(); i++) {
Element testCase = (Element) testCases.item(i);
String className = testCase.getAttribute("classname");
String testName = testCase.getAttribute("name");
BigDecimal duration = parse(testCase.getAttribute("time"));
duration = duration.multiply(BigDecimal.valueOf(1000));
NodeList failures = testCase.getElementsByTagName("failure");
TestResult testResult = model.addTest(className, testName, duration.longValue(),
deviceName, projectName, flavorName);
for (int j = 0; j < failures.getLength(); j++) {
Element failure = (Element) failures.item(j);
testResult.addFailure(
failure.getAttribute("message"), failure.getTextContent(),
deviceName, projectName, flavorName);
}
}
NodeList ignoredTestCases = document.getElementsByTagName("ignored-testcase");
for (int i = 0; i < ignoredTestCases.getLength(); i++) {
Element testCase = (Element) ignoredTestCases.item(i);
String className = testCase.getAttribute("classname");
String testName = testCase.getAttribute("name");
model.addTest(className, testName, 0, deviceName, projectName, flavorName).ignored();
}
String suiteClassName = document.getDocumentElement().getAttribute("name");
ClassTestResults suiteResults = model.addTestClass(suiteClassName);
NodeList stdOutElements = document.getElementsByTagName("system-out");
for (int i = 0; i < stdOutElements.getLength(); i++) {
suiteResults.addStandardOutput(stdOutElements.item(i).getTextContent());
}
NodeList stdErrElements = document.getElementsByTagName("system-err");
for (int i = 0; i < stdErrElements.getLength(); i++) {
suiteResults.addStandardError(stdErrElements.item(i).getTextContent());
}
} catch (Exception e) {
throw new GradleException(String.format("Could not load test results from '%s'.", file), e);
} finally {
try {
Closeables.close(inputStream, true /* swallowIOException */);
} catch (IOException e) {
// cannot happen
}
}
}
private void generateFiles(AllTestResults model) {
try {
generatePage(model, new OverviewPageRenderer(reportType), new File(reportDir, "index.html"));
for (PackageTestResults packageResults : model.getPackages()) {
generatePage(packageResults, new PackagePageRenderer(reportType),
new File(reportDir, packageResults.getFilename(reportType) + ".html"));
for (ClassTestResults classResults : packageResults.getClasses()) {
generatePage(classResults, new ClassPageRenderer(reportType),
new File(reportDir, classResults.getFilename(reportType) + ".html"));
}
}
} catch (Exception e) {
throw new GradleException(
String.format("Could not generate test report to '%s'.", reportDir), e);
}
}
private <T extends CompositeTestResults> void generatePage(T model, PageRenderer<T> renderer,
File outputFile) throws Exception {
htmlRenderer.renderer(renderer).writeTo(model, outputFile);
}
/**
* Regardless of the default locale, comma ('.') is used as decimal separator
*
* @param source
* @return
* @throws java.text.ParseException
*/
public BigDecimal parse(String source) throws ParseException {
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator('.');
DecimalFormat format = new DecimalFormat("#.#", symbols);
format.setParseBigDecimal(true);
return (BigDecimal) format.parse(source);
}
}