Skip to content

Commit

Permalink
Merge pull request #5 from pgottinger/master
Browse files Browse the repository at this point in the history
[JENKINS-12126] See all/successful/failed/skipped test results in Test Trend Chart
  • Loading branch information
mambu committed Jun 26, 2012
2 parents e235dfc + 3169d37 commit cd1981d
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 122 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.403</version>
<version>1.420</version>
</parent>
<artifactId>dashboard-view</artifactId>
<packaging>hpi</packaging>
Expand Down
308 changes: 187 additions & 121 deletions src/main/java/hudson/plugins/view/dashboard/test/TestTrendChart.java
Expand Up @@ -33,165 +33,231 @@

public class TestTrendChart extends DashboardPortlet {

private int graphWidth = 300;
private int graphHeight = 220;
private int dateRange = 365;
public enum DisplayStatus {
ALL, SUCCESS, SKIPPED, FAILED
}

private int graphWidth = 300;
private int graphHeight = 220;
private int dateRange = 365;
private DisplayStatus display = DisplayStatus.ALL;

@DataBoundConstructor
public TestTrendChart(String name, int graphWidth, int graphHeight, int dateRange) {
public TestTrendChart(String name, int graphWidth, int graphHeight,
String display, int dateRange) {
super(name);
this.graphWidth = graphWidth;
this.graphHeight = graphHeight;
this.dateRange = dateRange;
this.graphWidth = graphWidth;
this.graphHeight = graphHeight;
this.dateRange = dateRange;

this.display = DisplayStatus.valueOf(display.toUpperCase());
}

public int getDateRange() {
return dateRange;
}

public int getDateRange() {
return dateRange;
}
public int getGraphWidth() {
return graphWidth <= 0 ? 300 : graphWidth;
}

public int getGraphWidth() {
return graphWidth <= 0 ? 300 : graphWidth;
}
public int getGraphHeight() {
return graphHeight <= 0 ? 220 : graphHeight;
}

public int getGraphHeight() {
return graphHeight <= 0 ? 220 : graphHeight;
}
public String getDisplay() {
return display.toString();
}

/**
* Graph of duration of tests over time.
*/
public Graph getSummaryGraph() {
// The standard equals doesn't work because two LocalDate objects can
// be differente even if the date is the same (different internal timestamp)
Comparator<LocalDate> localDateComparator = new Comparator<LocalDate>() {
@Override public int compare(LocalDate d1, LocalDate d2) {
if(d1.isEqual(d2))
return 0;
if(d1.isAfter(d2))
return 1;
return -1;
}
};
// The standard equals doesn't work because two LocalDate objects can
// be differente even if the date is the same (different internal
// timestamp)
Comparator<LocalDate> localDateComparator = new Comparator<LocalDate>() {
@Override
public int compare(LocalDate d1, LocalDate d2) {
if (d1.isEqual(d2))
return 0;
if (d1.isAfter(d2))
return 1;
return -1;
}
};

// We need a custom comparator for LocalDate objects
final Map<LocalDate, TestResultSummary> summaries = //new HashMap<LocalDate, TestResultSummary>();
new TreeMap<LocalDate, TestResultSummary>(localDateComparator);
final Map<LocalDate, TestResultSummary> summaries = // new
// HashMap<LocalDate,
// TestResultSummary>();
new TreeMap<LocalDate, TestResultSummary>(localDateComparator);
LocalDate today = new LocalDate();

// for each job, for each day, add last build of the day to summary
for (Job job : getDashboard().getJobs()) {
Run run = job.getFirstBuild();

if (run != null) { // execute only if job has builds
LocalDate runDay = new LocalDate(run.getTimestamp());
LocalDate firstDay = (dateRange != 0) ? new LocalDate().minusDays(dateRange) : runDay;

while (run != null) {
runDay = new LocalDate(run.getTimestamp());
Run nextRun = run.getNextBuild();

if (nextRun != null) {
LocalDate nextRunDay = new LocalDate(nextRun.getTimestamp());
// skip run before firstDay, but keep if next build is after start date
if (!runDay.isBefore(firstDay)
|| runDay.isBefore(firstDay) && !nextRunDay.isBefore(firstDay)) {
// if next run is not the same day, use this test to summarize
if (nextRunDay.isAfter(runDay)) {
summarize(summaries, run, (runDay.isBefore(firstDay) ? firstDay : runDay), nextRunDay.minusDays(1));
}
}
} else {
// use this run's test result from last run to today
summarize(summaries, run, (runDay.isBefore(firstDay) ? firstDay : runDay), today);
}

run = nextRun;
}
}
if (run != null) { // execute only if job has builds
LocalDate runDay = new LocalDate(run.getTimestamp());
LocalDate firstDay = (dateRange != 0) ? new LocalDate()
.minusDays(dateRange) : runDay;

while (run != null) {
runDay = new LocalDate(run.getTimestamp());
Run nextRun = run.getNextBuild();

if (nextRun != null) {
LocalDate nextRunDay = new LocalDate(
nextRun.getTimestamp());
// skip run before firstDay, but keep if next build is
// after start date
if (!runDay.isBefore(firstDay)
|| runDay.isBefore(firstDay)
&& !nextRunDay.isBefore(firstDay)) {
// if next run is not the same day, use this test to
// summarize
if (nextRunDay.isAfter(runDay)) {
summarize(summaries, run,
(runDay.isBefore(firstDay) ? firstDay
: runDay),
nextRunDay.minusDays(1));
}
}
} else {
// use this run's test result from last run to today
summarize(
summaries,
run,
(runDay.isBefore(firstDay) ? firstDay : runDay),
today);
}

run = nextRun;
}
}
}

return new Graph(-1, getGraphWidth(), getGraphHeight()) {

@Override
protected JFreeChart createGraph() {
final JFreeChart chart = ChartFactory.createStackedAreaChart(
null, // chart title
Messages.Dashboard_Date(), // unused
Messages.Dashboard_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));

null, // chart title
Messages.Dashboard_Date(), // unused
Messages.Dashboard_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);

switch (display) {
case SUCCESS:
ar.setSeriesPaint(0, ColorPalette.BLUE);
break;
case SKIPPED:
ar.setSeriesPaint(0, ColorPalette.YELLOW);
break;
case FAILED:
ar.setSeriesPaint(0, ColorPalette.RED);
break;
default:
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,LocalDateLabel> dsb = new DataSetBuilder<String,LocalDateLabel>();

for (Map.Entry<LocalDate, TestResultSummary> entry : summaries.entrySet()) {
LocalDateLabel label = new LocalDateLabel(entry.getKey());
dsb.add( entry.getValue().getFailed(), Messages.Dashboard_Failed(), label);
dsb.add( entry.getValue().getSkipped(), Messages.Dashboard_Skipped(), label);
dsb.add( entry.getValue().getSuccess(), Messages.Dashboard_Total(), label);
}
return dsb.build();
}


private CategoryDataset buildDataSet(
Map<LocalDate, TestResultSummary> summaries) {
DataSetBuilder<String, LocalDateLabel> dsb = new DataSetBuilder<String, LocalDateLabel>();

for (Map.Entry<LocalDate, TestResultSummary> entry : summaries
.entrySet()) {
LocalDateLabel label = new LocalDateLabel(entry.getKey());

switch (display) {
case SUCCESS:
dsb.add(entry.getValue().getSuccess(),
Messages.Dashboard_Total(), label);
break;
case SKIPPED:
dsb.add(entry.getValue().getSkipped(),
Messages.Dashboard_Skipped(), label);
break;
case FAILED:
dsb.add(entry.getValue().getFailed(),
Messages.Dashboard_Failed(), label);
break;
default:
dsb.add(entry.getValue().getSuccess(),
Messages.Dashboard_Total(), label);
dsb.add(entry.getValue().getFailed(),
Messages.Dashboard_Failed(), label);
dsb.add(entry.getValue().getSkipped(),
Messages.Dashboard_Skipped(), label);
}
}
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)) {
if (testResult.getTests() != 0) {
TestResultSummary trs = summaries.get(curr);
if (trs == null) {
trs = new TestResultSummary();
summaries.put(curr, trs);
}

trs.addTestResult(testResult);
}
for (LocalDate curr = firstDay; curr.compareTo(lastDay) <= 0; curr = curr
.plusDays(1)) {
if (testResult.getTests() != 0) {
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> {
public static class DescriptorImpl extends Descriptor<DashboardPortlet> {

@Override
public String getDisplayName() {
Expand Down
Expand Up @@ -32,6 +32,29 @@ THE SOFTWARE.
<f:entry title="${%Graph height}">
<f:textbox name="portlet.graphHeight" field="graphHeight" default="220" />
</f:entry>
<f:entry name="display" title="Display" field="display">
<select name="portlet.display">
<j:choose>
<j:when test="${instance.getDisplay().compareTo('ALL')==0}"><option value="ALL" selected="true">All tests</option></j:when>
<j:otherwise><option value="ALL">All tests</option></j:otherwise>
</j:choose>

<j:choose>
<j:when test="${instance.getDisplay().compareTo('SUCCESS')==0}"><option value="SUCCESS" selected="true">Only successful tests</option></j:when>
<j:otherwise><option value="SUCCESS">Only successful tests</option></j:otherwise>
</j:choose>

<j:choose>
<j:when test="${instance.getDisplay().compareTo('FAILED')==0}"><option value="FAILED" selected="true">Only failed tests</option></j:when>
<j:otherwise><option value="FAILED">Only failed tests</option></j:otherwise>
</j:choose>

<j:choose>
<j:when test="${instance.getDisplay().compareTo('SKIPPED')==0}"><option value="SKIPPED" selected="true">Only skipped tests</option></j:when>
<j:otherwise><option value="SKIPPED">Only skipped tests</option></j:otherwise>
</j:choose>
</select>
</f:entry>
<!--<f:optionalBlock name="dynamic" title="Specify a date range">-->
<f:entry title="${%Number of latest days to display}">
<f:textbox name="dateRange" field="dateRange" value="${it.dateRange}"/>
Expand Down

0 comments on commit cd1981d

Please sign in to comment.