package hudson.plugins.view.dashboard.test;
import hudson.Extension;
import hudson.model.Descriptor;
import hudson.model.Job;
import hudson.model.Run;
import hudson.plugins.view.dashboard.DashboardPortlet;
import hudson.util.ColorPalette;
import hudson.util.DataSetBuilder;
import hudson.util.Graph;
import hudson.util.ShiftedCategoryAxis;
import hudson.util.StackedAreaRenderer2;
import java.awt.Color;
import java.util.HashMap;
import java.util.Map;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.StackedAreaRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.ui.RectangleInsets;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.kohsuke.stapler.DataBoundConstructor;
public class TestTrendChart extends DashboardPortlet {
private int graphWidth = 300;
private int graphHeight = 220;
@DataBoundConstructor
public TestTrendChart(String name, int graphWidth, int graphHeight) {
super(name);
this.graphWidth = graphWidth;
this.graphHeight = graphHeight;
}
public int getGraphWidth() {
return graphWidth <= 0 ? 300 : graphWidth;
}
public int getGraphHeight() {
return graphHeight <= 0 ? 220 : graphHeight;
}
/**
* Graph of duration of tests over time.
*/
public Graph getSummaryGraph() {
// find the first test execution
DateTime first = new DateTime();
for (Job job : getDashboard().getJobs()) {
Run firstRun = job.getFirstBuild();
if (firstRun != null) {
DateTime date = new DateTime(firstRun.getTimestamp());
if (date.isBefore(first)) {
first = date;
}
}
}
// hold onto summaries
final Map<LocalDate, TestResultSummary> summaries = new HashMap<LocalDate, TestResultSummary>();
LocalDate firstDay = new LocalDate(first);
LocalDate today = new LocalDate();
// for each job, for each day, add to summary
for (Job job : getDashboard().getJobs()) {
Run run = job.getFirstBuild();
LocalDate lastRunDay = firstDay;
while (run != null) {
Run nextRun = run.getNextBuild();
if (nextRun != null) {
LocalDate runDay = new LocalDate(run.getTimestamp());
// if next run is the next day, use this test to summarize
if (new LocalDate(nextRun.getTimestamp()).isAfter(runDay)) {
summarize(summaries, run, lastRunDay, runDay);
lastRunDay = runDay.plusDays(1);
}
} else {
// use this run's test result from last run to today
summarize(summaries, run, lastRunDay, today);
}
run = nextRun;
}
}
return new Graph(-1, getGraphWidth(), getGraphHeight()) {
@Override
protected JFreeChart createGraph() {
final JFreeChart chart = ChartFactory.createStackedAreaChart(
null, // chart title
"date", // unused
"count", // range axis label
buildDataSet(summaries), // data
PlotOrientation.VERTICAL, // orientation
false, // include legend
false, // tooltips
false // urls
);
chart.setBackgroundPaint(Color.white);
final CategoryPlot plot = chart.getCategoryPlot();
plot.setBackgroundPaint(Color.WHITE);
plot.setOutlinePaint(null);
plot.setForegroundAlpha(0.8f);
plot.setRangeGridlinesVisible(true);
plot.setRangeGridlinePaint(Color.black);
CategoryAxis domainAxis = new ShiftedCategoryAxis(null);
plot.setDomainAxis(domainAxis);
domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
domainAxis.setLowerMargin(0.0);
domainAxis.setUpperMargin(0.0);
domainAxis.setCategoryMargin(0.0);
final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
StackedAreaRenderer ar = new StackedAreaRenderer2();
plot.setRenderer(ar);
ar.setSeriesPaint(0,ColorPalette.RED); // Failures.
ar.setSeriesPaint(1,ColorPalette.YELLOW); // Skips.
ar.setSeriesPaint(2,ColorPalette.BLUE); // Total.
// crop extra space around the graph
plot.setInsets(new RectangleInsets(0,0,0,5.0));
return chart;
}
};
}
private CategoryDataset buildDataSet(Map<LocalDate, TestResultSummary> summaries) {
DataSetBuilder<String,LocalDate> dsb = new DataSetBuilder<String,LocalDate>();
for (Map.Entry<LocalDate, TestResultSummary> entry : summaries.entrySet()) {
dsb.add( entry.getValue().getFailed(), "failed", entry.getKey());
dsb.add( entry.getValue().getSkipped(), "skipped", entry.getKey());
dsb.add( entry.getValue().getSuccess(), "total", entry.getKey());
}
return dsb.build();
}
private void summarize(Map<LocalDate, TestResultSummary> summaries,
Run run, LocalDate firstDay, LocalDate lastDay) {
TestResult testResult = TestUtil.getTestResult(run);
// for every day between first day and last day inclusive
for (LocalDate curr = firstDay; curr.compareTo(lastDay) <= 0; curr = curr.plusDays(1)) {
TestResultSummary trs = summaries.get(curr);
if (trs == null) {
trs = new TestResultSummary();
summaries.put(curr, trs);
}
trs.addTestResult(testResult);
}
}
@Extension
public static class DescriptorImpl extends Descriptor<DashboardPortlet> {
@Override
public String getDisplayName() {
return "Test Trend Chart";
}
}
}