// Copyright © 2015 HSL <https://www.hsl.fi>
// This program is dual-licensed under the EUPL v1.2 and AGPLv3 licenses.
package fi.hsl.parkandride.core.domain.prediction;
import fi.hsl.parkandride.core.domain.Utilization;
import org.assertj.core.api.Condition;
import org.joda.time.DateTime;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static fi.hsl.parkandride.core.domain.CapacityType.CAR;
import static fi.hsl.parkandride.core.domain.Usage.HSL_TRAVEL_CARD;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(Enclosed.class)
public class UtilizationHistoryListTest {
static final DateTime LATEST_DATETIME = new DateTime(2015, 10, 23, 14, 0);
static final Long FACILITY_ID = 123L;
static final DateTime RANGE_START_DATETIME = LATEST_DATETIME.minusMinutes(20);
static final DateTime RANGE_END_DATETIME = LATEST_DATETIME.minusMinutes(10);
public static abstract class Base {
protected List<Utilization> utilizationList;
protected UtilizationHistoryList historyList;
@Before
public void setUp() throws Exception {
utilizationList = Arrays.asList(
newUtilization(LATEST_DATETIME.minusMinutes(25), 50),
newUtilization(LATEST_DATETIME.minusMinutes(20), 40),
newUtilization(LATEST_DATETIME.minusMinutes(15), 30),
newUtilization(LATEST_DATETIME.minusMinutes(10), 20),
newUtilization(LATEST_DATETIME.minusMinutes(5), 15),
newUtilization(LATEST_DATETIME, 10));
historyList = new UtilizationHistoryList(utilizationList);
}
@After
public void tearDown() throws Exception {
}
}
public static class GetLatest extends Base {
@Test
public void latest_utilization_is_returned() throws Exception {
assertThat(historyList.getLatest()).isPresent();
assertThat(historyList.getLatest().get().timestamp).isEqualTo(LATEST_DATETIME);
}
}
public static class GetRange extends Base {
@Test
public void when_start_and_end_timestamps_match_entries_then_boundary_entries_must_be_included() throws Exception {
assertThat(historyList.getRange(RANGE_START_DATETIME, RANGE_END_DATETIME))
.areNot(new TimestampBefore(RANGE_START_DATETIME))
.areNot(new TimestampAfter(RANGE_END_DATETIME))
.hasSize(3);
}
}
public static class GetUpdatesSince extends Base {
@Test
public void getUpdatesSince_is_inclusive() throws Exception {
assertThat(historyList.getUpdatesSince(RANGE_START_DATETIME))
.areNot(new TimestampBefore(RANGE_START_DATETIME))
.hasSize(5);
}
}
public static class GetAt extends Base {
@Test
public void when_getAt_is_called_with_matching_timestamp_then_Utilization_with_matching_timestamp_is_returned() {
assertThat(historyList.getAt(LATEST_DATETIME.minusMinutes(5)))
.isPresent()
.contains(newUtilization(LATEST_DATETIME.minusMinutes(5), 15));
}
@Test
public void when_getAt_is_called_with_non_matching_timestamp_then_Utilization_with_closest_previous_timestamp_is_returned() {
assertThat(historyList.getAt(LATEST_DATETIME.minusMinutes(1)))
.isPresent()
.contains(newUtilization(LATEST_DATETIME.minusMinutes(5), 15));
}
}
private static Utilization newUtilization(DateTime time, int spacesAvailable) {
Utilization u = new Utilization();
u.facilityId = FACILITY_ID;
u.capacityType = CAR;
u.usage = HSL_TRAVEL_CARD;
u.timestamp = time;
u.spacesAvailable = spacesAvailable;
return u;
}
private static class TimestampAfter extends Condition<Utilization> {
private final DateTime lowerBound;
public TimestampAfter(DateTime lowerBound) {
super("After " + lowerBound);
this.lowerBound = lowerBound;
}
@Override
public boolean matches(Utilization utilization) {
return utilization.timestamp.isAfter(lowerBound);
}
}
private static class TimestampBefore extends Condition<Utilization> {
private final DateTime upperBound;
public TimestampBefore(DateTime upperBound) {
super("Before " + upperBound);
this.upperBound = upperBound;
}
@Override
public boolean matches(Utilization utilization) {
return utilization.timestamp.isBefore(upperBound);
}
}
}