-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
package hudson.plugins.swarm; | ||
|
||
import static org.junit.Assert.assertNotNull; | ||
import static org.junit.Assert.assertTrue; | ||
|
||
import hudson.Functions; | ||
import hudson.model.Computer; | ||
import hudson.model.Node; | ||
import hudson.plugins.swarm.test.ProcessDestroyer; | ||
import hudson.plugins.swarm.test.TestUtils; | ||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; | ||
import org.jenkinsci.plugins.workflow.job.WorkflowJob; | ||
import org.jenkinsci.plugins.workflow.job.WorkflowRun; | ||
import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep; | ||
import org.junit.Assume; | ||
import org.junit.ClassRule; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.TemporaryFolder; | ||
import org.jvnet.hudson.test.BuildWatcher; | ||
import org.jvnet.hudson.test.RestartableJenkinsRule; | ||
|
||
public class PipelineJobTest { | ||
|
||
@Rule public RestartableJenkinsRule story = new RestartableJenkinsRule(); | ||
|
||
@ClassRule public static BuildWatcher buildWatcher = new BuildWatcher(); | ||
|
||
@ClassRule public static TemporaryFolder temporaryFolder = new TemporaryFolder(); | ||
|
||
private final ProcessDestroyer processDestroyer = new ProcessDestroyer(); | ||
|
||
/** Executes a shell script build on a Swarm Client agent. */ | ||
@Test | ||
public void buildShellScript() throws Exception { | ||
story.then( | ||
s -> { | ||
Node node = | ||
TestUtils.createSwarmClient(story.j, processDestroyer, temporaryFolder); | ||
|
||
WorkflowJob project = story.j.createProject(WorkflowJob.class); | ||
project.setConcurrentBuild(false); | ||
project.setDefinition(new CpsFlowDefinition(getFlow(node, 0), true)); | ||
|
||
WorkflowRun build = story.j.buildAndAssertSuccess(project); | ||
story.j.assertLogContains("ON_SWARM_CLIENT=true", build); | ||
tearDown(); | ||
}); | ||
} | ||
|
||
/** | ||
* Executes a shell script build on a Swarm Client agent that has disconnected while the Jenkins | ||
* master is still running. | ||
*/ | ||
@Test | ||
public void buildShellScriptAfterDisconnect() throws Exception { | ||
story.then( | ||
s -> { | ||
Node node = | ||
TestUtils.createSwarmClient(story.j, processDestroyer, temporaryFolder); | ||
|
||
WorkflowJob project = story.j.createProject(WorkflowJob.class); | ||
project.setConcurrentBuild(false); | ||
project.setDefinition(new CpsFlowDefinition(getFlow(node, 1), true)); | ||
|
||
WorkflowRun build = project.scheduleBuild2(0).waitForStart(); | ||
SemaphoreStep.waitForStart("wait-0/1", build); | ||
tearDown(); | ||
|
||
TestUtils.createSwarmClient( | ||
node.getNodeName(), story.j, processDestroyer, temporaryFolder); | ||
SemaphoreStep.success("wait-0/1", null); | ||
story.j.assertBuildStatusSuccess(story.j.waitForCompletion(build)); | ||
story.j.assertLogContains("ON_SWARM_CLIENT=true", build); | ||
tearDown(); | ||
}); | ||
} | ||
|
||
/** Same as the preceding test, but waits in "sh" rather than "node." */ | ||
@Test | ||
public void buildShellScriptAcrossDisconnect() throws Exception { | ||
Assume.assumeFalse( | ||
"TODO not sure how to write a corresponding batch script", Functions.isWindows()); | ||
story.then( | ||
s -> { | ||
Node node = | ||
TestUtils.createSwarmClient(story.j, processDestroyer, temporaryFolder); | ||
|
||
WorkflowJob project = story.j.createProject(WorkflowJob.class); | ||
File f1 = new File(story.j.jenkins.getRootDir(), "f1"); | ||
File f2 = new File(story.j.jenkins.getRootDir(), "f2"); | ||
new FileOutputStream(f1).close(); | ||
project.setConcurrentBuild(false); | ||
|
||
StringBuilder sb = new StringBuilder(); | ||
sb.append("node('" + node.getNodeName() + "') {\n"); | ||
sb.append( | ||
" sh 'touch \"" | ||
+ f2 | ||
+ "\"; while [ -f \"" | ||
+ f1 | ||
+ "\" ]; do sleep 1; done; echo finished waiting; rm \"" | ||
+ f2 | ||
+ "\"'\n"); | ||
sb.append(" echo 'OK, done'\n"); | ||
sb.append("}\n"); | ||
project.setDefinition(new CpsFlowDefinition(sb.toString(), true)); | ||
|
||
WorkflowRun build = project.scheduleBuild2(0).waitForStart(); | ||
while (!f2.isFile()) { | ||
Thread.sleep(100); | ||
} | ||
assertTrue(build.isBuilding()); | ||
Computer computer = node.toComputer(); | ||
assertNotNull(computer); | ||
tearDown(); | ||
while (computer.isOnline()) { | ||
Thread.sleep(100); | ||
} | ||
|
||
TestUtils.createSwarmClient( | ||
node.getNodeName(), story.j, processDestroyer, temporaryFolder); | ||
while (computer.isOffline()) { | ||
Thread.sleep(100); | ||
} | ||
assertTrue(f2.isFile()); | ||
assertTrue(f1.delete()); | ||
while (f2.isFile()) { | ||
Thread.sleep(100); | ||
} | ||
|
||
story.j.assertBuildStatusSuccess(story.j.waitForCompletion(build)); | ||
story.j.assertLogContains("finished waiting", build); | ||
story.j.assertLogContains("OK, done", build); | ||
tearDown(); | ||
}); | ||
} | ||
|
||
/** | ||
* Starts a Jenkins job on a Swarm Client agent, restarts Jenkins while the job is running, and | ||
* verifies that the job continues running on the same agent after Jenkins has been restarted. | ||
*/ | ||
@Test | ||
public void buildShellScriptAfterRestart() throws Exception { | ||
Assume.assumeNotNull( | ||
System.getProperty("port"), "This test requires a fixed port to be available."); | ||
|
||
story.then( | ||
s -> { | ||
// "-deleteExistingClients" is needed so that the Swarm Client can connect | ||
// after the restart. | ||
Node node = | ||
TestUtils.createSwarmClient( | ||
story.j, | ||
processDestroyer, | ||
temporaryFolder, | ||
"-deleteExistingClients"); | ||
|
||
WorkflowJob project = story.j.createProject(WorkflowJob.class, "test"); | ||
project.setConcurrentBuild(false); | ||
project.setDefinition(new CpsFlowDefinition(getFlow(node, 1), true)); | ||
|
||
WorkflowRun build = project.scheduleBuild2(0).waitForStart(); | ||
SemaphoreStep.waitForStart("wait-0/1", build); | ||
}); | ||
story.then( | ||
s -> { | ||
SemaphoreStep.success("wait-0/1", null); | ||
WorkflowJob project = | ||
story.j.jenkins.getItemByFullName("test", WorkflowJob.class); | ||
assertNotNull(project); | ||
WorkflowRun build = project.getBuildByNumber(1); | ||
story.j.assertBuildStatusSuccess(story.j.waitForCompletion(build)); | ||
story.j.assertLogContains("ON_SWARM_CLIENT=true", build); | ||
tearDown(); | ||
}); | ||
} | ||
|
||
private static String getFlow(Node node, int numSemaphores) { | ||
StringBuilder sb = new StringBuilder(); | ||
sb.append("node('" + node.getNodeName() + "') {\n"); | ||
for (int i = 0; i < numSemaphores; i++) { | ||
sb.append(" semaphore 'wait-" + i + "'\n"); | ||
} | ||
// TODO: Once JENKINS-41854 is fixed, remove the next two lines. | ||
sb.append("}\n"); | ||
sb.append("node('" + node.getNodeName() + "') {\n"); | ||
sb.append( | ||
" isUnix() ? sh('echo ON_SWARM_CLIENT=$ON_SWARM_CLIENT') : bat('echo ON_SWARM_CLIENT=%ON_SWARM_CLIENT%')"); | ||
sb.append("}\n"); | ||
|
||
return sb.toString(); | ||
} | ||
|
||
private void tearDown() throws IOException { | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
basil
Author
Member
|
||
try { | ||
processDestroyer.clean(); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(System.err); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package hudson.plugins.swarm; | ||
|
||
import hudson.Functions; | ||
import hudson.model.FreeStyleBuild; | ||
import hudson.model.FreeStyleProject; | ||
import hudson.model.Node; | ||
import hudson.plugins.swarm.test.ProcessDestroyer; | ||
import hudson.plugins.swarm.test.TestUtils; | ||
import hudson.tasks.BatchFile; | ||
import hudson.tasks.CommandInterpreter; | ||
import hudson.tasks.Shell; | ||
import java.io.IOException; | ||
import org.junit.ClassRule; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.TemporaryFolder; | ||
import org.jvnet.hudson.test.BuildWatcher; | ||
import org.jvnet.hudson.test.JenkinsRule; | ||
|
||
public class SwarmClientIntegrationTest { | ||
|
||
@Rule public JenkinsRule j = new JenkinsRule(); | ||
|
||
@ClassRule public static BuildWatcher buildWatcher = new BuildWatcher(); | ||
|
||
@ClassRule public static TemporaryFolder temporaryFolder = new TemporaryFolder(); | ||
|
||
private final ProcessDestroyer processDestroyer = new ProcessDestroyer(); | ||
|
||
/** Executes a shell script build on a Swarm Client agent. */ | ||
@Test | ||
public void buildShellScript() throws Exception { | ||
Node node = TestUtils.createSwarmClient(j, processDestroyer, temporaryFolder); | ||
|
||
FreeStyleProject project = j.createFreeStyleProject(); | ||
project.setConcurrentBuild(false); | ||
project.setAssignedNode(node); | ||
CommandInterpreter command = | ||
Functions.isWindows() | ||
? new BatchFile("echo ON_SWARM_CLIENT=%ON_SWARM_CLIENT%") | ||
: new Shell("echo ON_SWARM_CLIENT=$ON_SWARM_CLIENT"); | ||
project.getBuildersList().add(command); | ||
|
||
FreeStyleBuild build = j.buildAndAssertSuccess(project); | ||
j.assertLogContains("ON_SWARM_CLIENT=true", build); | ||
tearDown(); | ||
} | ||
|
||
private void tearDown() throws IOException { | ||
This comment has been minimized.
Sorry, something went wrong.
darxriggs
Contributor
|
||
try { | ||
processDestroyer.clean(); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(System.err); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package hudson.plugins.swarm.test; | ||
|
||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
/** | ||
* Records processes started during Swarm Client integration tests so that they can be destroyed at | ||
* the end of each test method. | ||
*/ | ||
public final class ProcessDestroyer { | ||
|
||
private final Set<Process> processes = new HashSet<>(); | ||
|
||
/** Record a spawned process for later cleanup. */ | ||
public void record(Process process) { | ||
processes.add(process); | ||
} | ||
|
||
/** Clean up all processes that were spawned during the test. */ | ||
public void clean() throws InterruptedException { | ||
for (Process process : processes) { | ||
process.destroy(); | ||
process.waitFor(); | ||
} | ||
processes.clear(); | ||
} | ||
} |
I suggest to annotate this with
@After
so JUnit takes care of calling it even in case of failing assertions.