/* * 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-2012 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.workingday; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.libreplan.business.workingday.EffortDuration.hours; import static org.libreplan.business.workingday.EffortDuration.minutes; import static org.libreplan.business.workingday.EffortDuration.zero; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.junit.Test; import org.libreplan.business.workingday.EffortDuration; import org.libreplan.business.workingday.EffortDuration.Granularity; import org.libreplan.business.workingday.IntraDayDate; import org.libreplan.business.workingday.IntraDayDate.PartialDay; import org.libreplan.business.workingday.IntraDayDate.UntilEnd; import org.libreplan.business.workingday.ResourcesPerDay; /** * @author Óscar González Fernández * @author Manuel Rego Casasnovas <rego@igalia.com> */ public class IntraDayDateTest { private LocalDate today = new LocalDate(); private LocalDate tomorrow = today.plusDays(1); private EffortDuration oneHour = EffortDuration.hours(1); private EffortDuration halfHour = EffortDuration.minutes(30); @Test(expected = NullPointerException.class) public void needsANotNullDuration() { IntraDayDate.create(new LocalDate(), null); } @Test(expected = NullPointerException.class) public void needsANotNullDate() { IntraDayDate.create(null, EffortDuration.elapsing(1, Granularity.HOURS)); } @Test public void equalsAndHashCodeWorkOk() { LocalDate today = new LocalDate(); LocalDate tomorrow = today.plusDays(1); EffortDuration oneHour = EffortDuration.hours(1); EffortDuration halfHour = EffortDuration.minutes(30); assertEquals(IntraDayDate.create(today, halfHour), IntraDayDate.create(tomorrow.minusDays(1), halfHour)); assertEquals(IntraDayDate.create(today, halfHour).hashCode(), IntraDayDate .create(tomorrow.minusDays(1), halfHour).hashCode()); assertThat(IntraDayDate.create(today, halfHour), not(equalTo(IntraDayDate.create(today, oneHour)))); } @Test public void canKnowIfAreSameDay() { assertTrue(IntraDayDate.create(today, halfHour).areSameDay(today)); assertTrue(IntraDayDate.create(today, oneHour).areSameDay(today)); assertFalse(IntraDayDate.create(today, halfHour).areSameDay(tomorrow)); assertFalse(IntraDayDate.create(tomorrow, halfHour).areSameDay(today)); } @Test public void canGetDateTimeAtStartOfDay() { DateTime dateTime = IntraDayDate.create(today, halfHour) .toDateTimeAtStartOfDay(); assertThat(dateTime, equalTo(today.toDateTimeAtStartOfDay())); } @Test public void implementsComparable() { assertTrue(Comparable.class.isAssignableFrom(IntraDayDate.class)); assertTrue(IntraDayDate.create(today, halfHour).compareTo( IntraDayDate.create(today, oneHour)) < 0); assertTrue(IntraDayDate.create(today, oneHour).compareTo( IntraDayDate.create(tomorrow, halfHour)) < 0); } @Test public void hasMaxAndMinMethods() { IntraDayDate a = IntraDayDate.create(today, halfHour); IntraDayDate b = IntraDayDate.create(today, oneHour); assertThat(IntraDayDate.min(a, b), equalTo(a)); assertThat(IntraDayDate.max(a, b), equalTo(b)); } @Test public void untilTheSameDayReturnsZeroPartialDays() { Iterable<PartialDay> days = IntraDayDate.create(today, zero()) .daysUntil(IntraDayDate.create(today, zero())); assertFalse(days.iterator().hasNext()); } @Test public void untilTheNextDayReturnsOnePartialDay() { IntraDayDate start = IntraDayDate.create(today, zero()); IntraDayDate end = IntraDayDate.create(tomorrow, zero()); Iterable<PartialDay> days = start.daysUntil(end); assertThat(IntraDayDate.toList(days), delimitedBy(start, end)); } @Test(expected = IllegalArgumentException.class) public void untilADayAgo() { IntraDayDate start = IntraDayDate.create(today, zero()); IntraDayDate end = IntraDayDate.create(tomorrow, zero()); end.daysUntil(start); } @Test public void untilTwoDaysLaterReturnsTwoPartialDays() { IntraDayDate start = IntraDayDate.create(today, zero()); IntraDayDate middle = IntraDayDate.create(today.plusDays(1), zero()); IntraDayDate end = IntraDayDate.create(today.plusDays(2), zero()); Iterable<PartialDay> days = start.daysUntil(end); assertThat(IntraDayDate.toList(days), delimitedBy(start, middle, end)); } @Test public void canNowTheNumberOfDaysBetweenTwoDates() { IntraDayDate start = IntraDayDate.create(today, zero()); IntraDayDate end = IntraDayDate.create(today.plusDays(1), zero()); assertThat(start.numberOfDaysUntil(end), equalTo(1)); assertThat( start.numberOfDaysUntil(IntraDayDate.create(today, hours(8))), equalTo(0)); assertThat( IntraDayDate.create(today, hours(4)).numberOfDaysUntil( IntraDayDate.create(tomorrow, hours(3))), equalTo(0)); assertThat( IntraDayDate.create(today, hours(4)).numberOfDaysUntil( IntraDayDate.create(tomorrow, hours(4))), equalTo(1)); assertThat( IntraDayDate.create(today, hours(4)).numberOfDaysUntil( IntraDayDate.create(tomorrow, hours(5))), equalTo(1)); assertThat( IntraDayDate.create(today, hours(4)).numberOfDaysUntil( IntraDayDate.create(tomorrow.plusDays(1), hours(3))), equalTo(1)); } @Test(expected = IllegalArgumentException.class) public void theEndMustBeEqualOrBiggerThanTheStart() { IntraDayDate start = IntraDayDate.create(today, zero()); IntraDayDate end = IntraDayDate.create(today.plusDays(1), zero()); assertThat(end.numberOfDaysUntil(start), equalTo(1)); } public void theEndCanBeTheSameAsTheStart() { IntraDayDate start = IntraDayDate.create(today, zero()); assertThat(start.numberOfDaysUntil(start), equalTo(0)); } @Test public void worksWellWithEffortDurationsNotZero() { IntraDayDate start = IntraDayDate.create(today, halfHour); IntraDayDate end = IntraDayDate.create(today, oneHour); IntraDayDate anotherEnd = IntraDayDate.create(tomorrow, halfHour); assertThat(IntraDayDate.toList(start.daysUntil(end)), delimitedBy(start, end)); assertThat(IntraDayDate.toList(start.daysUntil(anotherEnd)), delimitedBy(start, IntraDayDate.create(tomorrow, zero()), anotherEnd)); } @Test public void canHaveAnExtraCondition() { IntraDayDate start = IntraDayDate.create(today, zero()); IntraDayDate end = IntraDayDate.create(today.plusDays(10), zero()); final List<PartialDay> generated = new ArrayList<IntraDayDate.PartialDay>(); Iterable<PartialDay> days = start.daysUntil(new UntilEnd(end) { @Override protected boolean hasNext(boolean lessThanEnd) { return lessThanEnd && generated.size() < 2; } }); for (PartialDay each : days) { generated.add(each); } assertThat(generated.size(), equalTo(2)); } @Test public void aPartialDayCanLimitAWorkingDayDuration() { PartialDay day = new PartialDay(IntraDayDate.create(today, halfHour), IntraDayDate.create(today, oneHour)); assertThat(day.limitWorkingDay(hours(10)), equalTo(minutes(30))); assertThat(day.limitWorkingDay(minutes(40)), equalTo(minutes(10))); PartialDay completeDay = new PartialDay(IntraDayDate.startOfDay(today), IntraDayDate.startOfDay(tomorrow)); assertThat(completeDay.limitWorkingDay(hours(10)), equalTo(hours(10))); PartialDay startsInTheMiddle = new PartialDay(IntraDayDate.create( today, EffortDuration.hours(3)), IntraDayDate.startOfDay(tomorrow)); assertThat(startsInTheMiddle.limitWorkingDay(hours(10)), equalTo(hours(7))); assertThat(startsInTheMiddle.limitWorkingDay(hours(3)), equalTo(zero())); assertThat(startsInTheMiddle.limitWorkingDay(hours(2)), equalTo(zero())); PartialDay startAndEndInSameDay = new PartialDay(IntraDayDate.create( today, EffortDuration.hours(3)), IntraDayDate.create(today, EffortDuration.hours(6))); assertThat(startAndEndInSameDay.limitWorkingDay(hours(4)), equalTo(hours(1))); assertThat(startAndEndInSameDay.limitWorkingDay(hours(5)), equalTo(hours(2))); assertThat(startAndEndInSameDay.limitWorkingDay(hours(6)), equalTo(hours(3))); assertThat(startAndEndInSameDay.limitWorkingDay(hours(10)), equalTo(hours(3))); } private Matcher<Iterable<PartialDay>> delimitedBy( final IntraDayDate... dates) { return new BaseMatcher<Iterable<PartialDay>>() { @Override public boolean matches(Object param) { List<IntraDayDate> list = toPoints(param); return list.equals(Arrays.asList(dates)); } @SuppressWarnings("unchecked") private List<IntraDayDate> toPoints(Object param) { return toPoints((Iterable<PartialDay>) param); } private List<IntraDayDate> toPoints(Iterable<PartialDay> param) { List<IntraDayDate> result = new ArrayList<IntraDayDate>(); PartialDay last = null; for (PartialDay each : param) { result.add(each.getStart()); last = each; } if (last != null) { result.add(last.getEnd()); } return result; } @Override public void describeTo(Description description) { description.appendText(Arrays.asList(group(dates)) .toString()); } private Object group(IntraDayDate[] dates) { List<Object> result = new ArrayList<Object>(); for (int i = 0; i < dates.length - 1; i++) { result.add(Arrays.asList(dates[i], dates[i + 1])); } return result; } }; } @Test public void roundsUpGoesToTheNextLocalDateOrKeepsTheSame() { assertThat(IntraDayDate.create(today, halfHour).roundUp(), equalTo(today.plusDays(1))); assertThat(IntraDayDate.startOfDay(today).roundUp(), equalTo(today)); } @Test public void roundDownGoesToPreviousLocalDateOrKeepsTheSame() { assertThat(IntraDayDate.create(today, halfHour).roundDown(), equalTo(today)); assertThat(IntraDayDate.startOfDay(today).roundDown(), equalTo(today)); } @Test public void canIncreaseAnIntraDayDayBySomeEffort() { LocalDate today = new LocalDate(); IntraDayDate intraDate = IntraDayDate.create(today, hours(2)); EffortDuration[] efforts = { hours(4), hours(8), hours(12), hours(0), hours(16) }; for (EffortDuration effort : efforts) { IntraDayDate newDay = intraDate.increaseBy( ResourcesPerDay.amount(1), effort); assertThat(newDay.getDate(), equalTo(today)); assertThat(newDay.getEffortDuration(), equalTo(hours(2) .plus(effort))); } } @Test public void theAmountOfTheIncreaseDependsOnTheAmountOfResourcesPerDay() { LocalDate today = new LocalDate(); IntraDayDate intraDate = IntraDayDate.startOfDay(today); EffortDuration effort = hours(6); ResourcesPerDay half = ResourcesPerDay.amount(new BigDecimal(BigInteger .valueOf(5), 1)); ResourcesPerDay oneQuarter = ResourcesPerDay.amount(new BigDecimal( BigInteger.valueOf(25), 2)); ResourcesPerDay[] resourcesPerDays = { ResourcesPerDay.amount(2), ResourcesPerDay.amount(3), half, oneQuarter, ResourcesPerDay.amount(4) }; EffortDuration[] ends = { hours(3), hours(2), hours(12), hours(24), hours(1).and(30, Granularity.MINUTES) }; for (int i = 0; i < resourcesPerDays.length; i++) { ResourcesPerDay r = resourcesPerDays[i]; EffortDuration end = ends[i]; IntraDayDate newDay = intraDate.increaseBy(r, effort); assertThat(newDay.getDate(), equalTo(today)); assertThat(newDay.getEffortDuration(), equalTo(end)); } } @Test public void canDecreaseAnIntraDayBySomeEffort() { LocalDate today = new LocalDate(); IntraDayDate intraDate = IntraDayDate.create(today, hours(10)); EffortDuration[] efforts = { hours(4), hours(8), hours(5), hours(10), hours(1) }; for (EffortDuration effort : efforts) { IntraDayDate newDay = intraDate.decreaseBy( ResourcesPerDay.amount(1), effort); assertThat(newDay.getDate(), equalTo(today)); assertThat(newDay.getEffortDuration(), equalTo(hours(10).minus(effort))); } } @Test public void theDateAlwaysKeepsBeingTheSameWhenDecreasing() { LocalDate today = new LocalDate(); IntraDayDate intraDate = IntraDayDate.create(today, hours(10)); assertThat(intraDate.decreaseBy(ResourcesPerDay.amount(1), hours(12)) .getDate(), equalTo(today)); } @Test public void calculateEffortBetweenTwoDates() { IntraDayDate start = IntraDayDate.create(today, zero()); IntraDayDate end = IntraDayDate.create(today.plusDays(3), hours(0)); assertThat(start.effortUntil(end), equalTo(hours(8 * 3))); end = IntraDayDate.create(today.plusDays(3), hours(8)); assertThat(start.effortUntil(end), equalTo(hours(8 * 4))); start = IntraDayDate.create(today, hours(8)); end = IntraDayDate.create(today.plusDays(3), hours(0)); assertThat(start.effortUntil(end), equalTo(hours(8 * 2))); start = IntraDayDate.create(today, hours(3)); assertThat(start.effortUntil(end), equalTo(hours(8 * 2 + 5))); end = IntraDayDate.create(today.plusDays(3), hours(6)); assertThat(start.effortUntil(end), equalTo(hours(8 * 2 + 5 + 6))); end = IntraDayDate.create(today.plusDays(3), minutes(30)); assertThat(start.effortUntil(end), equalTo(hours(8 * 2 + 5).plus(minutes(30)))); start = IntraDayDate.create(today, hours(5).plus(minutes(40))); assertThat(start.effortUntil(end), equalTo(hours(8 * 2 + 2).plus(minutes(50)))); } @Test public void calculateAddEffort() { IntraDayDate start = IntraDayDate.create(today, zero()); assertThat(start.addEffort(hours(24)), equalTo(IntraDayDate.create(today.plusDays(3), hours(0)))); assertThat(start.addEffort(hours(20)), equalTo(IntraDayDate.create(today.plusDays(2), hours(4)))); assertThat( start.addEffort(hours(20).plus(minutes(20))), equalTo(IntraDayDate.create(today.plusDays(2), hours(4).plus(minutes(20))))); start = IntraDayDate.create(today, hours(3)); assertThat(start.addEffort(hours(24)), equalTo(IntraDayDate.create(today.plusDays(3), hours(3)))); assertThat(start.addEffort(hours(20)), equalTo(IntraDayDate.create(today.plusDays(2), hours(7)))); assertThat(start.addEffort(hours(20).plus(minutes(20))), equalTo(IntraDayDate.create(today.plusDays(2), hours(7).plus(minutes(20))))); start = IntraDayDate.create(today, hours(3).plus(minutes(40))); assertThat( start.addEffort(hours(24)), equalTo(IntraDayDate.create(today.plusDays(3), hours(3).plus(minutes(40))))); assertThat( start.addEffort(hours(20)), equalTo(IntraDayDate.create(today.plusDays(2), hours(7).plus(minutes(40))))); assertThat(start.addEffort(hours(20).plus(minutes(20))), equalTo(IntraDayDate.create(today.plusDays(3), hours(0)))); assertThat( start.addEffort(hours(20).plus(minutes(10))), equalTo(IntraDayDate.create(today.plusDays(2), hours(7).plus(minutes(50))))); } }