/* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.jenkins.flakyTestHandler.junit;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import hudson.model.AbstractBuild;
import hudson.tasks.junit.Messages;
import hudson.tasks.junit.TestNameTransformer;
import hudson.tasks.test.MetaTabulatedResult;
import hudson.tasks.test.TestResult;
/**
* Cumulative test result for a package augmented with flaky information.
* Majority of code copied from hudson.tasks.junit.PackageResult
* https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/tasks/junit/
* PackageResult.java
*
* @author Qingzhou Luo
*/
public final class FlakyPackageResult extends MetaTabulatedResult implements Comparable<FlakyPackageResult> {
private final String packageName;
private transient String safeName;
/**
* All {@link FlakyClassResult}s keyed by their short name.
*/
private final Map<String,FlakyClassResult> classes = new TreeMap<String,FlakyClassResult>();
private int passCount,failCount,skipCount,flakeCount;
private final FlakyTestResult parent;
private float duration;
FlakyPackageResult(FlakyTestResult parent, String packageName) {
this.packageName = packageName;
this.parent = parent;
}
@Override
public AbstractBuild<?, ?> getOwner() {
return (parent == null ? null : parent.getOwner());
}
public FlakyTestResult getParent() {
return parent;
}
@Exported(visibility=999)
public String getName() {
return packageName;
}
@Override
public synchronized String getSafeName() {
if (safeName != null) {
return safeName;
}
Collection<FlakyPackageResult> siblings = (parent == null ? Collections.EMPTY_LIST : parent.getChildren());
return safeName = uniquifyName(
siblings,
safe(getName()));
}
@Override
public TestResult findCorrespondingResult(String id) {
String myID = safe(getName());
int base = id.indexOf(myID);
String className = id; // fall back value
if (base > 0) {
int classNameStart = base + myID.length() + 1;
if (classNameStart<id.length())
className = id.substring(classNameStart);
}
String subId = null;
int classNameEnd = className.indexOf('/');
if (classNameEnd > 0) {
subId = className.substring(classNameEnd + 1);
if (subId.length() == 0) {
subId = null;
}
className = className.substring(0, classNameEnd);
}
FlakyClassResult child = getClassResult(className);
if (child != null && subId != null)
return child.findCorrespondingResult(subId);
return child;
}
@Override
public String getTitle() {
return Messages.PackageResult_getTitle(getDisplayName());
}
@Override
public String getChildTitle() {
return Messages.PackageResult_getChildTitle();
}
@Override
public float getDuration() {
return duration;
}
@Exported
@Override
public int getPassCount() {
return passCount;
}
@Exported
@Override
public int getFailCount() {
return failCount;
}
@Exported
@Override
public int getSkipCount() {
return skipCount;
}
@Exported
public int getFlakeCount() {
return flakeCount;
}
@Override
public int getTotalCount() {
return passCount + failCount + skipCount + flakeCount;
}
@Override
public Object getDynamic(String name, StaplerRequest req, StaplerResponse rsp) {
FlakyClassResult result = getClassResult(name);
if (result != null) {
return result;
} else {
return super.getDynamic(name, req, rsp);
}
}
public FlakyClassResult getClassResult(String name) {
return classes.get(name);
}
@Exported(name="child")
public Collection<FlakyClassResult> getChildren() {
return classes.values();
}
/**
* Whether this test result has children.
*/
@Override
public boolean hasChildren() {
int totalTests = passCount + failCount + skipCount + flakeCount;
return (totalTests != 0);
}
/**
* Returns a list of the failed cases, in no particular
* sort order
*/
public List<FlakyCaseResult> getFailedTests() {
List<FlakyCaseResult> r = new ArrayList<FlakyCaseResult>();
for (FlakyClassResult clr : classes.values()) {
for (FlakyCaseResult cr : clr.getChildren()) {
if (cr.isFailed()) {
r.add(cr);
}
}
}
return r;
}
/**
* Gets the "children" of this test result that passed without a flake
*
* @return the children of this test result, if any, or an empty collection
*/
@Override
public Collection<? extends hudson.tasks.test.TestResult> getPassedTests() {
List<FlakyCaseResult> r = new ArrayList<FlakyCaseResult>();
for (FlakyClassResult clr : classes.values()) {
for (FlakyCaseResult cr : clr.getChildren()) {
if (cr.isPassed() && !cr.isFlaked()) {
r.add(cr);
}
}
}
return r;
}
/**
* Gets the "children" of this test result that were skipped
*
* @return the children of this test result, if any, or an empty list
*/
@Override
public Collection<? extends TestResult> getSkippedTests() {
List<FlakyCaseResult> r = new ArrayList<FlakyCaseResult>();
for (FlakyClassResult clr : classes.values()) {
for (FlakyCaseResult cr : clr.getChildren()) {
if (cr.isSkipped()) {
r.add(cr);
}
}
}
return r;
}
/**
* Gets the "children" of this test result that were flaky
*
* @return the children of this test result, if any, or an empty list
*/
public List<FlakyCaseResult> getFlakyTests() {
List<FlakyCaseResult> r = new ArrayList<FlakyCaseResult>();
for (FlakyClassResult clr : classes.values()) {
for (FlakyCaseResult cr : clr.getChildren()) {
if (cr.isFlaked()) {
r.add(cr);
}
}
}
return r;
}
/**
* @return true if every test was not skipped and every test did not fail, false otherwise.
*/
@Override
public boolean isPassed() {
return (failCount == 0 && skipCount == 0);
}
void add(FlakyCaseResult r) {
String n = r.getSimpleName(), sn = safe(n);
FlakyClassResult c = getClassResult(sn);
if (c == null) {
classes.put(sn, c = new FlakyClassResult(this, n));
}
c.add(r);
duration += r.getDuration();
}
/**
* Recount my children
*/
@Override
public void tally() {
passCount = 0;
failCount = 0;
skipCount = 0;
flakeCount = 0;
duration = 0;
for (FlakyClassResult cr : classes.values()) {
cr.tally();
passCount += cr.getPassCount();
failCount += cr.getFailCount();
skipCount += cr.getSkipCount();
flakeCount += cr.getFlakeCount();
duration += cr.getDuration();
}
}
void freeze() {
passCount = failCount = skipCount = flakeCount = 0;
for (FlakyClassResult cr : classes.values()) {
cr.freeze();
passCount += cr.getPassCount();
failCount += cr.getFailCount();
flakeCount += cr.getFlakeCount();
skipCount += cr.getSkipCount();
}
}
public int compareTo(FlakyPackageResult that) {
return this.packageName.compareTo(that.packageName);
}
public String getDisplayName() {
return TestNameTransformer.getTransformedName(packageName);
}
}