// This file is part of OpenTSDB. // Copyright (C) 2015 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.query.expression; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Collections; import java.util.List; import net.opentsdb.core.DataPoint; import net.opentsdb.core.DataPoints; import net.opentsdb.core.SeekableView; import net.opentsdb.core.SeekableViewsForTest; import net.opentsdb.core.TSQuery; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import com.stumbleupon.async.Deferred; @RunWith(PowerMockRunner.class) @PowerMockIgnore({"javax.management.*", "javax.xml.*", "ch.qos.*", "org.slf4j.*", "com.sum.*", "org.xml.*"}) @PrepareForTest({ TSQuery.class }) public class TestMovingAverage { private static long START_TIME = 1356998400000L; private static int INTERVAL = 60000; private static int NUM_POINTS = 5; private static String METRIC = "sys.cpu"; private TSQuery data_query; private SeekableView view; private DataPoints dps; private DataPoints[] group_bys; private List<DataPoints[]> query_results; private List<String> params; private MovingAverage func; @Before public void before() throws Exception { view = SeekableViewsForTest.generator(START_TIME, INTERVAL, NUM_POINTS, true, 1, 1); data_query = mock(TSQuery.class); when(data_query.startTime()).thenReturn(START_TIME); when(data_query.endTime()).thenReturn(START_TIME + (INTERVAL * NUM_POINTS)); dps = PowerMockito.mock(DataPoints.class); when(dps.iterator()).thenReturn(view); when(dps.metricNameAsync()).thenReturn(Deferred.fromResult(METRIC)); group_bys = new DataPoints[] { dps }; query_results = new ArrayList<DataPoints[]>(1); query_results.add(group_bys); params = new ArrayList<String>(1); func = new MovingAverage(); } @Test public void evaluateWindow1dps() throws Exception { params.add("1"); final DataPoints[] results = func.evaluate(data_query, query_results, params); assertEquals(1, results.length); assertEquals(METRIC, results[0].metricName()); long ts = START_TIME; double v = 1; for (DataPoint dp : results[0]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; v += 1; } } @Test public void evaluateWindow2dps() throws Exception { params.add("2"); final DataPoints[] results = func.evaluate(data_query, query_results, params); assertEquals(1, results.length); assertEquals(METRIC, results[0].metricName()); long ts = START_TIME; double v = 0; for (DataPoint dp : results[0]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; if (v < 1) { v = 1.5; } else { v += 1; } } } @Test public void evaluateWindow5dps() throws Exception { params.add("5"); final DataPoints[] results = func.evaluate(data_query, query_results, params); assertEquals(1, results.length); assertEquals(METRIC, results[0].metricName()); long ts = START_TIME; double v = 0; for (DataPoint dp : results[0]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; if (ts == 1356998640000L) { v = 3.0; } } } @Test public void evaluateWindow6dps() throws Exception { params.add("6"); final DataPoints[] results = func.evaluate(data_query, query_results, params); assertEquals(1, results.length); assertEquals(METRIC, results[0].metricName()); long ts = START_TIME; double v = 0; for (DataPoint dp : results[0]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; } } @Test public void evaluateWindow1min() throws Exception { params.add("'1min'"); final DataPoints[] results = func.evaluate(data_query, query_results, params); assertEquals(1, results.length); assertEquals(METRIC, results[0].metricName()); long ts = START_TIME; double v = 0; for (DataPoint dp : results[0]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; if (v < 1) { v = 2; } else { v += 1; } } } @Test public void evaluateWindow2min() throws Exception { params.add("'2min'"); final DataPoints[] results = func.evaluate(data_query, query_results, params); assertEquals(1, results.length); assertEquals(METRIC, results[0].metricName()); long ts = START_TIME; double v = 0; for (DataPoint dp : results[0]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; if (ts == 1356998520000L) { v = 2.5; } else if (v > 0) { v += 1; } } } @Test public void evaluateWindow3min() throws Exception { params.add("'3min'"); final DataPoints[] results = func.evaluate(data_query, query_results, params); assertEquals(1, results.length); assertEquals(METRIC, results[0].metricName()); long ts = START_TIME; double v = 0; for (DataPoint dp : results[0]) { System.out.println(dp.timestamp() + " : " + dp.doubleValue()); assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; if (ts == 1356998580000L) { v = 3; } else if (v > 0) { v += 1; } } } @Test public void evaluateWindow4min() throws Exception { params.add("'4min'"); final DataPoints[] results = func.evaluate(data_query, query_results, params); assertEquals(1, results.length); assertEquals(METRIC, results[0].metricName()); long ts = START_TIME; double v = 0; for (DataPoint dp : results[0]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; if (ts == 1356998640000L) { v = 3.5; } } } @Test public void evaluateWindow5min() throws Exception { params.add("'5min'"); final DataPoints[] results = func.evaluate(data_query, query_results, params); assertEquals(1, results.length); assertEquals(METRIC, results[0].metricName()); long ts = START_TIME; double v = 0; for (DataPoint dp : results[0]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; } } @Test public void evaluateGroupBy() throws Exception { params.add("1"); SeekableView view2 = SeekableViewsForTest.generator(START_TIME, INTERVAL, NUM_POINTS, true, 10, 1); DataPoints dps2 = PowerMockito.mock(DataPoints.class); when(dps2.iterator()).thenReturn(view2); when(dps2.metricNameAsync()).thenReturn(Deferred.fromResult("sys.mem")); group_bys = new DataPoints[] { dps, dps2 }; query_results.clear(); query_results.add(group_bys); final DataPoints[] results = func.evaluate(data_query, query_results, params); assertEquals(2, results.length); assertEquals(METRIC, results[0].metricName()); assertEquals("sys.mem", results[1].metricName()); long ts = START_TIME; double v = 1; for (DataPoint dp : results[0]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; v += 1; } ts = START_TIME; v = 10; for (DataPoint dp : results[1]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; v += 1; } } @Test public void evaluateSubQuery() throws Exception { params.add("1"); SeekableView view2 = SeekableViewsForTest.generator(START_TIME, INTERVAL, NUM_POINTS, true, 10, 1); DataPoints dps2 = PowerMockito.mock(DataPoints.class); when(dps2.iterator()).thenReturn(view2); when(dps2.metricNameAsync()).thenReturn(Deferred.fromResult("sys.mem")); DataPoints[] group_bys2 = new DataPoints[] { dps2 }; query_results.add(group_bys2); final DataPoints[] results = func.evaluate(data_query, query_results, params); assertEquals(2, results.length); assertEquals(METRIC, results[0].metricName()); assertEquals("sys.mem", results[1].metricName()); long ts = START_TIME; double v = 1; for (DataPoint dp : results[0]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; v += 1; } ts = START_TIME; v = 10; for (DataPoint dp : results[1]) { assertEquals(ts, dp.timestamp()); assertFalse(dp.isInteger()); assertEquals(v, dp.doubleValue(), 0.001); ts += INTERVAL; v += 1; } } @Test (expected = IllegalArgumentException.class) public void evaluateNullQuery() throws Exception { params.add("1"); func.evaluate(null, query_results, params); } @Test public void evaluateNullResults() throws Exception { params.add("1"); final DataPoints[] results = func.evaluate(data_query, null, params); assertEquals(0, results.length); } @Test (expected = IllegalArgumentException.class) public void evaluateNullParams() throws Exception { func.evaluate(data_query, query_results, null); } @Test public void evaluateEmptyResults() throws Exception { params.add("1"); final DataPoints[] results = func.evaluate(data_query, Collections.<DataPoints[]>emptyList(), params); assertEquals(0, results.length); } @Test (expected = IllegalArgumentException.class) public void evaluateEmptyParams() throws Exception { func.evaluate(data_query, query_results, params); } @Test (expected = IllegalArgumentException.class) public void evaluateWindowIsZeroDataPoints() throws Exception { params.add("0"); func.evaluate(data_query, query_results, params); } @Test (expected = IllegalArgumentException.class) public void evaluateWindowIsZeroTime() throws Exception { params.add("'0sec'"); func.evaluate(data_query, query_results, params); } @Test (expected = IllegalArgumentException.class) public void evaluateWindowTimedMissingQuotes() throws Exception { params.add("60sec"); func.evaluate(data_query, query_results, params); } @Test (expected = IllegalArgumentException.class) public void evaluateWindowNull() throws Exception { params.add(null); func.evaluate(data_query, query_results, params); } @Test (expected = IllegalArgumentException.class) public void evaluateWindowEmpty() throws Exception { params.add(""); func.evaluate(data_query, query_results, params); } @Test (expected = IllegalArgumentException.class) public void evaluateWindowUnknown() throws Exception { params.add("somethingelse"); func.evaluate(data_query, query_results, params); } @Test (expected = IllegalArgumentException.class) public void evaluateWindowNotFirstParam() throws Exception { params.add("somethingelse"); params.add("60"); func.evaluate(data_query, query_results, params); } @Test public void writeStringField() throws Exception { params.add("1"); assertEquals("movingAverage(inner_expression)", func.writeStringField(params, "inner_expression")); assertEquals("movingAverage(null)", func.writeStringField(params, null)); assertEquals("movingAverage()", func.writeStringField(params, "")); assertEquals("movingAverage(inner_expression)", func.writeStringField(null, "inner_expression")); } @Test public void parseParam() throws Exception { // second assertEquals(1000, func.parseParam("'1sec'")); assertEquals(1000, func.parseParam("'1s'")); assertEquals(5000, func.parseParam("'5sec'")); assertEquals(5000, func.parseParam("'5s'")); // minute assertEquals(60000, func.parseParam("'1min'")); assertEquals(60000, func.parseParam("'1m'")); assertEquals(300000, func.parseParam("'5min'")); assertEquals(300000, func.parseParam("'5m'")); // hour assertEquals(3600000, func.parseParam("'1hr")); assertEquals(3600000, func.parseParam("'1h'")); assertEquals(3600000, func.parseParam("'1hour'")); assertEquals(18000000, func.parseParam("'5hr'")); assertEquals(18000000, func.parseParam("'5h'")); assertEquals(18000000, func.parseParam("'5hour'")); // day assertEquals(86400000, func.parseParam("'1day'")); assertEquals(86400000, func.parseParam("'1d'")); assertEquals(432000000, func.parseParam("'5day'")); assertEquals(432000000, func.parseParam("'5d'")); // TODO - fix it, closing with a 1 seems to work instead of a ' assertEquals(1000, func.parseParam("'1sec1")); // missing quotes try { func.parseParam("'1sec"); fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException e) { } try { func.parseParam("1sec'"); fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException e) { } try { func.parseParam("1sec"); fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException e) { } // no numbers or units try { func.parseParam("'sec'"); fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException e) { } try { func.parseParam("'60'"); fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException e) { } // null or empty or short try { func.parseParam(null); fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException e) { } try { func.parseParam(""); fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException e) { } try { func.parseParam("'"); fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException e) { } // floating point try { func.parseParam("'1.5sec'"); fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException e) { } } }