Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass task listener to JUnit parsing #18

Merged
merged 1 commit into from
Dec 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ Publishes test results while tests run, rather than waiting for completion like
### Pipeline

```groovy
realtimeJUnit('target/test-results/*.xml') {
sh 'mvn verify'
realtimeJUnit('**/target/surefire-reports/TEST-*.xml') {
sh "mvn -Dmaven.test.failure.ignore=true clean package"
timja marked this conversation as resolved.
Show resolved Hide resolved
}
```

Expand Down
1 change: 0 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>junit</artifactId>
<version>1.51</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,22 @@

import hudson.AbortException;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.TaskListener;
import hudson.tasks.junit.JUnitParser;
import hudson.tasks.junit.TestResult;
import hudson.tasks.junit.pipeline.JUnitResultsStepExecution;
import hudson.tasks.test.PipelineTestDetails;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jenkinsci.plugins.workflow.FilePathUtils;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.steps.StepContext;

import static java.util.Objects.requireNonNull;

class PipelineRealtimeTestResultAction extends AbstractRealtimeTestResultAction {

Expand All @@ -42,13 +52,21 @@ class PipelineRealtimeTestResultAction extends AbstractRealtimeTestResultAction
private final String workspace;
private final boolean keepLongStdio;
private final String glob;
private final StepContext context;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just make sure this is not serialized to build.xml!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not appear to be:

    <org.jenkinsci.plugins.junitrealtimetestreporter.PipelineRealtimeTestResultAction plugin="junit-realtime-test-reporter@999999-SNAPSHOT">
      <descriptions class="concurrent-hash-map"/>
      <id>14</id>
      <node></node>
      <workspace>/Users/timja/code/jenkins/junit-realtime-test-reporter-plugin/work/workspace/junit-attachments-test_PR-8@2</workspace>
      <keepLongStdio>false</keepLongStdio>
      <glob>**/target/surefire-reports/TEST-*.xml</glob>
    </org.jenkinsci.plugins.junitrealtimetestreporter.PipelineRealtimeTestResultAction>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, but PipelineRealtimeTestResultAction is? Then this looks dangerous. Suggest making it transient and studying the conditions under which it might be saved.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, but PipelineRealtimeTestResultAction is? Then this looks dangerous. Suggest making it transient and studying the conditions under which it might be saved.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty results, the Junit plugin assumes it gets a non null task listener and it logs something when there's no results I think (although probably isn't annotating the methods as it's not really expected to have these methods called outside of the plugin).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Launcher is never used. The TaskListener you could perhaps replace with https://javadoc.jenkins.io/plugin/workflow-api/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.html#getListener--; not ideal but it is only used in this corner case anyway.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look tomorrow, how do I get to the FlowExecutionOwner?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checkcast Run to FEO.Executable

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #27


PipelineRealtimeTestResultAction(String id, FilePath ws, boolean keepLongStdio, String glob) {
PipelineRealtimeTestResultAction(
String id,
FilePath ws,
boolean keepLongStdio,
String glob,
StepContext context
) {
this.id = id;
node = FilePathUtils.getNodeName(ws);
workspace = ws.getRemote();
this.keepLongStdio = keepLongStdio;
this.glob = glob;
this.context = context;
}

@Override
Expand All @@ -71,7 +89,19 @@ protected TestResult parse() throws IOException, InterruptedException {
FilePath ws = FilePathUtils.find(node, workspace);
if (ws != null && ws.isDirectory()) {
LOGGER.log(Level.FINE, "parsing {0} in {1} on node {2} for {3}", new Object[] {glob, workspace, node, run});
return new JUnitParser(keepLongStdio, true).parseResult(glob, run, ws, null, null);

FlowNode node = context.get(FlowNode.class);
List<FlowNode> enclosingBlocks = JUnitResultsStepExecution
.getEnclosingStagesAndParallels(requireNonNull(node));

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

return new JUnitParser(keepLongStdio, true)
.parseResult(glob, run, pipelineTestDetails, ws,
context.get(Launcher.class), context.get(TaskListener.class));
} else {
throw new AbortException("skipping parse in nonexistent workspace for " + run);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand All @@ -61,6 +62,8 @@
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import static java.util.Objects.requireNonNull;

public class RealtimeJUnitStep extends Step {

private static final Logger LOGGER = Logger.getLogger(RealtimeJUnitStep.class.getName());
Expand Down Expand Up @@ -151,11 +154,20 @@ static class Execution extends StepExecution {

@Override
public boolean start() throws Exception {
Run<?, ?> r = getContext().get(Run.class);
String id = getContext().get(FlowNode.class).getId();
r.addAction(new PipelineRealtimeTestResultAction(id, getContext().get(FilePath.class), archiver.isKeepLongStdio(), archiver.getTestResults()));
StepContext context = getContext();
Run<?, ?> r = context.get(Run.class);
FlowNode flowNode = context.get(FlowNode.class);
String id = requireNonNull(flowNode).getId();
requireNonNull(r).addAction(new PipelineRealtimeTestResultAction(
id,
context.get(FilePath.class),
archiver.isKeepLongStdio(),
archiver.getTestResults(),
context
)
);
AbstractRealtimeTestResultAction.saveBuild(r);
getContext().newBodyInvoker().withCallback(new Callback(id, archiver)).start();
context.newBodyInvoker().withCallback(new Callback(id, archiver)).start();
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@

PerJobConfiguration.visualize_test_results_in_real_time=Visualize test results in real time
PipelineRealtimeTestResultAction.realtime_test_result_on_=Realtime Test Result on {0}
PipelineRealtimeTestResultAction.realtime_test_result_on_master=Realtime Test Result on Master
PipelineRealtimeTestResultAction.realtime_test_result_on_master=Realtime Test Result on Controller