Package org.libreplan.business.test.planner.entities

Source Code of org.libreplan.business.test.planner.entities.GenericResourceAllocationTest

/*
* 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 java.util.Arrays.asList;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.getCurrentArguments;
import static org.easymock.EasyMock.isA;
import static org.easymock.classextension.EasyMock.createNiceMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
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 static org.libreplan.business.workingday.EffortDuration.zero;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.easymock.IAnswer;
import org.easymock.classextension.EasyMock;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
import org.joda.time.Period;
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.calendars.entities.SameWorkHoursEveryDay;
import org.libreplan.business.planner.entities.GenericDayAssignment;
import org.libreplan.business.planner.entities.GenericResourceAllocation;
import org.libreplan.business.planner.entities.ResourceAllocation;
import org.libreplan.business.planner.entities.Task;
import org.libreplan.business.planner.entities.allocationalgorithms.ResourcesPerDayModification;
import org.libreplan.business.resources.entities.Criterion;
import org.libreplan.business.resources.entities.CriterionSatisfaction;
import org.libreplan.business.resources.entities.Resource;
import org.libreplan.business.resources.entities.VirtualWorker;
import org.libreplan.business.resources.entities.Worker;
import org.libreplan.business.scenarios.entities.Scenario;
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 GenericResourceAllocationTest {

    public static IntraDayDate plusDays(LocalDate date, int days) {
        return IntraDayDate.startOfDay(date.plusDays(days));
    }

    public static IntraDayDate minusDays(LocalDate date, int days) {
        return IntraDayDate.startOfDay(date.minusDays(days));
    }

    private GenericResourceAllocation genericResourceAllocation;
    private Set<Criterion> criterions;

    private List<Worker> workers;
    private List<ResourceCalendar> workerCalendars = null;
    private Worker worker1;
    private Worker worker2;
    private Worker worker3;

    private BaseCalendar baseCalendar;
    private Task task;

    private static Scenario mockScenario() {
        Scenario result = createNiceMock(Scenario.class);
        replay(result);
        return result;
    }

    private void givenGenericResourceAllocation() {
        task = givenTaskWithCriterions();
        givenGenericResourceAllocationForTask(task);
    }

    private Task givenTaskWithStartAndEnd(Interval interval) {
        Task task = createNiceMock(Task.class);
        setupCriterions(task);
        IntraDayDate start = IntraDayDate.startOfDay(interval.getStart()
                .toLocalDate());
        IntraDayDate end = IntraDayDate.startOfDay(interval.getEnd()
                .toLocalDate());
        expect(task.getStartDate()).andReturn(interval.getStart().toDate())
                .anyTimes();
        expect(task.getIntraDayStartDate()).andReturn(start).anyTimes();
        expect(task.getEndDate()).andReturn(interval.getEnd().toDate())
                .anyTimes();
        expect(task.getIntraDayEndDate()).andReturn(end).anyTimes();
        expect(task.getFirstDayNotConsolidated()).andReturn(start)
                .anyTimes();
        expect(task.getCalendar()).andReturn(baseCalendar).anyTimes();
        replay(task);
        return this.task = task;
    }

    private Task givenTaskWithCriterions() {
        Task task = createNiceMock(Task.class);
        setupCriterions(task);
        expect(task.getCalendar()).andReturn(baseCalendar).anyTimes();
        replay(task);
        return this.task = task;
    }

    private void setupCriterions(Task task) {
        expect(task.getCriterions()).andReturn(givenPredefinedCriterions())
                .anyTimes();
    }

    private void givenGenericResourceAllocationForTask(Task task) {
        genericResourceAllocation = GenericResourceAllocation.create(task);
    }

    private Set<Criterion> givenPredefinedCriterions() {
        Set<Criterion> result = new HashSet<Criterion>();
        Criterion criterion1 = createNiceMock(Criterion.class);
        setupIsSatisfiedByAll(criterion1);
        Criterion criterion2 = createNiceMock(Criterion.class);
        setupIsSatisfiedByAll(criterion2);
        replay(criterion1, criterion2);
        result.add(criterion1);
        result.add(criterion2);
        this.criterions = result;
        return result;
    }

    private void setupIsSatisfiedByAll(Criterion criterion) {
        expect(
                criterion.isSatisfiedBy(isA(Resource.class),
                        isA(LocalDate.class))).andReturn(true).anyTimes();
    }

    private void givenWorkersWithoutLoadAndWithoutCalendar() {
        worker1 = createNiceMock(Worker.class);
        worker2 = createNiceMock(Worker.class);
        worker3 = createNiceMock(Worker.class);
        mockZeroLoad(worker1, worker2, worker3);
        buildWorkersList();
        replay(worker1, worker2, worker3);
    }

    public static void mockZeroLoad(Resource... resources) {
        for (Resource each : resources) {
            expect(
                    each.getAssignedDurationDiscounting(isA(Map.class),
                            isA(LocalDate.class))).andReturn(zero()).anyTimes();
        }
    }

    private void buildWorkersList() {
        workers = new ArrayList<Worker>();
        workers.add(worker1);
        workers.add(worker2);
        workers.add(worker3);
    }

    private static class LoadSpec {

        public static LoadSpec withHours(int hours) {
            return new LoadSpec(hours(hours));
        }

        private final EffortDuration defaultLoad;

        private LoadSpec(EffortDuration defaultLoad) {
            Validate.notNull(defaultLoad);
            this.defaultLoad = defaultLoad;
        }

        private Map<LocalDate, EffortDuration> exceptions = new HashMap<LocalDate, EffortDuration>();

        LoadSpec withException(LocalDate date, EffortDuration loadAtThatDate) {
            Validate.notNull(date);
            Validate.notNull(loadAtThatDate);
            exceptions.put(date, loadAtThatDate);
            return this;
        }

        EffortDuration getLoad(LocalDate date) {
            if (exceptions.containsKey(date)) {
                return exceptions.get(date);
            }
            return defaultLoad;
        }

    }

    private Worker createWorkerWithLoad(ResourceCalendar resourceCalendar,
            int hours) {
        return createWorkerWithLoad(resourceCalendar,
                new LoadSpec(hours(hours)));
    }

    private Worker createWorkerWithLoad(ResourceCalendar resourceCalendar,
            final LoadSpec loadSpec) {
        Worker result = createNiceMock(Worker.class);
        expect(result.getCalendar()).andReturn(resourceCalendar).anyTimes();
        expect(
                result.getAssignedDurationDiscounting(isA(Map.class),
                        isA(LocalDate.class))).andAnswer(
                new IAnswer<EffortDuration>() {

                    @Override
                    public EffortDuration answer() throws Throwable {
                        Object[] currentArguments = EasyMock
                                .getCurrentArguments();
                        LocalDate date = (LocalDate) currentArguments[1];
                        return loadSpec.getLoad(date);
                    }
                }).anyTimes();
        expect(result.getSatisfactionsFor(isA(Criterion.class))).andReturn(
                satisfactionsForPredefinedCriterions(result)).anyTimes();
        replay(result);
        return result;
    }

    private List<CriterionSatisfaction> satisfactionsForPredefinedCriterions(
            Resource resource) {
        List<CriterionSatisfaction> result = new ArrayList<CriterionSatisfaction>();
        for (Criterion each : criterions) {
            result.add(CriterionSatisfaction.create(each, resource,
                    fromVeryEarlyTime()));
        }
        return result;
    }

    private org.libreplan.business.resources.entities.Interval fromVeryEarlyTime() {
        return org.libreplan.business.resources.entities.Interval
                .from(new LocalDate(0, 1, 1));
    }

    private void givenCalendarsForResources(int capacity1, int capacity2,
            int capacity3) {
        givenCalendarsForResources(fromHours(capacity1), fromHours(capacity2),
                fromHours(capacity3));
    }

    private Capacity fromHours(int hours) {
        return Capacity.create(hours(hours)).overAssignableWithoutLimit();
    }

    private void givenCalendarsForResources(Capacity capacity1,
            Capacity capacity2, Capacity capacity3) {
        workerCalendars = new ArrayList<ResourceCalendar>();
        workerCalendars.add(createCalendar(ResourceCalendar.class, capacity1));
        workerCalendars.add(createCalendar(ResourceCalendar.class, capacity2));
        workerCalendars.add(createCalendar(ResourceCalendar.class, capacity3));
    }

    private void givenWorkersWithLoads(int hours1, int hours2, int hours3) {
        givenWorkersWithLoads(LoadSpec.withHours(hours1),
                LoadSpec.withHours(hours2), LoadSpec.withHours(hours3));
    }

    private void givenWorkersWithLoads(LoadSpec load1, LoadSpec load2,
            LoadSpec load3) {
        ResourceCalendar[] calendars;
        if (workerCalendars == null) {
            calendars = new ResourceCalendar[] { null, null, null };
        } else {
            calendars = new ResourceCalendar[] { workerCalendars.get(0),
                    workerCalendars.get(1), workerCalendars.get(2) };
        }
        worker1 = createWorkerWithLoad(calendars[0], load1);
        worker2 = createWorkerWithLoad(calendars[1], load2);
        worker3 = createWorkerWithLoad(calendars[2], load3);
        buildWorkersList();
    }

    private void givenBaseCalendarWithoutExceptions(int hoursPerDay) {
        BaseCalendar baseCalendar = createCalendar(BaseCalendar.class, Capacity
                .create(hours(hoursPerDay)).overAssignableWithoutLimit());
        this.baseCalendar = baseCalendar;
    }

    private <T extends BaseCalendar> T createCalendar(Class<T> klass,
            final Capacity capacity) {
        return createCalendar(klass, capacity, 1);
    }

    private <T extends BaseCalendar> T createCalendar(Class<T> klass,
            final Capacity capacity, int units) {
        final Capacity capacityMultipliedByUnits = capacity.multiplyBy(units);
        BaseCalendar baseCalendar = createNiceMock(klass);
        expect(baseCalendar.getCapacityOn(isA(PartialDay.class))).andAnswer(
                new IAnswer<EffortDuration>() {

                    @Override
                    public EffortDuration answer() throws Throwable {
                        PartialDay day = (PartialDay) getCurrentArguments()[0];
                        return day.limitWorkingDay(capacityMultipliedByUnits
                                .getStandardEffort());
                    }
                }).anyTimes();
        expect(baseCalendar.isActive(isA(LocalDate.class))).andReturn(true)
                .anyTimes();
        expect(baseCalendar.canWorkOn(isA(LocalDate.class))).andReturn(true)
                .anyTimes();
        expect(baseCalendar.getAvailability()).andReturn(
                AvailabilityTimeLine.allValid()).anyTimes();
        IAnswer<EffortDuration> durationAnswer = new IAnswer<EffortDuration>() {
            @Override
            public EffortDuration answer() throws Throwable {
                PartialDay day = (PartialDay) getCurrentArguments()[0];
                ResourcesPerDay resourcesPerDay = (ResourcesPerDay) getCurrentArguments()[1];
                return capacityMultipliedByUnits.limitDuration(resourcesPerDay
                        .asDurationGivenWorkingDayOf(day.limitWorkingDay(capacity
                                .getStandardEffort())));
            }
        };
        expect(
                baseCalendar.asDurationOn(isA(PartialDay.class),
                        isA(ResourcesPerDay.class))).andAnswer(durationAnswer)
                .anyTimes();
        expect(baseCalendar.getCapacityWithOvertime(isA(LocalDate.class)))
                .andReturn(capacityMultipliedByUnits).anyTimes();

        if (baseCalendar instanceof ResourceCalendar) {
            ResourceCalendar resourceCalendar = (ResourceCalendar) baseCalendar;
            expect(resourceCalendar.getCapacity()).andReturn(units).anyTimes();
        }
        replay(baseCalendar);
        return klass.cast(baseCalendar);
    }

    @Test
    public void theCriterionsAreCopied() {
        givenGenericResourceAllocation();
        GenericResourceAllocation copied = (GenericResourceAllocation) genericResourceAllocation
                .copy(mockScenario());
        assertThat(copied.getCriterions(), equalTo(criterions));
    }

    @Test
    public void hasTheCriterionsOfTheTask() {
        givenGenericResourceAllocation();
        assertThat(genericResourceAllocation.getCriterions(),
                equalTo(criterions));
    }

    @Test
    public void getOrderedAssignmentsReturnsEmptyListIfNotExistsWorker() {
        givenWorkersWithoutLoadAndWithoutCalendar();
        givenGenericResourceAllocation();
        List<GenericDayAssignment> assignments = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertNotNull(assignments);
        assertTrue(assignments.isEmpty());
    }

    @Test
    public void theGeneratedDayAssignmentsAreRelatedWithTheAllocation() {
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start, Period.days(2)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();

        genericResourceAllocation.forResources(Arrays.asList(worker1))
                .allocate(ResourcesPerDay.amount(1));

        List<GenericDayAssignment> assignments = genericResourceAllocation
                .getAssignments();
        assertThat(assignments,
                haveResourceAllocation(genericResourceAllocation));
    }

    @Test
    public void allocatingGeneratesDayAssignmentsForEachDay() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start, Period
                .days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();

        genericResourceAllocation.forResources(Arrays.asList(worker1))
                .allocate(ResourcesPerDay.amount(1));

        List<GenericDayAssignment> orderedAssignmentsFor = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertThat(orderedAssignmentsFor, from(start).consecutiveDays(
                TASK_DURATION_DAYS));
    }

    @Test
    public void canAllocateSomeResourcesPerDayUntilSomeEndDate() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start, Period
                .days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();
        ResourcesPerDay resourcesPerDay = ResourcesPerDay.amount(1);

        genericResourceAllocation.forResources(Arrays.asList(worker1))
                .resourcesPerDayUntil(plusDays(start, 2))
                .allocate(resourcesPerDay);

        List<GenericDayAssignment> orderedAssignmentsFor = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        int hoursPerDay = resourcesPerDay.asDurationGivenWorkingDayOf(
                EffortDuration.hours(8)).getHours();
        assertThat(orderedAssignmentsFor, haveHours(hoursPerDay, hoursPerDay));
    }

    @Test
    public void allocatingUntilSomeEndDateDeletesAssignmentsAfterThatDate() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start, Period
                .days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();
        ResourcesPerDay resourcesPerDay = ResourcesPerDay.amount(1);

        genericResourceAllocation.forResources(Arrays.asList(worker1))
                .allocate(resourcesPerDay);
        genericResourceAllocation.forResources(Arrays.asList(worker1))
                .resourcesPerDayUntil(plusDays(start, 2))
                .allocate(resourcesPerDay);

        List<GenericDayAssignment> orderedAssignmentsFor = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        int hoursPerDay = resourcesPerDay.asDurationGivenWorkingDayOf(
                EffortDuration.hours(8)).getHours();
        assertThat(orderedAssignmentsFor, haveHours(hoursPerDay, hoursPerDay));
    }

    @Test
    public void whenAllocatingUntilSomeEndDateBeforeTheStartNothingIsDone() {
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start, Period.days(4)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();
        ResourcesPerDay resourcesPerDay = ResourcesPerDay.amount(1);

        genericResourceAllocation.forResources(Arrays.asList(worker1))
                .resourcesPerDayUntil(minusDays(start, 1))
                .allocate(resourcesPerDay);

        assertTrue(genericResourceAllocation.getOrderedAssignmentsFor(worker1)
                .isEmpty());
    }

    @Test(expected = IllegalArgumentException.class)
    public void whenAllocatingUntilSomeEndDateTheEndDateMustNotBeNull() {
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start, Period.days(4)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();
        ResourcesPerDay resourcesPerDay = ResourcesPerDay.amount(1);

        genericResourceAllocation.forResources(Arrays.asList(worker1))
                .resourcesPerDayUntil((IntraDayDate) null)
                .allocate(resourcesPerDay);
    }

    @Test
    public void allocatingUntilEndDateEqualToStartImpliesNoAssignmentsAndZeroResourcesPerDay() {
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start, Period.days(4)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();
        ResourcesPerDay resourcesPerDay = ResourcesPerDay.amount(1);

        genericResourceAllocation.forResources(Arrays.asList(worker1))
                .resourcesPerDayUntil(IntraDayDate.startOfDay(start))
                .allocate(resourcesPerDay);
        assertThat(genericResourceAllocation.getResourcesPerDay(),
                equalTo(ResourcesPerDay.amount(0)));
        assertTrue(genericResourceAllocation.getOrderedAssignmentsFor(worker1)
                .isEmpty());
    }

    @Test
    public void theResourcesPerDayAreChangedWhenTheAllocationIsDone() {
        givenTaskWithStartAndEnd(toInterval(new LocalDate(2006, 10, 5), Period
                .days(2)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();
        ResourcesPerDay assignedResourcesPerDay = ResourcesPerDay.amount(5);
        genericResourceAllocation.forResources(workers).allocate(
                assignedResourcesPerDay);
        assertThat(genericResourceAllocation.getResourcesPerDay(),
                equalTo(assignedResourcesPerDay));
    }

    @Test
    public void allocatingSeveralResourcesPerDayHavingJustOneResourceProducesOvertime() {
        LocalDate start = new LocalDate(2006, 10, 5);
        final Integer standardHoursPerDay = SameWorkHoursEveryDay
                .getDefaultWorkingDay()
                .getCapacityOn(PartialDay.wholeDay(start)).getHours();
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(standardHoursPerDay);
        givenTaskWithStartAndEnd(toInterval(start, Period
                .days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();

        genericResourceAllocation.forResources(Arrays.asList(worker1))
                .allocate(ResourcesPerDay.amount(2));

        List<GenericDayAssignment> orderedAssignmentsFor = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertThat(orderedAssignmentsFor.get(0).getHours(),
                equalTo(standardHoursPerDay * 2));
    }

    @Test
    public void theHoursAreGivenBasedOnTheWorkingHoursSpecifiedByTheCalendar() {
        LocalDate start = new LocalDate(2006, 10, 5);
        final int TASK_DURATION_DAYS = 1;
        final int halfWorkingDay = 4;
        givenBaseCalendarWithoutExceptions(halfWorkingDay);
        givenTaskWithStartAndEnd(toInterval(start, Period
                .days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();

        genericResourceAllocation.forResources(Arrays.asList(worker1))
                .allocate(ResourcesPerDay.amount(1));

        List<GenericDayAssignment> assigmments = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertThat(assigmments, haveHours(halfWorkingDay));
    }

    @Test
    public void ifThereisNoTaskCalendarTheWorkingHoursAreSpecifiedbyTheDefaultWorkingDay() {
        LocalDate start = new LocalDate(2006, 10, 5);
        final int TASK_DURATION_DAYS = 1;
        final Integer defaultWorkableHours = SameWorkHoursEveryDay
                .getDefaultWorkingDay()
                .getCapacityOn(PartialDay.wholeDay(start)).getHours();
        givenBaseCalendarWithoutExceptions(defaultWorkableHours);
        givenTaskWithStartAndEnd(toInterval(start, Period
                .days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();

        genericResourceAllocation.forResources(Arrays.asList(worker1))
                .allocate(ResourcesPerDay.amount(1));

        List<GenericDayAssignment> assigmments = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertThat(assigmments.get(0).getHours(), equalTo(defaultWorkableHours));
    }

    @Test
    public void itGivesAllTheLoadItCanToTheLessLoadedResource() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start, Period
                .days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithLoads(3, 12, 1);

        genericResourceAllocation.forResources(workers).allocate(
                ResourcesPerDay.amount(1));

        List<GenericDayAssignment> assignmentsWorker1 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertThat(assignmentsWorker1, haveHours(1, 1, 1, 1));
        List<GenericDayAssignment> assignmentsWorker2 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker2);
        assertThat(assignmentsWorker2, haveHours());
        List<GenericDayAssignment> assignmentsWorker3 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker3);
        assertThat(assignmentsWorker3, haveHours(7, 7, 7, 7));
    }

    @Test
    public void itTakesIntoAccountTheLoadForEachDay() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start,
                Period.days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithLoads(
                LoadSpec.withHours(3)
                        .withException(start.plusDays(1), hours(1))
                        .withException(start.plusDays(3), hours(8)),
                LoadSpec.withHours(12).withException(start.plusDays(3), zero()),
                LoadSpec.withHours(1)
                        .withException(start.plusDays(1), hours(3))
                        .withException(start.plusDays(3), hours(8)));

        genericResourceAllocation.forResources(workers).allocate(
                ResourcesPerDay.amount(1));

        List<GenericDayAssignment> assignmentsWorker1 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertThat(assignmentsWorker1, haveHours(1, 7, 1));
        List<GenericDayAssignment> assignmentsWorker2 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker2);
        assertThat(assignmentsWorker2, haveHours(8));
        List<GenericDayAssignment> assignmentsWorker3 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker3);
        assertThat(assignmentsWorker3, haveHours(7, 1, 7));
    }

    @Test
    public void previouslyPickedResourcesHaveMorePriority() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start,
                Period.days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithLoads(
                LoadSpec.withHours(0)
                        .withException(start.plusDays(3), hours(4)),
                LoadSpec.withHours(12),
                LoadSpec.withHours(1)
                        .withException(start.plusDays(3), hours(0)));

        genericResourceAllocation.forResources(workers).allocate(
                ResourcesPerDay.amount(1));

        List<GenericDayAssignment> assignmentsWorker3 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker3);
        assertThat(assignmentsWorker3, haveHours(4));
        List<GenericDayAssignment> assignmentsWorker1 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertThat(assignmentsWorker1, haveHours(8, 8, 8, 4));
        List<GenericDayAssignment> assignmentsWorker2 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker2);
        assertThat(assignmentsWorker2, haveHours());
    }

    @Test
    public void doesntSurpassTheExtraHours() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start,
                Period.days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);

        Capacity workingDay = Capacity.create(hours(8));
        Capacity with2ExtraHours = workingDay
                .withAllowedExtraEffort(hours(2));
        givenCalendarsForResources(with2ExtraHours, with2ExtraHours,
                workingDay.overAssignableWithoutLimit());
        givenWorkersWithLoads(0, 0, 0);

        genericResourceAllocation.forResources(workers).allocate(
                ResourcesPerDay.amount(4));

        List<GenericDayAssignment> assignmentsWorker1 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertThat(assignmentsWorker1, haveHours(10, 10, 10, 10));
        List<GenericDayAssignment> assignmentsWorker2 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker2);
        assertThat(assignmentsWorker2, haveHours(10, 10, 10, 10));
        List<GenericDayAssignment> assignmentsWorker3 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker3);
        assertThat(assignmentsWorker3, haveHours(12, 12, 12, 12));

    }

    @Test
    public void itGivesAllTheLoadItCanToTheLessLoadedResourceAndThenToTheNextOne() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start,
                Period.days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithLoads(0, 0, 0);

        genericResourceAllocation.forResources(asList(worker1, worker2))
                .allocate(ResourcesPerDay.amount(2));

        List<GenericDayAssignment> assignmentsWorker1 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertThat(assignmentsWorker1, haveHours(8, 8, 8, 8));
        List<GenericDayAssignment> assignmentsWorker2 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker2);
        assertThat(assignmentsWorker2, haveHours(8, 8, 8, 8));
    }

    @Test
    public void virtualWorkersAreGivenMoreLoad() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start, Period
                .days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithLoads(8, 8, 8);
        givenVirtualWorkerWithCapacityAndLoad(Capacity.create(hours(8))
                .overAssignableWithoutLimit(), 5, hours(40));

        genericResourceAllocation.forResources(workers).allocate(
                ResourcesPerDay.amount(1));
        List<GenericDayAssignment> assignmentsWorker3 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker3);
        assertThat(assignmentsWorker3, haveHours(1, 1, 1, 1));

        List<GenericDayAssignment> assignmentsWorker1 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertThat(assignmentsWorker1, haveHours(1, 1, 1, 1));

        List<GenericDayAssignment> virtualWorkerAssignments = genericResourceAllocation
                .getOrderedAssignmentsFor(workers.get(workers.size() - 1));
        assertThat(virtualWorkerAssignments, haveHours(5, 5, 5, 5));

        List<GenericDayAssignment> assignmentsWorker2 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker2);
        assertThat(assignmentsWorker2, haveHours(1, 1, 1, 1));
    }

    @Test
    public void itWorksWithCalendarsReturningZeroHours() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        givenCalendarsForResources(4, 4, 0);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start, Period
                .days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithLoads(4, 4, 4);

        genericResourceAllocation.forResources(workers).allocate(
                ResourcesPerDay.amount(1));

        List<GenericDayAssignment> assignmentsWorker1 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker1);
        assertThat(assignmentsWorker1, haveHours(4, 4, 4, 4));
        List<GenericDayAssignment> assignmentsWorker2 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker2);
        assertThat(assignmentsWorker2, haveHours(4, 4, 4, 4));
        List<GenericDayAssignment> assignmentsWorker3 = genericResourceAllocation
                .getOrderedAssignmentsFor(worker3);
        assertThat(assignmentsWorker3, haveHours());
    }

    @Test
    public void theEndHourOfTheAllocationIsTheBiggestAllocationDoneIfThereIsSpareSpaceLeft() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        givenCalendarsForResources(8, 8, 8);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start,
                Period.days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithLoads(8, 6, 2);
        IntraDayDate end = ResourceAllocation.allocating(
                Arrays.asList(ResourcesPerDayModification.create(
                        genericResourceAllocation,
                        ResourcesPerDay.amount(new BigDecimal(1)), workers)))
                .untilAllocating(hours(12));
        assertThat(end.getDate(), equalTo(start.plusDays(1)));
        EffortDuration biggestLastAssignment = hours(4);
        assertThat(end.getEffortDuration(), equalTo(biggestLastAssignment));
    }

    @Test
    public void theEndOfTheAllocationIsTheNextDayIfThereIsNoSpareSpaceLeft() {
        final int TASK_DURATION_DAYS = 4;
        givenBaseCalendarWithoutExceptions(8);
        givenCalendarsForResources(8, 8, 8);
        LocalDate start = new LocalDate(2006, 10, 5);
        givenTaskWithStartAndEnd(toInterval(start,
                Period.days(TASK_DURATION_DAYS)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithLoads(8, 2, 6);
        IntraDayDate end = ResourceAllocation.allocating(
                Arrays.asList(ResourcesPerDayModification.create(
                        genericResourceAllocation, ResourcesPerDay.amount(1),
                        workers))).untilAllocating(hours(16));
        assertThat(end.getDate(), equalTo(start.plusDays(2)));
    }

    private void givenVirtualWorkerWithCapacityAndLoad(
            Capacity capacityPerDayAndUnit,
            int capacityUnits,
            EffortDuration load) {
        VirtualWorker worker = createNiceMock(VirtualWorker.class);
        expect(
                worker.getAssignedDurationDiscounting(isA(Map.class),
                        isA(LocalDate.class))).andReturn(load)
                .anyTimes();
        expect(worker.getCalendar()).andReturn(
                createCalendar(ResourceCalendar.class, capacityPerDayAndUnit,
                        capacityUnits)).anyTimes();
        replay(worker);
        workers.add(worker);
    }

    private static Interval toInterval(LocalDate start, Period period) {
        return new Interval(start.toDateTimeAtStartOfDay(), start.plus(period)
                .toDateTimeAtStartOfDay());
    }

    @Test
    public void canAllocateHoursOnInterval() {
        final int workableHoursDay = 8;
        givenBaseCalendarWithoutExceptions(workableHoursDay);
        LocalDate start = new LocalDate(2006, 10, 5);
        final int days = 4;
        givenTaskWithStartAndEnd(toInterval(start, Period.days(days)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithLoads(3, 12, 1);

        genericResourceAllocation.forResources(workers).allocate(
                ResourcesPerDay.amount(1));

        assertThat(genericResourceAllocation.getAssignedHours(),
                equalTo(workableHoursDay * days));

        final int hoursOnSubinterval = 3;
        int daysSubinterval = 2;
        genericResourceAllocation.forResources(workers)
                .onIntervalWithinTask(start, start.plusDays(daysSubinterval))
                .allocateHours(hoursOnSubinterval);
        assertThat(genericResourceAllocation.getAssignedHours(),
                equalTo(hoursOnSubinterval + (days - daysSubinterval)
                        * workableHoursDay));
    }

    @Test
    public void theRelatedResourcesCanBeRetrieved() {
        givenTaskWithStartAndEnd(toInterval(new LocalDate(2006, 10, 5), Period
                .days(4)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithoutLoadAndWithoutCalendar();

        List<Resource> resourcesGiven = Arrays.<Resource> asList(worker1,
                worker2);
        genericResourceAllocation.forResources(resourcesGiven)
                .allocate(ResourcesPerDay.amount(1));
        assertThat(asSet(genericResourceAllocation.getAssociatedResources()),
                equalTo(asSet(genericResourceAllocation
                        .getAssociatedResources())));
    }

    private Set<Resource> asSet(Collection<Resource> associatedResources) {
        return new HashSet<Resource>(associatedResources);
    }

    @Test
    public void canAllocateHoursOnIntervalUsingPreviousResources() {
        final int workableHoursDay = 8;
        givenBaseCalendarWithoutExceptions(workableHoursDay);
        LocalDate start = new LocalDate(2006, 10, 5);
        final int days = 4;
        givenTaskWithStartAndEnd(toInterval(start, Period.days(days)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithLoads(3, 12, 1);

        genericResourceAllocation.forResources(workers).allocate(
                ResourcesPerDay.amount(1));

        final int hoursOnSubinterval = 3;
        int daysSubinterval = 2;
        genericResourceAllocation.withPreviousAssociatedResources().onIntervalWithinTask(
                start,
                start.plusDays(daysSubinterval)).allocateHours(
                hoursOnSubinterval);
        assertThat(genericResourceAllocation.getAssignedHours(),
                equalTo(hoursOnSubinterval + (days - daysSubinterval)
                        * workableHoursDay));
    }

    @Test
    public void allocatingWithPreviousAssociatedResourcesCanBeUsedSafelyWhenNoAllocationHasBeenDone() {
        final int workableHoursDay = 8;
        givenBaseCalendarWithoutExceptions(workableHoursDay);
        LocalDate start = new LocalDate(2006, 10, 5);
        final int days = 4;
        givenTaskWithStartAndEnd(toInterval(start, Period.days(days)));
        givenGenericResourceAllocationForTask(task);

        genericResourceAllocation.withPreviousAssociatedResources().allocate(
                ResourcesPerDay.amount(1));

        assertThat(genericResourceAllocation.getAssignedHours(), equalTo(0));
    }

    @Test
    public void afterAllocatingMoreHoursOnIntervalTheResourcesPerDayAreIncreased() {
        final int workableHoursDay = 8;
        givenBaseCalendarWithoutExceptions(workableHoursDay);
        LocalDate start = new LocalDate(2006, 10, 5);
        final int days = 4;
        givenTaskWithStartAndEnd(toInterval(start, Period.days(days)));
        givenGenericResourceAllocationForTask(task);
        givenWorkersWithLoads(8, 8, 8);

        genericResourceAllocation.forResources(workers).allocate(
                ResourcesPerDay.amount(3));
        ResourcesPerDay original = genericResourceAllocation
                .getResourcesPerDay();
        genericResourceAllocation.forResources(workers).onIntervalWithinTask(start,
                start.plusDays(2)).allocateHours(60);
        ResourcesPerDay current = genericResourceAllocation
                .getResourcesPerDay();
        assertTrue(current.getAmount()
                .compareTo(original.getAmount()) > 0);
    }

}
TOP

Related Classes of org.libreplan.business.test.planner.entities.GenericResourceAllocationTest

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.