/** @deprecated use {@link JenkinsRule#waitForCompletion} instead */ public void waitForWorkflowToComplete() throws Exception { do { waitForWorkflowToSuspend(e); } while (!e.isComplete()); }
@Override public ListenableFuture<List<StepExecution>> getCurrentExecutions(final boolean innerMostOnly) { if (programPromise == null || isComplete()) { return Futures.immediateFuture(Collections.<StepExecution>emptyList());
if (programPromise == null || isComplete()) { return CpsThreadDump.EMPTY;
if (isComplete() || this.getDurabilityHint().isPersistWithEveryStep()) {
if (isComplete() || this.getDurabilityHint().isPersistWithEveryStep() || !isResumeBlocked()) { return;
if (isComplete()) { if (done == Boolean.TRUE && !super.isComplete()) { LOGGER.log(Level.INFO, "Completed flow without FlowEndNode: "+this+" heads:"+getHeadsAsString());
/** Waits until the build to resume or die. */ static void waitForBuildToResumeOrFail(WorkflowRun run) throws Exception { CpsFlowExecution execution = (CpsFlowExecution)(run.getExecution()); long nanoStartTime = System.nanoTime(); while (true) { if (!run.isBuilding()) { return; } long currentTime = System.nanoTime(); if (TimeUnit.SECONDS.convert(currentTime-nanoStartTime, TimeUnit.NANOSECONDS) > 10) { StringBuilder builder = new StringBuilder(); builder.append("Run result: "+run.getResult()); builder.append(" and execution != null:"+run.getExecution() != null+" "); FlowExecution exec = run.getExecution(); if (exec instanceof CpsFlowExecution) { CpsFlowExecution cpsFlow = (CpsFlowExecution)exec; builder.append(", FlowExecution is paused: "+cpsFlow.isPaused()) .append(", FlowExecution is complete: "+cpsFlow.isComplete()) .append(", FlowExecution result: "+cpsFlow.getResult()) .append(", FlowExecution PersistedClean: "+cpsFlow.persistedClean).append('\n'); } throw new TimeoutException("Build didn't resume or fail in a timely fashion. "+builder.toString()); } Thread.sleep(100L); } }
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 universal post-build cleanup was done, regardless of pass/fail state. */ static void verifyCompletedCleanly(Jenkins j, WorkflowRun run) throws Exception { // Assert that we have the appropriate flow graph entries FlowExecution exec = run.getExecution(); List<FlowNode> heads = exec.getCurrentHeads(); Assert.assertEquals(1, heads.size()); verifyNoTasksRunning(j); Assert.assertEquals(0, exec.getCurrentExecutions(false).get().size()); if (exec instanceof CpsFlowExecution) { CpsFlowExecution cpsFlow = (CpsFlowExecution)exec; assert cpsFlow.getStorage() != null; Assert.assertFalse("Should always be able to retrieve script", StringUtils.isEmpty(cpsFlow.getScript())); Assert.assertNull("We should have no Groovy shell left or that's a memory leak", cpsFlow.getShell()); Assert.assertNull("We should have no Groovy shell left or that's a memory leak", cpsFlow.getTrustedShell()); Assert.assertTrue(cpsFlow.done); assert cpsFlow.isComplete(); assert cpsFlow.heads.size() == 1; Map.Entry<Integer, FlowHead> finalHead = cpsFlow.heads.entrySet().iterator().next(); assert finalHead.getValue().get() instanceof FlowEndNode; Assert.assertEquals(cpsFlow.storage.getNode(finalHead.getValue().get().getId()), finalHead.getValue().get()); } verifyExecutionRemoved(run); }
/** 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"); } }
/** * I should be able to have DSL call into async step and then bring it to the completion. */ @Test public void suspendExecutionAndComeBack() throws Exception { CpsFlowDefinition flow = new CpsFlowDefinition("semaphore 'watch'\nprintln 'Yo'", false); // get this going... createExecution(flow); exec.start(); SemaphoreStep.waitForStart("watch/1", null); assertFalse("Expected the execution to be suspended but it has completed", exec.isComplete()); FlowExecutionOwner owner = exec.getOwner(); exec = roundtripXStream(exec); // poor man's simulation of Jenkins restart exec.onLoad(owner); // now resume workflow execution SemaphoreStep.success("watch/1", null); exec.waitForSuspension(); assertTrue(exec.isComplete()); }
/** * The code getting evaluated must also get sandbox transformed. */ @Test public void evaluateShallSandbox() throws Exception { CpsFlowDefinition flow = new CpsFlowDefinition("evaluate('Jenkins.getInstance()')", true); createExecution(flow); exec.start(); exec.waitForSuspension(); String msg = dumpError(); // execution should have failed with error, pointing that Jenkins.getInstance() is not allowed from sandbox assertTrue(exec.isComplete()); assertEquals(Result.FAILURE, exec.getResult()); assertTrue(msg, msg.contains("org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use staticMethod jenkins.model.Jenkins getInstance")); }
/** Need to be careful that internal method names in {@link CpsScript} are not likely identifiers in user scripts. */ @Test public void methodNameClash() throws Exception { CpsFlowDefinition flow = new CpsFlowDefinition("def build() {20}; def initialize() {10}; def env() {10}; def getShell() {2}; assert build() + initialize() + env() + shell == 42", false); createExecution(flow); exec.start(); while (!exec.isComplete()) { exec.waitForSuspension(); } assertEquals(dumpError(), Result.SUCCESS, exec.getResult()); }
/** * Test the 'evaluate' method call. * The first test case. */ @Test public void evaluate() throws Exception { CpsFlowDefinition flow = new CpsFlowDefinition("assert evaluate('1+2+3')==6", false); createExecution(flow); exec.start(); exec.waitForSuspension(); assertTrue(exec.isComplete()); assertEquals(dumpError(), Result.SUCCESS, exec.getResult()); }
@Issue("JENKINS-38167") @Test public void bindingDuringConstructor() throws Exception { CpsFlowDefinition flow = new CpsFlowDefinition("@groovy.transform.Field def opt = (binding.hasVariable('opt')) ? opt : 'default'", false); createExecution(flow); exec.start(); while (!exec.isComplete()) { exec.waitForSuspension(); } assertEquals(dumpError(), Result.SUCCESS, exec.getResult()); }
/** * The code getting evaluated must also get CPS transformation. */ @Ignore("TODO usually future == null, perhaps because CpsThread.resume is intended to be @CpsVmThreadOnly (so assumes that the promise is just set is not cleared by runNextChunk) yet we are calling it from the test thread; extremely dubious test design, should probably be using SemaphoreStep to be more realistic") @Test public void evaluateShallBeCpsTransformed() throws Exception { CpsFlowDefinition flow = new CpsFlowDefinition("evaluate('1+com.cloudbees.groovy.cps.Continuable.suspend(2+3)')", false); createExecution(flow); exec.start(); exec.waitForSuspension(); // TODO: can't we assert that the suspend() ended with value 5? // this should have paused at suspend, so we are going to resume it by having it return a value we control assertFalse(dumpError(), exec.isComplete()); ListenableFuture<CpsThreadGroup> pp = exec.programPromise; assertNotNull(pp); Future<Object> future = pp.get().getThread(0).resume(new Outcome(7,null)); assertNotNull(future); assertEquals(8, future.get()); exec.waitForSuspension(); assertTrue(dumpError(), exec.isComplete()); assertEquals(dumpError(), Result.SUCCESS, exec.getResult()); }
@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"); } });
static void verifyFailedCleanly(Jenkins j, WorkflowRun run) throws Exception { if (run.isBuilding()) { // Give the run a little bit of time to see if it can resume or not FlowExecution exec = run.getExecution(); if (exec instanceof CpsFlowExecution) { waitForBuildToResumeOrFail(run); } else { Thread.sleep(4000L); } } if (run.getExecution() instanceof CpsFlowExecution) { CpsFlowExecution cfe = (CpsFlowExecution)(run.getExecution()); assert cfe.isComplete() || (cfe.programPromise != null && cfe.programPromise.isDone()); } assert !run.isBuilding(); if (run.getExecution() instanceof CpsFlowExecution) { Assert.assertEquals(Result.FAILURE, ((CpsFlowExecution) run.getExecution()).getResult()); } Assert.assertEquals(Result.FAILURE, run.getResult()); assert !run.isBuilding(); // TODO verify all blocks cleanly closed out, so Block start and end nodes have same counts and FlowEndNode is last node verifyCompletedCleanly(j, run); }
CpsFlowExecution exec = (CpsFlowExecution) owner.getOrNull(); assertNotNull(exec); assertFalse(exec.isComplete()); fail("not sure why CpsThreadDumpAction was missing here");