diff --git a/test/src/test/java/hudson/model/QueueCrashTest.java b/test/src/test/java/hudson/model/QueueCrashTest.java new file mode 100644 index 0000000000..79a403647d --- /dev/null +++ b/test/src/test/java/hudson/model/QueueCrashTest.java @@ -0,0 +1,72 @@ +package hudson.model; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import hudson.ExtensionList; +import java.io.File; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.RestartableJenkinsRule; + +public class QueueCrashTest { + + @Rule public RestartableJenkinsRule rr = new RestartableJenkinsRule(); + + @Test + public void persistQueueOnCrash() { + rr.thenWithHardShutdown(j -> { + // Speed up the test run by shortening the periodic save interval from 60 seconds to 5 + // seconds. + Queue.Saver.DELAY_SECONDS = 5; + + scheduleSomeBuild(j); + assertBuildIsScheduled(j); + + // Wait for the periodic save to complete. + ExtensionList.lookupSingleton(Queue.Saver.class) + .getNextSave() + .get(30, TimeUnit.SECONDS); + + // Ensure the periodic save process saved the queue, since the cleanup process will not + // run on a crash. + assertTrue(new File(j.jenkins.getRootDir(), "queue.xml").exists()); + }); + rr.then(QueueCrashTest::assertBuildIsScheduled); + } + + @Test + public void doNotPersistQueueOnCrashBeforeSave() { + rr.thenWithHardShutdown(j -> { + // Avoid periodic save in order to simulate the scenario of a crash before initial save. + Queue.Saver.DELAY_SECONDS = (int) TimeUnit.DAYS.toSeconds(1); + + scheduleSomeBuild(j); + assertBuildIsScheduled(j); + + // Ensure the queue has not been saved in order to test that a crash in this scenario + // results in the queue being lost. + assertFalse(new File(j.jenkins.getRootDir(), "queue.xml").exists()); + }); + rr.then(QueueCrashTest::assertBuildIsNotScheduled); + } + + private static void assertBuildIsScheduled(JenkinsRule j) { + j.jenkins.getQueue().maintain(); + assertFalse(j.jenkins.getQueue().isEmpty()); + } + + private static void assertBuildIsNotScheduled(JenkinsRule j) { + j.jenkins.getQueue().maintain(); + assertTrue(j.jenkins.getQueue().isEmpty()); + } + + private static void scheduleSomeBuild(JenkinsRule j) throws IOException { + FreeStyleProject p = j.createFreeStyleProject(); + p.setAssignedLabel(Label.get("waitforit")); + p.scheduleBuild2(0); + } +} diff --git a/test/src/test/java/hudson/model/QueueRestartTest.java b/test/src/test/java/hudson/model/QueueRestartTest.java index 540e6754e6..861d41462a 100644 --- a/test/src/test/java/hudson/model/QueueRestartTest.java +++ b/test/src/test/java/hudson/model/QueueRestartTest.java @@ -24,66 +24,65 @@ package hudson.model; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; -import hudson.ExtensionList; +import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.junit.Rule; import org.junit.Test; -import org.junit.runners.model.Statement; -import org.jvnet.hudson.test.RestartableJenkinsRule; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.RealJenkinsRule; +import org.jvnet.hudson.test.recipes.LocalData; public class QueueRestartTest { - @Rule - public RestartableJenkinsRule j = new RestartableJenkinsRule(); + @Rule public RealJenkinsRule rr = new RealJenkinsRule(); + @LocalData("quietDown") @Test - public void persistQueueOnRestart() { - j.addStep(new Statement() { - @Override public void evaluate() throws Throwable { - Queue.Saver.DELAY_SECONDS = 24 * 60 * 60; // Avoid period save as we are after the explicit one - scheduleSomeBuild(); - assertBuildIsScheduled(); - } - }); - j.addStep(new Statement() { - @Override public void evaluate() { - assertBuildIsScheduled(); - } - }); + public void persistQueueOnRestart() throws Throwable { + // Avoid periodic save in order to test that the cleanup process saves the queue. + rr.javaOptions("-Dhudson.model.Queue.Saver.DELAY_SECONDS=" + TimeUnit.DAYS.toSeconds(1)); + + rr.then(QueueRestartTest::queueBuild); + rr.then(QueueRestartTest::assertBuildFinishes); } + @LocalData("quietDown") @Test - public void persistQueueOnCrash() { - j.addStepWithDirtyShutdown(new Statement() { - @Override public void evaluate() throws Throwable { - Queue.Saver.DELAY_SECONDS = 0; // Call Queue#save() on every queue modification simulating time has passed before crash - scheduleSomeBuild(); - assertBuildIsScheduled(); + public void persistQueueOnConsecutiveRestarts() throws Throwable { + // Avoid periodic save in order to test that the cleanup process saves the queue. + rr.javaOptions("-Dhudson.model.Queue.Saver.DELAY_SECONDS=" + TimeUnit.DAYS.toSeconds(1)); - // Save have no delay though is not synchronous - ExtensionList.lookup(Queue.Saver.class).get(0).getNextSave().get(3, TimeUnit.SECONDS); - - assertTrue("queue.xml does not exist", j.j.jenkins.getQueue().getXMLQueueFile().exists()); - } - }); - j.addStep(new Statement() { - @Override public void evaluate() { - assertBuildIsScheduled(); - } - }); + rr.then(QueueRestartTest::queueBuild); + rr.then(QueueRestartTest::assertBuildIsScheduled); + rr.then(QueueRestartTest::assertBuildFinishes); } - private void assertBuildIsScheduled() { - assertEquals(1, j.j.jenkins.getQueue().getItems().length); - } - - private void scheduleSomeBuild() throws IOException { - FreeStyleProject p = j.j.createFreeStyleProject(); - p.setAssignedLabel(Label.get("waitforit")); + private static void queueBuild(JenkinsRule j) throws IOException { + FreeStyleProject p = j.createFreeStyleProject("p"); p.scheduleBuild2(0); + assertBuildIsScheduled(j); + + // Ensure the queue has not been saved in order to test that the cleanup process saves + // the queue. + assertFalse(new File(j.jenkins.getRootDir(), "queue.xml").exists()); + } + + private static void assertBuildFinishes(JenkinsRule j) throws Exception { + assertBuildIsScheduled(j); + j.jenkins.doCancelQuietDown(); + FreeStyleProject p = j.jenkins.getItemByFullName("p", FreeStyleProject.class); + FreeStyleBuild b; + while ((b = p.getLastBuild()) == null) { + Thread.sleep(100); + } + j.assertBuildStatusSuccess(j.waitForCompletion(b)); + } + + private static void assertBuildIsScheduled(JenkinsRule j) { + j.jenkins.getQueue().maintain(); + assertFalse(j.jenkins.getQueue().isEmpty()); } } diff --git a/test/src/test/resources/hudson/model/QueueRestartTest/quietDown/init.groovy.d/quietDown.groovy b/test/src/test/resources/hudson/model/QueueRestartTest/quietDown/init.groovy.d/quietDown.groovy new file mode 100644 index 0000000000..7c12e429bb --- /dev/null +++ b/test/src/test/resources/hudson/model/QueueRestartTest/quietDown/init.groovy.d/quietDown.groovy @@ -0,0 +1,4 @@ +import jenkins.model.Jenkins + +// Start in a state that doesn't do any builds. +Jenkins.get().doQuietDown()