blob: ec269934db38ade00e7c959175bfb6eae4b2a028 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2016 Xored Software Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Xored Software Inc - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.rcptt.reporting.html;
import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Predicates.compose;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.not;
import static com.google.common.base.Predicates.or;
import static com.google.common.collect.Iterables.filter;
import static org.eclipse.rcptt.reporting.html.internal.Plugin.UTILS;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.text.NumberFormat;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.rcptt.reporting.Q7Info;
import org.eclipse.rcptt.reporting.Q7Statistics;
import org.eclipse.rcptt.reporting.core.IQ7ReportConstants;
import org.eclipse.rcptt.reporting.core.IReportRenderer;
import org.eclipse.rcptt.reporting.html.internal.Plugin;
import org.eclipse.rcptt.reporting.util.ReportUtils;
import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Node;
import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Report;
import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Screenshot;
import org.eclipse.rcptt.util.FileUtil;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
public class HtmlReportRenderer implements IReportRenderer {
private final String summaryTemplate = loadAsString("/templates/summary.html");
protected final NumberFormat durationFormat = NumberFormat.getNumberInstance();
{
durationFormat.setMaximumFractionDigits(1);
durationFormat.setMinimumFractionDigits(1);
}
static String loadAsString(String path) {
try {
byte[] content = FileUtil.getStreamContent(HtmlReportRenderer.class.getResourceAsStream(path));
return new String(content, UTF_8);
} catch (IOException e) {
throw new RuntimeException("Failed to load " + path);
}
}
@Override
public IStatus generateReport(IContentFactory factory, String reportName,
Iterable<Report> reportList) {
PrintWriter writer = null;
try {
OutputStream stream = factory.createFileStream(reportName + ".html");
writer = new PrintWriter(new OutputStreamWriter(stream, Charsets.UTF_8));
renderReport(writer, reportList, factory);
} catch (Exception e) {
return Plugin.UTILS.createError(e);
} finally {
FileUtil.safeClose(writer);
}
return Status.OK_STATUS;
}
protected void renderReport(PrintWriter writer, Iterable<Report> reports, IContentFactory content)
throws CoreException {
writer.println("<html>");
renderHead(writer, null);
writer.println("<body onload=\"installDetailsWorkaround()\">");
Q7Statistics statistics = ReportUtils.calculateStatistics(reports.iterator());
renderSummary(writer, reports, statistics);
Iterable<Report> passedReports = filter(reports, compose(equalTo(IStatus.OK), reportStatus));
Iterable<Report> skippedReports = filter(reports, compose(matches(IStatus.CANCEL), reportStatus));
Iterable<Report> failedReports = filter(reports,
compose(not(or(equalTo(IStatus.OK), matches(IStatus.CANCEL))), reportStatus));
writer.println("<h1 class=\"failure\">Failed Tests (" + statistics.getFailed() + ")</h1>");
renderFailed(writer, content, failedReports);
writer.println("<h1 class=\"skipped\">Skipped Tests (" + statistics.getSkipped() + ")</h1>");
writer.println("<table class=\"skipped\">");
for (Report report : skippedReports) {
renderNameAndDuration(writer, report, content);
}
writer.println("</table>");
writer.println("<details class=\"closed\"><summary><h1 class=\"passed\">Passed Tests ("
+ statistics.getPassed()
+ ")</h1></summary>");
writer.println("<table class=\"passed detailsContent\">");
for (Report report : passedReports) {
renderNameAndDuration(writer, report, content);
}
writer.println("</table>");
writer.println("</body></html>");
}
protected void renderHead(PrintWriter writer, String title) {
writer.println("<head>");
if (!Strings.isNullOrEmpty(title))
writer.println("<title>" + title + "</title>");
writer.println("<style>");
writer.println(loadAsString("/templates/rcptt.css"));
writer.println("</style>");
writer.println("<script type=\"text/javascript\">");
writer.println(loadAsString("/templates/rcptt.js"));
writer.println("</script>");
writer.println("</head>");
}
protected void renderFailed(PrintWriter writer, IContentFactory content, Iterable<Report> failedReports)
throws CoreException {
for (Report report:failedReports) {
try {
renderFailed(writer, report, content);
writer.println("<hr/>");
} catch (Exception e) {
UTILS.log(UTILS.createError(e));
e.printStackTrace(writer);
}
}
}
protected void renderNameAndDuration(PrintWriter writer, Report report, IContentFactory content)
throws CoreException {
Node root = report.getRoot();
long millseconds = root.getDuration();
String duration = durationFormat.format((float) (millseconds) / 1000f);
writer.println("<tr><td>" + root.getName() + "</td><td>" + duration + " s</td></tr>");
}
private int screenshotCount = 0;
protected void renderFailed(PrintWriter writer, Report report, final IContentFactory content) {
final IContentFactory images = content.createFolder("images");
FullSingleTestHtmlRenderer renderer = new FullSingleTestHtmlRenderer(writer, durationFormat, new Function<Screenshot, String>() {
@Override
public String apply(Screenshot input) {
return "images/" + writeScreenshot(images, String.format("%3d", screenshotCount++), input);
}
});
renderer.render(report);
}
private void renderSummary(PrintWriter writer, Iterable<Report> reports, Q7Statistics statistics) {
renderStatistics(writer, statistics);
}
private void renderStatistics(PrintWriter writer, Q7Statistics statistics) {
Replacer r = new Replacer(summaryTemplate);
fillStatistics(statistics, r);
writer.append(r.toString());
}
private int percent(int value, int total) {
return (int) (((float) value / total) * 100);
}
private void fillStatistics(Q7Statistics statistics, Replacer r) {
r.replace("totalCount", statistics.getTotal());
r.replace("failedCount", statistics.getFailed());
r.replace("failedPercent", percent(statistics.getFailed(), statistics.getTotal()));
r.replace("skippedPercent", percent(statistics.getSkipped(), statistics.getTotal()));
r.replace("skippedCount", statistics.getSkipped());
r.replace("elapsed", durationFormat.format((float) (statistics.getTime()) / 1000f));
}
private static class Replacer {
private String current;
public Replacer(String current) {
super();
this.current = current;
}
/**
* Replaces ${key} with value
*/
public void replace(String key, String value) {
current = current.replaceAll("\\$\\{" + key + "\\}", value);
}
public void replace(String key, int value) {
replace(key, "" + value);
}
@Override
public String toString() {
return current;
}
}
public String[] getGeneratedFileNames(String reportName) {
return new String[] { reportName + ".html" };
}
private static final Function<Report, Integer> reportStatus = new Function<Report, Integer>() {
@Override
public Integer apply(Report input) {
if (input.getRoot() == null)
return IStatus.ERROR;
Q7Info info = (Q7Info) input.getRoot().getProperties().get(IQ7ReportConstants.ROOT);
if (info == null || info.getResult() == null)
return IStatus.ERROR;
return info.getResult().getSeverity();
}
};
Predicate<Integer> matches(final int mask) {
return new Predicate<Integer>() {
@Override
public boolean apply(Integer input) {
return (input & mask) != 0;
}
};
}
private String writeScreenshot(IContentFactory images, String key,
Screenshot value) {
String ext = value.getKind().name().toLowerCase();
String fileName = key + "." + ext;
OutputStream stream = null;
try {
stream = images.createFileStream(fileName);
stream.write(value.getData());
} catch (Exception e) {
throw new RuntimeException("Failed to write screenshot " + key);
} finally {
FileUtil.safeClose(stream);
}
return fileName;
}
protected String getNodeName(Node node) {
String name = node.getName();
Q7Info info = (Q7Info) node.getProperties().get("q7");
if (info != null && info.getVariant() != null && info.getVariant().size() != 0) {
name += "_" + ReportUtils.combineNames(info.getVariant(), ",");
}
name = name.trim().replace(" ", "");
return FileUtil.rlimitSize(FileUtil.getID(name), 20).toLowerCase();
}
}