// 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.filter;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.opentsdb.core.BaseTsdbTest;
import net.opentsdb.core.TSDB;
import net.opentsdb.uid.NoSuchUniqueName;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import com.stumbleupon.async.DeferredGroupException;
@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*", "javax.xml.*",
"ch.qos.*", "org.slf4j.*",
"com.sum.*", "org.xml.*"})
@PrepareForTest({ TSDB.class })
public class TestTagVFilter extends BaseTsdbTest {
@Test (expected = IllegalArgumentException.class)
public void getFilterNullTagk() throws Exception {
TagVFilter.getFilter(null, "myflter");
}
@Test (expected = IllegalArgumentException.class)
public void getFilterEmptyTagk() throws Exception {
TagVFilter.getFilter(null, "myflter");
}
@Test (expected = IllegalArgumentException.class)
public void getFilterEmptyFilter() throws Exception {
TagVFilter.getFilter(TAGK_STRING, "");
}
@Test (expected = IllegalArgumentException.class)
public void getFilterNullFilter() throws Exception {
TagVFilter.getFilter(TAGK_STRING, null);
}
@Test
public void getFilterGroupBy() throws Exception {
assertNull(TagVFilter.getFilter(TAGK_STRING, "*"));
}
@Test
public void getFilterLiteral() throws Exception {
assertNull(TagVFilter.getFilter(TAGK_STRING, TAGV_STRING));
}
@Test
public void getFilterGroupByPiped() throws Exception {
assertNull(TagVFilter.getFilter(TAGK_STRING, "web01|web02"));
}
@Test
public void getFilterWildcard() throws Exception {
final TagVFilter filter = TagVFilter.getFilter(TAGK_STRING,
TagVWildcardFilter.FILTER_NAME + "(*bonk.com)");
assertEquals(TAGK_STRING, filter.getTagk());
assertTrue(filter instanceof TagVWildcardFilter);
assertFalse(((TagVWildcardFilter)filter).isCaseInsensitive());
}
@Test
public void getFilterWildcardInsensitive() throws Exception {
final TagVFilter filter = TagVFilter.getFilter(TAGK_STRING,
TagVWildcardFilter.TagVIWildcardFilter.FILTER_NAME + "(*bonk.com)");
assertEquals(TAGK_STRING, filter.getTagk());
assertTrue(filter instanceof TagVWildcardFilter);
assertTrue(((TagVWildcardFilter)filter).isCaseInsensitive());
}
@Test
public void getFilterWildcardFatfinger() throws Exception {
// falls through to the shortcut
final TagVFilter filter = TagVFilter.getFilter(TAGK_STRING,
"wil@*sugarbean");
assertEquals(TAGK_STRING, filter.getTagk());
assertTrue(filter instanceof TagVWildcardFilter);
assertTrue(((TagVWildcardFilter)filter).isCaseInsensitive());
}
@Test
public void getFilterWildcardImplicit() throws Exception {
final TagVFilter filter = TagVFilter.getFilter(TAGK_STRING, "*bonk.com");
assertEquals(TAGK_STRING, filter.getTagk());
assertTrue(filter instanceof TagVWildcardFilter);
assertTrue(((TagVWildcardFilter)filter).isCaseInsensitive());
}
@Test
public void getFilterPipe() throws Exception {
final TagVFilter filter = TagVFilter.getFilter(TAGK_STRING,
TagVLiteralOrFilter.FILTER_NAME + "(quirm|bonk)");
assertEquals(TAGK_STRING, filter.getTagk());
assertTrue(filter instanceof TagVLiteralOrFilter);
assertFalse(((TagVLiteralOrFilter)filter).isCaseInsensitive());
}
@Test
public void getFilterPipeInsensitive() throws Exception {
final TagVFilter filter = TagVFilter.getFilter(TAGK_STRING,
TagVLiteralOrFilter.TagVILiteralOrFilter.FILTER_NAME + "(quirm|bonk)");
assertEquals(TAGK_STRING, filter.getTagk());
assertTrue(filter instanceof TagVLiteralOrFilter);
assertTrue(((TagVLiteralOrFilter)filter).isCaseInsensitive());
}
@Test
public void getFilterPipeFatfinger() throws Exception {
assertNull(TagVFilter.getFilter(TAGK_STRING, "lite@sugarbean|granny"));
}
@Test
public void getFilterRegex() throws Exception {
final TagVFilter filter = TagVFilter.getFilter(TAGK_STRING,
TagVRegexFilter.FILTER_NAME + "(.*sugarbean)");
assertEquals(TAGK_STRING, filter.getTagk());
assertTrue(filter instanceof TagVRegexFilter);
}
@Test
public void getFilterRegexFatFinger() throws Exception {
// falls through to the implicity
final TagVFilter filter = TagVFilter.getFilter(TAGK_STRING, "rexp@.*sugarbean");
assertEquals(TAGK_STRING, filter.getTagk());
assertTrue(filter instanceof TagVWildcardFilter);
assertTrue(((TagVWildcardFilter)filter).isCaseInsensitive());
}
@Test
public void getFilterRegexCase() throws Exception {
final TagVFilter filter = TagVFilter.getFilter(TAGK_STRING,
TagVRegexFilter.FILTER_NAME.toUpperCase() + "(.*sugarbean)");
assertEquals(TAGK_STRING, filter.getTagk());
assertTrue(filter instanceof TagVRegexFilter);
}
@Test (expected = IllegalArgumentException.class)
public void getFilterMissingClosingParens() throws Exception {
TagVFilter.getFilter(TAGK_STRING, TagVRegexFilter.FILTER_NAME + "(.*sugarbean");
}
@Test (expected = IllegalArgumentException.class)
public void getFilterEmptyParens() throws Exception {
TagVFilter.getFilter(TAGK_STRING, TagVRegexFilter.FILTER_NAME + "()");
}
@Test (expected = IllegalArgumentException.class)
public void getFilterUnknownType() throws Exception {
TagVFilter.getFilter(TAGK_STRING, "dummyfilter(nothere)");
}
@Test
public void resolveName() throws Exception {
final TagVFilter filter = new TagVWildcardFilter(TAGK_STRING, "*omnia");
filter.resolveTagkName(tsdb).join();
assertArrayEquals(TAGK_BYTES, filter.getTagkBytes());
assertTrue(filter.getTagVUids().isEmpty());
}
@Test
public void resolveNameLiteral() throws Exception {
final TagVFilter filter = new TagVLiteralOrFilter(TAGK_STRING, TAGV_STRING);
filter.resolveTagkName(tsdb).join();
assertArrayEquals(TAGK_BYTES, filter.getTagkBytes());
assertEquals(1, filter.getTagVUids().size());
assertArrayEquals(TAGV_BYTES, filter.getTagVUids().get(0));
}
@Test
public void resolveNameLiterals() throws Exception {
final TagVFilter filter = new TagVLiteralOrFilter(TAGK_STRING, "web01|web02");
filter.resolveTagkName(tsdb).join();
assertArrayEquals(TAGK_BYTES, filter.getTagkBytes());
assertEquals(2, filter.getTagVUids().size());
assertArrayEquals(TAGV_BYTES, filter.getTagVUids().get(0));
assertArrayEquals(TAGV_B_BYTES, filter.getTagVUids().get(1));
}
@Test (expected = DeferredGroupException.class)
public void resolveNameLiteralsNSUNTagV() throws Exception {
final TagVFilter filter = new TagVLiteralOrFilter(TAGK_STRING, "web01|web03");
filter.resolveTagkName(tsdb).join();
}
@Test
public void resolveNameLiteralsNSUNTagvSkipped() throws Exception {
config.overrideConfig("tsd.query.skip_unresolved_tagvs", "true");
final TagVFilter filter = new TagVLiteralOrFilter(TAGK_STRING, "web01|web03");
filter.resolveTagkName(tsdb).join();
assertArrayEquals(TAGK_BYTES, filter.getTagkBytes());
assertEquals(1, filter.getTagVUids().size());
assertArrayEquals(TAGV_BYTES, filter.getTagVUids().get(0));
}
@Test
public void resolveNameLiteralsTooMany() throws Exception {
config.overrideConfig("tsd.query.filter.expansion_limit", "1");
final TagVFilter filter = new TagVLiteralOrFilter(TAGK_STRING, "web01|web02");
filter.resolveTagkName(tsdb).join();
assertArrayEquals(TAGK_BYTES, filter.getTagkBytes());
assertTrue(filter.getTagVUids().isEmpty());
}
@Test
public void resolveNameLiteralsCaseInsensitive() throws Exception {
final TagVFilter filter = new TagVLiteralOrFilter(TAGK_STRING, "web01|web02",
true);
filter.resolveTagkName(tsdb).join();
assertArrayEquals(TAGK_BYTES, filter.getTagkBytes());
assertTrue(filter.getTagVUids().isEmpty());
}
@Test (expected = NoSuchUniqueName.class)
public void resolveNameNSUN() throws Exception {
final TagVFilter filter = new TagVWildcardFilter(NSUN_TAGK, "*omnia");
filter.resolveTagkName(tsdb).join();
}
@Test (expected = NullPointerException.class)
public void resolveNameNullTSDB() throws Exception {
new TagVWildcardFilter("host", "*omnia").resolveTagkName(null);
}
@Test
public void comparableTest() throws Exception {
final TagVFilter filter_a = new TagVWildcardFilter("host", "*omnia");
Whitebox.setInternalState(filter_a, "tagk_bytes", new byte[] { 0, 0, 0, 1 });
final TagVFilter filter_b = new TagVRegexFilter("dc", ".*katch");
Whitebox.setInternalState(filter_b, "tagk_bytes", new byte[] { 0, 0, 0, 2 });
assertEquals(0, filter_a.compareTo(filter_a));
assertEquals(-1, filter_a.compareTo(filter_b));
assertEquals(1, filter_b.compareTo(filter_a));
Whitebox.setInternalState(filter_a, "tagk_bytes", (byte[])null);
assertEquals(0, filter_a.compareTo(filter_a));
assertEquals(-1, filter_a.compareTo(filter_b));
assertEquals(1, filter_b.compareTo(filter_a));
Whitebox.setInternalState(filter_b, "tagk_bytes", (byte[])null);
assertEquals(0, filter_a.compareTo(filter_a));
assertEquals(0, filter_a.compareTo(filter_b));
assertEquals(0, filter_b.compareTo(filter_a));
}
@Test
public void stripParentheses() throws Exception {
assertEquals(".*sugarbean", TagVFilter.stripParentheses(
TagVRegexFilter.FILTER_NAME + "(.*sugarbean)"));
}
@Test
public void stripParenthesesEmptyParentheses() throws Exception {
// let the filter's ctor handle this case
assertEquals("", TagVFilter.stripParentheses(
TagVRegexFilter.FILTER_NAME + "()"));
}
@Test (expected = IllegalArgumentException.class)
public void stripParenthesesMissingClosing() throws Exception {
TagVFilter.stripParentheses(TagVRegexFilter.FILTER_NAME + "(.*sugarbean");
}
@Test (expected = IllegalArgumentException.class)
public void stripParenthesesMissingOpening() throws Exception {
TagVFilter.stripParentheses("regexp.*sugarbean)");
}
@Test (expected = IllegalArgumentException.class)
public void stripParenthesesNull() throws Exception {
TagVFilter.stripParentheses(null);
}
@Test (expected = IllegalArgumentException.class)
public void stripParenthesesEmpty() throws Exception {
TagVFilter.stripParentheses("");
}
@SuppressWarnings("unchecked")
@Test
public void tagsToFiltersOldGroupBy() throws Exception {
final Map<String, String> tags = new HashMap<String, String>(3);
tags.put("host", "quirm"); // literal
tags.put("owner", "vimes|vetinary"); // pipe
tags.put("colo", "*"); // group by all
final List<TagVFilter> filters = new ArrayList<TagVFilter>(3);
TagVFilter.tagsToFilters(tags, filters);
assertEquals(3, filters.size());
for (final TagVFilter filter : filters) {
if (filter.getTagk().equals("host")) {
assertTrue(filter instanceof TagVLiteralOrFilter);
assertFalse(((TagVLiteralOrFilter)filter).isCaseInsensitive());
assertEquals(1, ((Set<String>)Whitebox
.getInternalState(filter, "literals")).size());
} else if (filter.getTagk().equals("owner")) {
assertTrue(filter instanceof TagVLiteralOrFilter);
assertFalse(((TagVLiteralOrFilter)filter).isCaseInsensitive());
assertEquals(2, ((Set<String>)Whitebox
.getInternalState(filter, "literals")).size());
} else if (filter.getTagk().equals("colo")) {
assertTrue(filter instanceof TagVWildcardFilter);
assertTrue(((TagVWildcardFilter)filter).isCaseInsensitive());
} else {
fail("Unexpected filter type: " + filter);
}
assertTrue(filter.isGroupBy());
}
}
@Test
public void tagsToFiltersNewFunctions() throws Exception {
final Map<String, String> tags = new HashMap<String, String>(4);
tags.put("host", "*beybi");
tags.put("owner", "wildcard(*snapcase*)");
tags.put("colo", "regexp(.*opolis)");
tags.put("geo", "literal_or(tsort|chalk)");
final List<TagVFilter> filters = new ArrayList<TagVFilter>(3);
TagVFilter.tagsToFilters(tags, filters);
assertEquals(4, filters.size());
for (final TagVFilter filter : filters) {
if (filter.getTagk().equals("host")) {
assertTrue(filter instanceof TagVWildcardFilter);
assertTrue(((TagVWildcardFilter)filter).isCaseInsensitive());
} else if (filter.getTagk().equals("owner")) {
assertTrue(filter instanceof TagVWildcardFilter);
assertFalse(((TagVWildcardFilter)filter).isCaseInsensitive());
} else if (filter.getTagk().equals("colo")) {
assertTrue(filter instanceof TagVRegexFilter);
} else if (filter.getTagk().equals("geo")) {
assertTrue(filter instanceof TagVLiteralOrFilter);
assertFalse(((TagVLiteralOrFilter)filter).isCaseInsensitive());
} else {
fail("Unexpected filter type: " + filter);
}
assertTrue(filter.isGroupBy());
}
}
@Test (expected = IllegalArgumentException.class)
public void tagsToFiltersNoSuchFunction() throws Exception {
final Map<String, String> tags = new HashMap<String, String>(1);
tags.put("host", "doesnotexist(*beybi)");
final List<TagVFilter> filters = new ArrayList<TagVFilter>(1);
TagVFilter.tagsToFilters(tags, filters);
}
@Test
public void tagsToFiltersDuplicate() throws Exception {
final Map<String, String> tags = new HashMap<String, String>(1);
tags.put("host", "*beybi");
final List<TagVFilter> filters = new ArrayList<TagVFilter>(1);
filters.add(new TagVWildcardFilter("host", "*beybi", true));
assertFalse(filters.get(0).isGroupBy());
TagVFilter.tagsToFilters(tags, filters);
assertEquals(1, filters.size());
assertTrue(filters.get(0).isGroupBy());
}
@Test
public void tagsToFiltersSameTagDiffValues() throws Exception {
final Map<String, String> tags = new HashMap<String, String>(1);
tags.put("host", "*beybi");
final List<TagVFilter> filters = new ArrayList<TagVFilter>(1);
filters.add(new TagVWildcardFilter("host", "*helit", true));
assertFalse(filters.get(0).isGroupBy());
TagVFilter.tagsToFilters(tags, filters);
assertEquals(2, filters.size());
}
@Test
public void getCopy() {
final TagVFilter filter = TagVFilter.Builder()
.setFilter("*")
.setTagk(TAGK_STRING)
.setType("wildcard")
.setGroupBy(true)
.build();
final TagVFilter copy = filter.getCopy();
assertNotSame(filter, copy);
assertEquals(filter.filter, copy.filter);
assertEquals(filter.tagk, copy.tagk);
assertEquals(filter.getType(), copy.getType());
assertEquals(filter.group_by, copy.group_by);
}
// TODO - test the plugin loader similar to the other plugins
}