package hudson.plugins.fitnesse;
import hudson.model.AbstractBuild;
import hudson.model.Hudson;
import hudson.model.ModelObject;
import hudson.plugins.fitnesse.NativePageCounts.Counts;
import hudson.tasks.test.TestObject;
import hudson.tasks.test.TestResult;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
public class FitnesseResults extends TestResult implements Comparable<FitnesseResults>{
private static final long serialVersionUID = 1L;
private transient boolean durationCalculated;
private transient long durationInMillis;
private transient List<FitnesseResults> failed = null;
private transient List<FitnesseResults> skipped = null;
private transient List<FitnesseResults> passed = null;
private Counts pageCounts;
private TestObject parent;
private List<FitnesseResults> details = new ArrayList<FitnesseResults>();
private AbstractBuild<?, ?> owner;
public FitnesseResults(Counts pageCounts) {
this.pageCounts = pageCounts;
}
FitnesseResults(NativePageCounts allCounts) {
this(allCounts.getSummary());
for (Counts detail : allCounts.getDetails()) {
addDetail(new FitnesseResults(detail));
}
}
void addDetail(FitnesseResults fitnesseResults) {
details.add(fitnesseResults);
fitnesseResults.setParent(this);
}
/**
* {@link TestObject} Required to compare builds with one another e.g. show history graph
*/
@Override
public TestResult findCorrespondingResult(final String id) {
if (id.equals(getId())) return this;
List<FitnesseResults> match = filteredCopyOfDetails(new ResultsFilter() {
public boolean include(FitnesseResults fitnesseResults) {
return id.equals(fitnesseResults.getId());
}
});
return (match.size() == 0 ? null : match.get(0));
}
public void setOwner(AbstractBuild<?, ?> build) {
this.owner = build;
}
@Override
public AbstractBuild<?, ?> getOwner() {
if (owner != null) return owner;
if (parent != null) return parent.getOwner();
return null;
}
@Override
public void setParent(TestObject parentObject) {
this.parent = parentObject;
}
@Override
public TestObject getParent() {
return parent;
}
/**
* Required for {@link TestObject#getId}
*/
@Override
public String getName() {
return pageCounts.page;
}
/**
* {@link ModelObject}
*/
public String getDisplayName() {
return getName();
}
@Override
public int getFailCount() {
return pageCounts.wrong;
}
@Override
public int getPassCount() {
return pageCounts.right;
}
@Override
public int getSkipCount() {
return getIgnoredCount() + getExceptionCount();
}
/**
* referenced in summary.jelly
*/
public int getIgnoredCount() {
return pageCounts.ignored;
}
/**
* referenced in summary.jelly
*/
public int getExceptionCount() {
return pageCounts.exceptions;
}
public boolean isFailedOverall() {
return getFailCount() > 0;
}
public boolean isPassedOverall() {
return isPassed();
}
@Override
public boolean isPassed() {
return !isFailedOverall() && !isSkippedOverall();
}
public boolean isSkippedOverall() {
if (isFailedOverall()) return false;
if (getExceptionCount() > 0) return true;
return getPassCount() == 0;
}
@Override
public float getDuration() {
if (!durationCalculated) calculateDurationInMillis();
return durationInMillis / 1000.0f;
}
private void calculateDurationInMillis() {
FitnesseResults earliest = null, latest = null;
for (FitnesseResults detail : details) {
if (earliest == null) {
earliest = detail;
} else if (detail.isEarlierThan(earliest)) {
earliest = detail;
}
if (latest == null) {
latest = detail;
} else if (detail.isLaterThan(latest)) {
latest = detail;
}
}
durationInMillis = latest.millisAfter(earliest);
durationCalculated = true;
}
public boolean isEarlierThan(FitnesseResults other) {
try {
return pageCounts.resultsDateAsDate().before(other.pageCounts.resultsDateAsDate());
} catch (ParseException e) {
return false;
}
}
public boolean isLaterThan(FitnesseResults other) {
try {
return pageCounts.resultsDateAsDate().after(other.pageCounts.resultsDateAsDate());
} catch (ParseException e) {
return false;
}
}
public long millisAfter(FitnesseResults other) {
try {
return pageCounts.resultsDateAsDate().getTime() -
other.pageCounts.resultsDateAsDate().getTime();
} catch (ParseException e) {
return 0;
}
}
/**
* referenced in body.jelly
*/
public String getHeadlineText() {
return pageCounts.toString();
}
/**
* {@see TestObject#getTestResultAction()}
* Required to prevent looking for any old AbstractTestResultAction
* when e.g. looking for history across multiple builds
*/
@Override
public FitnesseResultsAction getTestResultAction() {
return getParentAction();
}
/**
* {@see TestResult#getParentAction()}
* Required to prevent looking for any old AbstractTestResultAction
* when e.g. looking for history across multiple builds
*/
@Override
public FitnesseResultsAction getParentAction() {
FitnesseResultsAction action = getOwner().getAction(FitnesseResultsAction.class);
return action;
}
/**
* {@link Comparable}
*/
public int compareTo(FitnesseResults other) {
return getDisplayName().compareTo(other.getDisplayName());
}
@Override
public Collection<FitnesseResults> getFailedTests() {
if (failed == null) {
failed = filteredCopyOfDetails(new ResultsFilter() {
public boolean include(FitnesseResults results) {
return results.isFailedOverall();
}
});
}
return failed;
}
@Override
public Collection<FitnesseResults> getPassedTests() {
if (passed == null) {
passed = filteredCopyOfDetails(new ResultsFilter() {
public boolean include(FitnesseResults results) {
return results.isPassedOverall();
}
});
}
return passed;
}
@Override
public Collection<FitnesseResults> getSkippedTests() {
if (skipped == null) {
skipped = filteredCopyOfDetails(new ResultsFilter() {
public boolean include(FitnesseResults results) {
return results.isSkippedOverall();
}
});
}
return skipped;
}
private List<FitnesseResults> filteredCopyOfDetails(ResultsFilter countsFilter) {
List<FitnesseResults> filteredCopy = new ArrayList<FitnesseResults>();
for (FitnesseResults result : details) {
if (countsFilter.include(result)) {
filteredCopy.add(result);
}
}
Collections.sort(filteredCopy);
return filteredCopy;
}
interface ResultsFilter {
public boolean include(FitnesseResults fitnesseResults);
}
/**
* referenced in summary.jelly
*/
public String toHtml(FitnesseResults results) {
FitnesseBuildAction buildAction = getOwner().getAction(FitnesseBuildAction.class);
if (buildAction == null) {
buildAction = FitnesseBuildAction.NULL_ACTION;
}
return buildAction.getLinkFor(results.getName(), Hudson.getInstance().getRootUrl());
}
/**
* called from links embedded in history/trend graphs
* TODO: Expose sub-suites as separate elements of the fitnesse report.
*/
@Override
public Object getDynamic(String token, StaplerRequest req, StaplerResponse rsp) {
return findCorrespondingResult(token);
}
}