package hudson.model; import hudson.Functions; import hudson.plugins.claim.ClaimBuildAction; import hudson.tasks.test.AbstractTestResultAction; import java.text.NumberFormat; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.jfree.util.Log; /** * Represents a job to be shown in a view. Based heavily on the XFPanelEntry in * XFPanel plugin. * * @author jrenaut */ public final class ViewEntry { /** * */ private final RadiatorView radiatorView; private Job<?, ?> job; private String backgroundColor; private String color; private Boolean broken; private Boolean building = false; private Boolean queued = false; private Integer queueNumber; /** * If the build is stable. */ private boolean stable; /** * C'tor * * @param job * the job to be represented * @param radiatorView * TODO */ public ViewEntry(RadiatorView radiatorView, Job<?, ?> job) { this.radiatorView = radiatorView; this.job = job; this.findStatus(); } /** * @return the job */ public Job<?, ?> getJob() { return this.job; } /** * @return the job's name */ public String getName() { return job.getName(); } /** * @return if this job is queued for build */ public Boolean getQueued() { return this.job.isInQueue(); } /** * @return the job's queue number, if any */ public Integer getQueueNumber() { return this.radiatorView.placeInQueue.get(this.job.getQueueItem()); } /** * @return background color for this job */ public String getBackgroundColor() { return this.backgroundColor; } /** * @return foreground color for this job */ public String getColor() { return this.color; } /** * @return true se o último build está quebrado */ public Boolean getBroken() { return this.broken; } /** * @return true if this job is currently being built */ public Boolean getBuilding() { return this.building; } /** * @return the URL for the last build */ public String getUrl() { return this.job.getUrl() + "lastBuild"; } /** * @return a list will all the currently building runs for this job. */ public List<Run<?, ?>> getBuildsInProgress() { List<Run<?, ?>> runs = new ArrayList<Run<?, ?>>(); Run<?, ?> run = this.job.getLastBuild(); if (run != null) { if (run.isBuilding()) { runs.add(run); } Run<?, ?> prev = run.getPreviousBuildInProgress(); while (prev != null) { runs.add(prev); prev = prev.getPreviousBuildInProgress(); } } return runs; } /** * @return total tests executed */ public int getTestCount() { Run<?, ?> run = this.job.getLastSuccessfulBuild(); if (run != null) { AbstractTestResultAction<?> tests = run.getAction(AbstractTestResultAction.class); return tests != null ? tests.getTotalCount() : 0; } return 0; } /** * @return total failed tests */ public int getFailCount() { Run<?, ?> run = this.job.getLastSuccessfulBuild(); if (run != null) { AbstractTestResultAction<?> tests = run.getAction(AbstractTestResultAction.class); return tests != null ? tests.getFailCount() : 0; } return 0; } /** * @return total successful tests */ public int getSuccessCount() { return this.getTestCount() - this.getFailCount(); } /** * @return difference between this job's last build successful tests and the * previous' */ public String getDiff() { Run<?, ?> run = this.job.getLastSuccessfulBuild(); if (run != null) { Run<?, ?> previous = this.getLastSuccessfulFrom(run); if (previous != null) { AbstractTestResultAction<?> tests = run.getAction(AbstractTestResultAction.class); AbstractTestResultAction<?> prevTests = previous .getAction(AbstractTestResultAction.class); if (tests != null && prevTests != null) { int currentSuccess = tests.getTotalCount() - tests.getFailCount(); int prevSuccess = prevTests.getTotalCount() - prevTests.getFailCount(); int diff = currentSuccess - prevSuccess; if (diff != 0) { return Functions.getDiffString(diff); } else { return ""; } } } } return ""; } /** * @param run * a run * @return the last successful run prior to the given run */ private Run<?, ?> getLastSuccessfulFrom(Run<?, ?> run) { Run<?, ?> r = run.getPreviousBuild(); while (r != null && (r.isBuilding() || r.getResult() == null || r.getResult().isWorseThan( Result.UNSTABLE))) { r = r.getPreviousBuild(); } return r; } /** * Elects a culprit/responsible for a broken build by choosing the last * commiter of a given build * * @return the culprit/responsible */ public String getCulprit() { Run<?, ?> run = this.job.getLastBuild(); String culprit = " - "; Set<String> culprits = new HashSet<String>(); while (run != null) { if (run instanceof AbstractBuild<?, ?>) { AbstractBuild<?, ?> build = (AbstractBuild<?, ?>) run; Iterator<User> it = build.getCulprits().iterator(); while (it.hasNext()) { culprits.add(it.next().getFullName()); } } run = run.getPreviousBuild(); if (run != null && Result.SUCCESS.equals(run.getResult())) { // don't look for culprits in successful builds. run = null; } } if (!culprits.isEmpty()) { culprit = StringUtils.join(culprits, ", "); } return culprit; } /** * @return color to be used to show the test diff */ public String getDiffColor() { String diff = this.getDiff().trim(); if (diff.length() > 0 && !Functions.getDiffString(0).equals(diff)) { if (diff.startsWith("-")) { return "#FF0000"; } else { return "#00FF00"; } } return "#FFFFFF"; } /** * @return the percentage of successful tests versus the total */ public String getSuccessPercentage() { if (this.getTestCount() > 0) { Double perc = this.getSuccessCount() / (this.getTestCount() * 1D); return NumberFormat.getPercentInstance().format(perc); } return ""; } /** * Determines some information of the current job like which colors use, * wether it's building or not or broken. */ private void findStatus() { Result result = radiatorView.getResult(job); this.stable = false; if (result.ordinal == Result.NOT_BUILT.ordinal) { this.backgroundColor = getColors().getOtherBG(); this.color = getColors().getOtherFG(); } else if (result.ordinal == Result.SUCCESS.ordinal) { this.backgroundColor = getColors().getOkBG(); this.color = getColors().getOkFG(); this.broken = false; this.stable = true; } else if (result.ordinal == Result.UNSTABLE.ordinal) { this.backgroundColor = getColors().getFailedBG(); this.color = getColors().getFailedFG(); this.broken = false; } else { this.backgroundColor = getColors().getBrokenBG(); this.color = getColors().getBrokenFG(); this.broken = true; } switch (this.job.getIconColor()) { case BLUE_ANIME: case YELLOW_ANIME: case RED_ANIME: case GREY_ANIME: case DISABLED_ANIME: this.building = true; break; default: this.building = false; } } private ViewEntryColors getColors() { return radiatorView.getColors(); } public String getLastCompletedBuild() { Run build = job.getLastCompletedBuild(); if (build != null) { return build.getTimestampString() + " (" + build.getDurationString() + ")"; } return null; } public String getLastStableBuild() { Run build = job.getLastStableBuild(); if (build != null) { return build.getTimestampString() + " (in " + build.getDurationString() + ")"; } return null; } /** * @return <code>true</code> if the build is stable. */ public boolean getStable() { return stable; } /** * If the claims plugin is installed, this will get details of the claimed * build failures. * * @return details of any claims for the broken build, or null if nobody has * claimed this build. */ public String getClaim() { String claim = null; if (Hudson.getInstance().getPlugin("claim") != null) { claim = "Not Claimed"; Run lastBuild = job.getLastBuild(); if (lastBuild != null && lastBuild.isBuilding()) { // claims can only be made against builds once they've finished, // so check the previous build if currently building. lastBuild = lastBuild.getPreviousBuild(); } if (lastBuild != null) { // TODO - check previous build if currently building. List<ClaimBuildAction> claimActionList = lastBuild .getActions(ClaimBuildAction.class); if (claimActionList.size() == 1) { ClaimBuildAction claimAction = claimActionList.get(0); if (claimAction.isClaimed()) { String by = claimAction.getClaimedByName(); String reason = claimAction.getReason(); claim = "Claimed by " + by; if (reason != null) { claim += ": " + reason; } } } else if (claimActionList.size() > 1) { claim = "Error parsing claim details"; Log.warn("Multiple ClaimBuildActions found for job " + job.toString()); } } claim += "."; } return claim; } }