// This file is part of OpenTSDB.
// Copyright (C) 2014 The OpenTSDB Authors.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 2.1 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 Lesser
// General Public License for more details. You should have received a copy
// of the GNU Lesser General Public License along with this program. If not,
// see <http://www.gnu.org/licenses/>.
package net.opentsdb.core;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.NoSuchElementException;
import org.junit.Test;
/** Helper class to mock SeekableView. */
public class SeekableViewsForTest {
/**
* Creates a {@link SeekableView} object to iterate the given data points.
* @param data_points Test data.
* @return A {@link SeekableView} object
*/
public static SeekableView fromArray(final DataPoint[] data_points) {
return new MockSeekableView(data_points);
}
/**
* Creates a {@link SeekableView} that generates a sequence of data points
* where the starting value is 1 and it is incremented by 1 each iteration.
* @param start_time Starting timestamp
* @param sample_period Average sample period of data points
* @param num_data_points Total number of data points to generate
* @param is_integer True to generate a sequence of integer data points.
* @return A {@link SeekableView} object
*/
public static SeekableView generator(final long start_time,
final long sample_period,
final int num_data_points,
final boolean is_integer) {
return generator(start_time, sample_period, num_data_points,
is_integer, 0, 1);
}
/**
* Creates a {@link SeekableView} that generates a sequence of data points.
* @param start_time Starting timestamp
* @param sample_period Average sample period of data points
* @param num_data_points Total number of data points to generate
* @param is_integer True to generate a sequence of integer data points.
* @param starting_value The starting data point value.
* @param increment How much to increment the values each iteration.
* @return A {@link SeekableView} object
*/
public static SeekableView generator(final long start_time,
final long sample_period,
final int num_data_points,
final boolean is_integer,
final double starting_value,
final double increment) {
return generator(start_time, sample_period, num_data_points,
is_integer, starting_value, increment, false);
}
/**
* Creates a {@link SeekableView} that generates a sequence of data points.
* @param start_time Starting timestamp
* @param sample_period Average sample period of data points
* @param num_data_points Total number of data points to generate
* @param is_integer True to generate a sequence of integer data points.
* @param starting_value The starting data point value.
* @param increment How much to increment the values each iteration.
* @param wholes_as_integer Whether or not to return whole numbers (1.0, 2.0,
* etc) as integers to test for functions that should support both.
* Note: Ignored if is_integer is true.
* @return A {@link SeekableView} object
*/
public static SeekableView generator(final long start_time,
final long sample_period,
final int num_data_points,
final boolean is_integer,
final double starting_value,
final double increment,
final boolean wholes_as_integer) {
return new DataPointGenerator(start_time, sample_period, num_data_points,
is_integer, starting_value, increment, wholes_as_integer);
}
/** Iterates an array of data points. */
public static class MockSeekableView implements SeekableView {
private final DataPoint[] data_points;
private int index = 0;
MockSeekableView(final DataPoint[] data_points) {
this.data_points = data_points;
}
@Override
public boolean hasNext() {
return data_points.length > index;
}
@Override
public DataPoint next() {
if (hasNext()) {
return data_points[index++];
}
throw new NoSuchElementException("no more values");
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void seek(long timestamp) {
for (index = 0; index < data_points.length; ++index) {
if (data_points[index].timestamp() >= timestamp) {
break;
}
}
}
public void resetIndex() {
index = 0;
}
}
/** Generates a sequence of data points. */
private static class DataPointGenerator implements SeekableView {
private final long sample_period_ms;
private final int num_data_points;
private final boolean is_integer;
private final double increment;
private final MutableDataPoint current_data = new MutableDataPoint();
private final MutableDataPoint next_data = new MutableDataPoint();
private final boolean wholes_as_integer;
private int dps_emitted = 0;
DataPointGenerator(final long start_time_ms, final long sample_period_ms,
final int num_data_points, final boolean is_integer,
final double starting_value, final double increment,
final boolean wholes_as_integer) {
this.sample_period_ms = sample_period_ms;
this.num_data_points = num_data_points;
this.is_integer = is_integer;
this.increment = increment;
this.wholes_as_integer = wholes_as_integer;
if (is_integer) {
next_data.reset(start_time_ms, (long)starting_value);
} else {
if (wholes_as_integer &&
(starting_value == Math.floor(starting_value)) &&
!Double.isInfinite(starting_value)) {
next_data.reset(start_time_ms, (long)starting_value);
} else {
next_data.reset(start_time_ms, starting_value);
}
}
}
@Override
public boolean hasNext() {
return dps_emitted < num_data_points;
}
@Override
public DataPoint next() {
if (hasNext()) {
current_data.reset(next_data);
advance();
return current_data;
}
throw new NoSuchElementException("no more values");
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void seek(long timestamp) {
while (next_data.timestamp() < timestamp && dps_emitted < num_data_points) {
advance();
}
}
private void advance() {
if (is_integer) {
next_data.reset(next_data.timestamp() + sample_period_ms,
next_data.longValue() + (long)increment);
} else {
final double next = next_data.toDouble() + increment;
if (wholes_as_integer &&
(next == Math.floor(next)) && !Double.isInfinite(next)) {
next_data.reset(next_data.timestamp() + sample_period_ms, (long)next);
} else {
next_data.reset(next_data.timestamp() + sample_period_ms, next);
}
}
dps_emitted++;
}
}
@Test
public void testDataPointGenerator() {
SeekableView dpg = generator(100000, 10000, 5, true);
DataPoint[] expected_data_points = new DataPoint[] {
MutableDataPoint.ofLongValue(100000, 0),
MutableDataPoint.ofLongValue(110000, 1),
MutableDataPoint.ofLongValue(120000, 2),
MutableDataPoint.ofLongValue(130000, 3),
MutableDataPoint.ofLongValue(140000, 4),
};
for (DataPoint expected: expected_data_points) {
assertTrue(dpg.hasNext());
DataPoint dp = dpg.next();
assertEquals(expected.timestamp(), dp.timestamp());
assertEquals(expected.longValue(), dp.longValue());
}
assertFalse(dpg.hasNext());
}
@Test
public void testDataPointGenerator_double() {
SeekableView dpg = generator(100000, 10000, 5, false);
DataPoint[] expected_data_points = new DataPoint[] {
MutableDataPoint.ofDoubleValue(100000, 0),
MutableDataPoint.ofDoubleValue(110000, 1),
MutableDataPoint.ofDoubleValue(120000, 2),
MutableDataPoint.ofDoubleValue(130000, 3),
MutableDataPoint.ofDoubleValue(140000, 4),
};
for (DataPoint expected: expected_data_points) {
assertTrue(dpg.hasNext());
DataPoint dp = dpg.next();
assertEquals(expected.timestamp(), dp.timestamp());
assertEquals(expected.doubleValue(), dp.doubleValue(), 0);
}
assertFalse(dpg.hasNext());
}
@Test
public void testDataPointGenerator_seek() {
SeekableView dpg = generator(100000, 10000, 5, true);
dpg.seek(119000);
DataPoint[] expected_data_points = new DataPoint[] {
MutableDataPoint.ofLongValue(120000, 2),
MutableDataPoint.ofLongValue(130000, 3),
MutableDataPoint.ofLongValue(140000, 4),
};
for (DataPoint expected: expected_data_points) {
assertTrue(dpg.hasNext());
DataPoint dp = dpg.next();
assertEquals(expected.timestamp(), dp.timestamp());
assertEquals(expected.longValue(), dp.longValue());
}
assertFalse(dpg.hasNext());
}
@Test
public void testDataPointGenerator_seekToFirst() {
SeekableView dpg = generator(100000, 10000, 5, true);
dpg.seek(100000);
DataPoint[] expected_data_points = new DataPoint[] {
MutableDataPoint.ofLongValue(100000, 0),
MutableDataPoint.ofLongValue(110000, 1),
MutableDataPoint.ofLongValue(120000, 2),
MutableDataPoint.ofLongValue(130000, 3),
MutableDataPoint.ofLongValue(140000, 4),
};
for (DataPoint expected: expected_data_points) {
assertTrue(dpg.hasNext());
DataPoint dp = dpg.next();
assertEquals(expected.timestamp(), dp.timestamp());
assertEquals(expected.longValue(), dp.longValue());
}
assertFalse(dpg.hasNext());
}
@Test
public void testDataPointGenerator_seekToSecond() {
SeekableView dpg = generator(100000, 10000, 5, true);
dpg.seek(100001);
DataPoint[] expected_data_points = new DataPoint[] {
MutableDataPoint.ofLongValue(110000, 1),
MutableDataPoint.ofLongValue(120000, 2),
MutableDataPoint.ofLongValue(130000, 3),
MutableDataPoint.ofLongValue(140000, 4),
};
for (DataPoint expected: expected_data_points) {
assertTrue(dpg.hasNext());
DataPoint dp = dpg.next();
assertEquals(expected.timestamp(), dp.timestamp());
assertEquals(expected.longValue(), dp.longValue());
}
assertFalse(dpg.hasNext());
}
@Test
public void testDataPointGeneratorWholes() {
SeekableView dpg = generator(100000, 10000, 5, false, 0, 1.5, true);
DataPoint[] expected_data_points = new DataPoint[] {
MutableDataPoint.ofLongValue(100000, 0),
MutableDataPoint.ofDoubleValue(110000, 1.5),
MutableDataPoint.ofLongValue(120000, 3),
MutableDataPoint.ofDoubleValue(130000, 4.5),
MutableDataPoint.ofLongValue(140000, 6),
};
for (DataPoint expected: expected_data_points) {
assertTrue(dpg.hasNext());
DataPoint dp = dpg.next();
assertEquals(expected.timestamp(), dp.timestamp());
if (expected.isInteger()) {
assertEquals(expected.longValue(), dp.longValue());
} else {
assertEquals(expected.doubleValue(), dp.doubleValue(), 0.001);
}
}
assertFalse(dpg.hasNext());
}
}