package com.spotify.heroic.filter; import com.google.common.collect.ImmutableList; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; import java.util.ArrayList; import java.util.List; import java.util.TreeSet; import static com.spotify.heroic.filter.Filter.and; import static com.spotify.heroic.filter.Filter.hasTag; import static com.spotify.heroic.filter.Filter.matchKey; import static com.spotify.heroic.filter.Filter.matchTag; import static com.spotify.heroic.filter.Filter.not; import static com.spotify.heroic.filter.Filter.or; import static com.spotify.heroic.filter.Filter.regex; import static com.spotify.heroic.filter.Filter.startsWith; import static org.junit.Assert.assertEquals; @RunWith(MockitoJUnitRunner.class) public class FilterTest { @Mock Filter f1; @Mock Filter f2; private final Filter a = hasTag("a"); private final Filter b = hasTag("b"); private final Filter c = hasTag("c"); private final Filter tag1a = matchTag("foo", "a"); private final Filter tag1b = matchTag("foo", "b"); private final Filter s1a = startsWith("foo", "abc"); private final Filter s1b = startsWith("foo", "abcdef"); private final String tag = "tag"; private final String value = "value"; /** * {@link com.spotify.heroic.filter.Filter.Visitor} methods should all defer to {@link * com.spotify.heroic.filter.Filter.Visitor#defaultAction(Filter)} unless defined. */ @Test public void visitorDefaultActionTest() { final Object ref = new Object(); final Filter.Visitor<Object> v = filter -> ref; assertEquals(ref, v.visitStartsWith(Mockito.mock(StartsWithFilter.class))); assertEquals(ref, v.visitHasTag(Mockito.mock(HasTagFilter.class))); assertEquals(ref, v.visitNot(Mockito.mock(NotFilter.class))); assertEquals(ref, v.visitTrue(Mockito.mock(TrueFilter.class))); assertEquals(ref, v.visitFalse(Mockito.mock(FalseFilter.class))); assertEquals(ref, v.visitMatchTag(Mockito.mock(MatchTagFilter.class))); assertEquals(ref, v.visitMatchKey(Mockito.mock(MatchKeyFilter.class))); assertEquals(ref, v.visitAnd(Mockito.mock(AndFilter.class))); assertEquals(ref, v.visitOr(Mockito.mock(OrFilter.class))); assertEquals(ref, v.visitRaw(Mockito.mock(RawFilter.class))); assertEquals(ref, v.visitRegex(Mockito.mock(RegexFilter.class))); } @Test public void optimizeAndTest() { final Filter ref = and(a, b).optimize(); Assert.assertTrue(ref instanceof AndFilter); assertEquals(2, ((AndFilter) ref).terms().size()); assertEquals(ref, and(a, b, b).optimize()); assertEquals(ref, and(b, a).optimize()); assertEquals(ref, and(b, and(a, b)).optimize()); } @Test public void optimizeOrTest() { final Filter ref = or(a, b).optimize(); Assert.assertTrue(ref instanceof OrFilter); assertEquals(2, ((OrFilter) ref).terms().size()); assertEquals(ref, or(a, b, b).optimize()); assertEquals(ref, or(b, a).optimize()); assertEquals(ref, or(b, or(a, b)).optimize()); } @Test public void testSortOrder() { final List<List<Filter>> inputs = ImmutableList.<List<Filter>>of(ImmutableList.of(a, b, c), ImmutableList.of(c, b, a), ImmutableList.of(c, b, a)); final List<Filter> reference = ImmutableList.of(a, b, c); for (final List<Filter> input : inputs) { final List<Filter> sorted = new ArrayList<>(new TreeSet<>(input)); assertEquals(reference, sorted); } } @Test public void testOrFlatten() { assertEquals(or(a, b, c), or(a, or(b, c)).optimize()); assertEquals(or(a, b, c), or(a, or(b, or(c))).optimize()); assertEquals(or(a, b, c), or(a, not(and(not(b), not(c)))).optimize()); } @Test public void testAndFlatten() { assertEquals(and(a, b, c), and(a, and(b, c)).optimize()); assertEquals(and(a, b, c), and(a, and(b, and(c))).optimize()); assertEquals(and(a, b, c), and(a, not(or(not(b), not(c)))).optimize()); } @Test public void optimizeNotNot() { assertEquals(a, not(not(a)).optimize()); } /** * The same expression, but inverted cannot co-exist in the same and statement. */ @Test public void testAndContradiction() throws Exception { // same filter inverted assertEquals(FalseFilter.get(), and(a, not(a)).optimize()); // same tag with different values assertEquals(FalseFilter.get(), and(tag1a, tag1b).optimize()); } /** * The same expression, but inverted cannot co-exist in the same and statement. */ @Test public void testOrTautology() throws Exception { assertEquals(TrueFilter.get(), or(a, not(a)).optimize()); } @Test public void testAndStartsWith() { assertEquals(s1b, and(s1a, s1b).optimize()); assertEquals(s1b, and(s1b, s1a).optimize()); } @Test public void testOrStartsWith() { assertEquals(s1a, or(s1a, s1b).optimize()); assertEquals(s1a, or(s1b, s1a).optimize()); } @Test public void factoryMethodTest() { assertEquals(new MatchTagFilter(tag, value), matchTag(tag, value)); assertEquals(new StartsWithFilter(tag, value), startsWith(tag, value)); assertEquals(new HasTagFilter(tag), hasTag(tag)); assertEquals(new MatchKeyFilter(value), matchKey(value)); assertEquals(new RegexFilter(tag, value), regex(tag, value)); assertEquals(new AndFilter(ImmutableList.of(f1, f2)), and(f1, f2)); assertEquals(new OrFilter(ImmutableList.of(f1, f2)), or(f1, f2)); assertEquals(new NotFilter(f1), not(f1)); } }