package hudson.plugins.PerfPublisher;
import java.awt.Color;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
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.CategoryItemRenderer;
import org.jfree.ui.RectangleInsets;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import hudson.model.ModelObject;
import hudson.model.AbstractBuild;
import hudson.model.Result;
import hudson.plugins.PerfPublisher.Report.Report;
import hudson.plugins.PerfPublisher.Report.ReportContainer;
import hudson.plugins.PerfPublisher.Report.Test;
import hudson.util.ChartUtil;
import hudson.util.ColorPalette;
import hudson.util.DataSetBuilder;
import hudson.util.ShiftedCategoryAxis;
import hudson.util.ChartUtil.NumberOnlyBuildLabel;
/**
* This class is dedicated to the report diff display
* it prepares and generate the content of the diff
* @see hudson.model.ModelObject
* @author gbossert
*
*/
public class ReportsDiff implements ModelObject {
private final AbstractBuild<?, ?> _owner;
/**
* The 2 reports to compare
*/
private ReportContainer report1;
private ReportContainer report2;
private ReportContainer report3;
/**
* The 3 builds numbers
*/
private int nb_build1;
private int nb_build2;
private int nb_build3;
/**
* Activate or not the short diff
*/
private boolean shortDiff;
/**
* The stappler request
*/
private String link;
/**
* The constructor
* @param owner
* @param shortDiff activate or not the short display
* @param nb_build1 number of the first build
* @param report1 first report
* @param nb_build2 number of the second build
* @param report2 second report
* @param nb_build3 number of the third build
* @param report3 third report
*/
public ReportsDiff(final AbstractBuild<?, ?> owner, StaplerRequest request, int nb_build1, ReportContainer report1, int nb_build2, ReportContainer report2, int nb_build3, ReportContainer report3) {
this._owner = owner;
this.report1 = report1;
this.report2 = report2;
this.report3 = report3;
this.nb_build1 = nb_build1;
this.nb_build2 = nb_build2;
this.nb_build3 = nb_build3;
this.link="?build1="+this.nb_build1+"&build2="+this.nb_build2;
if (this.nb_build3==0) {
this.link+="&build3=none";
} else {
this.link+="&build3="+this.nb_build3;
}
// Activate or not the short display
this.shortDiff = false;
if (request.getParameter("short")!=null && request.getParameter("short").equals("yes")) {
this.shortDiff = true;
this.link+="&short=no";
} else {
this.link+="&short=yes";
}
}
/**
* Getter for the first build number
* @return the first build number
*/
public String getBuild1Number() {
return ""+nb_build1;
}
/**
* Getter for the second build number
* @return the second build number
*/
public String getBuild2Number() {
return ""+nb_build2;
}
/**
* Getter for the third build number
* @return the third build number
*/
public String getBuild3Number() {
return ""+nb_build3;
}
/**
* Getter for the first report
* @return the first report
*/
public ReportContainer getReport1() {
return this.report1;
}
/**
* Getter for the tests list of the first report
* @return all the tests from the first report
*/
public List<Test> getReport1Tests() {
return this.report1.getTests();
}
/**
* Getter for the second report
* @return the second report
*/
public ReportContainer getReport2() {
return this.report2;
}
/**
* Getter for the tests of the second report
* @return all the tests from the second report
*/
public List<Test> getReport2Tests() {
return this.report2.getTests();
}
/**
* Getter for the third report
* @return the third report
*/
public ReportContainer getReport3() {
return this.report3;
}
public List<Test> getReport3Tests() {
return this.report3.getTests();
}
public String getHtmlTestsDiff() {
String style="Threecolumn";
if (nb_build3==0) {
style="Twocolumn";
}
StringBuilder strb = new StringBuilder();
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">Build number</div>");
strb.append("<div class=\""+style+"\"><b>Build "+this.nb_build1+"</b></div>");
strb.append("<div class=\""+style+"\"><b>Build "+this.nb_build2+"</b></div>");
if (nb_build3!=0) strb.append("<div class=\""+style+"\"><b>Build "+this.nb_build3+"</b></div>");
strb.append("</div>");
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">Build date</div>");
strb.append("<div class=\""+style+"\">"+this._owner.getProject().getBuildByNumber(nb_build1).getTimestampString2()+"</div>");
strb.append("<div class=\""+style+"\">"+this._owner.getProject().getBuildByNumber(nb_build2).getTimestampString2()+"</div>");
if (nb_build3!=0) strb.append("<div class=\""+style+"\">"+this._owner.getProject().getBuildByNumber(nb_build3).getTimestampString2()+"</div>");
strb.append("</div>");
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">Number of executed tests</div>");
strb.append("<div class=\""+style+"\">"+this.report1.getNumberOfExecutedTest()+"</div>");
strb.append("<div class=\""+style+"\">"+this.report2.getNumberOfExecutedTest()+"</div>");
if (nb_build3!=0) strb.append("<div class=\""+style+"\">"+this.report3.getNumberOfExecutedTest()+"</div>");
strb.append("</div>");
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">Number of not executed tests</div>");
strb.append("<div class=\""+style+"\">"+this.report1.getNumberOfNotExecutedTest()+"</div>");
strb.append("<div class=\""+style+"\">"+this.report2.getNumberOfNotExecutedTest()+"</div>");
if (nb_build3!=0) strb.append("<div class=\""+style+"\">"+this.report3.getNumberOfNotExecutedTest()+"</div>");
strb.append("</div>");
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">Number of succeeded tests</div>");
strb.append("<div class=\""+style+"\">"+this.report1.getNumberOfPassedTest()+"</div>");
strb.append("<div class=\""+style+"\">"+this.report2.getNumberOfPassedTest()+"</div>");
if (nb_build3!=0) strb.append("<div class=\""+style+"\">"+this.report3.getNumberOfPassedTest()+"</div>");
strb.append("</div>");
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">Number of failed tests</div>");
strb.append("<div class=\""+style+"\">"+this.report1.getNumberOfFailedTest()+"</div>");
strb.append("<div class=\""+style+"\">"+this.report2.getNumberOfFailedTest()+"</div>");
if (nb_build3!=0) strb.append("<div class=\""+style+"\">"+this.report3.getNumberOfFailedTest()+"</div>");
strb.append("</div>");
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">Compile time (Average/Number of measures)</div>");
strb.append("<div class=\""+style+"\">"+this.report1.getAverageOfCompileTime()+"/"+this.report1.getNumberOfCompileTimeTest()+"</div>");
strb.append("<div class=\""+style+"\">"+this.report2.getAverageOfCompileTime()+"/"+this.report2.getNumberOfCompileTimeTest()+"</div>");
if (nb_build3!=0) strb.append("<div class=\""+style+"\">"+this.report3.getAverageOfCompileTime()+"/"+this.report3.getNumberOfCompileTimeTest()+"</div>");
strb.append("</div>");
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">Execution time (Average/Number of measures)</div>");
strb.append("<div class=\""+style+"\">"+this.report1.getAverageOfExecutionTime()+"/"+this.report1.getNumberOfExecutionTimeTest()+"</div>");
strb.append("<div class=\""+style+"\">"+this.report2.getAverageOfExecutionTime()+"/"+this.report2.getNumberOfExecutionTimeTest()+"</div>");
if (nb_build3!=0) strb.append("<div class=\""+style+"\">"+this.report3.getAverageOfExecutionTime()+"/"+this.report3.getNumberOfExecutionTimeTest()+"</div>");
strb.append("</div>");
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">Performance (Average/Number of measures)</div>");
strb.append("<div class=\""+style+"\">"+this.report1.getAverageOfPerformance()+"/"+this.report1.getNumberOfPerformanceTest()+"</div>");
strb.append("<div class=\""+style+"\">"+this.report2.getAverageOfPerformance()+"/"+this.report2.getNumberOfPerformanceTest()+"</div>");
if (nb_build3!=0) strb.append("<div class=\""+style+"\">"+this.report3.getAverageOfPerformance()+"/"+this.report3.getNumberOfPerformanceTest()+"</div>");
strb.append("</div>");
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">Performance (Average/Number of measures)</div>");
strb.append("<div class=\""+style+"\">"+this.report1.getAverageOfPerformance()+"/"+this.report1.getNumberOfPerformanceTest()+"</div>");
strb.append("<div class=\""+style+"\">"+this.report2.getAverageOfPerformance()+"/"+this.report2.getNumberOfPerformanceTest()+"</div>");
if (nb_build3!=0) strb.append("<div class=\""+style+"\">"+this.report3.getAverageOfPerformance()+"/"+this.report3.getNumberOfPerformanceTest()+"</div>");
strb.append("<br style=\"clear:both;\"></div> ");
strb.append("<div class=\"legende\">");
strb.append("<div id=\"grey\">NOT EXECUTED</div>");
strb.append("<div id=\"yellow\">TIMED OUT</div>");
strb.append("<div id=\"red\">FAILED</div>");
strb.append("<div id=\"green\">SUCCESSFULL</div>");
strb.append("<div id=\"white\">DOESN'T EXIST</div>");
strb.append("<div id=\"link_display\">");
if (this.shortDiff) {
strb.append("<a href=\""+this.link+"\">Display the full diff</a>");
} else {
strb.append("<a href=\""+this.link+"\">Display the short diff</a>");
}
strb.append("</div>");
strb.append("</div>");
/**
* if short display :
* Get all the tests executed in the three builds
* else
* Get all the changed status tests in the three builds
*/
ArrayList<Test> global_test = new ArrayList<Test>();
global_test.addAll(this.report1.getTests());
for (int i=0; i<this.report2.getTests().size(); i++) {
boolean insert = true;
for (int j=0; j<global_test.size(); j++) {
if (global_test.get(j).getName().equals(this.report2.getTests().get(i).getName())) {
insert = false;
}
}
if (insert) {
global_test.add(this.report2.getTests().get(i));
}
}
if (this.report3!=null) {
for (int i=0; i<this.report3.getTests().size(); i++) {
boolean insert = true;
for (int j=0; j<global_test.size(); j++) {
if (global_test.get(j).getName().equals(this.report3.getTests().get(i).getName())) {
insert = false;
}
}
if (insert) {
global_test.add(this.report3.getTests().get(i));
}
}
}
Collections.sort(global_test);
for (int i=0; i<global_test.size(); i++) {
String color1="#fff";
String color2="#fff";
String color3="#fff";
String txt1="-";
String txt2="-";
String txt3="-";
if (this.report1.getTestWithName(global_test.get(i).getName()) != null) {
Test test1 = this.report1.getTestWithName(global_test.get(i).getName());
txt1="<a href=\"../../../"+this.nb_build1+"/PerfPublisher/testDetails."+test1.getNameForUrl()+"\">x</a>";
if (!test1.isExecuted()) {
color1="grey";
} else {
if (test1.isHasTimedOut()) {
color1 = "yellow";
} else {
if (test1.isSuccessfull()) {
color1="green";
} else { color1="red"; }
}
}
}
if (this.report2.getTestWithName(global_test.get(i).getName()) != null) {
Test test2 = this.report2.getTestWithName(global_test.get(i).getName());
txt2="<a href=\"../../../"+this.nb_build2+"/PerfPublisher/testDetails."+test2.getNameForUrl()+"\">x</a>";
if (!test2.isExecuted()) {
color2="grey";
} else {
if (test2.isHasTimedOut()) {
color2 = "yellow";
} else {
if (test2.isSuccessfull()) {
color2="green";
} else { color2="red"; }
}
}
}
if (nb_build3!=0) {
if (this.report3.getTestWithName(global_test.get(i).getName()) != null) {
Test test3 = this.report3.getTestWithName(global_test.get(i).getName());
txt3="<a href=\"../../../"+this.nb_build3+"/PerfPublisher/testDetails."+test3.getNameForUrl()+"\">x</a>";
if (!test3.isExecuted()) {
color3="grey";
} else {
if (test3.isHasTimedOut()) {
color3 = "yellow";
} else {
if (test3.isSuccessfull()) {
color3="green";
} else { color3="red"; }
}
}
}
if (shortDiff && (color1!=color2 || color1!=color3 || color2!=color3)) {
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">"+global_test.get(i).getName()+"</div>");
strb.append("<div class=\""+style+"\" style=\"background-color:"+color1+"\"> "+txt1+" </div>");
strb.append("<div class=\""+style+"\" style=\"background-color:"+color2+"\"> "+txt2+" </div>");
strb.append("<div class=\""+style+"\" style=\"background-color:"+color3+"\"> "+txt3+" </div>");
strb.append("</div>\n");
} else if (!shortDiff) {
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">"+global_test.get(i).getName()+"</div>");
strb.append("<div class=\""+style+"\" style=\"background-color:"+color1+"\"> "+txt1+" </div>");
strb.append("<div class=\""+style+"\" style=\"background-color:"+color2+"\"> "+txt2+" </div>");
strb.append("<div class=\""+style+"\" style=\"background-color:"+color3+"\"> "+txt3+" </div>");
strb.append("</div>\n");
}
} else {
if (shortDiff && (color1!=color2)) {
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">"+global_test.get(i).getName()+"</div>");
strb.append("<div class=\""+style+"\" style=\"background-color:"+color1+"\"> "+txt1+" </div>");
strb.append("<div class=\""+style+"\" style=\"background-color:"+color2+"\"> "+txt2+" </div>");
strb.append("</div>\n");
} else if (!shortDiff) {
strb.append("<div class=\"line\">");
strb.append("<div class=\"header\">"+global_test.get(i).getName()+"</div>");
strb.append("<div class=\""+style+"\" style=\"background-color:"+color1+"\"> "+txt1+" </div>");
strb.append("<div class=\""+style+"\" style=\"background-color:"+color2+"\"> "+txt2+" </div>");
strb.append("</div>\n");
}
}
}
return strb.toString();
}
public AbstractBuild<?, ?> getOwner() {
return _owner;
}
public String getDisplayName() {
return "Reports diff.";
}
}