FlowNode head = new FlowEndNode(this, iotaStr(), (FlowStartNode)startNodes.pop(), result, getCurrentHeads().toArray(new FlowNode[0])); if (outcome.isFailure()) { head.addAction(new ErrorAction(outcome.getAbnormal()));
FlowNode end = new FlowEndNode(this, iotaStr(), (FlowStartNode) startNodes.pop(), result, getCurrentHeads().toArray(new FlowNode[0])); end.addAction(new ErrorAction(failureReason)); head.setNewHead(end);
@Override public void evaluate() throws Throwable { rebuildContext(story.j); // make sure we are still running two heads assert e.getCurrentHeads().size()==3; assert b.isBuilding(); // we let one branch go at a time for (String branch : asList("A", "B")) { SemaphoreStep.success("suspend" + branch + "/1", null); waitForWorkflowToSuspend(); // until all execution joins into one, we retain all heads assert e.getCurrentHeads().size() == 3; assert b.isBuilding(); } // when we let the last one go, it will now run till the completion SemaphoreStep.success("suspendC/1", null); story.j.waitForCompletion(b); // make sure all the three branches have executed to the end. for (String branch : asList("A", "B", "C")) { story.j.assertLogContains(branch + " done", b); } // check the shape of the graph buildTable(); shouldHaveSteps(SemaphoreStep.DescriptorImpl.class, 3); shouldHaveSteps(EchoStep.DescriptorImpl.class, 3); shouldHaveParallelStepsInTheOrder("a","b","c"); } });
static void assertCleanInProgress(WorkflowRun run) throws Exception { Assert.assertTrue(run.isBuilding()); Assert.assertNull(run.getResult()); FlowExecution fe = run.getExecution(); AtomicBoolean hasExecutionInList = new AtomicBoolean(false); FlowExecutionList.get().forEach(f -> { if (fe != null && f == fe) { hasExecutionInList.set(true); } }); if (!hasExecutionInList.get()) { Assert.fail("Build completed but should still show in FlowExecutionList"); } CpsFlowExecution cpsExec = (CpsFlowExecution)fe; Assert.assertFalse(cpsExec.isComplete()); Assert.assertEquals(Boolean.FALSE, cpsExec.done); Assert.assertFalse(cpsExec.getCurrentHeads().get(0) instanceof FlowEndNode); Assert.assertTrue(cpsExec.startNodes != null && !cpsExec.startNodes.isEmpty()); }
/** Verifies all the assumptions about a cleanly finished build. */ static void assertCompletedCleanly(WorkflowRun run) throws Exception { while (run.isBuilding()) { Thread.sleep(100); // TODO seems to be unpredictable } Assert.assertNotNull(run.getResult()); FlowExecution fe = run.getExecution(); FlowExecutionList.get().forEach(f -> { if (fe != null && f == fe) { Assert.fail("FlowExecution still in FlowExecutionList!"); } }); Assert.assertTrue("Queue not empty after completion!", Queue.getInstance().isEmpty()); if (fe instanceof CpsFlowExecution) { CpsFlowExecution cpsExec = (CpsFlowExecution)fe; Assert.assertTrue(cpsExec.isComplete()); Assert.assertEquals(Boolean.TRUE, cpsExec.done); Assert.assertEquals(1, cpsExec.getCurrentHeads().size()); Assert.assertTrue(cpsExec.isComplete()); Assert.assertTrue(cpsExec.getCurrentHeads().get(0) instanceof FlowEndNode); Assert.assertTrue(cpsExec.startNodes == null || cpsExec.startNodes.isEmpty()); while (cpsExec.blocksRestart()) { Thread.sleep(100); // TODO ditto } } else { System.out.println("WARNING: no FlowExecutionForBuild"); } }
@Override public void evaluate() throws Throwable { p = jenkins().createProject(WorkflowJob.class, "demo"); p.setDefinition(new CpsFlowDefinition(join( "node {", " parallel(", " a: { semaphore 'suspendA'; echo 'A done' },", " b: { semaphore 'suspendB'; echo 'B done' },", " c: { semaphore 'suspendC'; echo 'C done' },", " )", "}" ), false)); startBuilding(); // let the workflow run until all parallel branches settle SemaphoreStep.waitForStart("suspendA/1", b); SemaphoreStep.waitForStart("suspendB/1", b); SemaphoreStep.waitForStart("suspendC/1", b); assert !e.isComplete(); assert e.getCurrentHeads().size()==3; assert b.isBuilding(); buildTable(); shouldHaveParallelStepsInTheOrder("a","b","c"); } });
assertEquals("Expecting 3 heads for 3 branches", 3,e.getCurrentHeads().size());