forked from jenkinsci/jenkins
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[JENKINS-67237]
BuildTrigger
waits until the dependency graph has b…
…een updated (jenkinsci#6450)
- Loading branch information
Showing
4 changed files
with
272 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
test/src/test/java/jenkins/model/JenkinsFutureDependencyGraphTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package jenkins.model; | ||
|
||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.is; | ||
import static org.hamcrest.Matchers.not; | ||
import static org.mockito.Mockito.doAnswer; | ||
import static org.mockito.Mockito.spy; | ||
|
||
import hudson.model.DependencyGraph; | ||
import java.util.concurrent.ExecutionException; | ||
import java.util.concurrent.Future; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.jvnet.hudson.test.Issue; | ||
import org.jvnet.hudson.test.JenkinsRule; | ||
import org.mockito.invocation.InvocationOnMock; | ||
import org.mockito.stubbing.Answer; | ||
|
||
public class JenkinsFutureDependencyGraphTest { | ||
|
||
@Rule | ||
public JenkinsRule j = new JenkinsRule(); | ||
|
||
@Issue("JENKINS-67237") | ||
@Test | ||
public void testGetFutureDependencyGraphWithoutASingleRebuildBeforeHand() throws InterruptedException, ExecutionException { | ||
Jenkins jenkins = j.jenkins; | ||
|
||
DependencyGraph resultingGraph = jenkins.getFutureDependencyGraph().get(); | ||
// If no dependency graph was calculated asynchronously, jenkins should return the synchronously calculated dependency graph. | ||
assertThat("The asynchronously calculated dependency graph should be equal to the synchronously calculated dependency graph but wasn't.", resultingGraph, is(jenkins.getDependencyGraph())); | ||
} | ||
|
||
@Issue("JENKINS-67237") | ||
@Test | ||
public void testStartRebuildOfDependecyGraphWhileScheduled() throws InterruptedException, ExecutionException { | ||
Jenkins jenkins = j.jenkins; | ||
|
||
Future<DependencyGraph> firstFutureDependencyGraph = jenkins.rebuildDependencyGraphAsync(); | ||
Future<DependencyGraph> secondFutureDependencyGraph = jenkins.rebuildDependencyGraphAsync(); | ||
|
||
assertThat("Two future dependency graphs that were scheduled in short succession should be equal, but weren't", firstFutureDependencyGraph, is(secondFutureDependencyGraph)); | ||
assertThat("Last scheduled future dependency graph should have been returned, but wasn't.", secondFutureDependencyGraph, is(jenkins.getFutureDependencyGraph())); | ||
} | ||
|
||
@Issue("JENKINS-67237") | ||
@Test | ||
public void testStartRebuildOfDependencyGraphWhileAlreadyRebuilding() throws InterruptedException, ExecutionException { | ||
RebuildDependencyGraphController rebuildDependencyGraphController = new RebuildDependencyGraphController(); | ||
Jenkins jenkins = mockJenkinsWithControllableDependencyGraph(rebuildDependencyGraphController); | ||
|
||
Future<DependencyGraph> firstFutureDependencyGraph = jenkins.rebuildDependencyGraphAsync(); | ||
// Wait until rebuild has started | ||
while (rebuildDependencyGraphController.getNumberOfStartedBuilds() < 1) { | ||
Thread.sleep(500); | ||
} | ||
|
||
Future<DependencyGraph> secondFutureDependencyGraph = jenkins.rebuildDependencyGraphAsync(); | ||
|
||
assertThat("Starting a new rebuild of the dependency graph while already rebuilding should result in two distinct future dependency graphs, but didn't.", firstFutureDependencyGraph, is(not(secondFutureDependencyGraph))); | ||
|
||
rebuildDependencyGraphController.setLetBuildFinish(true); | ||
// Wait for both builds to complete | ||
firstFutureDependencyGraph.get(); | ||
secondFutureDependencyGraph.get(); | ||
|
||
assertThat("Two dependency graphs should have been built, but weren't.", rebuildDependencyGraphController.getNumberOfFinishedBuilds(), is(2)); | ||
} | ||
|
||
@Issue("JENKINS-67237") | ||
@Test | ||
public void testStartRebuildOfDependencyGraphWhileAlreadyRebuildingAndAnotherOneScheduled() throws InterruptedException, ExecutionException { | ||
RebuildDependencyGraphController rebuildDependencyGraphController = new RebuildDependencyGraphController(); | ||
Jenkins jenkins = mockJenkinsWithControllableDependencyGraph(rebuildDependencyGraphController); | ||
|
||
jenkins.rebuildDependencyGraphAsync(); | ||
// Wait until rebuild has started | ||
while (rebuildDependencyGraphController.getNumberOfStartedBuilds() < 1) { | ||
Thread.sleep(500); | ||
} | ||
|
||
Future<DependencyGraph> secondFutureDependencyGraph = jenkins.rebuildDependencyGraphAsync(); | ||
Future<DependencyGraph> thirdFutureDependencyGraph = jenkins.rebuildDependencyGraphAsync(); | ||
|
||
assertThat("Two future dependency graphs that were scheduled in short succession should be equal, but weren't", secondFutureDependencyGraph, is(thirdFutureDependencyGraph)); | ||
assertThat("Last scheduled future dependency graph should have been returned, but wasn't.", jenkins.getFutureDependencyGraph(), is(thirdFutureDependencyGraph)); | ||
|
||
rebuildDependencyGraphController.setLetBuildFinish(true); | ||
// Wait for builds to complete | ||
thirdFutureDependencyGraph.get(); | ||
|
||
assertThat("Two dependency graphs should have been built, but weren't.", rebuildDependencyGraphController.getNumberOfFinishedBuilds(), is(2)); | ||
} | ||
|
||
private Jenkins mockJenkinsWithControllableDependencyGraph(RebuildDependencyGraphController rebuildDependencyGraphController) { | ||
Jenkins mockedJenkins = spy(j.jenkins); | ||
doAnswer(new Answer<Void>() { | ||
@Override | ||
public Void answer(InvocationOnMock invocation) throws Throwable { | ||
|
||
rebuildDependencyGraphController.increaseNumberOfStartedBuilds(); | ||
if (!rebuildDependencyGraphController.isLetBuildFinish()) { | ||
// NOOP | ||
} | ||
invocation.callRealMethod(); | ||
|
||
rebuildDependencyGraphController.increaseNumberOfFinishedBuilds(); | ||
return null; | ||
} | ||
}).when(mockedJenkins).rebuildDependencyGraph(); | ||
|
||
return mockedJenkins; | ||
} | ||
|
||
class RebuildDependencyGraphController { | ||
|
||
private volatile boolean letBuildFinish = false; | ||
private volatile int numberOfStartedBuilds = 0; | ||
private volatile int numberOfFinishedBuilds = 0; | ||
|
||
public boolean isLetBuildFinish() { | ||
return letBuildFinish; | ||
} | ||
|
||
public void setLetBuildFinish(boolean letBuildFinish) { | ||
this.letBuildFinish = letBuildFinish; | ||
} | ||
|
||
public int getNumberOfStartedBuilds() { | ||
return numberOfStartedBuilds; | ||
} | ||
|
||
public void increaseNumberOfStartedBuilds() { | ||
this.numberOfStartedBuilds++; | ||
} | ||
|
||
public int getNumberOfFinishedBuilds() { | ||
return numberOfFinishedBuilds; | ||
} | ||
|
||
public void increaseNumberOfFinishedBuilds() { | ||
this.numberOfFinishedBuilds++; | ||
} | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
test/src/test/java/jenkins/triggers/ReverseBuildTriggerAfterRestartTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package jenkins.triggers; | ||
|
||
import static org.junit.Assert.assertNotNull; | ||
|
||
import hudson.model.FreeStyleProject; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.jvnet.hudson.test.Issue; | ||
import org.jvnet.hudson.test.JenkinsSessionRule; | ||
|
||
public class ReverseBuildTriggerAfterRestartTest { | ||
|
||
@Rule | ||
public JenkinsSessionRule rule = new JenkinsSessionRule(); | ||
|
||
@Issue("JENKINS-67237") | ||
@Test | ||
public void testExecutionOfReverseBuildTriggersAfterRestart() throws Throwable { | ||
String nameOfUpstreamProject = "upstreamProject"; | ||
String nameOfDownstreamProject = "downstreamProject"; | ||
|
||
rule.then(j -> { | ||
j.createFreeStyleProject(nameOfUpstreamProject); | ||
FreeStyleProject downstreamProject = j.createFreeStyleProject(nameOfDownstreamProject); | ||
downstreamProject.addTrigger(new ReverseBuildTrigger(nameOfUpstreamProject)); | ||
downstreamProject.save(); | ||
}); | ||
|
||
rule.then(j -> { | ||
FreeStyleProject upstreamProject = j.jenkins.getItem(nameOfUpstreamProject, j.jenkins, FreeStyleProject.class); | ||
j.buildAndAssertSuccess(upstreamProject); | ||
j.waitUntilNoActivity(); | ||
|
||
FreeStyleProject downstreamProject = j.jenkins.getItem(nameOfDownstreamProject, j.jenkins, FreeStyleProject.class); | ||
assertNotNull(downstreamProject.getLastBuild()); | ||
}); | ||
} | ||
} |