RegularTimeSeriesIndexTest.java
/**
* Copyright (c) 2017, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.timeseries;
import com.google.common.collect.Lists;
import com.google.common.testing.EqualsTester;
import com.powsybl.commons.json.JsonUtil;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.threeten.extra.Interval;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
class RegularTimeSeriesIndexTest {
@Test
void test() {
RegularTimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T01:00:00Z"),
Duration.ofMinutes(15));
// test getters
assertEquals(Instant.parse("2015-01-01T00:00:00Z").toEpochMilli(), index.getStartTime());
assertEquals(Instant.parse("2015-01-01T01:00:00Z").toEpochMilli(), index.getEndTime());
assertEquals(Duration.ofMinutes(15).toMillis(), index.getSpacing());
assertEquals(Instant.parse("2015-01-01T00:00:00Z"), index.getStartInstant());
assertEquals(Instant.parse("2015-01-01T01:00:00Z"), index.getEndInstant());
assertEquals(Duration.ofMinutes(15), index.getTimeStep());
assertEquals(5, index.getPointCount());
Instant secondInstant = index.getStartInstant().plus(index.getTimeStep());
assertEquals(secondInstant, index.getInstantAt(1));
assertEquals(Instant.parse("2015-01-01T00:15:00Z"), index.getInstantAt(1));
assertEquals(Instant.parse("2015-01-01T00:15:00Z").toEpochMilli(), index.getTimeAt(1));
}
@Test
void testIteratorsAndStream() {
RegularTimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T01:00:00Z"),
Duration.ofMinutes(15));
// test iterator and stream
List<Instant> instants = Arrays.asList(Instant.parse("2015-01-01T00:00:00Z"),
Instant.parse("2015-01-01T00:15:00Z"),
Instant.parse("2015-01-01T00:30:00Z"),
Instant.parse("2015-01-01T00:45:00Z"),
Instant.parse("2015-01-01T01:00:00Z"));
assertEquals(instants, index.stream().toList());
assertEquals(instants, Lists.newArrayList(index.iterator()));
}
@Test
void testToString() {
RegularTimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T01:00:00Z"),
Duration.ofMinutes(15));
// test to string
assertEquals("RegularTimeSeriesIndex(startInstant=2015-01-01T00:00:00Z, endInstant=2015-01-01T01:00:00Z, timeStep=PT15M)",
index.toString());
}
@Test
void testJsonSerialization() {
RegularTimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T01:00:00Z"),
Duration.ofMinutes(15));
// test json
String jsonRefMillis = String.join(System.lineSeparator(),
"{",
" \"startTime\" : 1420070400000,",
" \"endTime\" : 1420074000000,",
" \"spacing\" : 900000",
"}");
String jsonRef = String.join(System.lineSeparator(),
"{",
" \"startInstant\" : 1420070400000000000,",
" \"endInstant\" : 1420074000000000000,",
" \"timeStep\" : 900000000000",
"}");
String jsonMillis = index.toJson();
String json = index.toJson(TimeSeriesIndex.ExportFormat.NANOSECONDS);
assertEquals(jsonRefMillis, jsonMillis);
assertEquals(jsonRef, json);
RegularTimeSeriesIndex index2 = JsonUtil.parseJson(json, RegularTimeSeriesIndex::parseJson);
assertNotNull(index2);
assertEquals(index, index2);
RegularTimeSeriesIndex index3 = JsonUtil.parseJson(jsonMillis, RegularTimeSeriesIndex::parseJson);
assertNotNull(index3);
assertEquals(index, index3);
}
@Test
void testDeprecatedConstructor() {
RegularTimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T01:00:00Z"),
Duration.ofMinutes(15));
// Deprecated contructor
RegularTimeSeriesIndex index4 = new RegularTimeSeriesIndex(Instant.parse("2015-01-01T00:00:00Z").toEpochMilli(),
Instant.parse("2015-01-01T01:00:00Z").toEpochMilli(),
Duration.ofMinutes(15).toMillis());
assertEquals(index, index4);
}
@Test
void testEquals() {
new EqualsTester()
.addEqualityGroup(RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T01:00:00Z"), Duration.ofMinutes(15)),
RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T01:00:00Z"), Duration.ofMinutes(15)))
.addEqualityGroup(RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T01:15:00Z"), Duration.ofMinutes(30)),
RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T01:15:00Z"), Duration.ofMinutes(30)))
.testEquals();
}
@Test
void testContructorErrorDuration() {
Interval interval = Interval.parse("2000-01-01T00:00:00Z/2100-01-01T00:10:00Z");
Duration duration = Duration.ofSeconds(-1);
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> RegularTimeSeriesIndex.create(interval, duration));
assertEquals("Bad timeStep value PT-1S", exception.getMessage());
}
@Test
void testContructorErrorTimeStep() {
Interval interval = Interval.parse("2015-01-01T00:00:00Z/2015-01-01T00:10:00Z");
Duration duration = Duration.ofMinutes(15);
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> RegularTimeSeriesIndex.create(interval, duration));
assertEquals("TimeStep PT15M is longer than interval PT10M", exception.getMessage());
}
@Test
void testContructorErrorPointCount() {
Interval interval = Interval.parse("2000-01-01T00:00:00Z/2100-01-01T00:10:00Z");
Duration duration = Duration.ofSeconds(1);
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> RegularTimeSeriesIndex.create(interval, duration));
assertEquals("Point Count 3155760601 is bigger than max allowed value 2147483647", exception.getMessage());
}
@Test
void testPointCountSimple() {
//2 data points at 0 and 10
assertEquals(2, new RegularTimeSeriesIndex(Instant.ofEpochMilli(0), Instant.ofEpochMilli(10), Duration.ofMillis(10)).getPointCount());
}
@Test
void testPointCountRounded() {
//We allow some imprecision to simplify calendar dates
assertEquals(3, new RegularTimeSeriesIndex(Instant.ofEpochMilli(0), Instant.ofEpochMilli(19), Duration.ofMillis(10)).getPointCount());
assertEquals(3, new RegularTimeSeriesIndex(Instant.ofEpochMilli(0), Instant.ofEpochMilli(20), Duration.ofMillis(10)).getPointCount());
assertEquals(3, new RegularTimeSeriesIndex(Instant.ofEpochMilli(0), Instant.ofEpochMilli(21), Duration.ofMillis(10)).getPointCount());
//Concrete example:
// 1 data every year for 10 years (rounding hides the year length differences):
// millisInYear is not exact because of leap years, but even when taking leap years into account,
// the number of seconds in a year in not predictable because of leap seconds.
// Still it's a good enough approximation give the correct result.
long millisInYear = 365L * 86400 * 1000;
assertEquals(10, new RegularTimeSeriesIndex(
Instant.parse("2000-01-01T00:00:00Z"),
Instant.parse("2009-01-01T00:00:00Z"),
Duration.ofMillis(millisInYear)).getPointCount());
}
@Test
void testPointCountHuge() {
// 1 data every 30 seconds for ~30years, ~30years+30s, ~30years+60s
assertEquals(30 * 365 * 24 * 120 + 1, new RegularTimeSeriesIndex(Instant.ofEpochMilli(0), Instant.ofEpochMilli(30L * 365 * 24 * 60 * 60 * 1000), Duration.ofMillis(30 * 1000)).getPointCount());
assertEquals(30 * 365 * 24 * 120 + 2, new RegularTimeSeriesIndex(Instant.ofEpochMilli(0), Instant.ofEpochMilli(30L * 365 * 24 * 60 * 60 * 1000 + 30 * 1000), Duration.ofMillis(30 * 1000)).getPointCount());
assertEquals(30 * 365 * 24 * 120 + 3, new RegularTimeSeriesIndex(Instant.ofEpochMilli(0), Instant.ofEpochMilli(30L * 365 * 24 * 60 * 60 * 1000 + 2 * 30 * 1000), Duration.ofMillis(30 * 1000)).getPointCount());
}
private static Stream<String> provideWrongJson() {
String jsonException1 = """
{
"startInstant" : 1420070400000000000,
"endInstant" : 1420074000000000000
}
""".replaceAll("\n", System.lineSeparator());
String jsonException2 = """
{
"endInstant" : 1420074000000000000,
"timeStep" : 900000000000
}
""".replaceAll("\n", System.lineSeparator());
String jsonException3 = """
{
"startInstant" : 1420070400000000000,
"timeStep" : 900000000000
}
""".replaceAll("\n", System.lineSeparator());
return Stream.of(jsonException1, jsonException2, jsonException3);
}
@Test
void testContructorErrorMaxDaysReached() {
Duration duration = Duration.ofDays(210 * 365);
Instant i0 = Instant.ofEpochMilli(0);
Instant i1 = Instant.ofEpochMilli(210L * 365 * 24 * 60 * 60 * 1000 + 1);
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
() -> new RegularTimeSeriesIndex(i0, i1, duration));
assertEquals("Time range or spacing exceeds 73000 days.", exception.getMessage());
}
@ParameterizedTest
@MethodSource("provideWrongJson")
void testParsingError(String json) {
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> JsonUtil.parseJson(json, RegularTimeSeriesIndex::parseJson));
assertEquals("Incomplete regular time series index json", exception.getMessage());
}
}