/* * This file is part of LibrePlan * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.libreplan.business.test.planner.entities; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.isA; import static org.easymock.EasyMock.verify; import static org.easymock.EasyMock.createNiceMock; import static org.easymock.EasyMock.replay; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.libreplan.business.test.planner.entities.DayAssignmentMatchers.consecutiveDays; import static org.libreplan.business.test.planner.entities.DayAssignmentMatchers.from; import static org.libreplan.business.test.planner.entities.DayAssignmentMatchers.haveHours; import static org.libreplan.business.test.planner.entities.DayAssignmentMatchers.haveResourceAllocation; import static org.libreplan.business.workingday.EffortDuration.hours; import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.easymock.IAnswer; import org.easymock.EasyMock; import org.joda.time.LocalDate; import org.junit.Test; import org.libreplan.business.calendars.entities.AvailabilityTimeLine; import org.libreplan.business.calendars.entities.BaseCalendar; import org.libreplan.business.calendars.entities.Capacity; import org.libreplan.business.calendars.entities.ResourceCalendar; import org.libreplan.business.planner.entities.DayAssignment; import org.libreplan.business.planner.entities.ResourceAllocation.DetachDayAssignmentOnRemoval; import org.libreplan.business.planner.entities.ResourceAllocation.IOnDayAssignmentRemoval; import org.libreplan.business.planner.entities.SpecificDayAssignment; import org.libreplan.business.planner.entities.SpecificResourceAllocation; import org.libreplan.business.planner.entities.Task; import org.libreplan.business.resources.entities.Worker; import org.libreplan.business.workingday.EffortDuration; import org.libreplan.business.workingday.IntraDayDate; import org.libreplan.business.workingday.IntraDayDate.PartialDay; import org.libreplan.business.workingday.ResourcesPerDay; public class SpecificResourceAllocationTest { public static IntraDayDate date(int year, int monthOfYear, int dayOfMonth) { return IntraDayDate.startOfDay(new LocalDate(year, monthOfYear, dayOfMonth)); } public static IntraDayDate plusDays(IntraDayDate date, int days) { return IntraDayDate.create(date.getDate().plusDays(days), date.getEffortDuration()); } public static IntraDayDate minusDays(IntraDayDate date, int days) { return IntraDayDate.create(date.getDate().minusDays(days), date.getEffortDuration()); } public static IntraDayDate plusDaysAndEffort(IntraDayDate date, int days, EffortDuration effort) { return IntraDayDate.create(date.getDate().plusDays(days), date.getEffortDuration().plus(effort)); } private BaseCalendar baseCalendar; private Task task; private SpecificResourceAllocation specificResourceAllocation; private Worker worker; private ResourceCalendar calendar; private void givenResourceCalendarAlwaysReturning(final int hours) { this.calendar = createResourceCalendarAlwaysReturning(hours); } public static ResourceCalendar createResourceCalendarAlwaysReturning(final int hours) { ResourceCalendar workerCalendar; workerCalendar = createNiceMock(ResourceCalendar.class); expect(workerCalendar.getCapacityOn(isA(PartialDay.class))) .andReturn(EffortDuration.hours(hours)).anyTimes(); IAnswer<? extends EffortDuration> asDurationAnswer = asDurationOnAnswer(hours(hours)); expect(workerCalendar.asDurationOn(isA(PartialDay.class), isA(ResourcesPerDay.class))) .andAnswer(asDurationAnswer) .anyTimes(); expect(workerCalendar.getCapacityWithOvertime(isA(LocalDate.class))) .andReturn(Capacity.create(hours(hours)).overAssignableWithoutLimit()) .anyTimes(); expect(workerCalendar.getAvailability()).andReturn(AvailabilityTimeLine.allValid()).anyTimes(); replay(workerCalendar); return workerCalendar; } private static IAnswer<? extends EffortDuration> asDurationOnAnswer(final EffortDuration duration) { return new IAnswer<EffortDuration>() { @Override public EffortDuration answer() throws Throwable { ResourcesPerDay perDay = (ResourcesPerDay) EasyMock.getCurrentArguments()[1]; return perDay.asDurationGivenWorkingDayOf(duration); } }; } private void givenResourceCalendar(final Capacity defaultAnswer, final Map<LocalDate, Capacity> answersForDates) { this.calendar = createNiceMock(ResourceCalendar.class); expect(this.calendar.getCapacityOn(isA(PartialDay.class))) .andAnswer(new IAnswer<EffortDuration>() { @Override public EffortDuration answer() throws Throwable { PartialDay day = (PartialDay) EasyMock.getCurrentArguments()[0]; LocalDate date = day.getDate(); if ( answersForDates.containsKey(date) ) { return day.limitWorkingDay(answersForDates.get(date).getStandardEffort()); } return day.limitWorkingDay(defaultAnswer.getStandardEffort()); } }) .anyTimes(); expect(this.calendar.getCapacityWithOvertime(isA(LocalDate.class))) .andAnswer(new IAnswer<Capacity>() { @Override public Capacity answer() throws Throwable { LocalDate date = (LocalDate) EasyMock.getCurrentArguments()[0]; if ( answersForDates.containsKey(date) ) { return answersForDates.get(date); } return defaultAnswer; } }) .anyTimes(); final IAnswer<EffortDuration> effortAnswer = new IAnswer<EffortDuration>() { @Override public EffortDuration answer() throws Throwable { PartialDay day = (PartialDay) EasyMock.getCurrentArguments()[0]; ResourcesPerDay resourcesPerDay = (ResourcesPerDay) EasyMock.getCurrentArguments()[1]; LocalDate date = day.getDate(); Capacity capacity = answersForDates.containsKey(date) ? answersForDates.get(date) : defaultAnswer; EffortDuration oneResourcePerDayWorkingDuration = day.limitWorkingDay(capacity.getStandardEffort()); EffortDuration amountRequestedDuration = resourcesPerDay.asDurationGivenWorkingDayOf(oneResourcePerDayWorkingDuration); return capacity.limitDuration(amountRequestedDuration); } }; expect(this.calendar.asDurationOn(isA(PartialDay.class), isA(ResourcesPerDay.class))) .andAnswer(effortAnswer) .anyTimes(); expect(this.calendar.getAvailability()).andReturn(AvailabilityTimeLine.allValid()).anyTimes(); replay(this.calendar); } private void givenWorker() { this.worker = createNiceMock(Worker.class); expect(this.worker.getCalendar()).andReturn(calendar).anyTimes(); replay(this.worker); } private void givenTask(IntraDayDate start, IntraDayDate end) { task = createNiceMock(Task.class); expect(task.getCalendar()).andReturn(baseCalendar).anyTimes(); expect(task.getStartDate()).andReturn(start.toDateTimeAtStartOfDay().toDate()).anyTimes(); expect(task.getIntraDayStartDate()).andReturn(start).anyTimes(); expect(task.getEndDate()).andReturn(end.toDateTimeAtStartOfDay().toDate()).anyTimes(); expect(task.getIntraDayEndDate()).andReturn(end).anyTimes(); expect(task.getFirstDayNotConsolidated()).andReturn(start).anyTimes(); replay(task); } private void givenSpecificResourceAllocation(IntraDayDate start, IntraDayDate end) { givenWorker(); givenTask(start, end); specificResourceAllocation = SpecificResourceAllocation.create(task); specificResourceAllocation.setResource(worker); } private void givenSpecificResourceAllocation(IntraDayDate start, int days) { givenSpecificResourceAllocation(start, plusDays(start, days)); } @Test public void theAllocationsDoneAreOrderedByDay() { givenSpecificResourceAllocation(date(2000, 2, 4), 2); specificResourceAllocation.allocate(ResourcesPerDay.amount(1)); assertThat(specificResourceAllocation.getAssignments(), consecutiveDays(2)); } @Test public void theAllocationsDoneHaveAsParentTheAllocation() { givenSpecificResourceAllocation(date(2000, 2, 4), 2); specificResourceAllocation.allocate(ResourcesPerDay.amount(1)); assertThat(specificResourceAllocation.getAssignments(), haveResourceAllocation(specificResourceAllocation)); } @Test public void theAllocationStartsAtTheStartDate() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 2); specificResourceAllocation.allocate(ResourcesPerDay.amount(1)); assertThat(specificResourceAllocation.getAssignments(), from(start)); } @Test public void theAllocationIsDoneEvenIfThereisOvertimeIfCapacitiesAreOverAssignable() { givenResourceCalendar(Capacity.create(hours(4)).overAssignableWithoutLimit(), Collections.emptyMap()); IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 2); specificResourceAllocation.allocate(ResourcesPerDay.amount(2)); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8)); } @Test public void ifNotOverassignableItOnlyDoesExtraEffortSpecified() { givenResourceCalendar(Capacity.create(hours(4)).withAllowedExtraEffort(hours(2)), Collections.emptyMap()); IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 2); specificResourceAllocation.allocate(ResourcesPerDay.amount(2)); assertThat(specificResourceAllocation.getAssignments(), haveHours(6, 6)); } @Test public void canAllocateUntilSomeEndDate() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.resourcesPerDayUntil(plusDays(start, 3)).allocate(ResourcesPerDay.amount(1)); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8, 8)); } @Test public void initiallyTheIntendedResourcesPerDayAreNull() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); assertThat(specificResourceAllocation.getIntendedResourcesPerDay(), nullValue()); } @Test public void afterAllocatingSomeResourcesPerDayTheIntendedResourcesPerDayAreNotNull() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); ResourcesPerDay specified = ResourcesPerDay.amount(1); specificResourceAllocation.resourcesPerDayUntil(plusDays(start, 3)).allocate(specified); assertThat(specificResourceAllocation.getIntendedResourcesPerDay(), equalTo(specified)); } @Test public void theIntendedAndTheRealResourcesPerDayCanBeDifferent() { givenResourceCalendar(Capacity.create(hours(8)).withAllowedExtraEffort(hours(2)), Collections.emptyMap()); IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.resourcesPerDayUntil(plusDays(start, 3)).allocate(ResourcesPerDay.amount(2)); BigDecimal intentededAmount = specificResourceAllocation.getIntendedResourcesPerDay().getAmount(); BigDecimal realAmount = specificResourceAllocation.getResourcesPerDay().getAmount(); assertTrue(intentededAmount.compareTo(realAmount) > 0); } @Test public void canAllocateFromEndUntilSomeStartDate() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.resourcesPerDayFromEndUntil(start).allocate(ResourcesPerDay.amount(1)); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8, 8, 8)); } @Test public void canAllocateFromEndInTheMiddleOfTheDayToStartDate() { IntraDayDate start = date(2000, 2, 4); IntraDayDate end = plusDaysAndEffort(start, 3, hours(3)); givenSpecificResourceAllocation(start, end); specificResourceAllocation.resourcesPerDayFromEndUntil(start).allocate(ResourcesPerDay.amount(1)); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8, 8, 3)); assertThat(specificResourceAllocation.getResourcesPerDay(), equalTo(ResourcesPerDay.amount(1))); } @Test public void theResourcesPerDayAreConvertedTakingIntoAccountTheWorkerCalendar() { givenResourceCalendarAlwaysReturning(4); IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 2); specificResourceAllocation.allocate(ResourcesPerDay.amount(1)); assertThat(specificResourceAllocation.getAssignments(), haveHours(4, 4)); } @Test(expected = IllegalArgumentException.class) public void cantAllocateOnAnWrongInterval() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); IntraDayDate dayBefore = minusDays(start, 1); specificResourceAllocation.onIntervalWithinTask(start, dayBefore).allocateHours(10); } @Test public void canAllocateZeroHours() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 2)).allocateHours(0); } @Test(expected = IllegalArgumentException.class) public void cantAllocateNegativeHours() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 1)).allocateHours(-1); } @Test public void someHoursInAnIntervalCanBeAssigned() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 2)).allocateHours(10); assertThat(specificResourceAllocation.getAssignments(), haveHours(5, 5)); } @Test public void theHoursForEachDayCanBeAssigned() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation .onIntervalWithinTask(start, plusDays(start, 4)) .allocate(Arrays.asList(hours(4), hours(8), hours(4), hours(8))); assertThat(specificResourceAllocation.getAssignments(), haveHours(4, 8, 4, 8)); } @Test public void ifLessDaysAreSpecifiedTheInitialDaysAreAllocated() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation .onIntervalWithinTask(start, plusDays(start, 4)) .allocate(Arrays.asList(hours(4), hours(8), hours(4))); assertThat(specificResourceAllocation.getAssignments(), haveHours(4, 8, 4)); } @Test public void ifMoreDaysAreSpecifiedTheInitialDaysAreAllocated() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation .onIntervalWithinTask(start, plusDays(start, 4)) .allocate(Arrays.asList(hours(4), hours(8), hours(4), hours(4), hours(3))); assertThat(specificResourceAllocation.getAssignments(), haveHours(4, 8, 4, 4)); } @Test public void theDaysSpecifiedOutsideBoundsAreDiscarded() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation .onIntervalWithinTask(minusDays(start, 2), plusDays(start, 1)) .allocate(Arrays.asList(hours(2), hours(3), hours(4))); assertThat(specificResourceAllocation.getAssignments(), haveHours(4)); } @Test public void combineOutsideBoundsAndZeroPadding() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation .onIntervalWithinTask(minusDays(start, 2), plusDays(start, 1)) .allocate(Arrays.asList(hours(2), hours(3))); assertThat(specificResourceAllocation.getAssignments(), haveHours()); } @Test public void theDaysSpecifiedOutsideTheTaskAreDiscarded() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation .onIntervalWithinTask(minusDays(start, 1), plusDays(start, 4)) .allocate(Arrays.asList(hours(10), hours(4), hours(8), hours(4), hours(4), hours(3))); List<SpecificDayAssignment> assignments = specificResourceAllocation.getAssignments(); assertThat(assignments, haveHours(4, 8, 4, 4)); } @Test public void theIntervalWithinTaskCanBeMadeOfIntraDayDates() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); IntraDayDate endInterval = plusDaysAndEffort(start, 2, EffortDuration.hours(4)); specificResourceAllocation.onIntervalWithinTask(start, endInterval).allocateHours(12); List<SpecificDayAssignment> assignments = specificResourceAllocation.getAssignments(); assertThat(DayAssignment.sum(assignments), equalTo(hours(12))); assertTrue(assignments.get(0).getDuration().compareTo(assignments.get(2).getDuration()) > 0); assertTrue(assignments.get(1).getDuration().compareTo(assignments.get(2).getDuration()) > 0); } @Test public void thePartOfTheIntervalUsedIsTheOneOverlapping() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.onIntervalWithinTask(plusDays(start, 1), plusDays(start, 6)).allocateHours(12); assertThat(specificResourceAllocation.getAssignments(), haveHours(4, 4, 4)); } @Test public void ifTheProvidedIntervalIsAfterTheTaskDoesNotAllocateAnything() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.onIntervalWithinTask(plusDays(start, 5), plusDays(start, 6)).allocateHours(12); assertTrue(specificResourceAllocation.getAssignments().isEmpty()); } @Test public void ifTheProvidedIntervalIsBeforeTheTaskDoesNotAllocateAnything() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.onIntervalWithinTask(minusDays(start, 5), minusDays(start, 2)).allocateHours(12); assertTrue(specificResourceAllocation.getAssignments().isEmpty()); } @Test public void thePartOfTheIntervalUsedIsTheOneOverlappingWithTheTask() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.fromStartUntil(plusDays(start, 2)).allocateHours(16); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 6)).allocateHours(12); assertThat(specificResourceAllocation.getAssignments(), haveHours(3, 3, 3, 3)); } @Test public void theEndIsNotChangedIfAZeroAllocationIsDoneInTheLastDay() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); IntraDayDate end = plusDays(start, 4); specificResourceAllocation.fromStartUntil(end).allocateHours(32); specificResourceAllocation.onIntervalWithinTask(plusDays(start, 3), end).allocateHours(0); assertThat(specificResourceAllocation.getIntraDayEndDate(), equalTo(end)); } @Test public void theEndCanGrowUntilReachingTheEndOfTheTask() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); IntraDayDate end = plusDays(start, 4); specificResourceAllocation.fromStartUntil(minusDays(end, 1)).allocateHours(24); assertThat(specificResourceAllocation.getIntraDayEndDate(), equalTo(minusDays(end, 1))); specificResourceAllocation.onIntervalWithinTask(start, end).allocateHours(32); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8, 8, 8)); assertThat(specificResourceAllocation.getIntraDayEndDate(), equalTo(end)); } @Test public void theStartIsNotChangedIfAZeroAllocationIsDoneInTheFirstDay() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.fromStartUntil(plusDays(start, 4)).allocateHours(32); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 1)).allocateHours(0); assertThat(specificResourceAllocation.getIntraDayStartDate(), equalTo(start)); } @Test public void thereIsAWayForAllocatingOutsideTheBoundsOfTheTask() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.fromStartUntil(plusDays(start, 4)).allocateHours(32); specificResourceAllocation.onInterval(plusDays(start, 5), plusDays(start, 6)).allocateHours(8); assertThat(specificResourceAllocation.getIntraDayEndDate(), equalTo(plusDays(start, 6))); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8, 8, 8, 8)); } @Test public void canAllocateOutsideTheBoundsUsingAnIntervalMadeOfIntraDayDates() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation .onInterval(start, IntraDayDate.create(start.getDate().plusDays(4), hours(4))) .allocateHours(36); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8, 8, 8, 4)); } @Test public void canAllocateOutsideTheBoundsSpecifyingTheHoursForEachDay() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation .onInterval(minusDays(start, 1), plusDaysAndEffort(start, 4, hours(4))) .allocate(Arrays.asList(hours(8), hours(2), hours(8), hours(8), hours(8), hours(4))); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 2, 8, 8, 8, 4)); } @Test public void allocatingZeroHoursAtTheEndShrinksTheAllocation() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.fromStartUntil(plusDays(start, 4)).allocateHours(32); specificResourceAllocation.onInterval(plusDays(start, 3), plusDays(start, 4)).allocateHours(0); assertThat(specificResourceAllocation.getIntraDayEndDate(), equalTo(plusDays(start, 3))); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8, 8)); } @Test public void allocatingZeroHoursAtTheStartShrinksTheAllocation() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.fromStartUntil(plusDays(start, 4)).allocateHours(32); specificResourceAllocation.onInterval(start, plusDays(start, 1)).allocateHours(0); assertThat(specificResourceAllocation.getIntraDayStartDate(), equalTo(plusDays(start, 1))); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8, 8)); } @Test public void canAssignFromStartUntilEnd() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.fromStartUntil(plusDays(start, 4)).allocateHours(32); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8, 8, 8)); assertThat(specificResourceAllocation.getResourcesPerDay(), equalTo(ResourcesPerDay.amount(1))); } @Test public void canAssignFromEndUntilStart() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.fromEndUntil(start).allocateHours(32); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8, 8, 8)); assertThat(specificResourceAllocation.getResourcesPerDay(), equalTo(ResourcesPerDay.amount(1))); } @Test public void ifEndIsInTheMiddleOfADayFromEndUntilStartCalculatesResourcesPerDayCorrectly() { IntraDayDate start = date(2000, 2, 4); IntraDayDate end = plusDaysAndEffort(start, 3, hours(4)); givenSpecificResourceAllocation(start, end); specificResourceAllocation.fromEndUntil(start).allocateHours(28); assertThat(specificResourceAllocation.getAssignments(), haveHours(8, 8, 8, 4)); assertThat(specificResourceAllocation.getResourcesPerDay(), equalTo(ResourcesPerDay.amount(1))); } @Test public void canBeNotifiedWhenADayAssignmentIsRemoved() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 2)).allocateHours(10); List<SpecificDayAssignment> currentAssignments = specificResourceAllocation.getAssignments(); IOnDayAssignmentRemoval dayAssignmentRemovalMock = createMock(IOnDayAssignmentRemoval.class); for (SpecificDayAssignment each : currentAssignments) { dayAssignmentRemovalMock.onRemoval(specificResourceAllocation, each); expectLastCall().once(); } specificResourceAllocation.setOnDayAssignmentRemoval(dayAssignmentRemovalMock); replay(dayAssignmentRemovalMock); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 2)).allocateHours(10); verify(dayAssignmentRemovalMock); } @Test public void canAutomaticallyDetachDayAssignmentsWhenRemoved() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 2)).allocateHours(10); List<SpecificDayAssignment> assignments = specificResourceAllocation.getAssignments(); for (SpecificDayAssignment each : assignments) { assertThat(each.getSpecificResourceAllocation(), notNullValue()); } specificResourceAllocation.setOnDayAssignmentRemoval(new DetachDayAssignmentOnRemoval()); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 2)).allocateHours(10); for (SpecificDayAssignment each : assignments) { assertThat(each.getSpecificResourceAllocation(), nullValue()); } } @Test public void thePreviousAssignmentsAreReplacedWhenAllocationHoursOnInterval() { givenResourceCalendarAlwaysReturning(3); IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.allocate(ResourcesPerDay.amount(1)); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 2)).allocateHours(10); assertThat(specificResourceAllocation.getAssignments(), haveHours(5, 5, 3, 3)); } @Test public void allocatingZeroHoursOnIntervalRemovesThem() { givenResourceCalendarAlwaysReturning(3); IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.allocate(ResourcesPerDay.amount(1)); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 2)).allocateHours(0); assertThat(specificResourceAllocation.getAssignments(), from(plusDays(start, 2))); } @Test public void theResourcesPerDayAreRecalculatedWhenAllocationHoursOnInterval() { givenResourceCalendarAlwaysReturning(3); IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 4); ResourcesPerDay original = ResourcesPerDay.amount(1); specificResourceAllocation.allocate(original); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 2)).allocateHours(10); ResourcesPerDay newResourcesPerDay = specificResourceAllocation.getResourcesPerDay(); assertTrue("Expecting that the resources per day is increased", newResourcesPerDay.getAmount().compareTo(original.getAmount()) > 0); } @Test @SuppressWarnings("serial") public void theResourcesPerDayAreTheOnesSpecifiedEvenIfInTheLastDayNoAllocationCanBeDone() { final IntraDayDate start = date(2000, 2, 4); givenResourceCalendar(Capacity.create(hours(8)).overAssignableWithoutLimit(), new HashMap<LocalDate, Capacity>() { { put(plusDays(start, 3).getDate(), Capacity.create(hours(8)).notOverAssignableWithoutLimit()); } }); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.resourcesPerDayUntil(plusDays(start, 4)).allocate(ResourcesPerDay.amount(1)); assertThat(specificResourceAllocation.getResourcesPerDay(), equalTo(ResourcesPerDay.amount(1))); } @Test public void theResourcesPerDayAreTheOnesSpecifiedEvenIfTheStartIsInTheMiddleOfTheDay() { final IntraDayDate start = IntraDayDate.create(new LocalDate(2000, 2, 4), hours(4)); givenResourceCalendarAlwaysReturning(8); IntraDayDate end = IntraDayDate.startOfDay(start.getDate().plusDays(4)); givenSpecificResourceAllocation(start, end); specificResourceAllocation.resourcesPerDayUntil(end).allocate(ResourcesPerDay.amount(1)); assertThat(specificResourceAllocation.getResourcesPerDay(), equalTo(ResourcesPerDay.amount(1))); } @Test public void theHoursAreDistributedTakingIntoAccountTheWorkableHours() { final IntraDayDate start = date(2000, 2, 4); givenResourceCalendar(Capacity.create(hours(8)).overAssignableWithoutLimit(), new HashMap<LocalDate, Capacity>() { { put(start.getDate(), Capacity.create(hours(2)).notOverAssignableWithoutLimit()); } }); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 2)).allocateHours(10); assertThat(specificResourceAllocation.getAssignments(), haveHours(2, 8)); } @SuppressWarnings("serial") @Test public void youCanAllocateHoursPreservingTheCurrentShape() { final IntraDayDate start = date(2000, 2, 4); givenResourceCalendar(Capacity.create(hours(8)).overAssignableWithoutLimit(), new HashMap<LocalDate, Capacity>() { { put(start.getDate(), Capacity.create(hours(2)).notOverAssignableWithoutLimit()); put(plusDays(start, 1).getDate(), Capacity.create(hours(4)).notOverAssignableWithoutLimit()); put(plusDays(start, 3).getDate(), Capacity.create(hours(6)).notOverAssignableWithoutLimit()); } }); givenSpecificResourceAllocation(start, 4); specificResourceAllocation.onIntervalWithinTask(start, plusDays(start, 4)).allocateHours(20); assertThat(specificResourceAllocation.getAssignments(), haveHours(2, 4, 8, 6)); specificResourceAllocation.allocateWholeAllocationKeepingProportions(EffortDuration.hours(18), plusDays(start, 2)); assertThat(specificResourceAllocation.getAssignments(), haveHours(6, 12)); } @Test public void theEndDateOfTheAllocationIsExclusive() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 2); specificResourceAllocation.allocate(ResourcesPerDay.amount(1)); assertThat(specificResourceAllocation.getIntraDayEndDate(), equalTo(date(2000, 2, 6))); } @Test public void theAllocationIsFinishedByEndDate() { IntraDayDate start = date(2000, 2, 4); givenSpecificResourceAllocation(start, 2); specificResourceAllocation.allocate(ResourcesPerDay.amount(1)); assertTrue(specificResourceAllocation.isAlreadyFinishedBy(specificResourceAllocation.getEndDate())); } }