|
28 | 28 | import static org.hamcrest.Matchers.containsString; |
29 | 29 | import static org.hamcrest.Matchers.equalTo; |
30 | 30 | import static org.hamcrest.Matchers.hasItem; |
| 31 | +import static org.hamcrest.Matchers.instanceOf; |
31 | 32 | import static org.hamcrest.Matchers.nullValue; |
32 | 33 | import static org.junit.Assert.assertEquals; |
33 | 34 | import static org.junit.Assert.assertFalse; |
|
52 | 53 | import java.net.HttpURLConnection; |
53 | 54 | import java.net.MalformedURLException; |
54 | 55 | import java.net.URL; |
| 56 | +import java.nio.file.Files; |
| 57 | +import java.nio.file.Path; |
| 58 | +import java.nio.file.Paths; |
55 | 59 | import java.util.ArrayList; |
56 | 60 | import java.util.Arrays; |
57 | 61 | import java.util.List; |
|
63 | 67 | import java.util.logging.Handler; |
64 | 68 | import java.util.logging.Level; |
65 | 69 | import java.util.logging.Logger; |
| 70 | +import java.util.stream.Collectors; |
66 | 71 | import jenkins.model.Jenkins; |
67 | 72 | import org.apache.commons.io.FileUtils; |
68 | 73 | import org.hamcrest.Matchers; |
|
75 | 80 | import org.htmlunit.WebRequest; |
76 | 81 | import org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException; |
77 | 82 | import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted; |
| 83 | +import org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.TimingFlowNodeStorage; |
78 | 84 | import org.jenkinsci.plugins.workflow.cps.GroovySourceFileAllowlist.DefaultAllowlist; |
79 | 85 | import org.jenkinsci.plugins.workflow.flow.FlowExecution; |
80 | 86 | import org.jenkinsci.plugins.workflow.flow.FlowExecutionList; |
81 | 87 | import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner; |
| 88 | +import org.jenkinsci.plugins.workflow.graph.FlowNode; |
| 89 | +import org.jenkinsci.plugins.workflow.graphanalysis.DepthFirstScanner; |
82 | 90 | import org.jenkinsci.plugins.workflow.job.WorkflowJob; |
83 | 91 | import org.jenkinsci.plugins.workflow.job.WorkflowRun; |
84 | 92 | import org.jenkinsci.plugins.workflow.pickles.Pickle; |
|
90 | 98 | import org.jenkinsci.plugins.workflow.steps.StepExecution; |
91 | 99 | import org.jenkinsci.plugins.workflow.support.pickles.SingleTypedPickleFactory; |
92 | 100 | import org.jenkinsci.plugins.workflow.support.pickles.TryRepeatedly; |
| 101 | +import org.jenkinsci.plugins.workflow.support.storage.BulkFlowNodeStorage; |
| 102 | +import org.jenkinsci.plugins.workflow.support.storage.FlowNodeStorage; |
| 103 | +import org.jenkinsci.plugins.workflow.support.storage.SimpleXStreamFlowNodeStorage; |
93 | 104 | import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep; |
94 | 105 | import org.junit.ClassRule; |
95 | 106 | import org.junit.Rule; |
@@ -841,4 +852,46 @@ public boolean takesImplicitBlockArgument() { |
841 | 852 | } |
842 | 853 | } |
843 | 854 |
|
| 855 | + @Test public void flowNodeStorageOptimizedUponExecutionCompletion() throws Throwable { |
| 856 | + sessions.then(r -> { |
| 857 | + WorkflowJob p = r.createProject(WorkflowJob.class, "p"); |
| 858 | + p.setDefinition(new CpsFlowDefinition( |
| 859 | + "echo 'Hello, world!'\n" + |
| 860 | + "semaphore('wait')\n" + |
| 861 | + "echo 'Goodbye, world!'", true)); |
| 862 | + WorkflowRun b = p.scheduleBuild2(0).waitForStart(); |
| 863 | + SemaphoreStep.waitForStart("wait/1", b); |
| 864 | + CpsFlowExecution e = (CpsFlowExecution) b.getExecution(); |
| 865 | + FlowNodeStorage storage = ((TimingFlowNodeStorage) e.getStorage()).delegate; |
| 866 | + assertThat(storage, instanceOf(SimpleXStreamFlowNodeStorage.class)); |
| 867 | + Path oldStorageDir = e.getStorageDir().toPath(); |
| 868 | + assertThat(oldStorageDir.getFileName(), equalTo(Paths.get("workflow"))); |
| 869 | + SemaphoreStep.success("wait/1", null); |
| 870 | + r.assertBuildStatusSuccess(r.waitForCompletion(b)); |
| 871 | + storage = ((TimingFlowNodeStorage) e.getStorage()).delegate; |
| 872 | + assertThat(storage, instanceOf(BulkFlowNodeStorage.class)); |
| 873 | + assertFalse("workflow/ should have been deleted", Files.exists(oldStorageDir)); |
| 874 | + Path newStorageDir = e.getStorageDir().toPath(); |
| 875 | + assertThat(newStorageDir.getFileName(), equalTo(Paths.get("workflow-completed"))); |
| 876 | + assertThat(Files.list(newStorageDir).collect(Collectors.toList()), contains(newStorageDir.resolve("flowNodeStore.xml"))); |
| 877 | + List<FlowNode> nodes = new DepthFirstScanner().allNodes(b.getExecution()); |
| 878 | + assertThat(nodes.stream().map(FlowNode::getDisplayFunctionName).collect(Collectors.toList()), equalTo( |
| 879 | + List.of("End of Pipeline", "echo", "semaphore", "echo", "Start of Pipeline"))); |
| 880 | + }); |
| 881 | + sessions.then(r -> { |
| 882 | + WorkflowJob p = r.jenkins.getItemByFullName("p", WorkflowJob.class); |
| 883 | + WorkflowRun b = p.getBuildByNumber(1); |
| 884 | + CpsFlowExecution e = (CpsFlowExecution) b.getExecution(); |
| 885 | + FlowNodeStorage storage = ((TimingFlowNodeStorage) e.getStorage()).delegate; |
| 886 | + assertThat(storage, instanceOf(BulkFlowNodeStorage.class)); |
| 887 | + Path newStorageDir = e.getStorageDir().toPath(); |
| 888 | + assertFalse("workflow/ should have been deleted", Files.exists(newStorageDir.resolveSibling("workflow"))); |
| 889 | + assertThat(newStorageDir.getFileName(), equalTo(Paths.get("workflow-completed"))); |
| 890 | + assertThat(Files.list(newStorageDir).collect(Collectors.toList()), contains(newStorageDir.resolve("flowNodeStore.xml"))); |
| 891 | + List<FlowNode> nodes = new DepthFirstScanner().allNodes(b.getExecution()); |
| 892 | + assertThat(nodes.stream().map(FlowNode::getDisplayFunctionName).collect(Collectors.toList()), equalTo( |
| 893 | + List.of("End of Pipeline", "echo", "semaphore", "echo", "Start of Pipeline"))); |
| 894 | + }); |
| 895 | + } |
| 896 | + |
844 | 897 | } |
0 commit comments