// 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.Before; import org.junit.Test; /** * Tests {@link RateSpan}. */ public class TestRateSpan { private static final DataPoint[] DATA_POINTS = new DataPoint[] { MutableDataPoint.ofDoubleValue(1356998400000L, 40.0), MutableDataPoint.ofLongValue(1356998400000L + 2000000, 50), MutableDataPoint.ofLongValue(1357002000000L, 40), MutableDataPoint.ofDoubleValue(1357002000000L + 5000, 50.0), MutableDataPoint.ofLongValue(1357005600000L, 40), MutableDataPoint.ofDoubleValue(1357005600000L + 2000000, 50.0) }; private static final DataPoint[] RATE_DATA_POINTS = new DataPoint[] { MutableDataPoint.ofDoubleValue(1356998400000L, 40.0 / 1356998400), MutableDataPoint.ofDoubleValue(1356998400000L + 2000000, 10.0 / 2000.0), MutableDataPoint.ofDoubleValue(1357002000000L, -10.0 / (1357002000L - 1356998400L - 2000)), MutableDataPoint.ofDoubleValue(1357002000000L + 5000, 10.0 / 5.0), MutableDataPoint.ofDoubleValue(1357005600000L, -10.0 / (1357005600L - 1357002005L)), MutableDataPoint.ofDoubleValue(1357005600000L + 2000000, 10.0 / 2000.0) }; private static final DataPoint[] RATES_AFTER_SEEK = new DataPoint[] { // The first rate is calculated against the time zero, not the previous // data point. MutableDataPoint.ofDoubleValue(1357002000000L, 40.0 / 1357002000), RATE_DATA_POINTS[3], RATE_DATA_POINTS[4], RATE_DATA_POINTS[5] }; private static final long COUNTER_MAX = 70; private static final DataPoint[] RATES_FOR_COUNTER = new DataPoint[] { MutableDataPoint.ofDoubleValue(1356998400000L, 40.0 / 1356998400), MutableDataPoint.ofDoubleValue(1356998400000L + 2000000, 10.0 / 2000.0), MutableDataPoint.ofDoubleValue(1357002000000L, (40.0 + 20) / 1600.0), MutableDataPoint.ofDoubleValue(1357002000000L + 5000, 10.0 / 5.0), MutableDataPoint.ofDoubleValue(1357005600000L, (40.0 + 20) / 3595), MutableDataPoint.ofDoubleValue(1357005600000L + 2000000, 10.0 / 2000.0) }; private SeekableView source; private RateOptions options; @Before public void before() { source = SeekableViewsForTest.fromArray(DATA_POINTS); options = new RateOptions(); } @Test public void testRateSpan() { RateSpan rate_span = new RateSpan(source, options); // The first rate is between the time zero and the first data point. assertTrue(rate_span.hasNext()); DataPoint dp = rate_span.next(); assertFalse(dp.isInteger()); assertEquals(1356998400000L, dp.timestamp()); assertEquals(40.0 / 1356998400L, dp.doubleValue(), 0); // The second rate comes from the first two data points. assertTrue(rate_span.hasNext()); DataPoint dp2 = rate_span.next(); assertFalse(dp2.isInteger()); assertEquals(1356998400000L + 2000000, dp2.timestamp()); assertEquals(10.0 / 2000.0, dp2.doubleValue(), 0); } @Test public void testNext_iterateAll() { RateSpan rate_span = new RateSpan(source, options); for (DataPoint rate : RATE_DATA_POINTS) { assertTrue(rate_span.hasNext()); assertTrue(rate_span.hasNext()); DataPoint dp = rate_span.next(); String msg = String.format("expected rate = '%s' ", rate); assertFalse(msg, dp.isInteger()); assertEquals(msg, rate.timestamp(), dp.timestamp()); assertEquals(msg, rate.doubleValue(), dp.doubleValue(), 0.0000001); } assertFalse(rate_span.hasNext()); assertFalse(rate_span.hasNext()); } @Test(expected = UnsupportedOperationException.class) public void testRemove() { RateSpan rate_span = new RateSpan(source, options); rate_span.remove(); } @Test(expected = NoSuchElementException.class) public void testNext_noMoreData() { source = SeekableViewsForTest.fromArray(new DataPoint[] { MutableDataPoint.ofLongValue(1356998400000L, 40) }); RateSpan rate_span = new RateSpan(source, options); // The first rate exists. assertTrue(rate_span.hasNext()); rate_span.next(); // No second rate. assertFalse(rate_span.hasNext()); rate_span.next(); } @Test public void testSeek() { RateSpan rate_span = new RateSpan(source, options); rate_span.seek(1357002000000L); for (DataPoint rate : RATES_AFTER_SEEK) { assertTrue(rate_span.hasNext()); assertTrue(rate_span.hasNext()); DataPoint dp = rate_span.next(); String msg = String.format("expected rate = '%s' ", rate); assertFalse(msg, dp.isInteger()); assertEquals(msg, rate.timestamp(), dp.timestamp()); assertEquals(msg, rate.doubleValue(), dp.doubleValue(), 0.0000001); } assertFalse(rate_span.hasNext()); assertFalse(rate_span.hasNext()); } @Test(expected = IllegalStateException.class) public void testNext_decreasingTimestamps() { source = SeekableViewsForTest.fromArray(new DataPoint[] { MutableDataPoint.ofLongValue(1357002000000L + 5000, 50), MutableDataPoint.ofLongValue(1357002000000L + 4000, 50) }); RateSpan rate_span = new RateSpan(source, options); rate_span.next(); } @Test(expected = IllegalStateException.class) public void testMoveToNextRate_duplicatedTimestamps() { source = SeekableViewsForTest.fromArray(new DataPoint[] { MutableDataPoint.ofLongValue(1356998400000L, 40), MutableDataPoint.ofLongValue(1356998400000L + 2000000, 50), MutableDataPoint.ofLongValue(1356998400000L + 2000000, 50) }); RateSpan rate_span = new RateSpan(source, options); rate_span.next(); // Abandons the first rate to test next ones. assertTrue(rate_span.hasNext()); rate_span.next(); } @Test public void testCalculateDelta_bigLongValues() { source = SeekableViewsForTest.fromArray(new DataPoint[] { MutableDataPoint.ofLongValue(1356998400000L, Long.MAX_VALUE - 100), MutableDataPoint.ofLongValue(1356998500000L, Long.MAX_VALUE - 20) }); RateSpan rate_span = new RateSpan(source, options); rate_span.next(); // Abandons the first rate to test next ones. assertTrue(rate_span.hasNext()); DataPoint dp = rate_span.next(); assertFalse(dp.isInteger()); assertEquals(1356998500000L, dp.timestamp()); assertEquals(0.8, dp.doubleValue(), 0); assertFalse(rate_span.hasNext()); } @Test public void testNext_counter() { options = new RateOptions(true, COUNTER_MAX, RateOptions.DEFAULT_RESET_VALUE); RateSpan rate_span = new RateSpan(source, options); for (DataPoint rate : RATES_FOR_COUNTER) { assertTrue(rate_span.hasNext()); assertTrue(rate_span.hasNext()); DataPoint dp = rate_span.next(); String msg = String.format("expected rate = '%s' ", rate); assertFalse(msg, dp.isInteger()); assertEquals(msg, rate.timestamp(), dp.timestamp()); assertEquals(msg, rate.doubleValue(), dp.doubleValue(), 0.0000001); } assertFalse(rate_span.hasNext()); assertFalse(rate_span.hasNext()); } @Test public void testNext_counterLongMax() { options = new RateOptions(true, Long.MAX_VALUE, 0); source = SeekableViewsForTest.fromArray(new DataPoint[] { MutableDataPoint.ofLongValue(1356998430000L, Long.MAX_VALUE - 55), MutableDataPoint.ofLongValue(1356998460000L, Long.MAX_VALUE - 25), MutableDataPoint.ofLongValue(1356998490000L, 5), }); DataPoint[] rates = new DataPoint[] { MutableDataPoint.ofDoubleValue(1356998430000L, (Long.MAX_VALUE - 55) / 1356998430.0), MutableDataPoint.ofDoubleValue(1356998460000L, 1), MutableDataPoint.ofDoubleValue(1356998490000L, 1) }; RateSpan rate_span = new RateSpan(source, options); for (DataPoint rate : rates) { assertTrue(rate_span.hasNext()); DataPoint dp = rate_span.next(); String msg = String.format("expected rate = '%s' ", rate); assertFalse(msg, dp.isInteger()); assertEquals(msg, rate.timestamp(), dp.timestamp()); assertEquals(msg, rate.doubleValue(), dp.doubleValue(), 0.0000001); } assertFalse(rate_span.hasNext()); } @Test public void testNext_counterWithResetValue() { final long RESET_VALUE = 1; source = SeekableViewsForTest.fromArray(new DataPoint[] { MutableDataPoint.ofLongValue(1356998400000L, 40), MutableDataPoint.ofLongValue(1356998401000L, 50), MutableDataPoint.ofLongValue(1356998402000L, 40) }); DataPoint[] rates = new DataPoint[] { MutableDataPoint.ofDoubleValue(1356998400000L, 40 / 1356998400.0), MutableDataPoint.ofDoubleValue(1356998401000L, 10), // Not 60 because the change is too big compared to the reset value. MutableDataPoint.ofDoubleValue(1356998402000L, 0) }; options = new RateOptions(true, COUNTER_MAX, RESET_VALUE); RateSpan rate_span = new RateSpan(source, options); for (DataPoint rate : rates) { assertTrue(rate_span.hasNext()); assertTrue(rate_span.hasNext()); DataPoint dp = rate_span.next(); String msg = String.format("expected rate = '%s' ", rate); assertFalse(msg, dp.isInteger()); assertEquals(msg, rate.timestamp(), dp.timestamp()); assertEquals(msg, rate.doubleValue(), dp.doubleValue(), 0.0000001); } assertFalse(rate_span.hasNext()); assertFalse(rate_span.hasNext()); } @Test public void testNext_counterDroResets() { final long RESET_VALUE = 1; source = SeekableViewsForTest.fromArray(new DataPoint[] { MutableDataPoint.ofLongValue(1356998400000L, 40), MutableDataPoint.ofLongValue(1356998401000L, 50), MutableDataPoint.ofLongValue(1356998402000L, 40), MutableDataPoint.ofLongValue(1356998403000L, 50) }); DataPoint[] rates = new DataPoint[] { MutableDataPoint.ofDoubleValue(1356998400000L, 40 / 1356998400.0), MutableDataPoint.ofDoubleValue(1356998401000L, 10), // drop the point before MutableDataPoint.ofDoubleValue(1356998403000L, 10) }; options = new RateOptions(true, COUNTER_MAX, RESET_VALUE, true); RateSpan rate_span = new RateSpan(source, options); for (DataPoint rate : rates) { assertTrue(rate_span.hasNext()); assertTrue(rate_span.hasNext()); DataPoint dp = rate_span.next(); String msg = String.format("expected rate = '%s' ", rate); assertFalse(msg, dp.isInteger()); assertEquals(msg, rate.timestamp(), dp.timestamp()); assertEquals(msg, rate.doubleValue(), dp.doubleValue(), 0.0000001); } assertFalse(rate_span.hasNext()); assertFalse(rate_span.hasNext()); } @Test public void testNext_counterDroResetsNothingAfter() { final long RESET_VALUE = 1; source = SeekableViewsForTest.fromArray(new DataPoint[] { MutableDataPoint.ofLongValue(1356998400000L, 40), MutableDataPoint.ofLongValue(1356998401000L, 50), MutableDataPoint.ofLongValue(1356998402000L, 40) }); DataPoint[] rates = new DataPoint[] { MutableDataPoint.ofDoubleValue(1356998400000L, 40 / 1356998400.0), MutableDataPoint.ofDoubleValue(1356998401000L, 10), }; options = new RateOptions(true, COUNTER_MAX, RESET_VALUE, true); RateSpan rate_span = new RateSpan(source, options); for (DataPoint rate : rates) { assertTrue(rate_span.hasNext()); assertTrue(rate_span.hasNext()); DataPoint dp = rate_span.next(); String msg = String.format("expected rate = '%s' ", rate); assertFalse(msg, dp.isInteger()); assertEquals(msg, rate.timestamp(), dp.timestamp()); assertEquals(msg, rate.doubleValue(), dp.doubleValue(), 0.0000001); } assertFalse(rate_span.hasNext()); assertFalse(rate_span.hasNext()); } }