@CheckForNull protected Result getExecutionResult(@Nonnull WorkflowRun r) { FlowExecution execution = r.getExecution(); if (execution instanceof CpsFlowExecution) { return ((CpsFlowExecution) execution).getResult(); } else { return r.getResult(); } }
/** 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 assertResultMatchExecutionAndRun(WorkflowRun run, Result[] executionAndBuildResult) throws Exception { Assert.assertEquals(executionAndBuildResult[0], ((CpsFlowExecution)(run.getExecution())).getResult()); Assert.assertEquals(executionAndBuildResult[1], run.getResult()); }
/** * 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()); }
/** Simulates something happening badly during final shutdown, which may cause build to not appear done. */ @Test public void completedFinalFlowNodeNotPersisted() throws Exception { final int[] build = new int[1]; final Result[] executionAndBuildResult = new Result[2]; story.thenWithHardShutdown( j -> { WorkflowRun run = runBasicBuild(j, DEFAULT_JOBNAME, build); String finalId = run.getExecution().getCurrentHeads().get(0).getId(); // Hack but deletes the file from disk CpsFlowExecution cpsExec = (CpsFlowExecution)(run.getExecution()); File f = new File(cpsExec.getStorageDir(), finalId+".xml"); f.delete(); executionAndBuildResult[0] = ((CpsFlowExecution)(run.getExecution())).getResult(); executionAndBuildResult[1] = run.getResult(); }); story.then(j-> { WorkflowJob r = j.jenkins.getItemByFullName(DEFAULT_JOBNAME, WorkflowJob.class); WorkflowRun run = r.getBuildByNumber(build[0]); assertCompletedCleanly(run); // assertNulledExecution(run); Assert.assertEquals(Result.SUCCESS, run.getResult()); assertResultMatchExecutionAndRun(run, executionAndBuildResult); }); } /** Perhaps there was a serialization error breaking the FlowGraph persistence for non-durable mode. */
/** Perhaps there was a serialization error breaking the FlowGraph persistence for non-durable mode. */ @Test public void completedNoNodesPersisted() throws Exception { final int[] build = new int[1]; final Result[] executionAndBuildResult = new Result[2]; story.thenWithHardShutdown( j -> { WorkflowRun run = runBasicBuild(j, DEFAULT_JOBNAME, build); FileUtils.deleteDirectory(((CpsFlowExecution)(run.getExecution())).getStorageDir()); executionAndBuildResult[0] = ((CpsFlowExecution)(run.getExecution())).getResult(); executionAndBuildResult[1] = run.getResult(); }); story.then(j-> { WorkflowJob r = j.jenkins.getItemByFullName(DEFAULT_JOBNAME, WorkflowJob.class); WorkflowRun run = r.getBuildByNumber(build[0]); assertCompletedCleanly(run); // assertNulledExecution(run); Assert.assertEquals(Result.SUCCESS, run.getResult()); assertResultMatchExecutionAndRun(run, executionAndBuildResult); }); }
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); }
/** * 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()); }
/** Simulates case where done flag was not persisted. */ @Test public void completedButWrongDoneStatus() throws Exception { final int[] build = new int[1]; final Result[] executionAndBuildResult = new Result[2]; story.thenWithHardShutdown( j -> { WorkflowRun run = runBasicBuild(j, DEFAULT_JOBNAME, build); String finalId = run.getExecution().getCurrentHeads().get(0).getId(); // Hack but deletes the FlowNodeStorage from disk CpsFlowExecution cpsExec = (CpsFlowExecution)(run.getExecution()); cpsExec.done = false; cpsExec.saveOwner(); executionAndBuildResult[0] = ((CpsFlowExecution)(run.getExecution())).getResult(); executionAndBuildResult[1] = run.getResult(); }); story.then(j-> { WorkflowJob r = j.jenkins.getItemByFullName(DEFAULT_JOBNAME, WorkflowJob.class); WorkflowRun run = r.getBuildByNumber(build[0]); assertCompletedCleanly(run); Assert.assertEquals(Result.SUCCESS, run.getResult()); assertResultMatchExecutionAndRun(run, executionAndBuildResult); }); }