Skip to content

Commit

Permalink
Merge pull request #7 from dcendents/jenkins-50050
Browse files Browse the repository at this point in the history
JENKINS-50050 Add support for pipeline test result details
  • Loading branch information
olivergondza committed Apr 5, 2018
2 parents a16781c + 9ca2434 commit b929d69
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 3 deletions.
10 changes: 8 additions & 2 deletions pom.xml
Expand Up @@ -64,7 +64,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>junit</artifactId>
<version>1.21</version>
<version>1.24</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand All @@ -80,7 +80,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-api</artifactId>
<version>2.20</version>
<version>2.22</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
Expand Down Expand Up @@ -131,6 +131,12 @@
<version>2.16</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>pipeline-stage-step</artifactId>
<version>2.3</version>
<scope>test</scope>
</dependency>
<!-- upper bound deps -->
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand Down
Expand Up @@ -29,12 +29,16 @@
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.Descriptor;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.tasks.junit.JUnitResultArchiver;
import hudson.tasks.junit.TestDataPublisher;
import hudson.tasks.junit.TestResult;
import hudson.tasks.junit.TestResultAction;
import hudson.tasks.junit.pipeline.JUnitResultsStepExecution;
import hudson.tasks.test.PipelineTestDetails;

import java.util.Collections;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -193,8 +197,24 @@ private void finished(StepContext context, boolean success) throws Exception {
}
TaskListener listener = context.get(TaskListener.class);
try {
FilePath workspace = context.get(FilePath.class);
workspace.mkdirs();
Launcher launcher = context.get(Launcher.class);
FlowNode node = context.get(FlowNode.class);

List<FlowNode> enclosingBlocks = JUnitResultsStepExecution.getEnclosingStagesAndParallels(node);

PipelineTestDetails pipelineTestDetails = new PipelineTestDetails();
pipelineTestDetails.setNodeId(id);
pipelineTestDetails.setEnclosingBlocks(JUnitResultsStepExecution.getEnclosingBlockIds(enclosingBlocks));
pipelineTestDetails.setEnclosingBlockNames(JUnitResultsStepExecution.getEnclosingBlockNames(enclosingBlocks));

// TODO might block CPS VM thread. Not trivial to solve: JENKINS-43276
archiver.perform(r, context.get(FilePath.class), context.get(Launcher.class), listener);
TestResultAction action = JUnitResultArchiver.parseAndAttach(archiver, pipelineTestDetails, r, workspace, launcher, listener);

if (action != null && action.getResult().getFailCount() > 0) {
r.setResult(Result.UNSTABLE);
}
} catch (Exception x) {
if (provisional != null) {
listener.getLogger().println("Final archiving failed; recording " + provisional.getTotalCount() + " provisional test results.");
Expand Down
Expand Up @@ -25,10 +25,21 @@

import hudson.model.Result;
import hudson.slaves.DumbSlave;
import hudson.tasks.junit.TestResult;
import hudson.tasks.junit.TestResultAction;
import java.util.Collections;
import java.util.logging.Level;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.jenkinsci.plugins.workflow.actions.LabelAction;
import org.jenkinsci.plugins.workflow.actions.ThreadNameAction;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.graphanalysis.DepthFirstScanner;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.steps.StepConfigTester;
Expand All @@ -42,6 +53,8 @@
import org.jvnet.hudson.test.LoggerRule;
import org.jvnet.hudson.test.RestartableJenkinsRule;

import com.google.common.base.Predicate;

public class RealtimeJUnitStepTest {

@ClassRule
Expand Down Expand Up @@ -175,6 +188,68 @@ public void evaluate() throws Throwable {
});
}

@Test
public void testResultDetails() {
rr.addStep(new Statement() {
@Override
public void evaluate() throws Throwable {
WorkflowJob p = rr.j.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"node {\n" +
" stage('stage1') {\n" +
" realtimeJUnit('a.xml') {\n" +
" writeFile text: '''<testsuite name='a'><testcase name='a1'/><testcase name='a2'/></testsuite>''', file: 'a.xml'\n" +
" }\n" +
" }\n" +
" stage('stage2') {\n" +
" realtimeJUnit('b.xml') {\n" +
" writeFile text: '''<testsuite name='b'><testcase name='b1'/><testcase name='b2'><error>b2 failed</error></testcase></testsuite>''', file: 'b.xml'\n" +
" }\n" +
" }\n" +
"}", true));
WorkflowRun b1 = rr.j.assertBuildStatus(Result.UNSTABLE, p.scheduleBuild2(0).get());
TestResultAction a = b1.getAction(TestResultAction.class);
assertNotNull(a);
assertEquals(4, a.getTotalCount());
assertEquals(1, a.getFailCount());
assertEquals(Collections.emptyList(), b1.getActions(AbstractRealtimeTestResultAction.class));

FlowExecutionOwner owner = b1.asFlowExecutionOwner();
FlowExecution execution = owner.getOrNull();
DepthFirstScanner scanner = new DepthFirstScanner();

FlowNode stage1Id = scanner.findFirstMatch(execution, new BlockNamePredicate("stage1"));
TestResult stage1Results = a.getResult().getResultForPipelineBlock(stage1Id.getId());
assertNotNull(stage1Results);
assertEquals(2, stage1Results.getTotalCount());
assertEquals(0, stage1Results.getFailCount());

FlowNode stage2Id = scanner.findFirstMatch(execution, new BlockNamePredicate("stage2"));
TestResult stage2Results = a.getResult().getResultForPipelineBlock(stage2Id.getId());
assertNotNull(stage2Results);
assertEquals(2, stage2Results.getTotalCount());
assertEquals(1, stage2Results.getFailCount());
}
});
}

private static class BlockNamePredicate implements Predicate<FlowNode> {
private final String blockName;
public BlockNamePredicate(@Nonnull String blockName) {
this.blockName = blockName;
}
@Override
public boolean apply(@Nullable FlowNode input) {
if (input != null) {
LabelAction labelAction = input.getPersistentAction(LabelAction.class);
if (labelAction != null && labelAction instanceof ThreadNameAction) {
return blockName.equals(((ThreadNameAction) labelAction).getThreadName());
}
return labelAction != null && blockName.equals(labelAction.getDisplayName());
}
return false;
}
}
// TODO test distinct parallel / repeated archiving

}

0 comments on commit b929d69

Please sign in to comment.