package hudson.plugins.testng.results;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import hudson.model.Run;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
/**
* Handle results related to a single test class
*/
@SuppressFBWarnings(value="SE_NO_SERIALVERSIONID", justification="XStream does not care")
@SuppressWarnings("serial")
public class ClassResult extends BaseResult {
private String pkgName; //name of package containing this class
private List<MethodResult> testMethodList = new ArrayList<MethodResult>();
//cache
@SuppressFBWarnings(value="SE_BAD_FIELD", justification="HashMap is Serializable")
private Map<String, GroupedTestRun> testRunMap = null;
//cached values, updated via tally
private transient long startTime;
private transient long endTime;
private transient int fail;
private transient int skip;
private transient int pass;
public ClassResult(String pkgName, String name) {
super(name);
this.pkgName = pkgName;
}
public String getPkgName() {
return pkgName;
}
public String getCanonicalName() {
if (PackageResult.NO_PKG_NAME.equals(pkgName)) {
return getName();
} else {
return pkgName + "." + getName();
}
}
/**
* Called only from jelly file
*
* @return
*/
public Map<String, GroupedTestRun> getTestRunMap() {
if (testRunMap != null) {
return testRunMap;
}
//group all the test methods based on their run
testRunMap = new HashMap<String, GroupedTestRun>();
for (MethodResult methodResult : this.testMethodList) {
String methodTestRunId = methodResult.getTestRunId();
GroupedTestRun group;
if (this.testRunMap.containsKey(methodTestRunId)) {
group = this.testRunMap.get(methodTestRunId);
} else {
group = new GroupedTestRun(methodTestRunId,
methodResult.getParentTestName(),
methodResult.getParentSuiteName());
this.testRunMap.put(methodTestRunId, group);
}
if (methodResult.isConfig()) {
group.addConfigurationMethod(methodResult);
} else {
group.addTestMethod(methodResult);
}
}
return testRunMap;
}
@Override
public void setRun(Run<?, ?> run) {
super.setRun(run);
for (MethodResult _m : this.testMethodList) {
_m.setRun(run);
}
}
@Exported
@Override
public float getDuration() {
return (endTime - startTime) / 1000f;
}
public long getStartTime() {
return startTime; //in ms
}
public long getEndTime() {
return endTime; //in ms
}
@Override
@Exported(visibility = 9, name = "fail")
public int getFailCount() {
return fail;
}
@Override
@Exported(visibility = 9, name = "skip")
public int getSkipCount() {
return skip;
}
@Override
@Exported(visibility = 9)
public int getTotalCount() {
return super.getTotalCount();
}
@Override
public int getPassCount() {
return pass;
}
public void addTestMethods(List<MethodResult> list) {
this.testMethodList.addAll(list);
}
public void tally() {
this.fail = 0;
this.skip = 0;
this.pass = 0;
this.startTime = Long.MAX_VALUE; //start with max
this.endTime = 0; //start with min
Map<String, Integer> methodInstanceMap = new HashMap<String, Integer>();
for (MethodResult methodResult : this.testMethodList) {
if (!methodResult.isConfig()) {
if ("FAIL".equals(methodResult.getStatus())) {
this.fail++;
} else if ("SKIP".equals(methodResult.getStatus())) {
this.skip++;
} else {
this.pass++;
}
}
/*
It's possible that timestamps were not parsed correctly, so check for -1 values.
And then, we check for the oldest start time and latest end time to figure out
time taken to execute a class. (Same would be done for PackageResults and
TestNGResults as well.
Note that this helps give a better idea of time taken for test execution when
tests were run in parallel, but still doesn't give a good picture when all tests
within a class finished execute within a second, because millisecond information
is not available on the start time of method execution.
*/
long timestamp = methodResult.getStartTime();
if (timestamp != -1 && this.startTime > timestamp) {
startTime = timestamp;
}
timestamp = methodResult.getEndTime();
if (timestamp != -1 && this.endTime < timestamp) {
endTime = timestamp;
}
methodResult.setParent(this);
/*
* Setup testUuids to ensure that methods with same names can be
* reached using unique urls
*/
String methodName = methodResult.getName();
if (methodInstanceMap.containsKey(methodName)) {
int currIdx = methodInstanceMap.get(methodName);
methodResult.setTestUuid(String.valueOf(++currIdx));
methodInstanceMap.put(methodName, currIdx);
} else {
methodInstanceMap.put(methodName, 0);
}
}
}
/*
Overriding because instead of comparing token to name, for methods,
we need to compare it with the safe name (which includes testUuid, if
applicable)
*/
@Override
public Object getDynamic(String token, StaplerRequest req, StaplerResponse rsp) {
if (this.testMethodList != null) {
for (MethodResult methodResult : this.testMethodList) {
//append the uuid as well
if (token.equals(methodResult.getSafeName())) {
return methodResult;
}
}
}
return null;
}
@Exported(name = "test-method")
public List<MethodResult> getTestMethods() {
List<MethodResult> list = new ArrayList<MethodResult>();
for (MethodResult methodResult : this.testMethodList) {
if (!methodResult.isConfig()) {
list.add(methodResult);
}
}
return list;
}
public List<MethodResult> getConfigurationMethods() {
List<MethodResult> list = new ArrayList<MethodResult>();
for (MethodResult methodResult : this.testMethodList) {
if (methodResult.isConfig()) {
list.add(methodResult);
}
}
return list;
}
@Override
public List<MethodResult> getChildren() {
return testMethodList;
}
@Override
public boolean hasChildren() {
return testMethodList != null && !testMethodList.isEmpty();
}
}