1 // This file is part of OpenTSDB.
2 // Copyright (C) 2013  The OpenTSDB Authors.
3 //
4 // This program is free software: you can redistribute it and/or modify it
5 // under the terms of the GNU Lesser General Public License as published by
6 // the Free Software Foundation, either version 2.1 of the License, or (at your
7 // option) any later version.  This program is distributed in the hope that it
8 // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
9 // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
10 // General Public License for more details.  You should have received a copy
11 // of the GNU Lesser General Public License along with this program.  If not,
12 // see <http://www.gnu.org/licenses/>.
13 package net.opentsdb.core;
14 
15 import static org.junit.Assert.assertArrayEquals;
16 import static org.junit.Assert.assertEquals;
17 import static org.junit.Assert.assertNotNull;
18 import static org.junit.Assert.assertNull;
19 import static org.junit.Assert.assertSame;
20 import static org.junit.Assert.assertTrue;
21 import static org.junit.Assert.fail;
22 
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 
27 import net.opentsdb.core.TsdbQuery.ForTesting;
28 import net.opentsdb.query.filter.TagVFilter;
29 import net.opentsdb.query.filter.TagVWildcardFilter;
30 import net.opentsdb.storage.MockBase;
31 import net.opentsdb.uid.NoSuchUniqueName;
32 import net.opentsdb.utils.DateTime;
33 
34 import org.junit.Before;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 import org.powermock.api.mockito.PowerMockito;
38 import org.powermock.core.classloader.annotations.PrepareForTest;
39 import org.powermock.modules.junit4.PowerMockRunner;
40 
41 import com.stumbleupon.async.DeferredGroupException;
42 
43 /**
44  * This class is for unit testing the TsdbQuery class. Pretty much making sure
45  * the various ctors and methods function as expected. For actually running the
46  * queries and validating the group by and aggregation logic, see
47  * {@link TestTsdbQueryQueries}
48  */
49 @RunWith(PowerMockRunner.class)
50 @PrepareForTest({ DateTime.class })
51 public final class TestTsdbQuery extends BaseTsdbTest {
52   private TsdbQuery query = null;
53 
54   @Before
beforeLocal()55   public void beforeLocal() throws Exception {
56     query = new TsdbQuery(tsdb);
57   }
58 
59   @Test
setStartTime()60   public void setStartTime() throws Exception {
61     query.setStartTime(1356998400L);
62     assertEquals(1356998400L, query.getStartTime());
63   }
64 
65   @Test
setStartTimeZero()66   public void setStartTimeZero() throws Exception {
67     query.setStartTime(0L);
68   }
69 
70   @Test (expected = IllegalArgumentException.class)
setStartTimeInvalidNegative()71   public void setStartTimeInvalidNegative() throws Exception {
72     query.setStartTime(-1L);
73   }
74 
75   @Test (expected = IllegalArgumentException.class)
setStartTimeInvalidTooBig()76   public void setStartTimeInvalidTooBig() throws Exception {
77     query.setStartTime(17592186044416L);
78   }
79 
80   @Test (expected = IllegalArgumentException.class)
setStartTimeEqualtoEndTime()81   public void setStartTimeEqualtoEndTime() throws Exception {
82     query.setEndTime(1356998400L);
83     query.setStartTime(1356998400L);
84   }
85 
86   @Test (expected = IllegalArgumentException.class)
setStartTimeGreaterThanEndTime()87   public void setStartTimeGreaterThanEndTime() throws Exception {
88     query.setEndTime(1356998400L);
89     query.setStartTime(1356998460L);
90   }
91 
92   @Test
setEndTime()93   public void setEndTime() throws Exception {
94     query.setEndTime(1356998400L);
95     assertEquals(1356998400L, query.getEndTime());
96   }
97 
98   @Test (expected = IllegalStateException.class)
getStartTimeNotSet()99   public void getStartTimeNotSet() throws Exception {
100     query.getStartTime();
101   }
102 
103   @Test (expected = IllegalArgumentException.class)
setEndTimeInvalidNegative()104   public void setEndTimeInvalidNegative() throws Exception {
105     query.setEndTime(-1L);
106   }
107 
108   @Test (expected = IllegalArgumentException.class)
setEndTimeInvalidTooBig()109   public void setEndTimeInvalidTooBig() throws Exception {
110     query.setEndTime(17592186044416L);
111   }
112 
113   @Test (expected = IllegalArgumentException.class)
setEndTimeEqualtoEndTime()114   public void setEndTimeEqualtoEndTime() throws Exception {
115     query.setStartTime(1356998400L);
116     query.setEndTime(1356998400L);
117   }
118 
119   @Test (expected = IllegalArgumentException.class)
setEndTimeGreaterThanEndTime()120   public void setEndTimeGreaterThanEndTime() throws Exception {
121     query.setStartTime(1356998460L);
122     query.setEndTime(1356998400L);
123   }
124 
125   @Test
getEndTimeNotSet()126   public void getEndTimeNotSet() throws Exception {
127     PowerMockito.mockStatic(DateTime.class);
128     PowerMockito.when(DateTime.currentTimeMillis()).thenReturn(1357300800000L);
129     assertEquals(1357300800000L, query.getEndTime());
130   }
131 
132   @Test
setTimeSeries()133   public void setTimeSeries() throws Exception {
134     query.setTimeSeries(METRIC_STRING, tags, Aggregators.SUM, false);
135     assertNotNull(query);
136     assertArrayEquals(METRIC_BYTES, ForTesting.getMetric(query));
137     assertEquals(1, ForTesting.getFilters(query).size());
138     assertEquals(1, ForTesting.getGroupBys(query).size());
139     assertArrayEquals(TAGK_BYTES, ForTesting.getGroupBys(query).get(0));
140     assertEquals(1, ForTesting.getRowKeyLiterals(query).size());
141     assertEquals(1, ForTesting.getRowKeyLiterals(query).get(TAGV_BYTES).length);
142     assertArrayEquals(TAGV_BYTES,
143         ForTesting.getRowKeyLiterals(query).get(TAGV_BYTES)[0]);
144   }
145 
146   @Test (expected = NullPointerException.class)
setTimeSeriesNullTags()147   public void setTimeSeriesNullTags() throws Exception {
148     query.setTimeSeries(METRIC_STRING, null, Aggregators.SUM, false);
149   }
150 
151   @Test
setTimeSeriesEmptyTags()152   public void setTimeSeriesEmptyTags() throws Exception {
153     tags.clear();
154     query.setTimeSeries(METRIC_STRING, tags, Aggregators.SUM, false);
155     assertNotNull(query);
156   }
157 
158   @Test (expected = NoSuchUniqueName.class)
setTimeSeriesNosuchMetric()159   public void setTimeSeriesNosuchMetric() throws Exception {
160     query.setTimeSeries(NSUN_METRIC, tags, Aggregators.SUM, false);
161   }
162 
163   @Test (expected = NoSuchUniqueName.class)
setTimeSeriesNosuchTagk()164   public void setTimeSeriesNosuchTagk() throws Exception {
165     tags.clear();
166     tags.put(NSUN_TAGK, TAGV_STRING);
167     query.setTimeSeries(METRIC_STRING, tags, Aggregators.SUM, false);
168   }
169 
170   @Test (expected = NoSuchUniqueName.class)
setTimeSeriesNosuchTagv()171   public void setTimeSeriesNosuchTagv() throws Exception {
172     tags.put(TAGK_STRING, NSUN_TAGV);
173     query.setTimeSeries(METRIC_STRING, tags, Aggregators.SUM, false);
174   }
175 
176   @Test
setTimeSeriesTS()177   public void setTimeSeriesTS() throws Exception {
178     final List<String> tsuids = new ArrayList<String>(2);
179     tsuids.add("000001000001000001");
180     tsuids.add("000001000001000002");
181     query.setTimeSeries(tsuids, Aggregators.SUM, false);
182     assertNotNull(query);
183   }
184 
185   @Test (expected = IllegalArgumentException.class)
setTimeSeriesTSNullList()186   public void setTimeSeriesTSNullList() throws Exception {
187     query.setTimeSeries(null, Aggregators.SUM, false);
188   }
189 
190   @Test (expected = IllegalArgumentException.class)
setTimeSeriesTSEmptyList()191   public void setTimeSeriesTSEmptyList() throws Exception {
192     final List<String> tsuids = new ArrayList<String>();
193     query.setTimeSeries(tsuids, Aggregators.SUM, false);
194   }
195 
196   @Test (expected = IllegalArgumentException.class)
setTimeSeriesTSDifferentMetrics()197   public void setTimeSeriesTSDifferentMetrics() throws Exception {
198     final List<String> tsuids = new ArrayList<String>(2);
199     tsuids.add("000001000001000001");
200     tsuids.add("000002000001000002");
201     query.setTimeSeries(tsuids, Aggregators.SUM, false);
202   }
203 
204   @Test
configureFromQuery()205   public void configureFromQuery() throws Exception {
206     setDataPointStorage();
207     final TSQuery ts_query = getTSQuery();
208     ts_query.validateAndSetQuery();
209     query = new TsdbQuery(tsdb);
210     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
211 
212     assertArrayEquals(METRIC_BYTES, ForTesting.getMetric(query));
213     assertEquals(1, ForTesting.getFilters(query).size());
214     assertArrayEquals(TAGK_BYTES, ForTesting.getGroupBys(query).get(0));
215     assertEquals(1, ForTesting.getGroupBys(query).size());
216     assertNotNull(ForTesting.getRateOptions(query));
217   }
218 
219   @Test
configureFromQueryWithRate()220   public void configureFromQueryWithRate() throws Exception {
221     setDataPointStorage();
222     final TSQuery ts_query = getTSQuery();
223     final RateOptions rate_options = new RateOptions();
224     rate_options.setResetValue(1024);
225     ts_query.getQueries().get(0).setRateOptions(rate_options);
226     ts_query.validateAndSetQuery();
227     query = new TsdbQuery(tsdb);
228     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
229 
230     assertArrayEquals(METRIC_BYTES, ForTesting.getMetric(query));
231     assertEquals(1, ForTesting.getFilters(query).size());
232     assertArrayEquals(TAGK_BYTES, ForTesting.getGroupBys(query).get(0));
233     assertEquals(1, ForTesting.getGroupBys(query).size());
234     assertTrue(rate_options == ForTesting.getRateOptions(query));
235   }
236 
237   @Test
configureFromQueryNoTags()238   public void configureFromQueryNoTags() throws Exception {
239     setDataPointStorage();
240     final TSQuery ts_query = getTSQuery();
241     ts_query.getQueries().get(0).setTags(Collections.EMPTY_MAP);
242     ts_query.validateAndSetQuery();
243     query = new TsdbQuery(tsdb);
244     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
245 
246     assertArrayEquals(METRIC_BYTES, ForTesting.getMetric(query));
247     assertEquals(0, ForTesting.getFilters(query).size());
248     assertNull(ForTesting.getGroupBys(query));
249     assertNull(ForTesting.getRowKeyLiterals(query));
250   }
251 
252   @Test
configureFromQueryGroupByAll()253   public void configureFromQueryGroupByAll() throws Exception {
254     setDataPointStorage();
255     final TSQuery ts_query = getTSQuery();
256     tags.clear();
257     tags.put(TAGK_STRING, "*");
258     ts_query.getQueries().get(0).setTags(tags);
259     ts_query.validateAndSetQuery();
260     query = new TsdbQuery(tsdb);
261     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
262 
263     assertArrayEquals(METRIC_BYTES, ForTesting.getMetric(query));
264     assertEquals(1, ForTesting.getFilters(query).size());
265     assertEquals(1, ForTesting.getGroupBys(query).size());
266     assertArrayEquals(TAGK_BYTES,
267         ForTesting.getGroupBys(query).get(0));
268     assertEquals(1, ForTesting.getRowKeyLiterals(query).size());
269     assertNull(ForTesting.getRowKeyLiterals(query).get(TAGV_BYTES));
270   }
271 
272   @Test
configureFromQueryGroupByPipe()273   public void configureFromQueryGroupByPipe() throws Exception {
274     setDataPointStorage();
275     final TSQuery ts_query = getTSQuery();
276     tags.clear();
277     tags.put(TAGK_STRING, TAGV_STRING + "|" + TAGV_B_STRING);
278     ts_query.getQueries().get(0).setTags(tags);
279     ts_query.validateAndSetQuery();
280     query = new TsdbQuery(tsdb);
281     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
282 
283     assertArrayEquals(METRIC_BYTES, ForTesting.getMetric(query));
284     assertEquals(1, ForTesting.getFilters(query).size());
285     assertEquals(1, ForTesting.getGroupBys(query).size());
286     assertArrayEquals(TAGK_BYTES, ForTesting.getGroupBys(query).get(0));
287     assertEquals(1, ForTesting.getRowKeyLiterals(query).size());
288     assertEquals(2, ForTesting.getRowKeyLiterals(query).get(TAGV_BYTES).length);
289     assertArrayEquals(TAGV_BYTES,
290         ForTesting.getRowKeyLiterals(query).get(TAGV_BYTES)[0]);
291     assertArrayEquals(TAGV_B_BYTES,
292         ForTesting.getRowKeyLiterals(query).get(TAGV_BYTES)[1]);
293   }
294 
295   @Test
configureFromQueryWithGroupByFilter()296   public void configureFromQueryWithGroupByFilter() throws Exception {
297     setDataPointStorage();
298     final TSQuery ts_query = getTSQuery();
299     tags.clear();
300     tags.put("host", TagVWildcardFilter.FILTER_NAME + "(*imes)");
301     ts_query.getQueries().get(0).setTags(tags);
302     ts_query.validateAndSetQuery();
303     query = new TsdbQuery(tsdb);
304     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
305 
306     assertArrayEquals(METRIC_BYTES, ForTesting.getMetric(query));
307     assertEquals(1, ForTesting.getFilters(query).size());
308     assertEquals(1, ForTesting.getGroupBys(query).size());
309     assertArrayEquals(TAGK_BYTES, ForTesting.getGroupBys(query).get(0));
310     assertEquals(1, ForTesting.getRowKeyLiterals(query).size());
311     assertNull(ForTesting.getRowKeyLiterals(query).get(TAGV_BYTES));
312     assertNotNull(ForTesting.getRateOptions(query));
313   }
314 
315   @Test
configureFromQueryWithFilter()316   public void configureFromQueryWithFilter() throws Exception {
317     setDataPointStorage();
318     final TSQuery ts_query = getTSQuery();
319     final List<TagVFilter> filters = new ArrayList<TagVFilter>(1);
320     filters.add(new TagVWildcardFilter("host", "*imes"));
321     ts_query.getQueries().get(0).setFilters(filters);
322     ts_query.validateAndSetQuery();
323     query = new TsdbQuery(tsdb);
324     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
325 
326     assertArrayEquals(METRIC_BYTES, ForTesting.getMetric(query));
327     assertEquals(1, ForTesting.getFilters(query).size());
328     assertNull(ForTesting.getGroupBys(query));
329     assertEquals(1, ForTesting.getRowKeyLiterals(query).size());
330     assertNull(ForTesting.getRowKeyLiterals(query).get(TAGV_BYTES));
331     assertNotNull(ForTesting.getRateOptions(query));
332   }
333 
334   @Test
configureFromQueryWithGroupByAndRegularFilters()335   public void configureFromQueryWithGroupByAndRegularFilters() throws Exception {
336     setDataPointStorage();
337     final TSQuery ts_query = getTSQuery();
338     final List<TagVFilter> filters = new ArrayList<TagVFilter>(1);
339     filters.add(new TagVWildcardFilter("host", "*imes"));
340     filters.add(TagVFilter.Builder().setFilter("*").setTagk("host")
341         .setType("wildcard").setGroupBy(true).build());
342     ts_query.getQueries().get(0).setFilters(filters);
343     ts_query.validateAndSetQuery();
344 
345     query = new TsdbQuery(tsdb);
346     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
347     assertArrayEquals(METRIC_BYTES, ForTesting.getMetric(query));
348     assertEquals(2, ForTesting.getFilters(query).size());
349     assertEquals(1, ForTesting.getGroupBys(query).size());
350     assertArrayEquals(TAGK_BYTES, ForTesting.getGroupBys(query).get(0));
351     assertEquals(1, ForTesting.getRowKeyLiterals(query).size());
352     assertNull(ForTesting.getRowKeyLiterals(query).get(TAGK_BYTES));
353     assertNotNull(ForTesting.getRateOptions(query));
354   }
355 
356   @Test (expected = IllegalArgumentException.class)
configureFromQueryNullSubs()357   public void configureFromQueryNullSubs() throws Exception {
358     final TSQuery ts_query = new TSQuery();
359     new TsdbQuery(tsdb).configureFromQuery(ts_query, 0);
360   }
361 
362   @Test (expected = IllegalArgumentException.class)
configureFromQueryEmptySubs()363   public void configureFromQueryEmptySubs() throws Exception {
364     final TSQuery ts_query = new TSQuery();
365     ts_query.setQueries(new ArrayList<TSSubQuery>(0));
366     new TsdbQuery(tsdb).configureFromQuery(ts_query, 0);
367   }
368 
369   @Test (expected = IllegalArgumentException.class)
configureFromQueryNegativeIndex()370   public void configureFromQueryNegativeIndex() throws Exception {
371     final TSQuery ts_query = getTSQuery();
372     new TsdbQuery(tsdb).configureFromQuery(ts_query, -1);
373   }
374 
375   @Test (expected = IllegalArgumentException.class)
configureFromQueryIndexOutOfBounds()376   public void configureFromQueryIndexOutOfBounds() throws Exception {
377     final TSQuery ts_query = getTSQuery();
378     new TsdbQuery(tsdb).configureFromQuery(ts_query, 2);
379   }
380 
381   @Test (expected = NoSuchUniqueName.class)
configureFromQueryNSUMetric()382   public void configureFromQueryNSUMetric() throws Exception {
383     setDataPointStorage();
384     final TSQuery ts_query = getTSQuery();
385     ts_query.getQueries().get(0).setMetric(NSUN_METRIC);
386     ts_query.validateAndSetQuery();
387     query = new TsdbQuery(tsdb);
388     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
389   }
390 
391   @Test (expected = DeferredGroupException.class)
configureFromQueryNSUTagk()392   public void configureFromQueryNSUTagk() throws Exception {
393     setDataPointStorage();
394     final TSQuery ts_query = getTSQuery();
395     tags.clear();
396     tags.put(NSUN_TAGK, TAGV_STRING);
397     ts_query.getQueries().get(0).setTags(tags);
398     ts_query.validateAndSetQuery();
399     query = new TsdbQuery(tsdb);
400     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
401   }
402 
403   @Test (expected = DeferredGroupException.class)
configureFromQueryNSUTagv()404   public void configureFromQueryNSUTagv() throws Exception {
405     setDataPointStorage();
406     final TSQuery ts_query = getTSQuery();
407     tags.clear();
408     tags.put(TAGK_STRING, NSUN_TAGV);
409     ts_query.getQueries().get(0).setTags(tags);
410     ts_query.validateAndSetQuery();
411     query = new TsdbQuery(tsdb);
412     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
413   }
414 
415   @Test (expected = DeferredGroupException.class)
configureFromQueryGroupByPipeNSUTagk()416   public void configureFromQueryGroupByPipeNSUTagk() throws Exception {
417     setDataPointStorage();
418     final TSQuery ts_query = getTSQuery();
419     tags.clear();
420     tags.put(NSUN_TAGK, TAGV_STRING + "|" + TAGV_B_STRING);
421     ts_query.getQueries().get(0).setTags(tags);
422     ts_query.validateAndSetQuery();
423     query = new TsdbQuery(tsdb);
424     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
425   }
426 
427   @Test (expected = DeferredGroupException.class)
configureFromQueryGroupByPipeNSUTagv()428   public void configureFromQueryGroupByPipeNSUTagv() throws Exception {
429     setDataPointStorage();
430     final TSQuery ts_query = getTSQuery();
431     tags.clear();
432     tags.put(TAGK_STRING, TAGV_STRING + "|" + NSUN_TAGV);
433     ts_query.getQueries().get(0).setTags(tags);
434     ts_query.validateAndSetQuery();
435     query = new TsdbQuery(tsdb);
436     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
437   }
438 
439   @Test
configureFromQueryGroupByPipeNSUTagvSkipUnresolved()440   public void configureFromQueryGroupByPipeNSUTagvSkipUnresolved()
441       throws Exception {
442     config.overrideConfig("tsd.query.skip_unresolved_tagvs", "true");
443     setDataPointStorage();
444     final TSQuery ts_query = getTSQuery();
445     tags.clear();
446     tags.put(TAGK_STRING, TAGV_STRING + "|" + NSUN_TAGV);
447     ts_query.getQueries().get(0).setTags(tags);
448     ts_query.validateAndSetQuery();
449     query = new TsdbQuery(tsdb);
450     query.configureFromQuery(ts_query, 0).joinUninterruptibly();
451 
452     assertArrayEquals(METRIC_BYTES, ForTesting.getMetric(query));
453     assertEquals(1, ForTesting.getFilters(query).size());
454     assertEquals(1, ForTesting.getGroupBys(query).size());
455     assertArrayEquals(TAGK_BYTES,
456         ForTesting.getGroupBys(query).get(0));
457   }
458 
459   @Test
deleteDatapoints()460   public void deleteDatapoints() throws Exception {
461     setDataPointStorage();
462 
463     tsdb.addPoint(METRIC_STRING, 1356998400, 42, tags).joinUninterruptibly();
464     query.setStartTime(1356998400);
465     query.setTimeSeries(METRIC_STRING, tags, Aggregators.SUM, false);
466 
467     query.setDelete(true);
468     final DataPoints[] dps1 = query.run();
469     assertEquals(1, dps1.length);
470     // second run should be empty
471     final DataPoints[] dps2 = query.run();
472     assertEquals(0, dps2.length);
473   }
474 
475   @Test
scannerException()476   public void scannerException() throws Exception {
477     storeLongTimeSeriesSeconds(true, false);
478     final RuntimeException ex = new RuntimeException("Boo!");
479     storage.throwException(MockBase.stringToBytes(
480         "00000150E22700000001000001"), ex, true);
481 
482     storage.dumpToSystemOut();
483     query.setStartTime(1356998400);
484     query.setEndTime(1357041600);
485     query.setTimeSeries(METRIC_STRING, tags, Aggregators.SUM, false);
486     try {
487       query.run();
488       fail("Expected a RuntimeException");
489     } catch (RuntimeException e) {
490       assertSame(ex, e);
491     }
492   }
493 
494   /** @return a simple TSQuery object for testing */
getTSQuery()495   private TSQuery getTSQuery() {
496     final TSQuery ts_query = new TSQuery();
497     ts_query.setStart("1356998400");
498 
499     final TSSubQuery sub_query = new TSSubQuery();
500     sub_query.setMetric(METRIC_STRING);
501     sub_query.setAggregator("sum");
502 
503     sub_query.setTags(tags);
504 
505     final ArrayList<TSSubQuery> sub_queries = new ArrayList<TSSubQuery>(1);
506     sub_queries.add(sub_query);
507 
508     ts_query.setQueries(sub_queries);
509     return ts_query;
510   }
511 }
512