package hudson.plugins.testabilityexplorer.report; import org.apache.commons.lang.StringUtils; import org.jfree.chart.JFreeChart; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import hudson.model.AbstractBuild; import hudson.model.HealthReport; import hudson.plugins.testabilityexplorer.PluginImpl; import hudson.plugins.testabilityexplorer.helpers.AbstractBuildAction; import hudson.plugins.testabilityexplorer.report.charts.BuildAndResults; import hudson.plugins.testabilityexplorer.report.charts.RangedClassesTrend; import hudson.plugins.testabilityexplorer.report.charts.RangedOverallTrend; import hudson.plugins.testabilityexplorer.report.charts.RangedTrend; import hudson.plugins.testabilityexplorer.report.costs.CostSummary; import hudson.plugins.testabilityexplorer.report.costs.Statistic; import hudson.plugins.testabilityexplorer.report.health.ReportBuilder; import hudson.util.ChartUtil; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Abstract {@link AbstractBuildAction} class that is capable of rendering different build reports. * * @author reik.schatz */ public abstract class AbstractBuildReport<T extends AbstractBuild<?, ?>> extends AbstractBuildAction<T> { private final Collection<Statistic> m_results; private final ReportBuilder m_reportBuilder; private final CostDetailBuilder m_detailBuilder; public AbstractBuildReport(Collection<Statistic> results, ReportBuilder reportBuilder, CostDetailBuilder detailBuilder) { m_results = results; m_reportBuilder = reportBuilder; m_detailBuilder = detailBuilder; } public Collection<Statistic> getResults() { return m_results == null ? new ArrayList<Statistic>() : new ArrayList<Statistic>(m_results); } void addResults(Collection<Statistic> statistics) { for (Statistic statistic : statistics) { if (!m_results.contains(statistic)) { m_results.add(statistic); } } } protected void mergeStatistics(Collection<Statistic> statistics, double weightFactor) { if (null != statistics) { for (Statistic statistic : statistics) { if (m_results.isEmpty()) { m_results.add(statistic); } else { boolean merged = false; //check if we need to update existing statistics for (Statistic stat : m_results) { if ((null == stat.getOwner() && null == statistic.getOwner()) || (null != stat.getOwner() && stat.getOwner().equals( statistic.getOwner()))) { stat.getCostSummary().merge(statistic.getCostSummary(), weightFactor); merged = true; } } if (!merged) { m_results.add(statistic); } } } for (Statistic stat : m_results) { stat.sort(); } } } /** {@inheritDoc} */ @Override public String getSummary() { String summary = ""; AbstractBuild<?, ?> build = getBuild(); if (build != null) { summary = " (Total: " + getTotals() + ")"; } return summary; } public int getNumberOfClasses(){ int numberOfClasses = 0; for (Statistic statistic : getResults()) { CostSummary summary = statistic.getCostSummary(); if (summary != null) { numberOfClasses += summary.getNumberOfClasses(); } } return numberOfClasses; } public int getExcellent(){ int excellent = 0; for (Statistic statistic : getResults()) { CostSummary summary = statistic.getCostSummary(); if (summary != null) { excellent += summary.getExcellent(); } } return excellent; } public int getGood(){ int good = 0; for (Statistic statistic : getResults()) { CostSummary summary = statistic.getCostSummary(); if (summary != null) { good += summary.getGood(); } } return good; } public int getNeedsWork(){ int needsWork = 0; for (Statistic statistic : getResults()) { CostSummary summary = statistic.getCostSummary(); if (summary != null) { needsWork += summary.getNeedsWork(); } } return needsWork; } public int getTotals() { int total = 0; for (Statistic statistic : getResults()) { CostSummary summary = statistic.getCostSummary(); if (summary != null) { total += summary.getTotal(); } } return total; } /** * This will be called implicitly by Stapler framework, if there is a dynamic part in the regular link.<br /> * Example: lets say your plugin is called <code>testability</code>. When requesting <code>http://hudson:8080/job/project/build/testability/foo</code> * this method gets called with <code>foo</code> as first parameter. * * @param link the dynamic part of the url after the plugin name * @param request StaplerRequest * @param response StaplerResponse * @return any object that you want to work with in .jelly files */ public Object getDynamic(final String link, final StaplerRequest request, final StaplerResponse response) { String originalRequestUri = request.getOriginalRequestURI(); return m_detailBuilder.buildDetail(link, originalRequestUri, getBuild(), getResults()); } /** {@inheritDoc} */ public HealthReport getBuildHealth() { return m_reportBuilder.computeHealth(getResults()); } /** * Renders a jfree chart graph into the given {@link StaplerResponse}. If the given {@link StaplerRequest} contains a * paramter {@code classes} set to {@code true}, a classes trend graph instead of the overall trend graph will be rendered. * * @param request a StaplerRequest * @param response a StaplerResponse * @param height the height of the charts (not used for now) * @throws IOException if there is a problem writing to the StaplerResponse */ public final void doTrendGraph(final StaplerRequest request, final StaplerResponse response, final int height) throws IOException { if (ChartUtil.awtProblemCause != null) { response.sendRedirect2(request.getContextPath() + "/images/headless.png"); return; } String classesTrend = request.getParameter("classes"); boolean displayClassesTrend = Boolean.valueOf(StringUtils.defaultIfEmpty(classesTrend, "false")); List<BuildAndResults> buildsAndResults = retrieveExistingBuildsAndResults(getBuild()); if (displayClassesTrend) { RangedTrend rangedTrend = new RangedClassesTrend(buildsAndResults); JFreeChart chart = createChart(rangedTrend); ChartUtil.generateGraph(request, response, chart, 800, 600); } else { RangedTrend rangedTrend = new RangedOverallTrend(buildsAndResults); JFreeChart chart = createChart(rangedTrend); ChartUtil.generateGraph(request, response, chart, 400, 200); } } List<BuildAndResults> retrieveExistingBuildsAndResults(AbstractBuild<?, ?> startingBuild) { List<BuildAndResults> buildsAndResults = new ArrayList<BuildAndResults>(); buildsAndResults.add(new BuildAndResults(startingBuild, getResults())); AbstractBuild<?, ?> previousBuild = getPreviousBuild(startingBuild); while (previousBuild != null) { AbstractBuildReport action = previousBuild.getAction(getClass()); if (action != null) { buildsAndResults.add(new BuildAndResults(previousBuild, action.getResults())); } previousBuild = getPreviousBuild(previousBuild); } return buildsAndResults; } /** * Creates a new JFreeChart based on the specified RangedTrend. * * @return the JFreeChart chart */ JFreeChart createChart(RangedTrend rangedTrend) { return m_reportBuilder.createGraph(rangedTrend); } /** * Returns the previous build of the given {@link AbstractBuild}. * @param build AbstractBuild * @return AbstractBuild or {@code null} */ AbstractBuild<?, ?> getPreviousBuild(AbstractBuild<?, ?> build) { return build.getPreviousBuild(); } @Override public String getGraphName() { return PluginImpl.GRAPH_NAME; } public String getIconFileName() { return PluginImpl.ICON_FILE_NAME; } public String getDisplayName() { return PluginImpl.DISPLAY_NAME; } public String getUrlName() { return PluginImpl.URL; } ReportBuilder getReportBuilder() { return m_reportBuilder; } CostDetailBuilder getDetailBuilder() { return m_detailBuilder; } }