/*
* The MIT License
*
* Copyright 2014 Ericsson.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.sonyericsson.hudson.plugins.gerrit.trigger.replication;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import hudson.model.Action;
import hudson.model.AbstractProject;
import hudson.model.CauseAction;
import hudson.model.Queue;
import hudson.model.Queue.Item;
import hudson.model.Queue.WaitingItem;
import hudson.model.queue.CauseOfBlockage;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import jenkins.model.Jenkins;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.sonymobile.tools.gerrit.gerritevents.GerritHandler;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.ChangeAbandoned;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.ChangeBasedEvent;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.ChangeMerged;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.ChangeRestored;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.CommentAdded;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.DraftPublished;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.GerritTriggeredEvent;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.PatchsetCreated;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.RefReplicated;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.RefUpdated;
import com.sonyericsson.hudson.plugins.gerrit.trigger.events.ManualPatchsetCreated;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.GerritCause;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.GerritManualCause;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.GerritTrigger;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.data.GerritSlave;
import com.sonyericsson.hudson.plugins.gerrit.trigger.mock.Setup;
/**
* Tests {@link com.sonyericsson.hudson.plugins.gerrit.trigger.replication.ReplicationQueueTaskDispatcher}.
* @author Hugo Arès <hugo.ares@ericsson.com>
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest(Jenkins.class)
public class ReplicationQueueTaskDispatcherTest {
private ReplicationQueueTaskDispatcher dispatcher;
private Queue queueMock;
private GerritHandler gerritHandlerMock;
private GerritTrigger gerritTriggerMock;
private AbstractProject<?, ?> abstractProjectMock;
private static final int HOURSBEFORECHANGEMERGEDFORPATCHSET = -8;
private static final int HOURBEFOREREPLICATIONCACHECREATED = -1;
/**
* Create ReplicationQueueTaskDispatcher with a mocked GerritHandler.
*/
@Before
public void setUp() {
gerritHandlerMock = mock(GerritHandler.class);
dispatcher = new ReplicationQueueTaskDispatcher(gerritHandlerMock, ReplicationCache.Factory.createCache());
gerritTriggerMock = mock(GerritTrigger.class);
queueMock = mock(Queue.class);
Jenkins jenkinsMock = mock(Jenkins.class);
when(jenkinsMock.getQueue()).thenReturn(queueMock);
PowerMockito.mockStatic(Jenkins.class);
when(Jenkins.getInstance()).thenReturn(jenkinsMock);
}
/**
* Test that it should register as event listener on init.
*/
@Test
public void shouldRegisterAsEventListenerOnInit() {
verify(gerritHandlerMock, times(1)).addListener(dispatcher);
}
/**
* Test that it should not block item without a gerritCause.
*/
@Test
public void shouldNotBlockItemdWithoutGerritCause() {
assertNull("Build should not be blocked", dispatcher.canRun(mock(Item.class)));
}
/**
* Test that it should not block item caused by ChangeAbandoned.
*/
@Test
public void shouldNotBlockItemCausedByChangeAbandoned() {
assertNull("Build should not be blocked", dispatcher.canRun(createItemWithOneSlave(new ChangeAbandoned())));
}
/**
* Test that it should not block item caused by ChangeRestored.
*/
@Test
public void shouldNotBlockItemCausedByChangeRestored() {
assertNull("Build should not be blocked", dispatcher.canRun(createItemWithOneSlave(new ChangeRestored())));
}
/**
* Test that it should not block item caused by CommentAdded.
*/
@Test
public void shouldNotBlockItemCausedByCommentAdded() {
assertNull("Build should not be blocked", dispatcher.canRun(createItemWithOneSlave(new CommentAdded())));
}
/**
* Test that it should not block item caused by ManualPatchsetCreated.
*/
@Test
public void shouldNotBlockItemCausedByManualPatchsetCreated() {
assertNull("Build should not be blocked",
dispatcher.canRun(createItemWithOneSlave(new ManualPatchsetCreated())));
}
/**
* Test that it should not block item with GerritCause that does not contain a GerritEvent.
*
* Not sure this is a valid use case under normal use but GerritCause have
* an empty constructor that do not value to GerritEvent so, let's test it.
*/
@Test
public void shouldNotBlockItemWithGerritCauseWithoutGerritEvent() {
assertNull("Build should not be blocked", dispatcher.canRun(createItemWithOneSlave(new GerritCause())));
}
/**
* Test that it should not block item without GerritTrigger configured.
*/
@Test
public void shouldNotBlockItemWithoutGerritTrigger() {
Item item = createItemWithOneSlave(new PatchsetCreated());
when(abstractProjectMock.getTrigger(GerritTrigger.class)).thenReturn(null);
assertNull("Build should not be blocked", dispatcher.canRun(item));
}
/**
* Test that it should not block item when it not configured to wait for slaves.
*/
@Test
public void shouldNotBlockItemWhenNotConfiguredToWaitForSlaves() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Item item = createItem(patchsetCreated, null);
assertNull("Build should not be blocked", dispatcher.canRun(item));
}
/**
* Test that it should not block buildable items.
*
* If you look at the Queue documentation, build enter the queue as waiting item,
* then they become blocked item and finally the become buildable items. QueueTaskDispatcher.canRun
* will get called one last time on buildable item before they can leave the queue but we do not
* want ReplicationQueueTaskDispatcher to block them at that point.
*/
@Test
public void shouldNotBlockBuildableItem() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Item item = createItem(patchsetCreated, new String[] {"slaveA", "slaveB", "slaveC"});
CauseOfBlockage cause = dispatcher.canRun(new Queue.BuildableItem((WaitingItem)item));
assertNull("Build should not be blocked", cause);
}
/**
* Test that it should block item until replication completes to slave.
*
* For this test case, item is configured with only one slave.
*/
@Test
public void shouldBlockedItemUntilPatchsetIsReplicatedToSlave() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Item item = createItem(patchsetCreated, new String[] {"slaveA"});
//item is blocked
CauseOfBlockage cause = dispatcher.canRun(item);
assertNotNull("The item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
assertTrue(cause.getShortDescription().contains("slaveA"));
//send an unrelated event (not RefReplicated)
dispatcher.gerritEvent(new ChangeAbandoned());
assertNotNull("the item should be blocked", dispatcher.canRun(item));
//send an unrelated replication event (other server)
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1",
"someOtherGerritServer", "slaveA", RefReplicated.SUCCEEDED_STATUS));
assertNotNull("the item should be blocked", dispatcher.canRun(item));
//send an unrelated replication event (other project)
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someOtherProject", "refs/changes/1/1/1",
"someGerritServer", "slaveA", RefReplicated.SUCCEEDED_STATUS));
assertNotNull("the item should be blocked", dispatcher.canRun(item));
//send an unrelated replication event (other ref)
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/2/2/2", "someGerritServer",
"slaveA", RefReplicated.SUCCEEDED_STATUS));
assertNotNull("the item should be blocked", dispatcher.canRun(item));
//send an unrelated replication event (other slave)
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveB", RefReplicated.SUCCEEDED_STATUS));
assertNotNull("the item should be blocked", dispatcher.canRun(item));
//send a malformed replication event (missing provider)
RefReplicated refReplicated = Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1",
"someGerritServer", "slaveB", RefReplicated.SUCCEEDED_STATUS);
refReplicated.setProvider(null);
dispatcher.gerritEvent(refReplicated);
assertNotNull("the item should be blocked", dispatcher.canRun(item));
//send the proper replication event
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveA", RefReplicated.SUCCEEDED_STATUS));
assertNull("Item should not be blocked", dispatcher.canRun(item));
assertNull("Item should not be tagged with replicationFailedAction",
item.getAction(ReplicationFailedAction.class));
verify(queueMock, times(1)).maintain();
}
/**
* Test that it should NOT block item if patchset has expired compared to
* when the Change Merged event is received.
*/
@Test
public void shouldNotBlockItemSinceChangeMergedIsReplicatedViaExpiredPatchset() {
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.HOUR, HOURSBEFORECHANGEMERGEDFORPATCHSET);
Date dateBefore8Hours = cal.getTime();
ChangeMerged changeMerged = Setup.createChangeMergedWithPatchSetDate("someGerritServer", "someProject",
"refs/changes/1/1/1", dateBefore8Hours);
Item item = createItem(changeMerged, new String[] {"slaveA"});
// item is NOT blocked since patchset createdOn date is older than cache
// we assume it was
CauseOfBlockage cause = dispatcher.canRun(item);
assertNull("The item should NOT be blocked", cause);
}
/**
* Test that it should NOT block item if patchset has expired compared to
* when the Replication Cache was first created.
*/
@Test
public void shouldNotBlockItemSinceChangeBasedEventIsReplicatedViaCreationDateBasedExpiredPatchset() {
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.HOUR, HOURBEFOREREPLICATIONCACHECREATED);
Date dateBefore1Hours = cal.getTime();
ChangeMerged changeMerged = Setup.createChangeMergedWithPatchSetDate("someGerritServer", "someProject",
"refs/changes/1/1/1", dateBefore1Hours);
Item item = createItem(changeMerged, new String[] {"slaveA"});
// item is NOT blocked since patchset createdOn date is older than cache
// we assume it was
CauseOfBlockage cause = dispatcher.canRun(item);
assertNull("The item should NOT be blocked", cause);
}
/**
* Test that it should NOT block item if patchset has expired compared to
* when the Change Merged event is received.
*/
@Test
public void shouldNotBlockItemSinceDraftPublishedIsReplicatedViaExpiredPatchset() {
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.HOUR, HOURSBEFORECHANGEMERGEDFORPATCHSET);
Date dateBefore8Hours = cal.getTime();
DraftPublished dp = Setup.createDraftPublishedWithPatchSetDate("someGerritServer", "someProject",
"refs/changes/1/1/1", dateBefore8Hours);
Item item = createItem(dp, new String[] {"slaveA"});
// item is NOT blocked since patchset createdOn date is older than cache
// we assume it was
CauseOfBlockage cause = dispatcher.canRun(item);
assertNull("The item should NOT be blocked", cause);
}
/**
* Helper method for ChangeBasedEvent replication
* @param cbe Change Based event
*/
private void patchSetIsReplicatedToOneSlaveAfterChangeBasedEvent(ChangeBasedEvent cbe) {
Item item = createItem(cbe, new String[] {"slaveA"});
//item is blocked
CauseOfBlockage cause = dispatcher.canRun(item);
assertNotNull("The item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
assertTrue(cause.getShortDescription().contains("slaveA"));
//send an unrelated event (not RefReplicated)
dispatcher.gerritEvent(new ChangeAbandoned());
assertNotNull("the item should be blocked", dispatcher.canRun(item));
//send the proper replication event
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveA", RefReplicated.SUCCEEDED_STATUS));
assertNull("Item should not be blocked", dispatcher.canRun(item));
assertNull("Item should not be tagged with replicationFailedAction",
item.getAction(ReplicationFailedAction.class));
verify(queueMock, times(1)).maintain();
}
/**
* Test that it should block item if patchset is replicated to a slave
* After the Change Merged event is received.
*/
@Test
public void shouldBlockItemUntilIfPatchSetIsReplicatedToOneSlaveAfterChangeMerged() {
ChangeMerged changeMerged = Setup.createChangeMerged("someGerritServer", "someProject",
"refs/changes/1/1/1");
patchSetIsReplicatedToOneSlaveAfterChangeBasedEvent(changeMerged);
}
/**
* Test that it should block item if patchset is replicated to a slave
* After the Draft Published event is received.
*/
@Test
public void shouldBlockItemUntilIfPatchSetIsReplicatedToOneSlaveAfterDraftPublished() {
DraftPublished draftPublished = Setup.createDraftPublished("someGerritServer", "someProject",
"refs/changes/1/1/1");
patchSetIsReplicatedToOneSlaveAfterChangeBasedEvent(draftPublished);
}
/**
* Test that it should block item if patchset is replicated to a slave
* Before the Change Merged event is received.
*/
@Test
public void shouldBlockItemUntilIfPatchSetIsReplicatedToOneSlaveBeforeChangeMerged() {
//send the replication event before change merged event
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveA", RefReplicated.SUCCEEDED_STATUS));
ChangeMerged changeMerged = Setup.createChangeMerged("someGerritServer", "someProject",
"refs/changes/1/1/1");
Item item = createItem(changeMerged, new String[] {"slaveA"});
assertNull("Item should not be blocked", dispatcher.canRun(item));
assertNull("Item should not be tagged with replicationFailedAction",
item.getAction(ReplicationFailedAction.class));
}
/**
* Test that it should block item if patchset is replicated to a slave
* Before the Change Merged event is received.
*/
@Test
public void shouldBlockItemUntilIfPatchSetIsReplicatedToOneSlaveBeforeDraftPublished() {
//send the replication event before draft published event
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveA", RefReplicated.SUCCEEDED_STATUS));
DraftPublished dp = Setup.createDraftPublished("someGerritServer", "someProject",
"refs/changes/1/1/1");
Item item = createItem(dp, new String[] {"slaveA"});
assertNull("Item should not be blocked", dispatcher.canRun(item));
assertNull("Item should not be tagged with replicationFailedAction",
item.getAction(ReplicationFailedAction.class));
}
/**
* Test that it should block item until replication completes to all slaves.
*
* For this test case, item is configured with more than one slave(3).
*/
@Test
public void shouldBlockItemUntilPatchsetIsReplicatedToAllSlaves() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Item item = createItem(patchsetCreated, new String[] {"slaveA", "slaveB", "slaveC"});
//item is blocked
CauseOfBlockage cause = dispatcher.canRun(item);
assertNotNull("the item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
assertTrue(cause.getShortDescription().contains("slaveA"));
assertTrue(cause.getShortDescription().contains("slaveB"));
assertTrue(cause.getShortDescription().contains("slaveC"));
//send replication event for slaveB
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveB", RefReplicated.SUCCEEDED_STATUS));
//item is still blocked
cause = dispatcher.canRun(item);
assertNotNull("the item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
assertTrue(cause.getShortDescription().contains("slaveA"));
assertFalse(cause.getShortDescription().contains("slaveB"));
assertTrue(cause.getShortDescription().contains("slaveC"));
//send replication event for slaveA
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveA", RefReplicated.SUCCEEDED_STATUS));
//item is still blocked
cause = dispatcher.canRun(item);
assertNotNull("the item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
assertFalse(cause.getShortDescription().contains("slaveA"));
assertFalse(cause.getShortDescription().contains("slaveB"));
assertTrue(cause.getShortDescription().contains("slaveC"));
//send replication event for slaveC
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveC", RefReplicated.SUCCEEDED_STATUS));
assertNull("Item should not be blocked", dispatcher.canRun(item));
assertNull("Item should not be tagged with replicationFailedAction",
item.getAction(ReplicationFailedAction.class));
verify(queueMock, times(1)).maintain();
}
/**
* Test that it should block item until slave replication timeout is reached.
* @throws InterruptedException if test fails
*/
@Test
public void shouldBlockItemUntilSlaveReplicationTimeoutIsReached() throws InterruptedException {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Item item = createItem(patchsetCreated, null);
//setup slaves to have one with timeout
List<GerritSlave> gerritSlaves = new ArrayList<GerritSlave>();
gerritSlaves.add(new GerritSlave("slave1", "host1", 0));
gerritSlaves.add(new GerritSlave("slave2", "host2", 1)); // slave timeout is 1 second
when(gerritTriggerMock.gerritSlavesToWaitFor("someGerritServer")).thenReturn(gerritSlaves);
//item is blocked
CauseOfBlockage cause = dispatcher.canRun(item);
assertNotNull("the item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
assertTrue(cause.getShortDescription().contains("slave1"));
assertTrue(cause.getShortDescription().contains("slave2"));
//wait to reach the timeout
Thread.sleep(TimeUnit.SECONDS.toMillis(2));
//timeout reached
cause = dispatcher.canRun(item);
assertNull("Item should not be blocked", cause);
ReplicationFailedAction replicationFailedAction = item.getAction(ReplicationFailedAction.class);
assertNotNull("Item should be tagged with replicationFailedAction", replicationFailedAction);
assertTrue(replicationFailedAction.getReason().contains("slave2"));
verify(queueMock, times(0)).maintain();
}
/**
* Test that it should block item until replication fails.
*/
@Test
public void shouldBlockItemUntilReplicationSucceeds() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Item item = createItem(patchsetCreated, new String[] {"slaveA", "slaveB", "slaveC"});
//item is blocked
CauseOfBlockage cause = dispatcher.canRun(item);
assertNotNull("the item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
assertTrue(cause.getShortDescription().contains("slaveA"));
assertTrue(cause.getShortDescription().contains("slaveB"));
assertTrue(cause.getShortDescription().contains("slaveC"));
//send replication event for slaveB
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveB", RefReplicated.SUCCEEDED_STATUS));
//item is still blocked
cause = dispatcher.canRun(item);
assertNotNull("the item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
assertTrue(cause.getShortDescription().contains("slaveA"));
assertFalse(cause.getShortDescription().contains("slaveB"));
assertTrue(cause.getShortDescription().contains("slaveC"));
//send failed replication event for slaveA
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveA", RefReplicated.FAILED_STATUS));
//send replication event for slaveC
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveC", RefReplicated.SUCCEEDED_STATUS));
assertNotNull("the item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
assertTrue(cause.getShortDescription().contains("slaveA"));
//send success replication event for slaveA
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveA", RefReplicated.SUCCEEDED_STATUS));
//item is no longer blocked and is tagged to succeed
cause = dispatcher.canRun(item);
assertNull("Item should not be blocked", cause);
verify(queueMock, times(1)).maintain();
}
/**
* Test that it should not block item when replication is completed before the queue task dispatcher
* is called to evaluate that queued item.
*
* This scenario is very likely if another QueueTaskDispatcher is registered, gets called before our and block the
* build for sometime(e.g. ThrottleConcurrentBuild plugin).
*/
@Test
public void shouldNotBlockItemWhenReplicationIsCompletedBeforeDispatcherIsCalled() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Item item = createItem(patchsetCreated, new String[] {"slaveA", "slaveB"});
//send replication event for slaveB
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveB", RefReplicated.SUCCEEDED_STATUS));
//send replication event for slaveA
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveA", RefReplicated.SUCCEEDED_STATUS));
assertNull("Item should not be blocked", dispatcher.canRun(item));
verify(queueMock, times(0)).maintain();
}
/**
* Test that it should block item until the proper replication event is received.A proper replication event
* is one that its timestamp is after the event timestamp.
*
* This scenario is only likely to happen for RefUpdated event, for example, getting subsequent refReplicated
* events for the same ref (e.g. /refs/heads/master).
*/
@Test
public void shouldBlockItemUntilProperReplicationEventIsReceived() {
//send replication events created before the actual event
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/heads/master", "someGerritServer",
"slaveB", RefReplicated.SUCCEEDED_STATUS));
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/heads/master", "someGerritServer",
"slaveA", RefReplicated.SUCCEEDED_STATUS));
RefUpdated refUpdated = Setup.createRefUpdated("someGerritServer", "someProject",
"master");
Item item = createItem(refUpdated, new String[] {"slaveA", "slaveB"});
//item is blocked since the cached replication events are time stamped before the actual event
CauseOfBlockage cause = dispatcher.canRun(item);
assertNotNull("the item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
assertTrue(cause.getShortDescription().contains("slaveA"));
assertTrue(cause.getShortDescription().contains("slaveB"));
//send replication events created after the actual event
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/heads/master", "someGerritServer",
"slaveB", RefReplicated.SUCCEEDED_STATUS));
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/heads/master", "someGerritServer",
"slaveA", RefReplicated.SUCCEEDED_STATUS));
assertNull("Item should not be blocked", dispatcher.canRun(item));
verify(queueMock, times(1)).maintain();
}
/**
* Test that it should not block item when the elapsed time between the event reception time stamp and the
* dispatcher get called is greater than the cache expiration time.
*
* Lets say that the cache expires after 6 hours, a build enter the queue and gets blocked by another
* QueueTaskDispatcher for 7 hours and this dispatcher gets called after, we assume that we received the
* replication event. We cannot cache the events forever otherwise will end up with serious memory issue.
*/
@Test
public void shouldNotBlockItemWhenElapsedTimeIsGreaterThenCacheExpirationTime() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/heads/master");
patchsetCreated.setReceivedOn(System.currentTimeMillis()
- TimeUnit.MINUTES.toMillis(ReplicationCache.DEFAULT_EXPIRATION_IN_MINUTES));
Item item = createItem(patchsetCreated, new String[] {"slaveA", "slaveB"});
assertNull("Item should not be blocked", dispatcher.canRun(item));
verify(queueMock, times(0)).maintain();
}
/**
* Test that it should fire queue maintenance maximum one time per replication event received.
*
* If more than one item is unblocked by a replication event, the dispatcher should update all the blocked
* item before calling the maintenance of the queue.
*/
@Test
public void shouldFireQueueMaintenanceMaximumOneTimePerReplicationEventReceived() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Item item1 = createItem(patchsetCreated, new String[] {"slaveA"});
Item item2 = createItem(patchsetCreated, new String[] {"slaveA"});
//items are blocked
CauseOfBlockage cause = dispatcher.canRun(item1);
assertNotNull("the item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
cause = dispatcher.canRun(item2);
assertNotNull("the item should be blocked", cause);
assertTrue("Should have returned a WaitingForReplication as CauseOfBlockage",
cause instanceof WaitingForReplication);
//send replication event for slaveA
dispatcher.gerritEvent(Setup.createRefReplicatedEvent("someProject", "refs/changes/1/1/1", "someGerritServer",
"slaveA", RefReplicated.SUCCEEDED_STATUS));
assertNull("Item should not be blocked", dispatcher.canRun(item1));
assertNull("Item should not be tagged with replicationFailedAction",
item1.getAction(ReplicationFailedAction.class));
assertNull("Item should not be blocked", dispatcher.canRun(item2));
assertNull("Item should not be tagged with replicationFailedAction",
item2.getAction(ReplicationFailedAction.class));
verify(queueMock, times(1)).maintain();
}
/**
* Create a queue item caused by the specified gerritEvent configure to wait for replication
* to one slave.
* @param gerritEvent The gerritEvent
* @return the queue item
*/
private Item createItemWithOneSlave(GerritTriggeredEvent gerritEvent) {
return createItem(gerritEvent, new String[]{"someSlave"});
}
/**
* Create a queue item caused by the specified gerritEvent configure to wait for replication
* to specified slaves.
* @param gerritEvent The gerritEvent
* @param slaves The slaves
* @return the queue item
*/
private Item createItem(GerritTriggeredEvent gerritEvent, String[] slaves) {
GerritCause gerritCause;
if (gerritEvent instanceof ManualPatchsetCreated) {
gerritCause = new GerritManualCause();
} else {
gerritCause = new GerritCause();
}
gerritCause.setEvent(gerritEvent);
return createItem(gerritCause, slaves);
}
/**
* Create a queue item caused by the specified gerritCause configure to wait for replication
* to one slave.
* @param gerritCause The gerritCause
* @return the queue item
*/
private Item createItemWithOneSlave(GerritCause gerritCause) {
return createItem(gerritCause, new String[]{"someSlave"});
}
/**
* Create a queue item caused by the specified gerritCause configure to wait for replication
* to specified slaves.
* @param gerritCause The gerritCause
* @param slaves The slaves
* @return the queue item
*/
private Item createItem(GerritCause gerritCause, String[] slaves) {
List<Action> actions = new ArrayList<Action>();
actions.add(new CauseAction(gerritCause));
abstractProjectMock = mock(AbstractProject.class);
when(abstractProjectMock.getTrigger(GerritTrigger.class)).thenReturn(gerritTriggerMock);
if (slaves != null && slaves.length > 0) {
List<GerritSlave> gerritSlaves = new ArrayList<GerritSlave>();
for (String slave : slaves) {
gerritSlaves.add(new GerritSlave(slave + " display name", slave , 0));
}
when(gerritTriggerMock.gerritSlavesToWaitFor(any(String.class))).thenReturn(gerritSlaves);
}
return new WaitingItem(Calendar.getInstance(), abstractProjectMock, actions);
}
}