// 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 org.junit.After;
import org.junit.Test;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import static fi.hsl.parkandride.core.domain.prediction.RelativizedAverageOfPreviousWeeksPredictor.LOOKBACK_MINUTES;
import static org.assertj.core.api.Assertions.assertThat;
@Transactional
public class RelativizedAverageOfPreviousWeeksPredictorTest extends AbstractPredictorTest {
public RelativizedAverageOfPreviousWeeksPredictorTest() {
super(new RelativizedAverageOfPreviousWeeksPredictor());
}
// the predictor is independent of system time, so the latest utilization defines "now"
@After
public void checkUpdatesLatestUtilization() {
if (latestInsertedUtilization.isPresent()) {
assertThat(predictorState.latestUtilization).isEqualTo(latestInsertedUtilization.get().timestamp);
}
}
@Test
public void when_less_than_6_days_of_history_then_no_predictions() {
insertUtilization(now.minusDays(6).plusSeconds(1), 666);
List<Prediction> predictions = predict();
assertThat(predictions).isEmpty();
}
@Test
public void when_1_week_old_history_exists_and_recent_utilization_is_at_same_level_then_predicts_similar_to_last_weeks_utilization() {
insertUtilization(now.minusDays(7), 10);
insertUtilization(now.minusDays(7).plusMinutes(5), 11);
insertUtilization(now.minusDays(7).plusMinutes(10), 12);
insertUtilization(now.minusDays(7).plusMinutes(15), 13);
insertUtilization(now.minusDays(7).plusMinutes(20), 14);
insertUtilization(now.minus(LOOKBACK_MINUTES), 10);
insertUtilization(now, 10);
List<Prediction> predictions = predict();
assertThat(predictions).containsSubsequence(
new Prediction(now, 10),
new Prediction(now.plusMinutes(5), 11),
new Prediction(now.plusMinutes(10), 12),
new Prediction(now.plusMinutes(15), 13),
new Prediction(now.plusMinutes(20), 14));
}
@Test
public void when_1_week_old_and_recent_history_have_same_trend_but_different_level_then_prediction_adapts_to_current_utilization_level() {
insertUtilization(now.minusDays(7).minus(LOOKBACK_MINUTES), 6);
insertUtilization(now.minusDays(7).minusMinutes(15), 7);
insertUtilization(now.minusDays(7).minusMinutes(10), 8);
insertUtilization(now.minusDays(7).minusMinutes(5), 9);
insertUtilization(now.minusDays(7), 10);
insertUtilization(now.minusDays(7).plusMinutes(5), 11);
insertUtilization(now.minusDays(7).plusMinutes(10), 12);
insertUtilization(now.minusDays(7).plusMinutes(15), 13);
insertUtilization(now.minusDays(7).plusMinutes(20), 14);
insertUtilization(now.minus(LOOKBACK_MINUTES), 16);
insertUtilization(now.minusMinutes(15), 17);
insertUtilization(now.minusMinutes(10), 18);
insertUtilization(now.minusMinutes(5), 19);
List<Prediction> predictions = predict();
assertThat(utilizationHistory.getLatest())
.isPresent();
assertThat(utilizationHistory.getLatest().get().spacesAvailable).isEqualTo(19);
assertThat(predictions).containsSubsequence(
new Prediction(now, 20),
new Prediction(now.plusMinutes(5), 21),
new Prediction(now.plusMinutes(10), 22),
new Prediction(now.plusMinutes(15), 23),
new Prediction(now.plusMinutes(20), 24));
}
@Test
public void when_recent_changes_are_twice_as_fast_as_history_changes_then_prediction_is_relative_to_recent_changes() {
insertUtilization(now.minusDays(7).minus(LOOKBACK_MINUTES), 6);
insertUtilization(now.minusDays(7).minusMinutes(15), 7);
insertUtilization(now.minusDays(7).minusMinutes(10), 8);
insertUtilization(now.minusDays(7).minusMinutes(5), 9);
insertUtilization(now.minusDays(7), 10);
insertUtilization(now.minusDays(7).plusMinutes(5), 11);
insertUtilization(now.minusDays(7).plusMinutes(10), 12);
insertUtilization(now.minusDays(7).plusMinutes(15), 13);
insertUtilization(now.minusDays(7).plusMinutes(20), 15);
insertUtilization(now.minus(LOOKBACK_MINUTES), 12);
insertUtilization(now.minusMinutes(15), 14);
insertUtilization(now.minusMinutes(10), 16);
insertUtilization(now.minusMinutes(5), 18);
List<Prediction> predictions = predict();
assertThat(utilizationHistory.getLatest())
.isPresent();
assertThat(utilizationHistory.getLatest().get().spacesAvailable).isEqualTo(18);
assertThat(predictions).containsSubsequence(
new Prediction(now, 20),
new Prediction(now.plusMinutes(5), 22),
new Prediction(now.plusMinutes(10), 24),
new Prediction(now.plusMinutes(15), 26),
new Prediction(now.plusMinutes(20), 30));
}
@Test
public void when_recent_changes_are_slower_than_changes_in_history_then_prediction_is_similar_to_history() {
insertUtilization(now.minusDays(7).minus(LOOKBACK_MINUTES), 2);
insertUtilization(now.minusDays(7).minusMinutes(15), 4);
insertUtilization(now.minusDays(7).minusMinutes(10), 6);
insertUtilization(now.minusDays(7).minusMinutes(5), 8);
insertUtilization(now.minusDays(7), 10);
insertUtilization(now.minusDays(7).plusMinutes(5), 12);
insertUtilization(now.minusDays(7).plusMinutes(10), 14);
insertUtilization(now.minusDays(7).plusMinutes(15), 16);
insertUtilization(now.minusDays(7).plusMinutes(20), 20);
insertUtilization(now.minus(LOOKBACK_MINUTES), 14);
insertUtilization(now.minusMinutes(15), 15);
insertUtilization(now.minusMinutes(10), 17);
insertUtilization(now.minusMinutes(5), 18);
List<Prediction> predictions = predict();
assertThat(utilizationHistory.getLatest())
.isPresent();
assertThat(utilizationHistory.getLatest().get().spacesAvailable).isEqualTo(18);
assertThat(predictions).containsSubsequence(
new Prediction(now, 20),
new Prediction(now.plusMinutes(5), 22),
new Prediction(now.plusMinutes(10), 24),
new Prediction(now.plusMinutes(15), 26),
new Prediction(now.plusMinutes(20), 30));
}
@Test
public void available_spaces_cannot_go_to_negative_values() {
insertUtilization(now.minusDays(7).minus(LOOKBACK_MINUTES), 20);
insertUtilization(now.minusDays(7).minusMinutes(15), 19);
insertUtilization(now.minusDays(7).minusMinutes(10), 18);
insertUtilization(now.minusDays(7).minusMinutes(5), 17);
insertUtilization(now.minusDays(7), 16);
insertUtilization(now.minusDays(7).plusMinutes(5), 15);
insertUtilization(now.minusDays(7).plusMinutes(10), 14);
insertUtilization(now.minusDays(7).plusMinutes(15), 13);
insertUtilization(now.minusDays(7).plusMinutes(20), 12);
insertUtilization(now.minus(LOOKBACK_MINUTES), 4);
insertUtilization(now.minusMinutes(15), 3);
insertUtilization(now.minusMinutes(10), 2);
insertUtilization(now.minusMinutes(5), 1);
List<Prediction> predictions = predict();
assertThat(utilizationHistory.getLatest())
.isPresent();
assertThat(utilizationHistory.getLatest().get().spacesAvailable).isEqualTo(1);
assertThat(predictions).containsSubsequence(
new Prediction(now, 0),
new Prediction(now.plusMinutes(5), 0),
new Prediction(now.plusMinutes(10), 0),
new Prediction(now.plusMinutes(15), 0),
new Prediction(now.plusMinutes(20), 0));
}
@Test
public void available_spaces_cannot_be_more_than_max_available_capacity() {
insertUtilization(now.minusDays(7), 10);
insertUtilization(now.minusDays(7).plusMinutes(5), 11);
insertUtilization(now.minusDays(7).plusMinutes(10), 12);
insertUtilization(now.minusDays(7).plusMinutes(15), 13);
insertUtilization(now.minusDays(7).plusMinutes(20), 14);
insertUtilization(now.minus(LOOKBACK_MINUTES), 10);
insertUtilization(now, 10);
availableMaxCapacity = 12;
List<Prediction> predictions = predict();
assertThat(predictions).containsSubsequence(
new Prediction(now, 10),
new Prediction(now.plusMinutes(5), 11),
new Prediction(now.plusMinutes(10), 12),
new Prediction(now.plusMinutes(15), 12),
new Prediction(now.plusMinutes(20), 12));
}
}