StoredDoubleTimeSeriesTest.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.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Iterators;
import com.powsybl.commons.json.JsonUtil;
import com.powsybl.timeseries.json.TimeSeriesJsonModule;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.threeten.extra.Interval;

import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
 */
class StoredDoubleTimeSeriesTest {

    @Test
    void test() throws IOException {
        RegularTimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T01:45:00Z"),
                                                                     Duration.ofMinutes(15));
        TimeSeriesMetadata metadata = new TimeSeriesMetadata("ts1", TimeSeriesDataType.DOUBLE, Collections.emptyMap(), index);
        UncompressedDoubleDataChunk chunk = new UncompressedDoubleDataChunk(2, new double[] {1d, 2d});
        CompressedDoubleDataChunk chunk2 = new CompressedDoubleDataChunk(5, 3, new double[] {3d, 4d}, new int[] {1, 2});
        assertEquals(TimeSeriesDataType.DOUBLE, chunk.getDataType());
        StoredDoubleTimeSeries timeSeries = new StoredDoubleTimeSeries(metadata, chunk, chunk2);
        assertSame(metadata, timeSeries.getMetadata());
        assertEquals(Arrays.asList(chunk, chunk2), timeSeries.getChunks());
        assertArrayEquals(new double[] {Double.NaN, Double.NaN, 1d, 2d, Double.NaN, 3d, 4d, 4d}, timeSeries.toArray(), 0d);
        DoublePoint[] pointsRef = {new DoublePoint(0, Instant.parse("2015-01-01T00:00:00Z"), Double.NaN),
                                   new DoublePoint(2, Instant.parse("2015-01-01T00:30:00Z"), 1d),
                                   new DoublePoint(3, Instant.parse("2015-01-01T00:45:00Z"), 2d),
                                   new DoublePoint(4, Instant.parse("2015-01-01T01:00:00Z"), Double.NaN),
                                   new DoublePoint(5, Instant.parse("2015-01-01T01:15:00Z"), 3d),
                                   new DoublePoint(6, Instant.parse("2015-01-01T01:30:00Z"), 4d)};
        assertArrayEquals(pointsRef, timeSeries.stream().toArray());
        assertArrayEquals(pointsRef, Iterators.toArray(timeSeries.iterator(), DoublePoint.class));

        // json test
        String jsonRef = String.join(System.lineSeparator(),
                "{",
                "  \"metadata\" : {",
                "    \"name\" : \"ts1\",",
                "    \"dataType\" : \"DOUBLE\",",
                "    \"tags\" : [ ],",
                "    \"regularIndex\" : {",
                "      \"startTime\" : 1420070400000,",
                "      \"endTime\" : 1420076700000,",
                "      \"spacing\" : 900000",
                "    }",
                "  },",
                "  \"chunks\" : [ {",
                "    \"offset\" : 2,",
                "    \"values\" : [ 1.0, 2.0 ]",
                "  }, {",
                "    \"offset\" : 5,",
                "    \"uncompressedLength\" : 3,",
                "    \"stepValues\" : [ 3.0, 4.0 ],",
                "    \"stepLengths\" : [ 1, 2 ]",
                "  } ]",
                "}"
               );
        String json = JsonUtil.toJson(timeSeries::writeJson);
        assertEquals(jsonRef, json);
        List<TimeSeries> timeSeriesList = TimeSeries.parseJson(json);
        assertEquals(1, timeSeriesList.size());
        String json2 = JsonUtil.toJson(timeSeriesList.get(0)::writeJson);
        assertEquals(json, json2);

        // test json with object mapper
        ObjectMapper objectMapper = JsonUtil.createObjectMapper()
                .registerModule(new TimeSeriesJsonModule());

