dk.hlyh.ciplugins.projecthealth.ProjectHealthProjectAction Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of project-health-report Show documentation
Show all versions of project-health-report Show documentation
This Hudson/Jenkins plugin shows project health
The newest version!
package dk.hlyh.ciplugins.projecthealth;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.Hudson;
import hudson.model.Result;
import hudson.model.Run;
import hudson.tasks.junit.CaseResult;
import hudson.tasks.test.AbstractTestResultAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.kohsuke.stapler.Stapler;
/**
* Shows statistics about project health and which test cases fail the most.
*
* @author Henrik Lynggaard Hansen
*/
public final class ProjectHealthProjectAction implements Action {
private static final Logger LOG = Logger.getLogger(ProjectHealthProjectAction.class.getName());
private static class CountComparator implements Comparator {
public CountComparator() {
}
public int compare(TestFailure o1, TestFailure o2) {
return o2.getCount() - o1.getCount();
}
}
// instance members
private final AbstractProject, ?> project;
private final Hudson hudson;
public ProjectHealthProjectAction(AbstractProject, ?> project) {
super();
this.project = project;
this.hudson = Hudson.getInstance();
}
public String getIconFileName() {
return "graph.gif";
}
public String getDisplayName() {
return "Project Health";
}
public String getUrlName() {
return "project-health";
}
public final AbstractProject, ?> getProject() {
return project;
}
public ProjectHealthState getState() {
ProjectHealthState state = new ProjectHealthState();
state.setAllBuilds(project.getBuilds());
parseInput(state);
setBuildLimit(state);
calculateTestFailures(state);
calculateGraphs(state);
return state;
}
/**
* Parses the input from the form and sets the parsed information in the state object.
*/
private void parseInput(ProjectHealthState state) {
// set options
String optionParam = Stapler.getCurrentRequest().getParameter("option");
String numOfBuildsParam = Stapler.getCurrentRequest().getParameter("num");
String firstBuildParam = Stapler.getCurrentRequest().getParameter("firstBuild");
String oldestBuildParam = Stapler.getCurrentRequest().getParameter("oldestBuild");
String groupOnErrorMessageParam = Stapler.getCurrentRequest().getParameter("groupOnErrorMsg");
LOG.fine("Project Health plugin called with: optionParam" + optionParam +
", numOfBuildsParam=" + numOfBuildsParam + ", firstBuildParam=" + firstBuildParam +
", oldestBuildParam" + oldestBuildParam + ", groupOnErrorMessageParam=" + groupOnErrorMessageParam);
// option
ProjectHealthState.OptionValue option = null;
try {
option = ProjectHealthState.OptionValue.valueOf(optionParam);
} catch (IllegalArgumentException ex) {
option = ProjectHealthState.OptionValue.All;
} catch (NullPointerException ex) {
option = ProjectHealthState.OptionValue.All;
}
state.setOptionParameter(option);
//recent build
if (numOfBuildsParam != null && !numOfBuildsParam.trim().isEmpty()) {
try {
state.setRecentBuildsParameter(Integer.parseInt(numOfBuildsParam));
} catch (NumberFormatException e) {
state.setOptionParameter(ProjectHealthState.OptionValue.All);
}
}
//period
if (firstBuildParam != null && !firstBuildParam.trim().isEmpty() && oldestBuildParam != null && !oldestBuildParam.trim().isEmpty()) {
try {
long first = Long.parseLong(firstBuildParam);
long last = Long.parseLong(oldestBuildParam);
if (last > first) {
long tmp = last;
last = first;
first = tmp;
}
//make search inclusive
first += 1;
last -= 1;
state.setFirstBuildParameter(first);
state.setLastBuildParameter(last);
} catch (NumberFormatException e) {
state.setOptionParameter(ProjectHealthState.OptionValue.All);
}
}
state.setGroupOnErrorMessageParameter(Boolean.parseBoolean(groupOnErrorMessageParam));
LOG.fine("Project Health plugin called with (parsed): optionParam"+state.getOptionParameter() +
", numOfBuildsParam=" + state.getRecentBuildsParameter() + ", firstBuildParam=" + state.getFirstBuildParameter() +
", oldestBuildParam" + state.getLastBuildParameter() + ", groupOnErrorMessageParam="+state.isGroupOnErrorMessageParameter());
}
/**
* Calculate the set of selected builds.
*/
private void setBuildLimit(ProjectHealthState state) {
List> selectedBuilds;
switch (state.getOptionParameter()) {
case Recent:
if (state.getRecentBuildsParameter() > 0 && state.getRecentBuildsParameter() < state.getAllBuilds().size()) {
selectedBuilds = (List>) state.getAllBuilds().subList(0, state.getRecentBuildsParameter());
} else {
selectedBuilds = (List>) state.getAllBuilds();
state.setOptionParameter(ProjectHealthState.OptionValue.All);
}
break;
case Period:
selectedBuilds = (List>) state.getAllBuilds().byTimestamp(state.getLastBuildParameter(), state.getFirstBuildParameter());
break;
case All:
selectedBuilds = (List>) state.getAllBuilds();
break;
default:
selectedBuilds = (List>) state.getAllBuilds();
break;
}
state.setTotalBuilds(selectedBuilds.size());
state.setSelectedBuilds(selectedBuilds);
state.setFirstSelectedBuild(selectedBuilds.get(0));
state.setLastSelectedBuild(selectedBuilds.get(selectedBuilds.size() - 1));
}
private void calculateTestFailures(ProjectHealthState state) {
Map resultMap = new HashMap();
int failureCount = 0;
int successCount = 0;
int testcaseFailures = 0;
boolean groupByErrorMessage = state.isGroupOnErrorMessageParameter();
for (Run, ?> build : state.getSelectedBuilds()) {
// skip building jobs
if (build.isBuilding()) {
continue;
}
if (build.getResult().isWorseThan(Result.SUCCESS)) {
failureCount++;
testcaseFailures = testcaseFailures + getFailuresForRun(groupByErrorMessage, resultMap, build);
} else {
successCount++;
}
}
state.setFailedBuilds(failureCount);
state.setGoodBuilds(successCount);
state.setTotalFailures(testcaseFailures);
List failures = new ArrayList(resultMap.values());
Collections.sort(failures, new CountComparator());
state.setFailures(failures);
}
private int getFailuresForRun(boolean groupByErrorMessage, Map failures, Run, ?> build) {
AbstractTestResultAction> tests = build.getAction(AbstractTestResultAction.class);
int testFailures = 0;
if (tests == null) {
return 0;
}
List failedTests = tests.getFailedTests();
for (CaseResult result : failedTests) {
String name = result.getFullName();
if (groupByErrorMessage) {
name = name + ": " + result.getErrorDetails();
}
TestFailure failure = failures.get(name);
if (failure == null) {
failure = new TestFailure();
failure.setTestcase(name);
failure.setCount(0);
failures.put(name, failure);
}
failure.setCount(failure.getCount() + 1);
testFailures += 1;
}
return testFailures;
}
private void calculateGraphs(ProjectHealthState state) {
state.setOverviewPie(getOverviewPie(state));
state.setFailuresPie(getFailurePie(state));
}
public String getOverviewPie(ProjectHealthState state) {
long successPct = (state.getGoodBuilds() * 100) / state.getSelectedBuilds().size();
long failedPct = (state.getFailedBuilds() * 100) / state.getSelectedBuilds().size();
String result = "https://chart.googleapis.com/chart?cht=p&chs=250x100";
result += "&chd=t:" + successPct + "," + failedPct;
result += "&chl=Success|Failure";
result += "&chco=729FCF,EF2929";
result += "&chtt=Project%20health";
return result;
}
public String getFailurePie(ProjectHealthState state) {
StringBuilder result = new StringBuilder(1024);
StringBuilder legends = new StringBuilder(1024);
StringBuilder datas = new StringBuilder(1024);
boolean first = true;
for (TestFailure failure : state.getFailures()) {
long failurePct = (failure.getCount() * 100) / state.getTotalFailures();
String shortName = failure.getTestcase().substring(failure.getTestcase().lastIndexOf(".") + 1);
if (first) {
legends.append(shortName);
datas.append(failurePct);
} else {
legends.append("|").append(shortName);
datas.append(",").append(failurePct);
}
first = false;
}
result.append("https://chart.googleapis.com/chart?cht=p&chs=850x300");
result.append("&chd=t:").append(datas);
result.append("&chl=").append(legends);
result.append("&chtt=Test+Failures&chco=EF2929");
return result.toString();
}
}