package hudson.plugins.cobertura;
import hudson.model.Action;
import hudson.model.AbstractBuild;
import hudson.model.HealthReport;
import hudson.model.HealthReportingAction;
import hudson.model.Result;
import hudson.model.Run;
import hudson.plugins.cobertura.targets.CoverageMetric;
import hudson.plugins.cobertura.targets.CoverageTarget;
import hudson.plugins.cobertura.targets.CoverageResult;
import hudson.util.DescribableList;
import hudson.util.Graph;
import jenkins.model.RunAction2;
import jenkins.tasks.SimpleBuildStep;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jfree.chart.JFreeChart;
import org.jvnet.localizer.Localizable;
import org.kohsuke.stapler.StaplerProxy;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
/**
* Created by IntelliJ IDEA.
*
* @author connollys
* @since 03-Jul-2007 08:43:08
*/
public class CoberturaBuildAction implements HealthReportingAction, StaplerProxy, Chartable, SimpleBuildStep.LastBuildAction, RunAction2 {
private transient Run<?, ?> owner;
private CoverageTarget healthyTarget;
private CoverageTarget unhealthyTarget;
private boolean failUnhealthy;
private boolean failUnstable;
private boolean autoUpdateHealth;
private boolean autoUpdateStability;
private boolean zoomCoverageChart;
private int maxNumberOfBuilds;
/**
* Overall coverage result.
*/
private Map<CoverageMetric, Ratio> result;
private HealthReport health = null;
private transient WeakReference<CoverageResult> report;
private boolean onlyStable;
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public HealthReport getBuildHealth() {
if (health != null) {
return health;
}
if (owner instanceof AbstractBuild) {
//try to get targets from root project (for maven modules targets are null)
DescribableList rootpublishers = ((AbstractBuild)owner).getProject().getRootProject().getPublishersList();
if (rootpublishers != null) {
CoberturaPublisher publisher = (CoberturaPublisher) rootpublishers.get(CoberturaPublisher.class);
if (publisher != null) {
healthyTarget = publisher.getHealthyTarget();
unhealthyTarget = publisher.getUnhealthyTarget();
}
}
}
if (healthyTarget == null || unhealthyTarget == null) {
return null;
}
if (result == null) {
CoverageResult projectCoverage = getResult();
result = new EnumMap<CoverageMetric, Ratio>(CoverageMetric.class);
result.putAll(projectCoverage.getResults());
}
Map<CoverageMetric, Integer> scores = healthyTarget.getRangeScores(unhealthyTarget, result);
int minValue = 100;
CoverageMetric minKey = null;
for (Map.Entry<CoverageMetric, Integer> e : scores.entrySet()) {
if (e.getValue() < minValue) {
minKey = e.getKey();
minValue = e.getValue();
}
}
if (minKey == null) {
if (result == null || result.size() == 0) {
return null;
} else {
for (Map.Entry<CoverageMetric, Integer> e : scores.entrySet()) {
minKey = e.getKey();
}
if (minKey != null) {
Localizable localizedDescription = Messages._CoberturaBuildAction_description(result.get(minKey).getPercentage(), result.get(minKey).toString(), minKey.getName());
health = new HealthReport(minValue, localizedDescription);
return health;
}
return null;
}
} else {
Localizable localizedDescription = Messages._CoberturaBuildAction_description(result.get(minKey).getPercentage(), result.get(minKey).toString(), minKey.getName());
health = new HealthReport(minValue, localizedDescription);
return health;
}
}
/**
* {@inheritDoc}
*/
public String getIconFileName() {
return "graph.gif"; //To change body of implemented methods use File | Settings | File Templates.
}
/**
* {@inheritDoc}
*/
public String getDisplayName() {
return Messages.CoberturaBuildAction_displayName(); //To change body of implemented methods use File | Settings | File Templates.
}
/**
* {@inheritDoc}
*/
public String getUrlName() {
return "cobertura"; //To change body of implemented methods use File | Settings | File Templates.
}
/**
* {@inheritDoc}
*/
public Object getTarget() {
return getResult(); //To change body of implemented methods use File | Settings | File Templates.
}
public Run<?, ?> getOwner() {
return owner;
}
private synchronized void setOwner(Run<?, ?> owner) {
this.owner = owner;
if (report != null) {
CoverageResult r = report.get();
if (r != null) {
r.setOwner(owner);
}
}
}
public Map<CoverageMetric, Ratio> getResults() {
return result;
}
/**
* Getter for property 'previousResult'.
*
* @return Value for property 'previousResult'.
*/
public CoberturaBuildAction getPreviousResult() {
return getPreviousResult(owner);
}
/**
* Gets the previous {@link CoberturaBuildAction} of the given build.
*/
/*package*/
static CoberturaBuildAction getPreviousResult(Run<?, ?> start) {
Run<?, ?> b = start;
while (true) {
b = BuildUtils.getPreviousNotFailedCompletedBuild(b);
if (b == null) {
return null;
}
assert b.getResult() != Result.FAILURE : "We asked for the previous not failed build";
CoberturaBuildAction r = b.getAction(CoberturaBuildAction.class);
if (r != null && r.includeOnlyStable() && b.getResult() != Result.SUCCESS) {
r = null;
}
if (r != null) {
return r;
}
}
}
private boolean includeOnlyStable() {
return onlyStable;
}
CoberturaBuildAction(CoverageResult r, CoverageTarget healthyTarget,
CoverageTarget unhealthyTarget, boolean onlyStable, boolean failUnhealthy,
boolean failUnstable, boolean autoUpdateHealth, boolean autoUpdateStability,
boolean zoomCoverageChart, int maxNumberOfBuilds) {
this.report = new WeakReference<CoverageResult>(r);
this.healthyTarget = healthyTarget;
this.unhealthyTarget = unhealthyTarget;
this.onlyStable = onlyStable;
this.failUnhealthy = failUnhealthy;
this.failUnstable = failUnstable;
this.autoUpdateHealth = autoUpdateHealth;
this.autoUpdateStability = autoUpdateStability;
this.zoomCoverageChart = zoomCoverageChart;
this.maxNumberOfBuilds = maxNumberOfBuilds;
if (result == null) {
result = new EnumMap<CoverageMetric, Ratio>(CoverageMetric.class);
result.putAll(r.getResults());
}
getBuildHealth(); // populate the health field so we don't have to parse everything all the time
}
/**
* Obtains the detailed
* {@link hudson.plugins.cobertura.targets.CoverageResult} instance.
*
* @return the {@link hudson.plugins.cobertura.targets.CoverageResult} instance.
*/
public synchronized CoverageResult getResult() {
if (report != null) {
CoverageResult r = report.get();
if (r != null) {
return r;
}
}
CoverageResult r = null;
for (File reportFile : CoberturaPublisher.getCoberturaReports(owner)) {
try {
r = CoberturaCoverageParser.parse(reportFile, r);
} catch (IOException e) {
logger.log(Level.WARNING, "Failed to load " + reportFile, e);
}
}
if (r != null) {
r.setOwner(owner);
report = new WeakReference<CoverageResult>(r);
return r;
} else {
return null;
}
}
private static final Logger logger = Logger.getLogger(CoberturaBuildAction.class.getName());
public static CoberturaBuildAction load(CoverageResult result, CoverageTarget healthyTarget,
CoverageTarget unhealthyTarget, boolean onlyStable, boolean failUnhealthy, boolean failUnstable,
boolean autoUpdateHealth, boolean autoUpdateStability, boolean zoomCoverageChart, int maxNumberOfBuilds) {
return new CoberturaBuildAction(result, healthyTarget, unhealthyTarget, onlyStable,
failUnhealthy, failUnstable, autoUpdateHealth, autoUpdateStability, zoomCoverageChart, maxNumberOfBuilds);
}
/**
* Generates the graph that shows the coverage trend up to this report.
*
* @param req the request
* @param rsp the response
* @throws IOException forwarded from StaplerResponse.sendRedirect2
*/
public void doGraph(StaplerRequest req, StaplerResponse rsp) throws IOException {
new Graph(owner.getTimestamp(), 500, 200) {
@Override
protected JFreeChart createGraph() {
return new CoverageChart(CoberturaBuildAction.this).createChart();
}
}.doPng(req, rsp);
}
public boolean getZoomCoverageChart() {
return zoomCoverageChart;
}
public int getMaxNumberOfBuilds() {
return maxNumberOfBuilds;
}
@Override
public Collection<? extends Action> getProjectActions() {
return Collections.singleton(new CoberturaProjectAction(owner, onlyStable));
}
@Override
public void onAttached(Run<?, ?> r) {
setOwner(r);
}
@Override
public void onLoad(Run<?,?> r) {
setOwner(r);
}
}