/*
* The MIT License
*
* Copyright 2014 Smartmatic International Corporation. All rights reserved.
*
* 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.dependency;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
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.Item;
import hudson.model.Queue;
import hudson.model.Queue.WaitingItem;
import hudson.model.queue.CauseOfBlockage;
import java.util.ArrayList;
import java.util.Calendar;
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.GerritDefaultValues;
import com.sonymobile.tools.gerrit.gerritevents.GerritHandler;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.GerritTriggeredEvent;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.PatchsetCreated;
import com.sonyericsson.hudson.plugins.gerrit.trigger.events.ManualPatchsetCreated;
import com.sonyericsson.hudson.plugins.gerrit.trigger.gerritnotifier.ToGerritRunListener;
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.mock.Setup;
/**
* Tests {@link com.sonyericsson.hudson.plugins.gerrit.trigger.dependency.DependencyQueueTaskDispatcher}.
* @author Yannick Bréhon <yannick.brehon@smartmatic.com>
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({ Jenkins.class, ToGerritRunListener.class, WaitingItem.class })
public class DependencyQueueTaskDispatcherTest {
private DependencyQueueTaskDispatcher dispatcher;
private Queue queueMock;
private GerritHandler gerritHandlerMock;
private GerritTrigger gerritTriggerMock;
private AbstractProject<?, ?> abstractProjectMock;
private AbstractProject<?, ?> abstractProjectDependencyMock;
private Jenkins jenkinsMock;
private ToGerritRunListener toGerritRunListenerMock;
/**
* Create DependencyQueueTaskDispatcher with a mocked GerritHandler.
*/
@Before
public void setUp() {
gerritHandlerMock = mock(GerritHandler.class);
dispatcher = new DependencyQueueTaskDispatcher(gerritHandlerMock);
gerritTriggerMock = mock(GerritTrigger.class);
queueMock = mock(Queue.class);
jenkinsMock = mock(Jenkins.class);
when(jenkinsMock.getQueue()).thenReturn(queueMock);
PowerMockito.mockStatic(Jenkins.class);
when(Jenkins.getInstance()).thenReturn(jenkinsMock);
toGerritRunListenerMock = mock(ToGerritRunListener.class);
PowerMockito.mockStatic(ToGerritRunListener.class);
when(ToGerritRunListener.getInstance()).thenReturn(toGerritRunListenerMock);
}
/**
* 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 an item the task of which is not an AbstractProject.
*/
@Test
public void shouldNotBlockNonAbstractProjects() {
List<Action> actions = new ArrayList<Action>();
Queue.Item item = new WaitingItem(Calendar.getInstance(), null, actions);
CauseOfBlockage cause = dispatcher.canRun(new Queue.BuildableItem((WaitingItem)item));
assertNull("Build should not be blocked", cause);
}
/**
* Test that it should not block item without a gerritCause.
*/
@Test
public void shouldNotBlockItemdWithoutGerritCause() {
assertNull("Build should not be blocked", dispatcher.canRun(mock(Queue.Item.class)));
}
/**
* 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(createItem(new GerritCause(), null)));
}
/**
* Test that it should not block buildable items.
*
* An Item is first checked when in the Queue. Once it canRun(), it is checked again when the executor
* actually pulls it from the queue. But at that time, dependency-wise, we don't care anymore.
*/
@Test
public void shouldNotBlockBuildableItem() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Queue.Item item = createItem(patchsetCreated, null);
CauseOfBlockage cause = dispatcher.canRun(new Queue.BuildableItem((WaitingItem)item));
assertNull("Build should not be blocked", cause);
}
/**
* Test that it should not block item without GerritTrigger configured.
*/
@Test
public void shouldNotBlockItemWithoutGerritTrigger() {
Queue.Item item = createItem(new PatchsetCreated(), null);
when(abstractProjectMock.getTrigger(GerritTrigger.class)).thenReturn(null);
assertNull("Build should not be blocked", dispatcher.canRun(item));
}
/**
* Test that it should not block item which has no dependencies.
*/
@Test
public void shouldNotBlockItemWithNoDependencies() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Queue.Item item = createItem(patchsetCreated, "");
assertNull("Build should not be blocked", dispatcher.canRun(item));
}
/**
* Test that it should not block item which has null dependencies.
*/
@Test
public void shouldNotBlockItemWithNullDependencies() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Queue.Item item = createItem(patchsetCreated, null);
assertNull("Build should not be blocked", dispatcher.canRun(item));
}
/**
* Test that an event marked as onTriggering blocks any build linked to it, and after
* onDoneTriggeringAll, the build should wait for the dependency to be done, and then should build.
*/
@Test
public void shouldBlockTriggeringEvents() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
dispatcher.onTriggeringAll(patchsetCreated);
Queue.Item item = createItem(patchsetCreated, "upstream");
CauseOfBlockage cause = dispatcher.canRun(item);
assertNotNull("Build should be blocked", cause);
dispatcher.onDoneTriggeringAll(patchsetCreated);
//Setting the dependency as "triggered but not built"
doReturn(true).when(toGerritRunListenerMock).
isProjectTriggeredAndIncomplete(abstractProjectDependencyMock, patchsetCreated);
cause = dispatcher.canRun(item);
assertNotNull("Build should be blocked", cause);
//Setting the dependency as "triggered and built"
doReturn(false).when(toGerritRunListenerMock).
isProjectTriggeredAndIncomplete(abstractProjectDependencyMock, patchsetCreated);
cause = dispatcher.canRun(item);
assertNull("Build should not be blocked", cause);
}
/**
* Test that it should not block a project whose dependencies are all built.
*/
@Test
public void shouldNotBlockIfDependenciesAreBuilt() {
PatchsetCreated patchsetCreated = Setup.createPatchsetCreated("someGerritServer", "someProject",
"refs/changes/1/1/1");
Queue.Item item = createItem(patchsetCreated, "upstream");
//Setting the dependency as "triggered and built"
doReturn(false).when(toGerritRunListenerMock).
isProjectTriggeredAndIncomplete(abstractProjectDependencyMock, patchsetCreated);
CauseOfBlockage cause = dispatcher.canRun(item);
assertNull("Build should not be blocked", cause);
}
/**
* Test that it should block currently triggering events of a ManualPatchsetCreated
* and then let them run free once triggering is done.
*/
@Test
public void shouldBlockTriggeringManualPatchsetCreated() {
ManualPatchsetCreated manualPatchsetCreated = spy(Setup.createManualPatchsetCreated());
//Event notification
dispatcher.gerritEvent(manualPatchsetCreated);
//Lifecycle handler should be notified of listening intent
verify(manualPatchsetCreated, times(1)).addListener(dispatcher);
Queue.Item item = createItem(manualPatchsetCreated, "upstream");
CauseOfBlockage cause = dispatcher.canRun(item);
assertNotNull("Build should be blocked", cause);
dispatcher.triggerScanDone(manualPatchsetCreated);
//Lifecycle handler should be notified of remove listener
verify(manualPatchsetCreated, times(1)).removeListener(dispatcher);
//Setting the dependency as "triggered but not built"
doReturn(true).when(toGerritRunListenerMock).
isProjectTriggeredAndIncomplete(abstractProjectDependencyMock, manualPatchsetCreated);
cause = dispatcher.canRun(item);
assertNotNull("Build should be blocked", cause);
//Setting the dependency as "triggered and built"
doReturn(false).when(toGerritRunListenerMock).
isProjectTriggeredAndIncomplete(abstractProjectDependencyMock, manualPatchsetCreated);
cause = dispatcher.canRun(item);
assertNull("Build should not be blocked", cause);
}
/**
* Create a queue item caused by the specified gerritEvent.
* @param gerritEvent The gerritEvent
* @param dependency The dependency project list in a comma-separated string to add to the queue item.
* @return the queue item
*/
private Queue.Item createItem(GerritTriggeredEvent gerritEvent, String dependency) {
GerritCause gerritCause;
if (gerritEvent instanceof ManualPatchsetCreated) {
gerritCause = new GerritManualCause();
} else {
gerritCause = new GerritCause();
}
gerritCause.setEvent(gerritEvent);
return createItem(gerritCause, dependency);
}
/**
* Create a queue item caused by the specified gerritCause.
* @param gerritCause The gerritCause
* @param dependency The dependency project list in a comma-separated string to add to the queue item.
* @return the queue item
*/
private Queue.Item createItem(GerritCause gerritCause, String dependency) {
List<Action> actions = new ArrayList<Action>();
actions.add(new CauseAction(gerritCause));
abstractProjectMock = mock(AbstractProject.class);
when(abstractProjectMock.getTrigger(GerritTrigger.class)).thenReturn(gerritTriggerMock);
abstractProjectDependencyMock = mock(AbstractProject.class);
when(abstractProjectDependencyMock.getTrigger(GerritTrigger.class)).thenReturn(gerritTriggerMock);
when(gerritTriggerMock.getDependencyJobsNames()).thenReturn(dependency);
when(jenkinsMock.getItem(eq("upstream"), any(Item.class), Item.class)).thenReturn(abstractProjectDependencyMock);
WaitingItem waitingItem = PowerMockito.spy(new WaitingItem(Calendar.getInstance(),
abstractProjectMock, actions));
when(waitingItem.getInQueueSince()).thenReturn(System.currentTimeMillis()
- TimeUnit.SECONDS.toMillis(GerritDefaultValues.DEFAULT_BUILD_SCHEDULE_DELAY));
return waitingItem;
}
}