diff --git a/pom.xml b/pom.xml index 70957c4..989c982 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.plugins plugin - 4.88 + 5.7 @@ -34,8 +34,8 @@ 999999-SNAPSHOT - 2.452 - ${jenkins.baseline}.4 + 2.479 + ${jenkins.baseline}.1 jenkinsci/${project.artifactId}-plugin @@ -57,7 +57,7 @@ io.jenkins.tools.bom bom-${jenkins.baseline}.x - 3850.vb_c5319efa_e29 + 4136.vca_c3202a_7fd1 import pom diff --git a/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StageChunkFinder.java b/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StageChunkFinder.java index c199d76..658be1e 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StageChunkFinder.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StageChunkFinder.java @@ -32,8 +32,7 @@ public boolean isChunkStart(@NonNull FlowNode current, @CheckForNull FlowNode pr return false; } else if (current instanceof BlockEndNode) { return false; - } else if (current instanceof StepStartNode) { - StepStartNode startNode = (StepStartNode)current; + } else if (current instanceof StepStartNode startNode) { if (!(startNode.getDescriptor() instanceof StageStep.DescriptorImpl)) { return false; } @@ -46,9 +45,9 @@ public boolean isChunkStart(@NonNull FlowNode current, @CheckForNull FlowNode pr @Override public boolean isChunkEnd(@NonNull FlowNode current, @CheckForNull FlowNode previous) { // First a block-scoped stage - if (current instanceof StepEndNode && ((StepEndNode) current).getDescriptor() instanceof StageStep.DescriptorImpl) { + if (current instanceof StepEndNode stepEndNode && stepEndNode.getDescriptor() instanceof StageStep.DescriptorImpl) { // We have to look for the labelaction because block-scoped stage creates two nested blocks - return ((StepEndNode) current).getStartNode().getAction(LabelAction.class) != null; + return stepEndNode.getStartNode().getAction(LabelAction.class) != null; } // Then a marker-scoped stage if (previous != null) { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StatusAndTiming.java b/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StatusAndTiming.java index f7f5609..8550140 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StatusAndTiming.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StatusAndTiming.java @@ -167,14 +167,14 @@ public static boolean isPendingInput(WorkflowRun run) { // Logic borrowed from Pipeline Stage View plugin, RuneEx InputAction inputAction = run.getAction(InputAction.class); if (inputAction != null) { - List executions = null; + List executions; try { executions = inputAction.getExecutions(); - } catch (InterruptedException|TimeoutException ex) { + } catch (InterruptedException | TimeoutException ex) { // Retry on timeout try { executions = inputAction.getExecutions(); - } catch (InterruptedException|TimeoutException ex2) { + } catch (InterruptedException | TimeoutException ex2) { // Assume we can't handle it, and default to most common state of not being an input step. executions = null; } @@ -210,8 +210,7 @@ public static GenericStatus computeChunkStatus2(@NonNull WorkflowRun run, @NonNu if (exec == null) { return null; } - if (chunk instanceof ParallelMemoryFlowChunk) { - ParallelMemoryFlowChunk par = ((ParallelMemoryFlowChunk) chunk); + if (chunk instanceof ParallelMemoryFlowChunk par) { return condenseStatus(computeBranchStatuses2(run, par).values()); } else { return computeChunkStatus2(run, chunk.getNodeBefore(), chunk.getFirstNode(), chunk.getLastNode(), chunk.getNodeAfter()); @@ -295,8 +294,8 @@ public static GenericStatus computeChunkStatus2(@NonNull WorkflowRun run, ErrorAction err = lastNode.getError(); if (err != null) { Throwable rootCause = err.getError(); - if (rootCause instanceof FlowInterruptedException) { - return GenericStatus.fromResult(((FlowInterruptedException) rootCause).getResult()); + if (rootCause instanceof FlowInterruptedException flowInterruptedException) { + return GenericStatus.fromResult(flowInterruptedException.getResult()); } else { return GenericStatus.FAILURE; } @@ -378,8 +377,8 @@ public static TimingInfo computeChunkTiming(@NonNull WorkflowRun run, long inter // Fudge boolean isLastChunk = after == null || exec.isCurrentHead(lastNode); if (isLastChunk && run.isBuilding()) { - if (exec.getCurrentHeads().size() > 1 && lastNode instanceof BlockEndNode) { // Check to see if all the action is on other branches - BlockStartNode start = ((BlockEndNode)lastNode).getStartNode(); + if (exec.getCurrentHeads().size() > 1 && lastNode instanceof BlockEndNode blockEndNode) { // Check to see if all the action is on other branches + BlockStartNode start = blockEndNode.getStartNode(); if (start.getAction(ThreadNameAction.class) != null) { endTime = TimingAction.getStartTime(lastNode); // Completed parallel branch, use the block end time } @@ -453,7 +452,7 @@ public static Map computeParallelBranchTimings(@NonNull Work for (int i=0; i computeBranchStatuses2(@NonNull Workflo for (int i=0; i sorted = scanner.filteredNodes(exec.getCurrentHeads(), Predicates.alwaysTrue()); - sorted.sort(new Comparator() { + sorted.sort(new Comparator<>() { @Override public int compare(FlowNode node1, FlowNode node2) { int node1Iota = parseIota(node1); @@ -625,8 +624,8 @@ private int parseIota(FlowNode node) { formatted.append(')'); } formatted.append(node.getClass().getSimpleName()).append(' ').append(node.getDisplayName()); - if (node instanceof BlockEndNode) { - formatted.append(" [st=").append(((BlockEndNode)node).getStartNode().getId()).append(']'); + if (node instanceof BlockEndNode blockEndNode) { + formatted.append(" [st=").append(blockEndNode.getStartNode().getId()).append(']'); } if (showActions) { for (Action a : node.getActions()) { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/TimingInfo.java b/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/TimingInfo.java index 2e991ea..9f3edab 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/TimingInfo.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/TimingInfo.java @@ -36,7 +36,7 @@ public TimingInfo(long totalDurationMillis, long pauseDurationMillis, long start this.startTimeMillis = startTimeMillis; } - public TimingInfo(){ + public TimingInfo() { // Basic constructor } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StageTest.java b/src/test/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StageTest.java index b617f3e..da79daa 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StageTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StageTest.java @@ -28,7 +28,7 @@ public class StageTest { public JenkinsRule jenkinsRule = new JenkinsRule(); public static class CollectingChunkVisitor extends StandardChunkVisitor { - Deque allChunks = new ArrayDeque<>(); + final Deque allChunks = new ArrayDeque<>(); public List getChunks() { return new ArrayList<>(allChunks); @@ -69,20 +69,20 @@ private static void assertChunkBoundary(FlowChunkWithContext chunk, int beforeId public void testBlockStage() throws Exception { WorkflowJob job = jenkinsRule.jenkins.createProject(WorkflowJob.class, "Blocky job"); - job.setDefinition(new CpsFlowDefinition("" + - "node {" + - " stage ('Build') { " + - " echo ('Building'); " + - " } \n" + - " stage ('Test') { " + - " echo ('Testing'); " + - " } \n" + - " stage ('Deploy') { " + - " writeFile file: 'file.txt', text:'content'; " + - " archive(includes: 'file.txt'); " + - " echo ('Deploying'); " + - " } \n" + - "}", true)); + job.setDefinition(new CpsFlowDefinition(""" + node { + stage ('Build') { + echo ('Building') + } + stage ('Test') { + echo ('Testing') + } + stage ('Deploy') { + writeFile file: 'file.txt', text:'content' + archive(includes: 'file.txt') + echo ('Deploying') + } + }""", true)); /* * Node dump follows, format: [ID]{parent,ids} flowNodeClassName stepDisplayName [st=startId if a block node] diff --git a/src/test/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StatusAndTimingTest.java b/src/test/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StatusAndTimingTest.java index 1ff2787..bc7e712 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StatusAndTimingTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/StatusAndTimingTest.java @@ -70,9 +70,9 @@ import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; public class StatusAndTimingTest { @@ -91,7 +91,7 @@ private static FlowNode[] getNodes(FlowExecution exec, int[] ids) throws IOExcep } @Test - public void testStatusCoercion() throws Exception { + public void testStatusCoercion() { // Test that we don't modify existing statuses for (GenericStatus st : StatusAndTiming.API_V1.getAllowedStatuses()) { Assert.assertEquals(st, StatusAndTiming.coerceStatusApi(st, StatusAndTiming.API_V1)); @@ -114,11 +114,12 @@ private static long doTiming(FlowExecution exec, int firstNodeId, int nodeAfterE @Test public void testBasicPass() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "Passes"); - job.setDefinition(new CpsFlowDefinition("" + - "sleep 1 \n" + - "echo 'first stage' \n" + - "sleep 1 \n" + - "echo 'done' \n", true)); + job.setDefinition(new CpsFlowDefinition(""" + sleep 1 + echo 'first stage' + sleep 1 + echo 'done' + """, true)); /* Node dump follows, format: [ID]{parent,ids}(millisSinceStartOfRun) flowClassName displayName [st=startId if a block node] @@ -153,7 +154,7 @@ public void testBasicPass() throws Exception { status = StatusAndTiming.computeChunkStatus2(run, n[0], n[1], n[4], n[5]); timing = StatusAndTiming.computeChunkTiming(run, 2, n[1], n[4], n[5]); assertEquals(GenericStatus.SUCCESS, status); - assertEquals(timing.getPauseDurationMillis(), 2); + assertEquals(2, timing.getPauseDurationMillis()); assertEquals(TimingAction.getStartTime(n[5]) - TimingAction.getStartTime(n[1]), timing.getTotalDurationMillis()); // Whole flow @@ -186,11 +187,12 @@ public void testBasicPass() throws Exception { @Test public void testFail() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "Fails"); - job.setDefinition(new CpsFlowDefinition("" + - "sleep 1 \n" + - "echo 'first stage' \n" + - "sleep 1 \n" + - "error('fails') \n", true)); + job.setDefinition(new CpsFlowDefinition(""" + sleep 1 + echo 'first stage' + sleep 1 + error('fails') + """, true)); /* Node dump follows, format: [ID]{parent,ids} flowClassName displayName [st=startId if a block node] Action format: @@ -234,12 +236,12 @@ public void testFail() throws Exception { @Test public void testBasicParallelFail() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "Fails"); - job.setDefinition(new CpsFlowDefinition("" + - "echo 'primero stage'\n" + - "def branches = ['failFast': false]\n" + - "branches['success'] = {sleep 1; echo 'succeed'}\n" + - "branches['fail'] = {error('autofail');}\n" + - "parallel branches", true)); + job.setDefinition(new CpsFlowDefinition(""" + echo 'primero stage' + def branches = ['failFast': false] + branches['success'] = {sleep 1; echo 'succeed'} + branches['fail'] = {error('autofail')} + parallel branches""", true)); /* * Node dump from a run follows, format: @@ -340,11 +342,12 @@ public void testBasicParallelFail() throws Exception { @Test public void testInProgress() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "Fails"); - job.setDefinition(new CpsFlowDefinition("" + - "sleep 1 \n" + - "echo 'first stage' \n" + - "sleep 1 \n" + - "semaphore('wait') \n", true)); + job.setDefinition(new CpsFlowDefinition(""" + sleep 1 + echo 'first stage' + sleep 1 + semaphore('wait') + """, true)); WorkflowRun run = job.scheduleBuild2(0).getStartCondition().get(); SemaphoreStep.waitForStart("wait/1", run); FlowExecution exec = run.getExecution(); @@ -362,10 +365,10 @@ public void testInProgress() throws Exception { @Test public void timingTest() throws Exception { // Problem here: for runs in progress we should be using current time if they're the last run node, aka the in-progress node - String jobScript = ""+ - "echo 'first stage'\n" + - "parallel 'long' : { sleep 30; }, \n" + - " 'short': { sleep 2; }"; + String jobScript = """ + echo 'first stage' + parallel 'long' : {sleep 30}, + 'short': {sleep 2}"""; // This must be amateur science fiction because the exposition for the setting goes on FOREVER ForkScanner scan = new ForkScanner(); @@ -398,12 +401,12 @@ public void timingTest() throws Exception { @Test public void testInProgressParallel() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "Fails"); - job.setDefinition(new CpsFlowDefinition("" + - "echo 'primero stage'\n" + - "def branches = ['failFast': false]\n" + - "branches['success'] = {echo 'succeed'}\n" + - "branches['pause'] = { sleep 1; semaphore 'wait'; }\n" + - "parallel branches", true)); + job.setDefinition(new CpsFlowDefinition(""" + echo 'primero stage' + def branches = ['failFast': false] + branches['success'] = {echo 'succeed'} + branches['pause'] = {sleep 1; semaphore 'wait'} + parallel branches""", true)); /* * Node dump follows, format: [ID]{parentIds,...} flowNodeClassName displayName [st=startId if a block node] @@ -552,12 +555,14 @@ public void busyStepTest() throws Exception { @Test public void queuedAndRunningOnAgent() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "queuedAndRunning"); - job.setDefinition(new CpsFlowDefinition("stage('some-stage') {\n" + - " node('test') {\n" + - " echo 'hello'\n" + - " semaphore 'wait'\n" + - " }\n" + - "}\n", true)); + job.setDefinition(new CpsFlowDefinition(""" + stage('some-stage') { + node('test') { + echo 'hello' + semaphore 'wait' + } + } + """, true)); WorkflowRun b1 = job.scheduleBuild2(0).waitForStart(); j.waitForMessage("Still waiting to schedule task", b1); @@ -591,12 +596,14 @@ public void queuedAndRunningOnAgent() throws Exception { @Test public void queuedAndCanceled() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "queuedAndCanceled"); - job.setDefinition(new CpsFlowDefinition("stage('some-stage') {\n" + - " node('test') {\n" + - " echo 'hello'\n" + - " semaphore 'wait'\n" + - " }\n" + - "}\n", true)); + job.setDefinition(new CpsFlowDefinition(""" + stage('some-stage') { + node('test') { + echo 'hello' + semaphore 'wait' + } + } + """, true)); WorkflowRun b1 = job.scheduleBuild2(0).waitForStart(); j.waitForMessage("Still waiting to schedule task", b1); @@ -660,21 +667,23 @@ public void queuedAndParallel() throws Exception { [25]{24}FlowEndNode End of Pipeline [st=2] ------------------------------------------------------------------------------------------ */ - job.setDefinition(new CpsFlowDefinition("stage('some-stage') {\n" + - " parallel(\n" + - " a: {\n" + - " node('second') {\n" + - " echo 'hello'\n" + - " semaphore 'wait-a'\n" + - " }\n" + - " },\n" + - " b: {\n" + - " node('first') {\n" + - " semaphore 'wait-b'\n" + - " }\n" + - " }\n" + - " )\n" + - "}\n", true)); + job.setDefinition(new CpsFlowDefinition(""" + stage('some-stage') { + parallel( + a: { + node('second') { + echo 'hello' + semaphore 'wait-a' + } + }, + b: { + node('first') { + semaphore 'wait-b' + } + } + ) + } + """, true)); WorkflowRun b1 = job.scheduleBuild2(0).waitForStart(); try { @@ -721,7 +730,7 @@ public void queuedAndParallel() throws Exception { SemaphoreStep.waitForStart("wait-a/1", b1); - // Now get the end nodes as of the entry of the semaphore on the a branch... + // Now get the end nodes as of the entry of the semaphore on the `a` branch... branchEndNodes = Arrays.asList(getNodes(execution, new int[]{18, 15})); statuses = StatusAndTiming.computeBranchStatuses2(b1, execution.getNode("5"), branchStartNodes, branchEndNodes, null); @@ -741,29 +750,30 @@ public void queuedAndParallel() throws Exception { @Issue("JENKINS-47219") public void parallelStagesOneSkipped() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "parallel stages, one skipped job"); - job.setDefinition(new CpsFlowDefinition("" + - "pipeline { \n" + - " agent any \n" + - " stages { \n" + - " stage('Run Tests') { \n"+ - " parallel { \n" + - " stage('Test on Windows') { \n" + - " when { \n" + - " branch 'cake' \n" + - " } \n"+ - " steps { \n"+ - " echo 'hello world' \n"+ - " } \n"+ - " } \n"+ - " stage('Test on Linux') { \n" + - " steps { \n"+ - " echo 'hello world' \n"+ - " } \n"+ - " } \n"+ - " } \n"+ - " } \n"+ - " } \n"+ - "} \n", + job.setDefinition(new CpsFlowDefinition(""" + pipeline { + agent any + stages { + stage('Run Tests') { + parallel { + stage('Test on Windows') { + when { + branch 'cake' + } + steps { + echo 'hello world' + } + } + stage('Test on Linux') { + steps { + echo 'hello world' + } + } + } + } + } + } + """, true)); WorkflowRun build = j.assertBuildStatusSuccess(job.scheduleBuild2(0)); @@ -778,13 +788,15 @@ public void parallelStagesOneSkipped() throws Exception { public void catchOutsideFailingStage() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "catchOutsideFailingStage"); job.setDefinition(new CpsFlowDefinition( - "try {\n" + - " stage('throws-error') {\n" + - " error('oops')\n" + - " }\n" + - "} catch(err) {\n" + - " echo('caught error')\n" + - "}\n", true)); + """ + try { + stage('throws-error') { + error('oops') + } + } catch(err) { + echo('caught error') + } + """, true)); WorkflowRun run = j.assertBuildStatusSuccess(job.scheduleBuild2(0)); StagesAndParallelBranchesVisitor visitor = new StagesAndParallelBranchesVisitor(run); assertEquals(1, visitor.chunks.size()); @@ -796,17 +808,18 @@ public void catchOutsideFailingStage() throws Exception { public void parallelFailFast() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "parallelFailFast"); job.setDefinition(new CpsFlowDefinition( - "parallel failFast: true,\n" + - " aborts: {\n" + - " sleep 5\n" + - " },\n" + - " fails: {\n" + - " sleep 1\n" + - " error('oops')\n" + - " },\n" + - " succeeds: {\n" + - " echo 'success'" + - " }", true)); + """ + parallel failFast: true, + aborts: { + sleep 5 + }, + fails: { + sleep 1 + error('oops') + }, + succeeds: { + echo 'success' + }""", true)); WorkflowRun run = j.assertBuildStatus(Result.FAILURE, job.scheduleBuild2(0)); StagesAndParallelBranchesVisitor visitor = new StagesAndParallelBranchesVisitor(run); assertEquals(3, visitor.chunks.size()); @@ -820,12 +833,14 @@ public void parallelFailFast() throws Exception { public void parallel() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "parallel"); job.setDefinition(new CpsFlowDefinition( - "parallel fails: {\n" + - " error('oops')\n" + - "},\n" + - "succeeds: {\n" + - " echo('succeeds')" + - "}\n", true)); + """ + parallel fails: { + error('oops') + }, + succeeds: { + echo('succeeds') + } + """, true)); WorkflowRun run = j.assertBuildStatus(Result.FAILURE, job.scheduleBuild2(0)); StagesAndParallelBranchesVisitor visitor = new StagesAndParallelBranchesVisitor(run); assertEquals(2, visitor.chunks.size()); @@ -838,18 +853,20 @@ public void parallel() throws Exception { public void unstableWithWarningAction() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "unstable"); job.setDefinition(new CpsFlowDefinition( - "stage('unstable') {\n" + - " echo('foo')\n" + - " unstable('oops')\n" + - " echo('foo')\n" + - "}\n" + - "stage('success') {\n" + - " echo('no problem')" + - "}\n" + - "stage('failure') {\n" + - " unstable('second oops')\n" + - " error('failure')\n" + - "}\n", true)); + """ + stage('unstable') { + echo('foo') + unstable('oops') + echo('foo') + } + stage('success') { + echo('no problem') + } + stage('failure') { + unstable('second oops') + error('failure') + } + """, true)); WorkflowRun run = j.assertBuildStatus(Result.FAILURE, job.scheduleBuild2(0)); StagesAndParallelBranchesVisitor visitor = new StagesAndParallelBranchesVisitor(run); assertEquals(3, visitor.chunks.size()); @@ -863,13 +880,15 @@ public void unstableWithWarningAction() throws Exception { public void unstableInBlockScopeStep() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "unstableInBlockScopeStep"); job.setDefinition(new CpsFlowDefinition( - "stage('unstable') {\n" + - " timeout(1) {\n" + - " timeout(1) {\n" + - " unstable('oops')\n" + - " }\n" + - " }\n" + - "}\n", true)); + """ + stage('unstable') { + timeout(1) { + timeout(1) { + unstable('oops') + } + } + } + """, true)); WorkflowRun run = j.assertBuildStatus(Result.UNSTABLE, job.scheduleBuild2(0)); StagesAndParallelBranchesVisitor visitor = new StagesAndParallelBranchesVisitor(run); assertEquals(1, visitor.chunks.size()); @@ -881,15 +900,17 @@ public void unstableInBlockScopeStep() throws Exception { public void catchErrorWithStageResult() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "catchErrorWithStageResult"); job.setDefinition(new CpsFlowDefinition( - "stage('failure') {\n" + - " catchError(stageResult: 'FAILURE') {\n" + - " error('oops')\n" + - " }\n" + - " unstable('failure takes priority')\n" + - "}\n" + - "stage('success') {\n" + - " echo('foo')" + - "}\n", true)); + """ + stage('failure') { + catchError(stageResult: 'FAILURE') { + error('oops') + } + unstable('failure takes priority') + } + stage('success') { + echo('foo') + } + """, true)); WorkflowRun run = j.assertBuildStatus(Result.FAILURE, job.scheduleBuild2(0)); StagesAndParallelBranchesVisitor visitor = new StagesAndParallelBranchesVisitor(run); assertEquals(2, visitor.chunks.size()); @@ -902,11 +923,13 @@ public void catchErrorWithStageResult() throws Exception { public void nestedStageParentStatus() throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "unstable"); job.setDefinition(new CpsFlowDefinition( - "stage('parent') {\n" + - " stage('child') {\n" + - " error('oops')\n" + - " }\n" + - "}\n", true)); + """ + stage('parent') { + stage('child') { + error('oops') + } + } + """, true)); WorkflowRun run = j.assertBuildStatus(Result.FAILURE, job.scheduleBuild2(0)); StagesAndParallelBranchesVisitor visitor = new StagesAndParallelBranchesVisitor(run); assertEquals(2, visitor.chunks.size()); @@ -919,7 +942,7 @@ public void nestedStageParentStatus() throws Exception { /** * Visitor that collects stages and parallel branches into chunks in a way similar to what * Blue Ocean does. - * + *

* This visitor should only be used for pipelines which have finished executing and for * which all stage and parallel branch names are unique. */