        assertEquals(timeSeries, objectMapper.readValue(objectMapper.writeValueAsString(timeSeries), DoubleTimeSeries.class));
    }

    @Test
    void splitTest() {
        doSplitTest(3, 10);
    }

    @Test
    void splitTestHuge() {
        doSplitTest(100000003, 100000010);
    }

    private void doSplitTest(int chunkposition, int totalsize) {
        TimeSeriesIndex index = Mockito.mock(TimeSeriesIndex.class);
        Mockito.when(index.getPointCount()).thenReturn(totalsize);
        TimeSeriesMetadata metadata = new TimeSeriesMetadata("ts1", TimeSeriesDataType.DOUBLE, Collections.emptyMap(), index);
        UncompressedDoubleDataChunk chunk = new UncompressedDoubleDataChunk(chunkposition,
                new double[] {0d, 0d, 0d, 0d, 0d });
        StoredDoubleTimeSeries timeSeries = new StoredDoubleTimeSeries(metadata, chunk);
        List<DoubleTimeSeries> split = timeSeries.split(2);

        // check there is 3 new chunks
        assertEquals(3, split.size());

        // check first chunk
        assertInstanceOf(StoredDoubleTimeSeries.class, split.get(0));
        assertEquals(1, ((StoredDoubleTimeSeries) split.get(0)).getChunks().size());
        assertInstanceOf(UncompressedDoubleDataChunk.class, ((StoredDoubleTimeSeries) split.get(0)).getChunks().get(0));
        assertEquals(chunkposition, ((StoredDoubleTimeSeries) split.get(0)).getChunks().get(0).getOffset());
        assertEquals(1, ((StoredDoubleTimeSeries) split.get(0)).getChunks().get(0).getLength());

        // check second chunk
        assertInstanceOf(StoredDoubleTimeSeries.class, split.get(1));
        assertEquals(1, ((StoredDoubleTimeSeries) split.get(1)).getChunks().size());
        assertInstanceOf(UncompressedDoubleDataChunk.class, ((StoredDoubleTimeSeries) split.get(1)).getChunks().get(0));
        assertEquals(chunkposition + 1, ((StoredDoubleTimeSeries) split.get(1)).getChunks().get(0).getOffset());
        assertEquals(2, ((StoredDoubleTimeSeries) split.get(1)).getChunks().get(0).getLength());

        // check third chunk
        assertInstanceOf(StoredDoubleTimeSeries.class, split.get(2));
        assertEquals(1, ((StoredDoubleTimeSeries) split.get(2)).getChunks().size());
        assertInstanceOf(UncompressedDoubleDataChunk.class, ((StoredDoubleTimeSeries) split.get(2)).getChunks().get(0));
        assertEquals(chunkposition + 3, ((StoredDoubleTimeSeries) split.get(2)).getChunks().get(0).getOffset());
        assertEquals(2, ((StoredDoubleTimeSeries) split.get(2)).getChunks().get(0).getLength());
    }

    @Test
    void testCreate() {
        TimeSeriesIndex index = new RegularTimeSeriesIndex(Instant.ofEpochMilli(0), Instant.ofEpochMilli(2), Duration.ofMillis(1));
        DoubleTimeSeries ts1 = TimeSeries.createDouble("ts1", index, 0d, 1d, 2d);
        assertEquals("ts1", ts1.getMetadata().getName());
        assertEquals(TimeSeriesDataType.DOUBLE, ts1.getMetadata().getDataType());
        assertArrayEquals(new double[] {0d, 1d, 2d}, ts1.toArray(), 0d);
    }

    @Test
    void splitMultiChunkTimeSeriesTest() {
        TimeSeriesIndex index = Mockito.mock(TimeSeriesIndex.class);
        Mockito.when(index.getPointCount()).thenReturn(6);
        TimeSeriesMetadata metadata = new TimeSeriesMetadata("ts1", TimeSeriesDataType.DOUBLE, Collections.emptyMap(), index);
        UncompressedDoubleDataChunk chunk = new UncompressedDoubleDataChunk(0,
                new double[]{0d, 1d, 2d, 3d, 4d, 5d});
        DataChunk.Split<DoublePoint, DoubleDataChunk> splitChunk = chunk.splitAt(3);
        StoredDoubleTimeSeries timeSeries = new StoredDoubleTimeSeries(metadata, splitChunk.getChunk1(), splitChunk.getChunk2());
        List<List<DoubleTimeSeries>> split = TimeSeries.split(Collections.singletonList(timeSeries), 2);

        // check there is 3 new time series
        assertEquals(3, split.size());

        // check first chunk
        assertEquals(1, split.get(0).size());
        assertInstanceOf(StoredDoubleTimeSeries.class, split.get(0).get(0));
        StoredDoubleTimeSeries ts = (StoredDoubleTimeSeries) split.get(0).get(0);
        assertEquals(1, ts.getChunks().size());
        assertInstanceOf(UncompressedDoubleDataChunk.class, ts.getChunks().get(0));
        assertEquals(0, ts.getChunks().get(0).getOffset());
        assertEquals(2, ts.getChunks().get(0).getLength());

        // check second chunk
        assertEquals(1, split.get(1).size());
        assertInstanceOf(StoredDoubleTimeSeries.class, split.get(1).get(0));
        ts = (StoredDoubleTimeSeries) split.get(1).get(0);
        assertEquals(1, ts.getChunks().size());
        assertInstanceOf(UncompressedDoubleDataChunk.class, ts.getChunks().get(0));
        assertEquals(2, ts.getChunks().get(0).getOffset());
        assertEquals(2, ts.getChunks().get(0).getLength());

        // check third chunk
        assertEquals(1, split.get(2).size());
        assertInstanceOf(StoredDoubleTimeSeries.class, split.get(2).get(0));
        ts = (StoredDoubleTimeSeries) split.get(2).get(0);
        assertEquals(1, ts.getChunks().size());
        assertInstanceOf(UncompressedDoubleDataChunk.class, ts.getChunks().get(0));
        assertEquals(4, ts.getChunks().get(0).getOffset());
        assertEquals(2, ts.getChunks().get(0).getLength());
    }

    @Test
    void testCreateError() {
        RegularTimeSeriesIndex index = new RegularTimeSeriesIndex(Instant.ofEpochMilli(0), Instant.ofEpochMilli(2), Duration.ofMillis(1));
        IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> TimeSeries.createDouble("ts1", index, 0d, 1d));
        assertTrue(e.getMessage().contains("Bad number of values 2, expected 3"));
    }
}