Package com.sonyericsson.hudson.plugins.gerrit.trigger.replication

Source Code of com.sonyericsson.hudson.plugins.gerrit.trigger.replication.ReplicationQueueTaskDispatcherTest

/*
* 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);
    }
}
TOP

Related Classes of com.sonyericsson.hudson.plugins.gerrit.trigger.replication.ReplicationQueueTaskDispatcherTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.