package hudson.plugins.mibsr; import hudson.model.AbstractBuild; import hudson.model.HealthReportingAction; import hudson.plugins.mibsr.GraphHelper; import hudson.plugins.mibsr.parser.BuildJobs; import hudson.util.ChartUtil; import hudson.util.DataSetBuilder; import org.apache.maven.plugin.invoker.model.BuildJob; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import java.io.IOException; import java.io.Serializable; import java.util.Calendar; import java.util.Collection; /** * TODO javadoc. * * @author Stephen Connolly * @since 09-Jan-2008 21:19:37 */ public abstract class AbstractBuildReport<T extends AbstractBuild<?, ?>> implements HealthReportingAction, Serializable { private final BuildJobs result; /** * Unique identifier for this class. */ private static final long serialVersionUID = 31415926L; /** * The owner of this Action. Ideally I'd like this to be final and set in the constructor, but Maven does not * let us do that, so we need a setter. */ private T build = null; /** * Constructs a new AbstractBuildReport. */ public AbstractBuildReport( BuildJobs result ) { this.result = result; } public Collection<BuildJob> getResults() { return result.getBuildJobs(); } public BuildJob getDynamic( String name, StaplerRequest req, StaplerResponse resp ) { return result.getDynamic( name, req, resp ); } public BuildJobs getTotals() { return result; } /** * The summary of this build report for display on the build index page. * * @return */ public String getSummary() { AbstractBuild<?, ?> prevBuild = getBuild().getPreviousBuild(); while ( prevBuild != null && prevBuild.getAction( getClass() ) == null ) { prevBuild = prevBuild.getPreviousBuild(); } if ( prevBuild == null ) { return result.toSummary(); } else { AbstractBuildReport action = prevBuild.getAction( getClass() ); return result.toSummary( action.getTotals() ); } } /** * {@inheritDoc} */ public String getIconFileName() { return PluginImpl.ICON_FILE_NAME; } /** * {@inheritDoc} */ public String getDisplayName() { return PluginImpl.DISPLAY_NAME; } /** * Getter for property 'graphName'. * * @return Value for property 'graphName'. */ public String getGraphName() { return PluginImpl.GRAPH_NAME; } /** * {@inheritDoc} */ public String getUrlName() { return PluginImpl.URL; } /** * Generates the graph that shows the coverage trend up to this report. */ public void doGraph( StaplerRequest req, StaplerResponse rsp ) throws IOException { if ( GraphHelper.isGraphUnsupported() ) { GraphHelper.redirectWhenGraphUnsupported( rsp, req ); return; } Calendar t = getBuild().getTimestamp(); if ( req.checkIfModified( t, rsp ) ) { return; // up to date } DataSetBuilder<String, ChartUtil.NumberOnlyBuildLabel> dataSetBuilder = new DataSetBuilder<String, ChartUtil.NumberOnlyBuildLabel>(); populateDataSetBuilder( dataSetBuilder ); ChartUtil.generateGraph( req, rsp, GraphHelper.buildChart( dataSetBuilder.build(), "# of tests" ), getGraphWidth(), getGraphHeight() ); } /** * Returns <code>true</code> if there is a graph to plot. * * @return Value for property 'graphAvailable'. */ public boolean isGraphActive() { AbstractBuild<?, ?> build = getBuild(); // in order to have a graph, we must have at least two points. int numPoints = 0; while ( numPoints < 2 ) { if ( build == null ) { return false; } if ( build.getAction( getClass() ) != null ) { numPoints++; } build = build.getPreviousBuild(); } return true; } protected void populateDataSetBuilder( DataSetBuilder<String, ChartUtil.NumberOnlyBuildLabel> dataset ) { for ( AbstractBuild<?, ?> build = getBuild(); build != null; build = build.getPreviousBuild() ) { ChartUtil.NumberOnlyBuildLabel label = new ChartUtil.NumberOnlyBuildLabel( build ); AbstractBuildReport action = build.getAction( getClass() ); if ( action != null ) { dataset.add( action.getTotals().getPassCount(), "Successful", label ); dataset.add( action.getTotals().getSkipCount(), "Skipped", label ); dataset.add( action.getTotals().getErrorCount(), "In error", label ); dataset.add( action.getTotals().getFailInitCount(), "Failed before run", label ); dataset.add( action.getTotals().getFailRunCount(), "Failed during run", label ); dataset.add( action.getTotals().getFailValidateCount(), "Failed after run", label ); } } } /** * Getter for property 'graphWidth'. * * @return Value for property 'graphWidth'. */ public int getGraphWidth() { return 500; } /** * Getter for property 'graphHeight'. * * @return Value for property 'graphHeight'. */ public int getGraphHeight() { return 200; } /** * Getter for property 'build'. * * @return Value for property 'build'. */ public synchronized T getBuild() { return build; } /** * Write once setter for property 'build'. * * @param build Value to set for property 'build'. */ public synchronized void setBuild( T build ) { // Ideally I'd prefer to use and AtomicReference... but I'm unsure how it would work with the serialization fun if ( this.build == null && this.build != build ) { this.build = build; } } /** * Override to control when the floating box should be displayed. * * @return <code>true</code> if the floating box should be visible. */ public boolean isFloatingBoxActive() { return true; } }