diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /predicate-search-core/src/test |
Publish
Diffstat (limited to 'predicate-search-core/src/test')
22 files changed, 2536 insertions, 0 deletions
diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/BinaryFormatTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/BinaryFormatTest.java new file mode 100644 index 00000000000..44477423fdb --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/BinaryFormatTest.java @@ -0,0 +1,124 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import com.yahoo.slime.Inspector; +import com.yahoo.slime.Slime; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class BinaryFormatTest { + + @Test + public void requireThatEncodeNullThrows() { + try { + BinaryFormat.encode(null); + fail(); + } catch (NullPointerException e) { + assertEquals("predicate", e.getMessage()); + } + } + + @Test + public void requireThatDecodeNullThrows() { + try { + BinaryFormat.decode(null); + fail(); + } catch (NullPointerException e) { + assertEquals("buf", e.getMessage()); + } + } + + @Test + public void requireThatDecodeEmptyThrows() { + try { + BinaryFormat.decode(new byte[0]); + fail(); + } catch (UnsupportedOperationException e) { + assertEquals("0", e.getMessage()); + } + } + + @Test + public void requireThatConjunctionCanBeSerialized() { + assertSerialize(new Conjunction(new FeatureSet("foo", "bar"), new FeatureSet("baz", "cox"))); + } + + @Test + public void requireThatDisjunctionCanBeSerialized() { + assertSerialize(new Disjunction(new FeatureSet("foo", "bar"), new FeatureSet("baz", "cox"))); + } + + @Test + public void requireThatFeatureRangeCanBeSerialized() { + assertSerialize(new FeatureRange("foo", null, null)); + assertSerialize(new FeatureRange("foo", null, 9L)); + assertSerialize(new FeatureRange("foo", 6L, null)); + assertSerialize(new FeatureRange("foo", 6L, 9L)); + } + + @Test + public void requireThatPartitionedFeatureRangeCanBeSerialized() { + FeatureRange expected = new FeatureRange("foo", 8L, 20L); + FeatureRange f = new FeatureRange("foo", 8L, 20L); + f.addPartition(new RangeEdgePartition("foo=0", 0, 8, -1)); + f.addPartition(new RangeEdgePartition("foo=20", 20, 0, 0)); + f.addPartition(new RangePartition("foo", 10, 19, false)); + assertSerializesTo(expected, f); + Slime slime = com.yahoo.slime.BinaryFormat.decode(BinaryFormat.encode(f)); + assertEquals(BinaryFormat.TYPE_FEATURE_RANGE, slime.get().field(BinaryFormat.NODE_TYPE).asLong()); + Inspector in1 = slime.get().field(BinaryFormat.HASHED_PARTITIONS); + assertEquals(1, in1.entries()); + assertEquals(0xf2b6d1cc6322cb99L, in1.entry(0).asLong()); + Inspector in2 = slime.get().field(BinaryFormat.HASHED_EDGE_PARTITIONS); + assertEquals(2, in2.entries()); + Inspector obj1 = in2.entry(0); + assertEquals(0xb2b301e26efffdc2L, obj1.field(BinaryFormat.HASH).asLong()); + assertEquals(0, obj1.field(BinaryFormat.VALUE).asLong()); + assertEquals(0x80000008L, obj1.field(BinaryFormat.PAYLOAD).asLong()); + Inspector obj2 = in2.entry(1); + assertEquals(0x22acb2ed72523c36L, obj2.field(BinaryFormat.HASH).asLong()); + assertEquals(20, obj2.field(BinaryFormat.VALUE).asLong()); + assertEquals(0x40000001L, obj2.field(BinaryFormat.PAYLOAD).asLong()); + } + + @Test + public void requireThatFeatureSetCanBeSerialized() { + assertSerialize(new FeatureSet("foo")); + assertSerialize(new FeatureSet("foo", "bar")); + assertSerialize(new FeatureSet("foo", "bar", "baz")); + } + + @Test + public void requireThatNegationCanBeSerialized() { + assertSerialize(new Negation(new FeatureSet("foo", "bar"))); + } + + @Test + public void requireThatBooleanCanBeSerialized() { + assertSerialize(new BooleanPredicate(true)); + assertSerialize(new BooleanPredicate(false)); + } + + @Test + public void requireThatUnknownNodeThrows() { + try { + BinaryFormat.encode(SimplePredicates.newString("foo")); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + private static void assertSerializesTo(Predicate expected, Predicate predicate) { + assertEquals(expected, BinaryFormat.decode(BinaryFormat.encode(predicate))); + } + + private static void assertSerialize(Predicate predicate) { + assertSerializesTo(predicate, predicate); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/BooleanPredicateTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/BooleanPredicateTest.java new file mode 100644 index 00000000000..7268b358889 --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/BooleanPredicateTest.java @@ -0,0 +1,47 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class BooleanPredicateTest { + + @Test + public void requireThatFalseIsAValue() { + assertTrue(PredicateValue.class.isAssignableFrom(BooleanPredicate.class)); + } + + @Test + public void requireThatCloneIsImplemented() throws CloneNotSupportedException { + BooleanPredicate node1 = new BooleanPredicate(true); + BooleanPredicate node2 = node1.clone(); + assertEquals(node1, node2); + assertNotSame(node1, node2); + } + + @Test + public void requireThatHashCodeIsImplemented() { + assertEquals(new BooleanPredicate(true).hashCode(), new BooleanPredicate(true).hashCode()); + assertEquals(new BooleanPredicate(false).hashCode(), new BooleanPredicate(false).hashCode()); + } + + @Test + public void requireThatEqualsIsImplemented() { + BooleanPredicate lhs = new BooleanPredicate(true); + assertTrue(lhs.equals(lhs)); + assertFalse(lhs.equals(new Object())); + + BooleanPredicate rhs = new BooleanPredicate(false); + assertFalse(lhs.equals(rhs)); + rhs.setValue(true); + assertTrue(lhs.equals(rhs)); + } + +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/ConjunctionTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/ConjunctionTest.java new file mode 100644 index 00000000000..6e12a00ba4d --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/ConjunctionTest.java @@ -0,0 +1,113 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class ConjunctionTest { + + @Test + public void requireThatConjunctionIsAnOperator() { + assertTrue(PredicateOperator.class.isAssignableFrom(Conjunction.class)); + } + + @Test + public void requireThatAccessorsWork() { + Conjunction node = new Conjunction(); + Predicate a = SimplePredicates.newString("a"); + node.addOperand(a); + assertEquals(Arrays.asList(a), node.getOperands()); + Predicate b = SimplePredicates.newString("b"); + node.addOperand(b); + assertEquals(Arrays.asList(a, b), node.getOperands()); + Predicate c = SimplePredicates.newString("c"); + Predicate d = SimplePredicates.newString("d"); + node.addOperands(Arrays.asList(c, d)); + assertEquals(Arrays.asList(a, b, c, d), node.getOperands()); + Predicate e = SimplePredicates.newString("e"); + Predicate f = SimplePredicates.newString("f"); + node.setOperands(Arrays.asList(e, f)); + assertEquals(Arrays.asList(e, f), node.getOperands()); + } + + @Test + public void requireThatConstructorsWork() { + Predicate foo = SimplePredicates.newString("foo"); + Predicate bar = SimplePredicates.newString("bar"); + Conjunction node = new Conjunction(foo, bar); + assertEquals(Arrays.asList(foo, bar), node.getOperands()); + + node = new Conjunction(Arrays.asList(foo, bar)); + assertEquals(Arrays.asList(foo, bar), node.getOperands()); + } + + @Test + public void requireThatCloneIsImplemented() throws CloneNotSupportedException { + Conjunction node1 = new Conjunction(SimplePredicates.newString("a"), SimplePredicates.newString("b")); + Conjunction node2 = node1.clone(); + assertEquals(node1, node2); + assertNotSame(node1, node2); + assertNotSame(node1.getOperands(), node2.getOperands()); + } + + @Test + public void requireThatHashCodeIsImplemented() { + assertEquals(new Conjunction().hashCode(), new Conjunction().hashCode()); + } + + @Test + public void requireThatEqualsIsImplemented() { + Conjunction lhs = new Conjunction(SimplePredicates.newString("foo"), + SimplePredicates.newString("bar")); + assertTrue(lhs.equals(lhs)); + assertFalse(lhs.equals(new Object())); + + Conjunction rhs = new Conjunction(); + assertFalse(lhs.equals(rhs)); + rhs.addOperand(SimplePredicates.newString("foo")); + assertFalse(lhs.equals(rhs)); + rhs.addOperand(SimplePredicates.newString("bar")); + assertTrue(lhs.equals(rhs)); + } + + @Test + public void requireThatNodeDelimiterIsAND() { + assertEquals("", newConjunction().toString()); + assertEquals("foo", newConjunction("foo").toString()); + assertEquals("foo and bar", newConjunction("foo", "bar").toString()); + assertEquals("foo and bar and baz", newConjunction("foo", "bar", "baz").toString()); + } + + @Test + public void requireThatSimpleConjunctionsArePrettyPrinted() { + assertEquals("foo and bar", + new Conjunction(SimplePredicates.newString("foo"), + SimplePredicates.newString("bar")).toString()); + } + + @Test + public void requireThatComplexConjunctionsArePrintedAsGroup() { + assertEquals("foo and bar and baz", + new Conjunction(SimplePredicates.newString("foo"), + new Conjunction(SimplePredicates.newString("bar"), + SimplePredicates.newString("baz"))).toString()); + assertEquals("foo and (bar or baz)", + new Conjunction(SimplePredicates.newString("foo"), + new Disjunction(SimplePredicates.newString("bar"), + SimplePredicates.newString("baz"))).toString()); + } + + private static Conjunction newConjunction(String... operands) { + return new Conjunction(SimplePredicates.newStrings(operands)); + } + +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/DisjunctionTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/DisjunctionTest.java new file mode 100644 index 00000000000..99dc46c4f5f --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/DisjunctionTest.java @@ -0,0 +1,113 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class DisjunctionTest { + + @Test + public void requireThatDisjunctionIsAnOperator() { + assertTrue(PredicateOperator.class.isAssignableFrom(Disjunction.class)); + } + + @Test + public void requireThatAccessorsWork() { + Disjunction node = new Disjunction(); + Predicate a = SimplePredicates.newString("a"); + node.addOperand(a); + assertEquals(Arrays.asList(a), node.getOperands()); + Predicate b = SimplePredicates.newString("b"); + node.addOperand(b); + assertEquals(Arrays.asList(a, b), node.getOperands()); + Predicate c = SimplePredicates.newString("c"); + Predicate d = SimplePredicates.newString("d"); + node.addOperands(Arrays.asList(c, d)); + assertEquals(Arrays.asList(a, b, c, d), node.getOperands()); + Predicate e = SimplePredicates.newString("e"); + Predicate f = SimplePredicates.newString("f"); + node.setOperands(Arrays.asList(e, f)); + assertEquals(Arrays.asList(e, f), node.getOperands()); + } + + @Test + public void requireThatConstructorsWork() { + Predicate foo = SimplePredicates.newString("foo"); + Predicate bar = SimplePredicates.newString("bar"); + Disjunction node = new Disjunction(foo, bar); + assertEquals(Arrays.asList(foo, bar), node.getOperands()); + + node = new Disjunction(Arrays.asList(foo, bar)); + assertEquals(Arrays.asList(foo, bar), node.getOperands()); + } + + @Test + public void requireThatCloneIsImplemented() throws CloneNotSupportedException { + Disjunction node1 = new Disjunction(SimplePredicates.newString("a"), SimplePredicates.newString("b")); + Disjunction node2 = node1.clone(); + assertEquals(node1, node2); + assertNotSame(node1, node2); + assertNotSame(node1.getOperands(), node2.getOperands()); + } + + @Test + public void requireThatHashCodeIsImplemented() { + assertEquals(new Disjunction().hashCode(), new Disjunction().hashCode()); + } + + @Test + public void requireThatEqualsIsImplemented() { + Disjunction lhs = new Disjunction(SimplePredicates.newString("foo"), + SimplePredicates.newString("bar")); + assertTrue(lhs.equals(lhs)); + assertFalse(lhs.equals(new Object())); + + Disjunction rhs = new Disjunction(); + assertFalse(lhs.equals(rhs)); + rhs.addOperand(SimplePredicates.newString("foo")); + assertFalse(lhs.equals(rhs)); + rhs.addOperand(SimplePredicates.newString("bar")); + assertTrue(lhs.equals(rhs)); + } + + @Test + public void requireThatNodeDelimiterIsOR() { + assertEquals("", newDisjunction().toString()); + assertEquals("foo", newDisjunction("foo").toString()); + assertEquals("foo or bar", newDisjunction("foo", "bar").toString()); + assertEquals("foo or bar or baz", newDisjunction("foo", "bar", "baz").toString()); + } + + @Test + public void requireThatSimpleDisjunctionsArePrettyPrinted() { + assertEquals("foo or bar", + new Disjunction(SimplePredicates.newString("foo"), + SimplePredicates.newString("bar")).toString()); + } + + @Test + public void requireThatComplexDisjunctionsArePrintedAsGroup() { + assertEquals("foo or bar or baz", + new Disjunction(SimplePredicates.newString("foo"), + new Disjunction(SimplePredicates.newString("bar"), + SimplePredicates.newString("baz"))).toString()); + assertEquals("foo or (bar and baz)", + new Disjunction(SimplePredicates.newString("foo"), + new Conjunction(SimplePredicates.newString("bar"), + SimplePredicates.newString("baz"))).toString()); + } + + private static Disjunction newDisjunction(String... operands) { + return new Disjunction(SimplePredicates.newStrings(operands)); + } + +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureConjunctionTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureConjunctionTest.java new file mode 100644 index 00000000000..e98400e86fa --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureConjunctionTest.java @@ -0,0 +1,52 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static com.yahoo.document.predicate.Predicates.feature; +import static com.yahoo.document.predicate.Predicates.not; + +/** + * @author bjorncs + */ +public class FeatureConjunctionTest { + + @Test + public void require_that_featureconjunction_with_valid_operands_can_be_constructed() { + new FeatureConjunction(Arrays.asList( + not(feature("a").inSet("1")), + feature("b").inSet("1"))); + } + + @Test(expected = IllegalArgumentException.class) + public void require_that_constructor_throws_exception_if_all_operands_are_not_featuresets() { + new FeatureConjunction(Arrays.asList( + not(feature("a").inSet("1")), + feature("b").inRange(1, 2))); + } + + @Test(expected = IllegalArgumentException.class) + public void require_that_constructor_throws_exception_if_single_operand() { + new FeatureConjunction(Arrays.asList(feature("a").inSet("1"))); + } + + @Test(expected = IllegalArgumentException.class) + public void require_that_constructor_throws_exception_if_no_operands() { + new FeatureConjunction(Collections.emptyList()); + } + + @Test(expected = IllegalArgumentException.class) + public void require_that_contructor_throws_exception_if_featuresets_contain_multiple_values() { + new FeatureConjunction(Arrays.asList(feature("a").inSet("1"), feature("b").inSet("2", "3"))); + } + + @Test(expected = IllegalArgumentException.class) + public void require_that_constructor_throws_exception_if_featureset_keys_are_not_unique() { + new FeatureConjunction(Arrays.asList( + not(feature("a").inSet("1")), + feature("a").inSet("2"))); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureRangeTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureRangeTest.java new file mode 100644 index 00000000000..efcc0e5b64a --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureRangeTest.java @@ -0,0 +1,263 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import java.util.Arrays; + +import static org.junit.Assert.*; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class FeatureRangeTest { + + @Test + public void requireThatFeatureRangeIsAValue() { + assertTrue(PredicateValue.class.isAssignableFrom(FeatureRange.class)); + } + + @Test + public void requireThatAccessorsWork() { + FeatureRange node = new FeatureRange("foo"); + assertEquals("foo", node.getKey()); + node.setKey("bar"); + assertEquals("bar", node.getKey()); + + node.setFromInclusive(69L); + assertEquals(69, node.getFromInclusive().intValue()); + node.setFromInclusive(null); + assertNull(node.getFromInclusive()); + + node.setToInclusive(69L); + assertEquals(69, node.getToInclusive().intValue()); + node.setToInclusive(null); + assertNull(node.getToInclusive()); + } + + @Test + public void requireThatConstructorsWork() { + FeatureRange node = new FeatureRange("foo"); + assertEquals("foo", node.getKey()); + assertNull(node.getFromInclusive()); + assertNull(node.getToInclusive()); + + node = new FeatureRange("foo", null, null); + assertEquals("foo", node.getKey()); + assertNull(node.getFromInclusive()); + assertNull(node.getToInclusive()); + + node = new FeatureRange("foo", 69L, null); + assertEquals("foo", node.getKey()); + assertEquals(69, node.getFromInclusive().intValue()); + assertNull(node.getToInclusive()); + + node = new FeatureRange("foo", null, 69L); + assertEquals("foo", node.getKey()); + assertNull(node.getFromInclusive()); + assertEquals(69, node.getToInclusive().intValue()); + + node = new FeatureRange("foo", 6L, 9L); + assertEquals("foo", node.getKey()); + assertEquals(6, node.getFromInclusive().intValue()); + assertEquals(9, node.getToInclusive().intValue()); + } + + @Test + public void requireThatCloneIsImplemented() throws CloneNotSupportedException { + FeatureRange node1 = new FeatureRange("foo", 6L, 9L); + FeatureRange node2 = node1.clone(); + assertEquals(node1, node2); + assertNotSame(node1, node2); + } + + @Test + public void requireThatHashCodeIsImplemented() { + assertEquals(new FeatureRange("key").hashCode(), new FeatureRange("key").hashCode()); + } + + @Test + public void requireThatEqualsIsImplemented() { + FeatureRange lhs = new FeatureRange("foo", 6L, 9L); + assertTrue(lhs.equals(lhs)); + assertFalse(lhs.equals(new Object())); + + FeatureRange rhs = new FeatureRange("bar"); + assertFalse(lhs.equals(rhs)); + rhs.setKey("foo"); + assertFalse(lhs.equals(rhs)); + rhs.setFromInclusive(6L); + assertFalse(lhs.equals(rhs)); + rhs.setToInclusive(9L); + assertTrue(lhs.equals(rhs)); + rhs.addPartition(new RangePartition("foo")); + assertFalse(lhs.equals(rhs)); + lhs.addPartition(new RangePartition("foo")); + assertTrue(lhs.equals(rhs)); + rhs.addPartition(new RangeEdgePartition("foo", 10, 0, 2)); + assertFalse(lhs.equals(rhs)); + lhs.addPartition(new RangeEdgePartition("foo", 10, 0, 2)); + assertTrue(lhs.equals(rhs)); + } + + @Test + public void requireThatFeatureKeyIsMandatoryInConstructor() { + try { + new FeatureRange(null); + fail(); + } catch (NullPointerException e) { + assertEquals("key", e.getMessage()); + } + } + + @Test + public void requireThatFeatureKeyIsMandatoryInSetter() { + FeatureRange node = new FeatureRange("foo"); + try { + node.setKey(null); + fail(); + } catch (NullPointerException e) { + assertEquals("key", e.getMessage()); + } + assertEquals("foo", node.getKey()); + } + + @Test + public void requireThatRangeCanBeSingleValue() { + FeatureRange node = new FeatureRange("key", 6L, 6L); + assertEquals(6, node.getFromInclusive().intValue()); + assertEquals(6, node.getToInclusive().intValue()); + node.setToInclusive(9L); + node.setFromInclusive(9L); + assertEquals(9, node.getFromInclusive().intValue()); + assertEquals(9, node.getToInclusive().intValue()); + } + + @Test + public void requireThatFromCanNotBeConstructedGreaterThanTo() { + try { + new FeatureRange("key", 9L, 6L); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Expected 'to' greater than or equal to 9, got 6.", e.getMessage()); + } + } + + @Test + public void requireThatFromCanNotBeSetGreaterThanTo() { + FeatureRange node = new FeatureRange("key", null, 6L); + try { + node.setFromInclusive(9L); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Expected 'from' less than or equal to 6, got 9.", e.getMessage()); + } + assertNull(node.getFromInclusive()); + + node = new FeatureRange("key", 6L, 9L); + try { + node.setFromInclusive(69L); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Expected 'from' less than or equal to 9, got 69.", e.getMessage()); + } + assertEquals(6, node.getFromInclusive().intValue()); + } + + @Test + public void requireThatToCanNotBeSetLessThanFrom() { + FeatureRange node = new FeatureRange("key", 9L, null); + try { + node.setToInclusive(6L); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Expected 'to' greater than or equal to 9, got 6.", e.getMessage()); + } + assertNull(node.getToInclusive()); + + node = new FeatureRange("key", 6L, 9L); + try { + node.setToInclusive(1L); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Expected 'to' greater than or equal to 6, got 1.", e.getMessage()); + } + assertEquals(9, node.getToInclusive().intValue()); + } + + @Test + public void requireThatKeyIsEscapedInToString() { + assertEquals("foo in [6..9]", + new FeatureRange("foo", 6L, 9L).toString()); + assertEquals("'\\foo' in [6..9]", + new FeatureRange("\foo", 6L, 9L).toString()); + assertEquals("'\\x27foo\\x27' in [6..9]", + new FeatureRange("'foo'", 6L, 9L).toString()); + } + + @Test + public void requireThatToStringIncludesLimits() { + assertEquals("foo in [6..9]", new FeatureRange("foo", 6L, 9L).toString()); + } + + @Test + public void requireThatToStringAllowsNullLimits() { + assertEquals("foo in [..]", new FeatureRange("foo").toString()); + } + + @Test + public void requireThatToStringAllowsNullFromLimit() { + assertEquals("foo in [..69]", new FeatureRange("foo", null, 69L).toString()); + } + + @Test + public void requireThatToStringAllowsNullToLimit() { + assertEquals("foo in [69..]", new FeatureRange("foo", 69L, null).toString()); + } + + @Test + public void requireThatSimpleStringsArePrettyPrinted() { + assertEquals("foo in [6..9]", + new FeatureRange("foo", 6L, 9L).toString()); + } + + @Test + public void requireThatComplexStringsAreEscaped() { + assertEquals("'\\foo' in [6..9]", + new FeatureRange("\foo", 6L, 9L).toString()); + } + + @Test + public void requireThatRangePartitionsCanBeAdded() { + FeatureRange range = new FeatureRange("foo", 10L, 22L); + range.addPartition(new RangePartition("foo=10-19")); + range.addPartition(new RangePartition("foo", 0, 0x8000000000000000L, true)); + range.addPartition(new RangeEdgePartition("foo=20", 20, 0, 2)); + assertEquals("foo in [10..22 (foo=20+[..2],foo=10-19,foo=-9223372036854775808-0)]", range.toString()); + } + + @Test + public void requireThatRangePartitionsCanBeCleared() { + FeatureRange range = new FeatureRange("foo", 10L, 22L); + range.addPartition(new RangePartition("foo=10-19")); + range.addPartition(new RangeEdgePartition("foo=20", 20, 0, 2)); + assertEquals("foo in [10..22 (foo=20+[..2],foo=10-19)]", range.toString()); + range.clearPartitions(); + assertEquals("foo in [10..22]", range.toString()); + } + + @Test + public void requireThatFeatureRangeCanBeBuiltFromMixedInNode() { + assertEquals(new FeatureRange("foo", 10L, 19L), + FeatureRange.buildFromMixedIn("foo", Arrays.asList("foo=10-19"), 10)); + assertEquals(new FeatureRange("foo", -19L, -10L), + FeatureRange.buildFromMixedIn("foo", Arrays.asList("foo=-10-19"), 10)); + assertEquals(new FeatureRange("foo", 10L, 19L), + FeatureRange.buildFromMixedIn("foo", Arrays.asList("foo=10,10,9"), 10)); + assertEquals(new FeatureRange("foo", 10L, 19L), + FeatureRange.buildFromMixedIn("foo", Arrays.asList("foo=10,10,1073741833"), 10)); + assertEquals(new FeatureRange("foo", 10L, 19L), + FeatureRange.buildFromMixedIn("foo", Arrays.asList("foo=10,10,2147483648"), 10)); + } + +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureSetTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureSetTest.java new file mode 100644 index 00000000000..7bc9d5f2c04 --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/FeatureSetTest.java @@ -0,0 +1,180 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class FeatureSetTest { + + @Test + public void requireThatFeatureSetIsAValue() { + assertTrue(PredicateValue.class.isAssignableFrom(FeatureSet.class)); + } + + @Test + public void requireThatAccessorsWork() { + FeatureSet node = new FeatureSet("key", "valueA", "valueB"); + assertEquals("key", node.getKey()); + assertValues(Arrays.asList("valueA", "valueB"), node); + node.addValue("valueC"); + assertValues(Arrays.asList("valueA", "valueB", "valueC"), node); + node.addValues(Arrays.asList("valueD", "valueE")); + assertValues(Arrays.asList("valueA", "valueB", "valueC", "valueD", "valueE"), node); + node.setValues(Arrays.asList("valueF", "valueG")); + assertValues(Arrays.asList("valueF", "valueG"), node); + } + + @Test + public void requireThatValueSetIsMutable() { + FeatureSet node = new FeatureSet("key"); + node.getValues().add("valueA"); + assertValues(Arrays.asList("valueA"), node); + + node = new FeatureSet("key", "valueA"); + node.getValues().add("valueB"); + assertValues(Arrays.asList("valueA", "valueB"), node); + } + + @Test + public void requireThatConstructorsWork() { + FeatureSet node = new FeatureSet("key", "valueA", "valueB"); + assertEquals("key", node.getKey()); + assertValues(Arrays.asList("valueA", "valueB"), node); + + node = new FeatureSet("key", Arrays.asList("valueA", "valueB")); + assertEquals("key", node.getKey()); + assertValues(Arrays.asList("valueA", "valueB"), node); + } + + @Test + public void requireThatCloneIsImplemented() throws CloneNotSupportedException { + FeatureSet node1 = new FeatureSet("key", "valueA", "valueB"); + FeatureSet node2 = node1.clone(); + assertEquals(node1, node2); + assertNotSame(node1, node2); + assertNotSame(node1.getValues(), node2.getValues()); + } + + @Test + public void requireThatHashCodeIsImplemented() { + assertEquals(new FeatureSet("key").hashCode(), new FeatureSet("key").hashCode()); + } + + @Test + public void requireThatEqualsIsImplemented() { + FeatureSet lhs = new FeatureSet("keyA", "valueA", "valueB"); + assertTrue(lhs.equals(lhs)); + assertFalse(lhs.equals(new Object())); + + FeatureSet rhs = new FeatureSet("keyB"); + assertFalse(lhs.equals(rhs)); + rhs.setKey("keyA"); + assertFalse(lhs.equals(rhs)); + rhs.addValue("valueA"); + assertFalse(lhs.equals(rhs)); + rhs.addValue("valueB"); + assertTrue(lhs.equals(rhs)); + } + + @Test + public void requireThatkeyIsMandatoryInConstructor() { + try { + new FeatureSet(null); + fail(); + } catch (NullPointerException e) { + assertEquals("key", e.getMessage()); + } + try { + new FeatureSet(null, Collections.<String>emptyList()); + fail(); + } catch (NullPointerException e) { + assertEquals("key", e.getMessage()); + } + } + + @Test + public void requireThatkeyIsMandatoryInSetter() { + FeatureSet node = new FeatureSet("foo"); + try { + node.setKey(null); + fail(); + } catch (NullPointerException e) { + assertEquals("key", e.getMessage()); + } + assertEquals("foo", node.getKey()); + } + + @Test + public void requireThatValueIsMandatoryInSetter() { + FeatureSet node = new FeatureSet("foo", "bar"); + try { + node.addValue(null); + fail(); + } catch (NullPointerException e) { + assertEquals("value", e.getMessage()); + } + assertValues(Arrays.asList("bar"), node); + } + + @Test + public void requireThatKeyIsEscapedInToString() { + assertEquals("foo in [val]", + new FeatureSet("foo", "val").toString()); + assertEquals("'\\foo' in [val]", + new FeatureSet("\foo", "val").toString()); + assertEquals("'\\x27foo\\x27' in [val]", + new FeatureSet("'foo'", "val").toString()); + } + + @Test + public void requireThatValuesAreEscapedInToString() { + assertEquals("key in [bar, foo]", + new FeatureSet("key", "foo", "bar").toString()); + assertEquals("key in ['\\foo', 'ba\\r']", + new FeatureSet("key", "\foo", "ba\r").toString()); + assertEquals("key in ['\\x27bar\\x27', '\\x27foo\\x27']", + new FeatureSet("key", "'foo'", "'bar'").toString()); + } + + @Test + public void requireThatSimpleStringsArePrettyPrinted() { + assertEquals("foo in [bar]", + new FeatureSet("foo", "bar").toString()); + } + + @Test + public void requireThatComplexStringsAreEscaped() { + assertEquals("'\\foo' in ['ba\\r']", + new FeatureSet("\foo", "ba\r").toString()); + } + + @Test + public void requireThatNegatedFeatureSetsArePrettyPrinted() { + assertEquals("country not in [no, se]", + new Negation(new FeatureSet("country", "no", "se")).toString()); + } + + private static void assertValues(Collection<String> expected, FeatureSet actual) { + List<String> tmp = new ArrayList<>(expected); + for (String value : actual.getValues()) { + assertNotNull(value, tmp.remove(value)); + } + assertTrue(tmp.toString(), tmp.isEmpty()); + } + +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/NegationTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/NegationTest.java new file mode 100644 index 00000000000..95666342152 --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/NegationTest.java @@ -0,0 +1,89 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class NegationTest { + + @Test + public void requireThatNegationIsAnOperator() { + assertTrue(PredicateOperator.class.isAssignableFrom(Negation.class)); + } + + @Test + public void requireThatAccessorsWork() { + Predicate foo = SimplePredicates.newString("foo"); + Negation node = new Negation(foo); + assertSame(foo, node.getOperand()); + + Predicate bar = SimplePredicates.newString("bar"); + node.setOperand(bar); + assertSame(bar, node.getOperand()); + } + + @Test + public void requireThatCloneIsImplemented() throws CloneNotSupportedException { + Negation node1 = new Negation(SimplePredicates.newString("a")); + Negation node2 = node1.clone(); + assertEquals(node1, node2); + assertNotSame(node1, node2); + assertNotSame(node1.getOperand(), node2.getOperand()); + } + + @Test + public void requireThatHashCodeIsImplemented() { + Predicate predicate = SimplePredicates.newPredicate(); + assertEquals(new Negation(predicate).hashCode(), new Negation(predicate).hashCode()); + } + + @Test + public void requireThatEqualsIsImplemented() { + Negation lhs = new Negation(SimplePredicates.newString("foo")); + assertTrue(lhs.equals(lhs)); + assertFalse(lhs.equals(new Object())); + + Negation rhs = new Negation(SimplePredicates.newString("bar")); + assertFalse(lhs.equals(rhs)); + rhs.setOperand(SimplePredicates.newString("foo")); + assertTrue(lhs.equals(rhs)); + } + + @Test + public void requireThatChildIsMandatoryInConstructor() { + try { + new Negation(null); + fail(); + } catch (NullPointerException e) { + assertEquals("operand", e.getMessage()); + } + } + + @Test + public void requireThatChildIsMandatoryInSetter() { + Predicate operand = SimplePredicates.newPredicate(); + Negation negation = new Negation(operand); + try { + negation.setOperand(null); + fail(); + } catch (NullPointerException e) { + assertEquals("operand", e.getMessage()); + } + assertSame(operand, negation.getOperand()); + } + + @Test + public void requireThatChildIsIncludedInToString() { + assertEquals("not (foo)", new Negation(SimplePredicates.newString("foo")).toString()); + } + +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateHashTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateHashTest.java new file mode 100644 index 00000000000..759bfe9f48f --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateHashTest.java @@ -0,0 +1,102 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:magnarn@yahoo-inc.com">Magnar Nedland</a> + */ +public class PredicateHashTest{ + @Test + public void requireThatShortStringsGetsHashes() { + assertHashesTo(0x82af3d1de65ec252L, "abcdefg"); + assertHashesTo(0xdc50d922fb0e91d6L, "雅虎"); + assertHashesTo(0x709bd6ff1a84dc14L, "country=日本"); + assertHashesTo(0x28e8de732ab0e809L, "foo"); + + assertHashesTo(0x8db63936938575bfL, ""); + assertHashesTo(0x1d48fd74d88633acL, "a"); + assertHashesTo(0xd30019bef51f4a75L, "ab"); + assertHashesTo(0x9cb12e2bfea87243L, "abc"); + assertHashesTo(0x207e64432ec23f4bL, "abcd"); + assertHashesTo(0xbb1277971caf7a56L, "abcde"); + assertHashesTo(0xfde595baae539176L, "abcdef"); + assertHashesTo(0x82af3d1de65ec252L, "abcdefg"); + assertHashesTo(0x1cac1cd5db905a5fL, "abcdefgh"); + assertHashesTo(0x1ce1c26a201525deL, "abcdefghi"); + assertHashesTo(0x2237a417a20c1025L, "abcdefghij"); + assertHashesTo(0xd98f47421abc3754L, "abcdefghijk"); + assertHashesTo(0xb974917101764d3aL, "abcdefghijkl"); + assertHashesTo(0xde3b7ffe3e6dd61fL, "abcdefghijklm"); + assertHashesTo(0x31d95fa68634f482L, "abcdefghijklmn"); + assertHashesTo(0xde99d87fdbeca8faL, "abcdefghijklmn1"); + assertHashesTo(0x0afc8571f275c392L, "abcdefghijklmn12"); + assertHashesTo(0xbd00379443b0606cL, "abcdefghijklmn123"); + assertHashesTo(0x855c704c68e095c5L, "abcdefghijklmn1234"); + assertHashesTo(0xe9233cb6e4fad097L, "abcdefghijklmn12345"); + assertHashesTo(0x1103ca46bd6e8d2fL, "abcdefghijklmn123456"); + assertHashesTo(0x0c7097be717354d1L, "abcdefghijklmn1234567"); + assertHashesTo(0x3e75293210127583L, "abcdefghijklmn12345678"); + assertHashesTo(0xa66286e1294d8197L, "abcdefghijklmn123456789"); + assertHashesTo(0x79fac97d13f4cc84L, "abcdefghijklmn1234567890"); + } + + @Test + public void requireThatLongStringsGetsHashes() { + assertHashesTo(0x79fac97d13f4cc84L, "abcdefghijklmn1234567890"); + assertHashesTo(0xd7af1798f1d5de44L, "abcdefghijklmn1234567890a"); + assertHashesTo(0x5a259ad887478cccL, "abcdefghijklmn1234567890ab"); + assertHashesTo(0x4e8d95bab8d64191L, "abcdefghijklmn1234567890abc"); + assertHashesTo(0xf63b94d31db2fe1aL, "abcdefghijklmn1234567890abcd"); + assertHashesTo(0x47a1977d65709aceL, "abcdefghijklmn1234567890abcde"); + assertHashesTo(0x52e1fb6d6aff3aeeL, "abcdefghijklmn1234567890abcdef"); + assertHashesTo(0xc16de639b6e69ad3L, "abcdefghijklmn1234567890abcdefg"); + assertHashesTo(0x87c22dd1e285dd6fL, "abcdefghijklmn1234567890abcdefgh"); + assertHashesTo(0x775a3542d88b4972L, "abcdefghijklmn1234567890abcdefghi"); + assertHashesTo(0x7b0c82116edf338bL, "abcdefghijklmn1234567890abcdefghij"); + assertHashesTo(0x0fe73b58f6b23cb6L, "abcdefghijklmn1234567890abcdefghijk"); + assertHashesTo(0x27ab8d02387e64e0L, "abcdefghijklmn1234567890abcdefghijkl"); + assertHashesTo(0xdd161af20b41be04L, "abcdefghijklmn1234567890abcdefghijklm"); + assertHashesTo(0x67739554f61fffcbL, "abcdefghijklmn1234567890abcdefghijklmn"); + assertHashesTo(0xa765cc6be247dfb2L, "abcdefghijklmn1234567890abcdefghijklmn1"); + assertHashesTo(0x9e201896cc600501L, "abcdefghijklmn1234567890abcdefghijklmn12"); + assertHashesTo(0xfc5077792bfed491L, "abcdefghijklmn1234567890abcdefghijklmn123"); + assertHashesTo(0x96a7acb73fd13601L, "abcdefghijklmn1234567890abcdefghijklmn1234"); + assertHashesTo(0x45de4237e48a0ba8L, "abcdefghijklmn1234567890abcdefghijklmn12345"); + assertHashesTo(0x3b65da96300e107eL, "abcdefghijklmn1234567890abcdefghijklmn123456"); + assertHashesTo(0xbd95c3591ee587bdL, "abcdefghijklmn1234567890abcdefghijklmn1234567"); + assertHashesTo(0x2688cb2d10e8629bL, "abcdefghijklmn1234567890abcdefghijklmn12345678"); + assertHashesTo(0xcd383d98f9483ef0L, "abcdefghijklmn1234567890abcdefghijklmn123456789"); + assertHashesTo(0x220e374268970e84L, "abcdefghijklmn1234567890abcdefghijklmn1234567890"); + assertHashesTo(0xd50ef002ed96bf0bL, "abcdefghijklmn1234567890abcdefghijklmn1234567890a"); + assertHashesTo(0x5ec9b42099bb25c6L, "abcdefghijklmn1234567890abcdefghijklmn1234567890ab"); + assertHashesTo(0x05c603997a19dbceL, "abcdefghijklmn1234567890abcdefghijklmn1234567890abc"); + assertHashesTo(0xcee3fce2a3e38762L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcd"); + assertHashesTo(0xc0d9791b19897f0aL, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcde"); + assertHashesTo(0xde98d0f8250ec703L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdef"); + assertHashesTo(0xa7688d5834fa7d2aL, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefg"); + assertHashesTo(0xad514e8250667cdeL, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefgh"); + assertHashesTo(0xf562662deca536c3L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghi"); + assertHashesTo(0x9d1b8d2463cde877L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghij"); + assertHashesTo(0x24840f21eeb30861L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijk"); + assertHashesTo(0x40af2a3f14d31fdaL, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijkl"); + assertHashesTo(0x3514ad5e964b5c73L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklm"); + assertHashesTo(0x7bd6243490571844L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklmn"); + assertHashesTo(0x273de93a3bddd9e8L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklmn1"); + assertHashesTo(0x18e6850c3e2f85beL, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklmn12"); + assertHashesTo(0x044968ddc534d822L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklmn123"); + assertHashesTo(0x7430d9d503fe624dL, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklmn1234"); + assertHashesTo(0xf0bb1e5239c1d88cL, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklmn12345"); + assertHashesTo(0x2ee1ab348b7deaa0L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklmn123456"); + assertHashesTo(0x18b6da5df76680dfL, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklmn1234567"); + assertHashesTo(0x06c95ee4ddc93743L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklmn12345678"); + assertHashesTo(0x6406e477d8ca608dL, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklmn123456789"); + assertHashesTo(0x203397a04178d470L, "abcdefghijklmn1234567890abcdefghijklmn1234567890abcdefghijklmn1234567890"); + } + + private void assertHashesTo(long hash, String key) { + assertEquals(hash, PredicateHash.hash64(key)); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateOperatorTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateOperatorTest.java new file mode 100644 index 00000000000..b6de0bf3514 --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateOperatorTest.java @@ -0,0 +1,17 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class PredicateOperatorTest { + + @Test + public void requireThatOperatorIsAPredicate() { + assertTrue(Predicate.class.isAssignableFrom(PredicateOperator.class)); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateParserTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateParserTest.java new file mode 100644 index 00000000000..ee161c20feb --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateParserTest.java @@ -0,0 +1,155 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:magnarn@yahoo-inc.com">Magnar Nedland</a> + */ + +public class PredicateParserTest { + + @Test + public void requireThatParseErrorThrowsException() { + try { + Predicate.fromString("a in b"); + fail("Expected an exception"); + } catch (IllegalArgumentException e) { + assertEquals("line 1:5 no viable alternative at input 'b'", e.getMessage()); + } + } + + @Test + public void requireThatLexerErrorThrowsException() { + try { + Predicate.fromString("a-b in [b]"); + fail("Expected an exception"); + } catch (IllegalArgumentException e) { + assertEquals("line 1:2 no viable alternative at character 'b'", e.getMessage()); + } + } + + @Test + public void requireThatSingleValueLeafNodesParse() { + assertParsesTo("a in [b]", "a in [b]"); + assertParsesTo("0 in [1]", "0 in [1]"); + assertParsesTo("in in [in]", "in in [in]"); + assertParsesTo("and in [or]", "and in [or]"); + assertParsesTo("not in [not]", "not in [not]"); + assertParsesTo("'-234' in ['+200']", "'-234' in ['+200']"); + assertParsesTo("string in ['!@#$%^&*()[]']", "'string' in ['!@#$%^&*()[]']"); + assertParsesTo("a in [b]", "a in [b]"); + assertParsesTo("string in ['foo\\\\_\"\\t\\n\\f\\rbar']", + "string in ['foo\\\\_\\x22\\t\\n\\f\\rbar']"); + assertParsesTo("'\\xC3\\xB8' in [b]", "'ø' in [b]"); + assertParsesTo("'\\xEF\\xBF\\xBD' in [b]", "'\\xf8' in [b]"); + assertParsesTo("'\\xEF\\xBF\\xBD' in [b]", "'\\xef\\xbf\\xbd' in ['b']"); + assertParsesTo("'\\xE4\\xB8\\x9C\\xE8\\xA5\\xBF' in ['\\xE8\\x87\\xAA\\xE8\\xA1\\x8C\\xE8\\xBD\\xA6']", + "'东西' in ['自行车']"); + assertParsesTo("true in [false]", "true in [false]"); + } + + @Test + public void requireThatMultiValueLeafNodesParse() { + assertParsesTo("a in [b]", "a in [b]"); + assertParsesTo("0 in [1]", "0 in [1]"); + assertParsesTo("in in [and, in]", "in in [in, and]"); + assertParsesTo("a in [b, c, d, e, f]", "'a' in ['b', 'c', 'd', 'e', 'f']"); + } + + @Test + public void requireThatBothSingleAndDoubleQuotesWork() { + assertParsesTo("a in [b]", "'a' in ['b']"); + assertParsesTo("a in [b]", "\"a\" in [\"b\"]"); + assertParsesTo("'a\\x27' in [b]", "'a\\'' in ['b']"); + assertParsesTo("'a\"' in [b]", "\"a\\\"\" in [\"b\"]"); + } + + @Test + public void requireThatRangeLeafNodesParse() { + assertParsesTo("a in [0..100]", "a in [0..100]"); + assertParsesTo("0 in [..100]", "0 in [..100]"); + assertParsesTo("0 in [0..]", "0 in [0..]"); + assertParsesTo("0 in [..]", "0 in [..]"); + assertParsesTo("a in [-100..100]", "a in [-100..+100]"); + assertParsesTo("a in [-9223372036854775808..9223372036854775807]", + "a in [-9223372036854775808..+9223372036854775807]"); + } + + @Test + public void requireThatRangePartitionsAreIgnored() { + assertParsesTo("a in [0..100]", "a in [0..100 (a=0-99,a=100+[..0])]"); + assertParsesTo("a in [-100..0]", "a in [-100..0 (a=-0-99,a=-100+[..0])]"); + assertParsesTo("a in [-9223372036854775808..0]", "a in [-9223372036854775808..0 (a=-0-9223372036854775808)]"); + assertParsesTo("a in [2..8]", "a in [2..8 (a=0+[2..8])]"); + assertParsesTo("a in [0..8]", "a in [0..8 (a=0+[..8])]"); + assertParsesTo("a in [2..9]", "a in [2..9 (a=0+[2..])]"); + } + + @Test + public void requireThatNotInSetWorks() { + assertParsesTo("a not in [b]", "a not in [b]"); + } + + @Test + public void requireThatConjunctionWorks() { + assertParsesTo("a in [b] and c in [d]", "a in [b] and c in [d]"); + assertParsesTo("a in [b] and c in [d] and e in [f]", "a in [b] and c in [d] and e in [f]"); + } + + @Test + public void requireThatDisjunctionWorks() { + assertParsesTo("a in [b] or c in [d]", "a in [b] or c in [d]"); + assertParsesTo("a in [b] or c in [d] or e in [f]", "a in [b] or c in [d] or e in [f]"); + } + + @Test + public void requireThatParenthesesWorks() { + assertParsesTo("a in [b] or c in [d]", + "(a in [b]) or (c in [d])"); + assertParsesTo("a in [b] or c in [d] or e in [f]", + "(((a in [b]) or c in [d]) or e in [f])"); + assertParsesTo("(a in [b] and c in [d]) or e in [f]", + "a in [b] and c in [d] or e in [f]"); + assertParsesTo("a in [b] and (c in [d] or e in [f])", + "a in [b] and (c in [d] or e in [f])"); + assertParsesTo("a in [b] and (c in [d] or e in [f]) and g in [h]", + "a in [b] and (c in [d] or e in [f]) and g in [h]"); + } + + @Test + public void requireThatNotOutsideParenthesesWorks() { + assertParsesTo("a not in [b]", "not (a in [b])"); + } + + @Test + public void requireThatConjunctionsCanGetMoreThanTwoChildren() { + Predicate p = Predicate.fromString("a in [b] and c in [d] and e in [f] and g in [h]"); + assertTrue(p instanceof Conjunction); + assertEquals(4, ((Conjunction)p).getOperands().size()); + } + + @Test + public void requireThatDisjunctionsCanGetMoreThanTwoChildren() { + Predicate p = Predicate.fromString("a in [b] or c in [d] or e in [f] or g in [h]"); + assertTrue(p instanceof Disjunction); + assertEquals(4, ((Disjunction)p).getOperands().size()); + } + + @Test + public void requireThatBooleanCanBeParsed() { + assertParsesTo("true", "true"); + assertParsesTo("false", "false"); + assertParsesTo("true or false", "true or false"); + assertParsesTo("false and true", "false and true"); + } + + private static void assertParsesTo(String expected, String predicate_str) { + assertEquals(expected, // TODO: Predicate.fromString(expected) + Predicate.fromString(predicate_str).toString()); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateTest.java new file mode 100644 index 00000000000..621378f394b --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateTest.java @@ -0,0 +1,110 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import static com.yahoo.document.predicate.Predicates.and; +import static com.yahoo.document.predicate.Predicates.feature; +import static com.yahoo.document.predicate.Predicates.not; +import static com.yahoo.document.predicate.Predicates.or; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class PredicateTest { + + @Test + public void requireThatPredicateIsCloneable() { + assertTrue(Cloneable.class.isAssignableFrom(Predicate.class)); + } + + @Test + public void requireThatANDConstructsAConjunction() { + Predicate foo = SimplePredicates.newString("foo"); + Predicate bar = SimplePredicates.newString("bar"); + Predicate predicate = and(foo, bar); + assertEquals(Conjunction.class, predicate.getClass()); + assertEquals(new Conjunction(foo, bar), predicate); + } + + @Test + public void requireThatORConstructsADisjunction() { + Predicate foo = SimplePredicates.newString("foo"); + Predicate bar = SimplePredicates.newString("bar"); + Predicate predicate = or(foo, bar); + assertEquals(Disjunction.class, predicate.getClass()); + assertEquals(new Disjunction(foo, bar), predicate); + } + + @Test + public void requireThatNOTConstructsANegation() { + Predicate foo = SimplePredicates.newString("foo"); + Predicate predicate = not(foo); + assertEquals(Negation.class, predicate.getClass()); + assertEquals(new Negation(foo), predicate); + } + + @Test + public void requireThatFeatureBuilderCanConstructFeatureRange() { + assertEquals(new FeatureRange("key", 6L, 9L), + feature("key").inRange(6, 9)); + assertEquals(new Negation(new FeatureRange("key", 6L, 9L)), + feature("key").notInRange(6, 9)); + assertEquals(new FeatureRange("key", 7L, null), + feature("key").greaterThan(6)); + assertEquals(new FeatureRange("key", 6L, null), + feature("key").greaterThanOrEqualTo(6)); + assertEquals(new FeatureRange("key", null, 5L), + feature("key").lessThan(6)); + assertEquals(new FeatureRange("key", null, 9L), + feature("key").lessThanOrEqualTo(9)); + } + + @Test + public void requireThatFeatureBuilderCanConstructFeatureSet() { + assertEquals(new FeatureSet("key", "valueA", "valueB"), + feature("key").inSet("valueA", "valueB")); + assertEquals(new Negation(new FeatureSet("key", "valueA", "valueB")), + feature("key").notInSet("valueA", "valueB")); + } + + @Test + public void requireThatPredicatesCanBeConstructedUsingConstructors() { + assertEquals("country in [no, se] and age in [20..30]", + new Conjunction(new FeatureSet("country", "no", "se"), + new FeatureRange("age", 20L, 30L)).toString()); + assertEquals("country not in [no, se] or age in [20..] or height in [..160]", + new Disjunction(new Negation(new FeatureSet("country", "no", "se")), + new FeatureRange("age", 20L, null), + new FeatureRange("height", null, 160L)).toString()); + } + + @Test + public void requireThatPredicatesCanBeBuiltUsingChainedMethodCalls() { + assertEquals("country not in [no, se] or age in [20..] or height in [..160]", + new Disjunction() + .addOperand(new Negation(new FeatureSet("country").addValue("no").addValue("se"))) + .addOperand(new FeatureRange("age").setFromInclusive(20L)) + .addOperand(new FeatureRange("height").setToInclusive(160L)) + .toString()); + } + + @Test + public void requireThatPredicatesCanBeBuiltUsingSeparateMethodCalls() { + Conjunction conjunction = new Conjunction(); + FeatureSet countrySet = new FeatureSet("country"); + countrySet.addValue("no"); + countrySet.addValue("se"); + conjunction.addOperand(countrySet); + FeatureRange ageRange = new FeatureRange("age"); + ageRange.setFromInclusive(20L); + conjunction.addOperand(ageRange); + FeatureRange heightRange = new FeatureRange("height"); + heightRange.setToInclusive(160L); + conjunction.addOperand(heightRange); + assertEquals("country in [no, se] and age in [20..] and height in [..160]", + conjunction.toString()); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateValueTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateValueTest.java new file mode 100644 index 00000000000..810e9a8717c --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicateValueTest.java @@ -0,0 +1,17 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class PredicateValueTest { + + @Test + public void requireThatValueIsAPredicate() { + assertTrue(Predicate.class.isAssignableFrom(PredicateValue.class)); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicatesTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicatesTest.java new file mode 100644 index 00000000000..22832eedeff --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/PredicatesTest.java @@ -0,0 +1,38 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import static com.yahoo.document.predicate.Predicates.and; +import static com.yahoo.document.predicate.Predicates.feature; +import static com.yahoo.document.predicate.Predicates.not; +import static com.yahoo.document.predicate.Predicates.or; +import static com.yahoo.document.predicate.Predicates.value; +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class PredicatesTest { + + @Test + public void requireThatApiIsUsable() { + assertEquals( + new Disjunction( + new Conjunction(new FeatureSet("country", "de", "no"), + new Negation(new FeatureSet("gender", "female")), + new FeatureRange("age", 6L, 9L)), + new Conjunction(new Negation(new FeatureSet("country", "se")), + new FeatureSet("gender", "female"), + new FeatureRange("age", 69L, null))), + or(and(feature("country").inSet("de", "no"), + feature("gender").notInSet("female"), + feature("age").inRange(6, 9)), + and(not(feature("country").inSet("se")), + feature("gender").inSet("female"), + feature("age").greaterThanOrEqualTo(69)))); + + assertEquals(new BooleanPredicate(true), value(true)); + assertEquals(new BooleanPredicate(false), value(false)); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/RangeEdgePartitionTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/RangeEdgePartitionTest.java new file mode 100644 index 00000000000..d9f6714d8c8 --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/RangeEdgePartitionTest.java @@ -0,0 +1,62 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:magnarn@yahoo-inc.com">Magnar Nedland</a> + */ +public class RangeEdgePartitionTest { + + @Test + public void requireThatRangeEdgePartitionIsAValue() { + assertTrue(PredicateValue.class.isAssignableFrom(RangeEdgePartition.class)); + } + + @Test + public void requireThatConstructorsWork() { + RangeEdgePartition part = new RangeEdgePartition("foo=10", 10, 0, -1); + assertEquals("foo=10", part.getLabel()); + assertEquals(0, part.getLowerBound()); + assertEquals(-1, part.getUpperBound()); + } + + @Test + public void requireThatCloneIsImplemented() throws CloneNotSupportedException { + RangeEdgePartition node1 = new RangeEdgePartition("foo=10", 10, 0, 0); + RangeEdgePartition node2 = node1.clone(); + assertEquals(node1, node2); + assertNotSame(node1, node2); + } + + @Test + public void requireThatHashCodeIsImplemented() { + assertEquals(new RangeEdgePartition("foo=-10", 10, 2, 3).hashCode(), + new RangeEdgePartition("foo=-10", 10, 2, 3).hashCode()); + } + + @Test + public void requireThatEqualsIsImplemented() { + RangeEdgePartition lhs = new RangeEdgePartition("foo=10", 10, 5, 10); + assertTrue(lhs.equals(lhs)); + assertFalse(lhs.equals(new Object())); + + RangeEdgePartition rhs = new RangeEdgePartition("foo=20", 20, 0, 0); + assertFalse(lhs.equals(rhs)); + rhs = new RangeEdgePartition("foo=10", 10, 5, 10); + assertTrue(lhs.equals(rhs)); + assertFalse(lhs.equals(new RangeEdgePartition("foo=10", 10, 5, 11))); + assertFalse(lhs.equals(new RangeEdgePartition("foo=10", 10, 6, 10))); + assertFalse(lhs.equals(new RangeEdgePartition("foo=10", 11, 5, 10))); + assertFalse(lhs.equals(new RangeEdgePartition("foo=11", 10, 5, 10))); + } + + @Test + public void requireThatKeyIsEscapedInToString() { + assertEquals("foo=10+[2..3]", new RangeEdgePartition("foo=10", 10, 2, 3).toString()); + assertEquals("'\\foo=10'+[2..3]", new RangeEdgePartition("\foo=10", 10, 2, 3).toString()); + assertEquals("'\\x27foo\\x27=10'+[2..3]", new RangeEdgePartition("'foo'=10", 10, 2, 3).toString()); + }} diff --git a/predicate-search-core/src/test/java/com/yahoo/document/predicate/RangePartitionTest.java b/predicate-search-core/src/test/java/com/yahoo/document/predicate/RangePartitionTest.java new file mode 100644 index 00000000000..b9d2c865e9b --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/document/predicate/RangePartitionTest.java @@ -0,0 +1,60 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.document.predicate; + +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:magnarn@yahoo-inc.com">Magnar Nedland</a> + */ +public class RangePartitionTest { + + @Test + public void requireThatRangePartitionIsAValue() { + assertTrue(PredicateValue.class.isAssignableFrom(RangePartition.class)); + } + + @Test + public void requireThatConstructorsWork() { + RangePartition part = new RangePartition("foo=10-19"); + assertEquals("foo=10-19", part.getLabel()); + part = new RangePartition("foo", 10, 19, false); + assertEquals("foo=10-19", part.getLabel()); + part = new RangePartition("foo", 10, 19, true); + assertEquals("foo=-19-10", part.getLabel()); + } + + @Test + public void requireThatCloneIsImplemented() throws CloneNotSupportedException { + RangePartition node1 = new RangePartition("foo=300-399"); + RangePartition node2 = node1.clone(); + assertEquals(node1, node2); + assertNotSame(node1, node2); + } + + @Test + public void requireThatHashCodeIsImplemented() { + assertEquals(new RangePartition("foo=0-9").hashCode(), new RangePartition("foo=0-9").hashCode()); + } + + @Test + public void requireThatEqualsIsImplemented() { + RangePartition lhs = new RangePartition("foo=10-19"); + assertTrue(lhs.equals(lhs)); + assertFalse(lhs.equals(new Object())); + + RangePartition rhs = new RangePartition("bar=1000-1999"); + assertFalse(lhs.equals(rhs)); + rhs = new RangePartition("foo=10-19"); + assertTrue(lhs.equals(rhs)); + } + + @Test + public void requireThatKeyIsEscapedInToString() { + assertEquals("foo=10-19", new RangePartition("foo=10-19").toString()); + assertEquals("'\\foo=10-19'", new RangePartition("\foo=10-19").toString()); + assertEquals("'\\x27foo\\x27=10-19'", new RangePartition("'foo'=10-19").toString()); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/search/predicate/PredicateQueryParserTest.java b/predicate-search-core/src/test/java/com/yahoo/search/predicate/PredicateQueryParserTest.java new file mode 100644 index 00000000000..dcee3a0c55d --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/search/predicate/PredicateQueryParserTest.java @@ -0,0 +1,43 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.predicate; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author bjorncs + */ +public class PredicateQueryParserTest { + + @Test + public void require_that_json_is_correctly_parsed() { + String json = + "{" + + " \"features\":[" + + " {\"k\":\"k1\",\"v\":\"value1\",\"s\":\"0x1\"}," + + " {\"k\":\"k2\",\"v\":\"value2\",\"s\":\"0x3\"}" + + " ],\"rangeFeatures\":[" + + " {\"k\":\"range1\",\"v\":123456789123,\"s\":\"0xffff\"}," + + " {\"k\":\"range2\",\"v\":0,\"s\":\"0xffffffffffffffff\"}" + + " ]" + + "}"; + + PredicateQueryParser parser = new PredicateQueryParser(); + List<String> result = new ArrayList<>(); + parser.parseJsonQuery( + json, + (k, v, s) -> result.add(String.format("%s:%s:%#x", k, v, s)), + (k, v, s) -> result.add(String.format("%s:%d:%#x", k, v, s))); + + assertThat(result, is(Arrays.asList( + "k1:value1:0x1", "k2:value2:0x3", + "range1:123456789123:0xffff", "range2:0:0xffffffffffffffff"))); + } + +} diff --git a/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/AndOrSimplifierTest.java b/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/AndOrSimplifierTest.java new file mode 100644 index 00000000000..4ec6c496e73 --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/AndOrSimplifierTest.java @@ -0,0 +1,134 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.predicate.optimization; + +import com.yahoo.document.predicate.Predicate; +import org.junit.Test; + +import static com.yahoo.document.predicate.Predicates.and; +import static com.yahoo.document.predicate.Predicates.feature; +import static com.yahoo.document.predicate.Predicates.not; +import static com.yahoo.document.predicate.Predicates.or; +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:magnarn@yahoo-inc.com">Magnar Nedland</a> + */ +public class AndOrSimplifierTest { + + @Test + public void requireThatNestedConjunctionsAreCollapsed() { + assertSimplified(and(feature("a").inSet("b"), + feature("c").inSet("d")), + and(and(feature("a").inSet("b"), + feature("c").inSet("d")))); + } + + @Test + public void requireThatNestedConjuctionsAreCollapsedInPlace() { + assertSimplified(and(feature("a").inSet("b"), + feature("c").inSet("d"), + feature("e").inSet("f")), + and(feature("a").inSet("b"), + and(feature("c").inSet("d")), + feature("e").inSet("f"))); + } + + @Test + public void requireThatDeeplyNestedConjunctionsAreCollapsed() { + assertSimplified(and(feature("a").inSet("b"), + feature("c").inSet("d"), + feature("e").inSet("f"), + feature("g").inSet("h"), + feature("i").inSet("j")), + and(feature("a").inSet("b"), + and(feature("c").inSet("d")), + feature("e").inSet("f"), + and(and(feature("g").inSet("h"), + feature("i").inSet("j"))))); + } + + @Test + public void requireThatNestedDisjunctionsAreCollapsed() { + assertSimplified(or(feature("a").inSet("b"), + feature("c").inSet("d")), + or(or(feature("a").inSet("b"), + feature("c").inSet("d")))); + } + + @Test + public void requireThatNestedDisjuctionsAreCollapsedInPlace() { + assertSimplified(or(feature("a").inSet("b"), + feature("c").inSet("d"), + feature("e").inSet("f")), + or(feature("a").inSet("b"), + or(feature("c").inSet("d")), + feature("e").inSet("f"))); + } + + @Test + public void requireThatDeeplyNestedDisjunctionsAreCollapsed() { + assertSimplified(or(feature("a").inSet("b"), + feature("c").inSet("d"), + feature("e").inSet("f"), + feature("g").inSet("h"), + feature("i").inSet("j")), + or(feature("a").inSet("b"), + or(feature("c").inSet("d")), + feature("e").inSet("f"), + or(or(feature("g").inSet("h"), + feature("i").inSet("j"))))); + } + + @Test + public void requireThatConjunctionsAndDisjunctionsAreNotCollapsed() { + assertSimplified(and(or(feature("a").inSet("b"), + feature("c").inSet("d"))), + and(or(feature("a").inSet("b"), + feature("c").inSet("d")))); + } + + @Test + public void requireThatNotOrIsTranslatedToAndNot() { + assertSimplified( + and(feature("a").notInSet("b"), feature("c").inSet("d")), + not(or(feature("a").inSet("b"), feature("c").notInSet("d")))); + } + + @Test + public void requireThatNotAndIsTranslatedToOrNot() { + assertSimplified( + or(feature("a").notInSet("b"), feature("c").inSet("d")), + not(and(feature("a").inSet("b"), feature("c").notInSet("d")))); + } + + @Test + public void requireThatTreeWithoutNotIsNotAffected() { + assertSimplified( + and(feature("a").inSet("b"), feature("c").notInSet("d")), + and(feature("a").inSet("b"), feature("c").notInSet("d"))); + assertSimplified( + or(feature("a").inSet("b"), feature("c").notInSet("d")), + or(feature("a").inSet("b"), feature("c").notInSet("d"))); + assertSimplified(feature("a").inSet("b"), feature("a").inSet("b")); + } + + @Test + public void requireThatNotOfNotIsRemoved() { + assertSimplified(feature("a").inSet("b"), not(not(feature("a").inSet("b")))); + assertSimplified(feature("a").inSet("b"), not(feature("a").notInSet("b"))); + assertSimplified(feature("a").notInSet("b"), not(not(feature("a").notInSet("b")))); + } + + @Test + public void requireThatNotBeneathAndIsTranslated() { + assertSimplified( + and(feature("a").notInSet("b"), feature("c").inSet("d"), feature("b").inSet("c")), + and(not(or(feature("a").inSet("b"), feature("c").notInSet("d"))), feature("b").inSet("c"))); + } + + private static void assertSimplified(Predicate expected, Predicate input) { + AndOrSimplifier simplifier = new AndOrSimplifier(); + Predicate actual = simplifier.process(input, new PredicateOptions(10)); + assertEquals(expected, actual); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/BooleanSimplifierTest.java b/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/BooleanSimplifierTest.java new file mode 100644 index 00000000000..37d2352df57 --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/BooleanSimplifierTest.java @@ -0,0 +1,60 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.predicate.optimization; + +import com.yahoo.document.predicate.Predicate; +import org.junit.Test; + +import static com.yahoo.document.predicate.Predicates.*; +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:magnarn@yahoo-inc.com">Magnar Nedland</a> + */ +public class BooleanSimplifierTest { + + @Test + public void requireThatOrOfTrueIsTrue() { + assertSimplifiedTo(value(true), or(feature("a").inSet("b"), value(true))); + } + + @Test + public void requireThatFalseChildrenOfOrAreRemoved() { + assertSimplifiedTo(feature("a").inSet("b"), or(feature("a").inSet("b"), value(false))); + } + + @Test + public void requireThatAndOfFalseIsFalse() { + assertSimplifiedTo(value(false), and(feature("a").inSet("b"), value(false))); + } + + @Test + public void requireThatTrueChildrenOfAndAreRemoved() { + assertSimplifiedTo(feature("a").inSet("b"), and(feature("a").inSet("b"), value(true))); + } + + @Test + public void requireThatSingleChildAndOrAreRemoved() { + assertSimplifiedTo(feature("a").inSet("b"), and(or(and(feature("a").inSet("b"))))); + } + + @Test + public void requireThatValueChildrenOfNotAreInverted() { + assertSimplifiedTo(value(true), not(value(false))); + assertSimplifiedTo(value(false), not(value(true))); + assertSimplifiedTo(value(true), not(not(not(value(false))))); + assertSimplifiedTo(value(true), not(not(not(and(feature("a").inSet("b"), value(false)))))); + } + + @Test + public void requireThatComplexExpressionIsSimplified() { + assertSimplifiedTo( + Predicate.fromString("'pub_entity' not in [301951]"), + Predicate.fromString("true and true and true and true and true and 'pub_entity' not in [301951] and ((true and true and true and true) or (true and true and true and true) or (true and true and true and true and 'pub_entity' in [86271]))")); + } + + private void assertSimplifiedTo(Predicate expected, Predicate input) { + BooleanSimplifier simplifier = new BooleanSimplifier(); + Predicate actual = simplifier.process(input, new PredicateOptions(10)); + assertEquals(expected, actual); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/ComplexNodeTransformerTest.java b/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/ComplexNodeTransformerTest.java new file mode 100644 index 00000000000..45cdabfb4f2 --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/ComplexNodeTransformerTest.java @@ -0,0 +1,604 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.predicate.optimization; + +import com.yahoo.document.predicate.FeatureRange; +import com.yahoo.document.predicate.Predicate; +import com.yahoo.document.predicate.RangePartition; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:magnarn@yahoo-inc.com">Magnar Nedland</a> + */ +public class ComplexNodeTransformerTest { + + private long lowerBound = 0x8000000000000000L; + private long upperBound = 0x7fffffffffffffffL; + + private void testConvert(String predicate, String expected) { + int defaultArity = 10; + testConvert(predicate, expected, defaultArity); + } + + private void testConvert(String predicate, String expected, int arity) { + Predicate p = Predicate.fromString(predicate); + ComplexNodeTransformer transformer = new ComplexNodeTransformer(); + Predicate converted = transformer.process(p, new PredicateOptions(arity, lowerBound, upperBound)); + assertEquals(expected, converted.toString()); + } + + @Test + public void requireThatSingleValueRangesAreConverted() { + testConvert("foo in [0..0]", "foo in [0..0 (foo=0+[..0])]"); + testConvert("foo in [5..5]", "foo in [5..5 (foo=0+[5..5])]"); + testConvert("foo in [9..9]", "foo in [9..9 (foo=0+[9..])]"); + testConvert("foo in [10..10]", "foo in [10..10 (foo=10+[..0])]"); + testConvert("foo in [-5..-5]", "foo in [-5..-5 (foo=-0+[5..5])]"); + testConvert("foo in [-9..-9]", "foo in [-9..-9 (foo=-0+[9..])]"); + testConvert("foo in [-10..-10]", "foo in [-10..-10 (foo=-10+[..0])]"); + } + + @Test + public void requireThatVeryShortRangesAreConverted() { + testConvert("foo in [10..17]", "foo in [10..17 (foo=10+[..7])]"); + testConvert("foo in [12..19]", "foo in [12..19 (foo=10+[2..])]"); + testConvert("foo in [11..18]", "foo in [11..18 (foo=10+[1..8])]"); + testConvert("foo in [7..13]", "foo in [7..13 (foo=0+[7..],foo=10+[..3])]"); + testConvert("foo in [-17..-10]", "foo in [-17..-10 (foo=-10+[..7])]"); + testConvert("foo in [-19..-12]", "foo in [-19..-12 (foo=-10+[2..])]"); + testConvert("foo in [-18..-11]", "foo in [-18..-11 (foo=-10+[1..8])]"); + testConvert("foo in [-13..-7]", "foo in [-13..-7 (foo=-0+[7..],foo=-10+[..3])]"); + testConvert("foo in [-5..4]", "foo in [-5..4 (foo=-0+[..5],foo=0+[..4])]"); + } + + @Test + public void requireThatShortRangeIsConverted() { + testConvert("foo in [9..19]", "foo in [9..19 (foo=0+[9..],foo=10-19)]"); + testConvert("foo in [10..19]", "foo in [10..19 (foo=10-19)]"); + testConvert("foo in [10..20]", "foo in [10..20 (foo=20+[..0],foo=10-19)]"); + testConvert("foo in [12..21]", "foo in [12..21 (foo=10+[2..],foo=20+[..1])]"); + testConvert("foo in [-19..-9]", "foo in [-19..-9 (foo=-0+[9..],foo=-19-10)]"); + testConvert("foo in [-19..-10]", "foo in [-19..-10 (foo=-19-10)]"); + testConvert("foo in [-20..-10]", "foo in [-20..-10 (foo=-20+[..0],foo=-19-10)]"); + testConvert("foo in [-21..-12]", "foo in [-21..-12 (foo=-10+[2..],foo=-20+[..1])]"); + testConvert("foo in [-9..9]", "foo in [-9..9 (foo=-9-0,foo=0-9)]"); + testConvert("foo in [-10..10]", "foo in [-10..10 (foo=-10+[..0],foo=10+[..0],foo=-9-0,foo=0-9)]"); + } + + @Test + public void requireThatMediumRangeIsConverted() { + testConvert("foo in [10..39]", "foo in [10..39 (foo=10-19,foo=20-29,foo=30-39)]"); + testConvert("foo in [10..38]", "foo in [10..38 (foo=30+[..8],foo=10-19,foo=20-29)]"); + testConvert("foo in [18..39]", "foo in [18..39 (foo=10+[8..],foo=20-29,foo=30-39)]"); + testConvert("foo in [8..38]", "foo in [8..38 (foo=0+[8..],foo=30+[..8],foo=10-19,foo=20-29)]"); + testConvert("foo in [-39..-10]", "foo in [-39..-10 (foo=-19-10,foo=-29-20,foo=-39-30)]"); + testConvert("foo in [-38..-10]", "foo in [-38..-10 (foo=-30+[..8],foo=-19-10,foo=-29-20)]"); + testConvert("foo in [-39..-18]", "foo in [-39..-18 (foo=-10+[8..],foo=-29-20,foo=-39-30)]"); + testConvert("foo in [-38..-8]", "foo in [-38..-8 (foo=-0+[8..],foo=-30+[..8],foo=-19-10,foo=-29-20)]"); + testConvert("foo in [-19..19]", "foo in [-19..19 (foo=-9-0,foo=-19-10,foo=0-9,foo=10-19)]"); + } + + @Test + public void requireThatLargeRangeIsConverted() { + testConvert("foo in [10..199]", "foo in [10..199 (foo=10-19,foo=20-29,foo=30-39,foo=40-49,foo=50-59,foo=60-69,foo=70-79,foo=80-89,foo=90-99,foo=100-199)]"); + testConvert("foo in [8..207]", "foo in [8..207 (foo=0+[8..],foo=200+[..7],foo=10-19,foo=20-29,foo=30-39,foo=40-49,foo=50-59,foo=60-69,foo=70-79,foo=80-89,foo=90-99,foo=100-199)]"); + testConvert("foo in [999..2001]", "foo in [999..2001 (foo=990+[9..],foo=2000+[..1],foo=1000-1999)]"); + testConvert("foo in [999..2009]", "foo in [999..2009 (foo=990+[9..],foo=2000-2009,foo=1000-1999)]"); + testConvert("foo in [999..2099]", "foo in [999..2099 (foo=990+[9..],foo=2000-2099,foo=1000-1999)]"); + testConvert("foo in [999..2999]", "foo in [999..2999 (foo=990+[9..],foo=1000-1999,foo=2000-2999)]"); + testConvert("foo in [-199..-10]", "foo in [-199..-10 (foo=-19-10,foo=-29-20,foo=-39-30,foo=-49-40,foo=-59-50,foo=-69-60,foo=-79-70,foo=-89-80,foo=-99-90,foo=-199-100)]"); + testConvert("foo in [-207..-8]", "foo in [-207..-8 (foo=-0+[8..],foo=-200+[..7],foo=-19-10,foo=-29-20,foo=-39-30,foo=-49-40,foo=-59-50,foo=-69-60,foo=-79-70,foo=-89-80,foo=-99-90,foo=-199-100)]"); + testConvert("foo in [-2001..-999]", "foo in [-2001..-999 (foo=-990+[9..],foo=-2000+[..1],foo=-1999-1000)]"); + testConvert("foo in [-999..999]", "foo in [-999..999 (foo=-999-0,foo=0-999)]"); + } + + @Test + public void requireThatLongMaxIsConverted() { + testConvert("foo in [0..9223372036854775807]", + "foo in [0..9223372036854775807 (" + + "foo=9223372036854775800+[..7]," + + "foo=9223372036854775000-9223372036854775099," + + "foo=9223372036854775100-9223372036854775199," + + "foo=9223372036854775200-9223372036854775299," + + "foo=9223372036854775300-9223372036854775399," + + "foo=9223372036854775400-9223372036854775499," + + "foo=9223372036854775500-9223372036854775599," + + "foo=9223372036854775600-9223372036854775699," + + "foo=9223372036854775700-9223372036854775799," + + "foo=9223372036854770000-9223372036854770999," + + "foo=9223372036854771000-9223372036854771999," + + "foo=9223372036854772000-9223372036854772999," + + "foo=9223372036854773000-9223372036854773999," + + "foo=9223372036854774000-9223372036854774999," + + "foo=9223372036854700000-9223372036854709999," + + "foo=9223372036854710000-9223372036854719999," + + "foo=9223372036854720000-9223372036854729999," + + "foo=9223372036854730000-9223372036854739999," + + "foo=9223372036854740000-9223372036854749999," + + "foo=9223372036854750000-9223372036854759999," + + "foo=9223372036854760000-9223372036854769999," + + "foo=9223372036854000000-9223372036854099999," + + "foo=9223372036854100000-9223372036854199999," + + "foo=9223372036854200000-9223372036854299999," + + "foo=9223372036854300000-9223372036854399999," + + "foo=9223372036854400000-9223372036854499999," + + "foo=9223372036854500000-9223372036854599999," + + "foo=9223372036854600000-9223372036854699999," + + "foo=9223372036850000000-9223372036850999999," + + "foo=9223372036851000000-9223372036851999999," + + "foo=9223372036852000000-9223372036852999999," + + "foo=9223372036853000000-9223372036853999999," + + "foo=9223372036800000000-9223372036809999999," + + "foo=9223372036810000000-9223372036819999999," + + "foo=9223372036820000000-9223372036829999999," + + "foo=9223372036830000000-9223372036839999999," + + "foo=9223372036840000000-9223372036849999999," + + "foo=9223372036000000000-9223372036099999999," + + "foo=9223372036100000000-9223372036199999999," + + "foo=9223372036200000000-9223372036299999999," + + "foo=9223372036300000000-9223372036399999999," + + "foo=9223372036400000000-9223372036499999999," + + "foo=9223372036500000000-9223372036599999999," + + "foo=9223372036600000000-9223372036699999999," + + "foo=9223372036700000000-9223372036799999999," + + "foo=9223372030000000000-9223372030999999999," + + "foo=9223372031000000000-9223372031999999999," + + "foo=9223372032000000000-9223372032999999999," + + "foo=9223372033000000000-9223372033999999999," + + "foo=9223372034000000000-9223372034999999999," + + "foo=9223372035000000000-9223372035999999999," + + "foo=9223372000000000000-9223372009999999999," + + "foo=9223372010000000000-9223372019999999999," + + "foo=9223372020000000000-9223372029999999999," + + "foo=9223370000000000000-9223370999999999999," + + "foo=9223371000000000000-9223371999999999999," + + "foo=9223300000000000000-9223309999999999999," + + "foo=9223310000000000000-9223319999999999999," + + "foo=9223320000000000000-9223329999999999999," + + "foo=9223330000000000000-9223339999999999999," + + "foo=9223340000000000000-9223349999999999999," + + "foo=9223350000000000000-9223359999999999999," + + "foo=9223360000000000000-9223369999999999999," + + "foo=9223000000000000000-9223099999999999999," + + "foo=9223100000000000000-9223199999999999999," + + "foo=9223200000000000000-9223299999999999999," + + "foo=9220000000000000000-9220999999999999999," + + "foo=9221000000000000000-9221999999999999999," + + "foo=9222000000000000000-9222999999999999999," + + "foo=9200000000000000000-9209999999999999999," + + "foo=9210000000000000000-9219999999999999999," + + "foo=9000000000000000000-9099999999999999999," + + "foo=9100000000000000000-9199999999999999999," + + "foo=0-999999999999999999," + + "foo=1000000000000000000-1999999999999999999," + + "foo=2000000000000000000-2999999999999999999," + + "foo=3000000000000000000-3999999999999999999," + + "foo=4000000000000000000-4999999999999999999," + + "foo=5000000000000000000-5999999999999999999," + + "foo=6000000000000000000-6999999999999999999," + + "foo=7000000000000000000-7999999999999999999," + + "foo=8000000000000000000-8999999999999999999)]"); + + testConvert("foo in [9223372036854775807..9223372036854775807]", "foo in [9223372036854775807..9223372036854775807 (foo=9223372036854775800+[7..7])]"); + } + + @Test + public void requireThatLongMinIsConverted() { + testConvert("foo in [-9223372036854775808..-1]", + "foo in [-9223372036854775808..-1 (" + + "foo=-9223372036854775800+[..8]," + + "foo=-9223372036854775099-9223372036854775000," + + "foo=-9223372036854775199-9223372036854775100," + + "foo=-9223372036854775299-9223372036854775200," + + "foo=-9223372036854775399-9223372036854775300," + + "foo=-9223372036854775499-9223372036854775400," + + "foo=-9223372036854775599-9223372036854775500," + + "foo=-9223372036854775699-9223372036854775600," + + "foo=-9223372036854775799-9223372036854775700," + + "foo=-9223372036854770999-9223372036854770000," + + "foo=-9223372036854771999-9223372036854771000," + + "foo=-9223372036854772999-9223372036854772000," + + "foo=-9223372036854773999-9223372036854773000," + + "foo=-9223372036854774999-9223372036854774000," + + "foo=-9223372036854709999-9223372036854700000," + + "foo=-9223372036854719999-9223372036854710000," + + "foo=-9223372036854729999-9223372036854720000," + + "foo=-9223372036854739999-9223372036854730000," + + "foo=-9223372036854749999-9223372036854740000," + + "foo=-9223372036854759999-9223372036854750000," + + "foo=-9223372036854769999-9223372036854760000," + + "foo=-9223372036854099999-9223372036854000000," + + "foo=-9223372036854199999-9223372036854100000," + + "foo=-9223372036854299999-9223372036854200000," + + "foo=-9223372036854399999-9223372036854300000," + + "foo=-9223372036854499999-9223372036854400000," + + "foo=-9223372036854599999-9223372036854500000," + + "foo=-9223372036854699999-9223372036854600000," + + "foo=-9223372036850999999-9223372036850000000," + + "foo=-9223372036851999999-9223372036851000000," + + "foo=-9223372036852999999-9223372036852000000," + + "foo=-9223372036853999999-9223372036853000000," + + "foo=-9223372036809999999-9223372036800000000," + + "foo=-9223372036819999999-9223372036810000000," + + "foo=-9223372036829999999-9223372036820000000," + + "foo=-9223372036839999999-9223372036830000000," + + "foo=-9223372036849999999-9223372036840000000," + + "foo=-9223372036099999999-9223372036000000000," + + "foo=-9223372036199999999-9223372036100000000," + + "foo=-9223372036299999999-9223372036200000000," + + "foo=-9223372036399999999-9223372036300000000," + + "foo=-9223372036499999999-9223372036400000000," + + "foo=-9223372036599999999-9223372036500000000," + + "foo=-9223372036699999999-9223372036600000000," + + "foo=-9223372036799999999-9223372036700000000," + + "foo=-9223372030999999999-9223372030000000000," + + "foo=-9223372031999999999-9223372031000000000," + + "foo=-9223372032999999999-9223372032000000000," + + "foo=-9223372033999999999-9223372033000000000," + + "foo=-9223372034999999999-9223372034000000000," + + "foo=-9223372035999999999-9223372035000000000," + + "foo=-9223372009999999999-9223372000000000000," + + "foo=-9223372019999999999-9223372010000000000," + + "foo=-9223372029999999999-9223372020000000000," + + "foo=-9223370999999999999-9223370000000000000," + + "foo=-9223371999999999999-9223371000000000000," + + "foo=-9223309999999999999-9223300000000000000," + + "foo=-9223319999999999999-9223310000000000000," + + "foo=-9223329999999999999-9223320000000000000," + + "foo=-9223339999999999999-9223330000000000000," + + "foo=-9223349999999999999-9223340000000000000," + + "foo=-9223359999999999999-9223350000000000000," + + "foo=-9223369999999999999-9223360000000000000," + + "foo=-9223099999999999999-9223000000000000000," + + "foo=-9223199999999999999-9223100000000000000," + + "foo=-9223299999999999999-9223200000000000000," + + "foo=-9220999999999999999-9220000000000000000," + + "foo=-9221999999999999999-9221000000000000000," + + "foo=-9222999999999999999-9222000000000000000," + + "foo=-9209999999999999999-9200000000000000000," + + "foo=-9219999999999999999-9210000000000000000," + + "foo=-9099999999999999999-9000000000000000000," + + "foo=-9199999999999999999-9100000000000000000," + + "foo=-999999999999999999-0," + + "foo=-1999999999999999999-1000000000000000000," + + "foo=-2999999999999999999-2000000000000000000," + + "foo=-3999999999999999999-3000000000000000000," + + "foo=-4999999999999999999-4000000000000000000," + + "foo=-5999999999999999999-5000000000000000000," + + "foo=-6999999999999999999-6000000000000000000," + + "foo=-7999999999999999999-7000000000000000000," + + "foo=-8999999999999999999-8000000000000000000)]"); + testConvert("foo in [-9223372036854775808..-9223372036854775808]", "foo in [-9223372036854775808..-9223372036854775808 (foo=-9223372036854775800+[8..8])]"); + } + + @Test + public void requireThatLowAritiesWork() { + testConvert("foo in [10..39]", "foo in [10..39 (foo=10-11,foo=12-15,foo=32-39,foo=16-31)]", 2); + testConvert("foo in [2..32]", "foo in [2..32 (foo=32+[..0],foo=2-3,foo=4-7,foo=8-15,foo=16-31)]", 2); + testConvert("foo in [-31..63]", "foo in [-31..63 (foo=-31-0,foo=0-63)]", 2); + testConvert("foo in [0..9223372036854775807]", "foo in [0..9223372036854775807 (foo=0-9223372036854775807)]", 2); + testConvert("foo in [9223372036854775807..9223372036854775807]", "foo in [9223372036854775807..9223372036854775807 (foo=9223372036854775806+[1..])]", 2); + testConvert("foo in [-9223372036854775808..-1]", "foo in [-9223372036854775808..-1 (foo=-9223372036854775808+[..0],foo=-9223372036854775807-0)]", 2); + testConvert("foo in [-9223372036854775808..-9223372036854775808]", "foo in [-9223372036854775808..-9223372036854775808 (foo=-9223372036854775808+[..0])]", 2); + testConvert("foo in [-9223372036854775808..9223372036854775807]", "foo in [-9223372036854775808..9223372036854775807 (foo=-9223372036854775808+[..0],foo=-9223372036854775807-0,foo=0-9223372036854775807)]", 2); + + testConvert("foo in [9223372036854775807..9223372036854775807]", "foo in [9223372036854775807..9223372036854775807 (foo=9223372036854775806+[1..1])]", 3); + testConvert("foo in [-9223372036854775808..-9223372036854775808]", "foo in [-9223372036854775808..-9223372036854775808 (foo=-9223372036854775806+[2..])]", 3); + testConvert("foo in [-9223372036854775808..-1]", + "foo in [-9223372036854775808..-1 (" + + "foo=-9223372036854775808-9223372036854775782," + + "foo=-9223372036854775700-9223372036854775620," + + "foo=-9223372036854775781-9223372036854775701," + + "foo=-9223372036854775376-9223372036854775134," + + "foo=-9223372036854775619-9223372036854775377," + + "foo=-9223372036854775133-9223372036854774405," + + "foo=-9223372036854774404-9223372036854767844," + + "foo=-9223372036854708794-9223372036854649746," + + "foo=-9223372036854767843-9223372036854708795," + + "foo=-9223372036854472598-9223372036854295452," + + "foo=-9223372036854649745-9223372036854472599," + + "foo=-9223372036854295451-9223372036853764011," + + "foo=-9223372036852169687-9223372036850575365," + + "foo=-9223372036853764010-9223372036852169688," + + "foo=-9223372036850575364-9223372036807528644," + + "foo=-9223372036420108154-9223372036032687666," + + "foo=-9223372036807528643-9223372036420108155," + + "foo=-9223372036032687665-9223372032545903265," + + "foo=-9223372022085550061-9223372011625196859," + + "foo=-9223372032545903264-9223372022085550062," + + "foo=-9223372011625196858-9223371980244137250," + + "foo=-9223371980244137249-9223371132955527807," + + "foo=-9223368591089699477-9223366049223871149," + + "foo=-9223371132955527806-9223368591089699478," + + "foo=-9223358423626386161-9223350798028901175," + + "foo=-9223366049223871148-9223358423626386162," + + "foo=-9223327921236446213-9223305044443991253," + + "foo=-9223350798028901174-9223327921236446214," + + "foo=-9223305044443991252-9223099153311896604," + + "foo=-9223099153311896603-9222481479915612657," + + "foo=-9222481479915612656-9205804298215946088," + + "foo=-9205804298215946087-9155772753116946381," + + "foo=-9155772753116946380-9005678117819947260," + + "foo=-8555394211928949896-8105110306037952534," + + "foo=-9005678117819947259-8555394211928949897," + + "foo=-4052555153018976266-0," + + "foo=-8105110306037952533-4052555153018976267)]", 3); + } + + @Test + public void requireThatHighAritiesWork() { + testConvert("foo in [10..39]", "foo in [10..39 (foo=0+[10..39])]", 1000); + testConvert("foo in [9000..11000]", "foo in [9000..11000 (foo=0+[9000..],foo=10000+[..1000])]", 10000); + testConvert("foo in [10..39]", "foo in [10..39 (foo=0+[10..39])]", 16384); + testConvert("foo in [10..32768]", "foo in [10..32768 (foo=0+[10..],foo=32768+[..0],foo=16384-32767)]", 16384); + + testConvert("foo in [9223372036854775807..9223372036854775807]", "foo in [9223372036854775807..9223372036854775807 (foo=9223372036854759424+[16383..])]", 16384); + testConvert("foo in [-9223372036854775808..-9223372036854775808]", "foo in [-9223372036854775808..-9223372036854775808 (foo=-9223372036854775808+[..0])]", 16384); + } + + @Test + public void requireThatOpenRangesWork() { + testConvert("foo in [-7..]", "foo in [-7.. (foo=-7-0,foo=0-9223372036854775807)]", 2); + testConvert("foo in [..7]", "foo in [..7 (foo=-9223372036854775808+[..0],foo=-9223372036854775807-0,foo=0-7)]", 2); + } + + @Test + public void requireThatUpperAndLowerBoundsWork() { + lowerBound = -999; + upperBound = 999; + testConvert("foo in [-9..]", "foo in [-9.. (foo=-9-0,foo=0-999)]", 10); + testConvert("foo in [..9]", "foo in [..9 (foo=-999-0,foo=0-9)]", 10); + testConvert("foo in [..]", "foo in [.. (foo=-999-0,foo=0-999)]", 10); + } + + @Test + public void requireThatUpperAndLowerBoundsPruneClosedRanges() { + lowerBound = -999; + upperBound = 999; + testConvert("foo in [0..10000]", "foo in [0..10000 (foo=0-999)]", 10); + lowerBound = 888; + testConvert("foo in [0..10000]", "foo in [0..10000 (foo=0-999)]", 10); + lowerBound = 900; + testConvert("foo in [0..10000]", "foo in [0..10000 (foo=0-999)]", 10); + } + + @Test + public void requireThatRangesOutsideBoundsAreSimplifiedToOneImpossibleRange() { + lowerBound = 900; + upperBound = 999; + testConvert("foo in [0..100]", "foo in [0..100 (foo=-9223372036854775799-9223372036854775790)]", 10); + testConvert("foo in [1000..1100]", "foo in [1000..1100 (foo=9223372036854775790-9223372036854775799)]", 10); + lowerBound = -999; + upperBound = -900; + testConvert("foo in [-2000..-1000]", "foo in [-2000..-1000 (foo=-9223372036854775799-9223372036854775790)]", 10); + testConvert("foo in [0..100]", "foo in [0..100 (foo=9223372036854775790-9223372036854775799)]", 10); + lowerBound = -9223372036854775790L; + upperBound = 9223372036854775790L; + testConvert("foo in [9223372036854775791..9223372036854775792]", "foo in [9223372036854775791..9223372036854775792 (foo=9223372036854775790+[1..1])]", 10); + testConvert("foo in [-9223372036854775792..-9223372036854775791]", "foo in [-9223372036854775792..-9223372036854775791 (foo=-9223372036854775790+[1..1])]", 10); + } + + @Test + public void requireThatUpperAndLowerBoundsAreAdjustedWithArity() { + lowerBound = -999; + upperBound = 999; + testConvert("foo in [0..10000]", "foo in [0..10000 (foo=0-999)]", 10); + testConvert("foo in [0..10000]", "foo in [0..10000 (foo=0-1023)]", 2); + testConvert("foo in [-10000..10000]", "foo in [-10000..10000 (foo=-999-0,foo=0-999)]", 10); + testConvert("foo in [-10000..10000]", "foo in [-10000..10000 (foo=-1023-0,foo=0-1023)]", 2); + + upperBound = 1000; + testConvert("foo in [0..10000]", "foo in [0..10000 (foo=0-9999)]", 10); + + lowerBound = 100; + upperBound = 999; + testConvert("foo in [0..10000]", "foo in [0..10000 (foo=0-999)]", 10); + testConvert("foo in [0..10000]", "foo in [0..10000 (foo=0-1023)]", 2); + + lowerBound = -999; + upperBound = -100; + testConvert("foo in [-10000..10000]", "foo in [-10000..10000 (foo=-999-0)]", 10); + testConvert("foo in [-10000..10000]", "foo in [-10000..10000 (foo=-1023-0)]", 2); + + lowerBound = -9223372036854775790L; + upperBound = 9223372036854775790L; + testConvert("foo in [-9223372036854775791..9223372036854775792]", + "foo in [-9223372036854775791..9223372036854775792 (" + + "foo=-9223372036854775790+[..0],foo=9223372036854775790+[..0]," + + "foo=-9223372036854775709-9223372036854775700," + + "foo=-9223372036854775719-9223372036854775710," + + "foo=-9223372036854775729-9223372036854775720," + + "foo=-9223372036854775739-9223372036854775730," + + "foo=-9223372036854775749-9223372036854775740," + + "foo=-9223372036854775759-9223372036854775750," + + "foo=-9223372036854775769-9223372036854775760," + + "foo=-9223372036854775779-9223372036854775770," + + "foo=-9223372036854775789-9223372036854775780," + + "foo=-9223372036854775099-9223372036854775000," + + "foo=-9223372036854775199-9223372036854775100," + + "foo=-9223372036854775299-9223372036854775200," + + "foo=-9223372036854775399-9223372036854775300," + + "foo=-9223372036854775499-9223372036854775400," + + "foo=-9223372036854775599-9223372036854775500," + + "foo=-9223372036854775699-9223372036854775600," + + "foo=-9223372036854770999-9223372036854770000," + + "foo=-9223372036854771999-9223372036854771000," + + "foo=-9223372036854772999-9223372036854772000," + + "foo=-9223372036854773999-9223372036854773000," + + "foo=-9223372036854774999-9223372036854774000," + + "foo=-9223372036854709999-9223372036854700000," + + "foo=-9223372036854719999-9223372036854710000," + + "foo=-9223372036854729999-9223372036854720000," + + "foo=-9223372036854739999-9223372036854730000," + + "foo=-9223372036854749999-9223372036854740000," + + "foo=-9223372036854759999-9223372036854750000," + + "foo=-9223372036854769999-9223372036854760000," + + "foo=-9223372036854099999-9223372036854000000," + + "foo=-9223372036854199999-9223372036854100000," + + "foo=-9223372036854299999-9223372036854200000," + + "foo=-9223372036854399999-9223372036854300000," + + "foo=-9223372036854499999-9223372036854400000," + + "foo=-9223372036854599999-9223372036854500000," + + "foo=-9223372036854699999-9223372036854600000," + + "foo=-9223372036850999999-9223372036850000000," + + "foo=-9223372036851999999-9223372036851000000," + + "foo=-9223372036852999999-9223372036852000000," + + "foo=-9223372036853999999-9223372036853000000," + + "foo=-9223372036809999999-9223372036800000000," + + "foo=-9223372036819999999-9223372036810000000," + + "foo=-9223372036829999999-9223372036820000000," + + "foo=-9223372036839999999-9223372036830000000," + + "foo=-9223372036849999999-9223372036840000000," + + "foo=-9223372036099999999-9223372036000000000," + + "foo=-9223372036199999999-9223372036100000000," + + "foo=-9223372036299999999-9223372036200000000," + + "foo=-9223372036399999999-9223372036300000000," + + "foo=-9223372036499999999-9223372036400000000," + + "foo=-9223372036599999999-9223372036500000000," + + "foo=-9223372036699999999-9223372036600000000," + + "foo=-9223372036799999999-9223372036700000000," + + "foo=-9223372030999999999-9223372030000000000," + + "foo=-9223372031999999999-9223372031000000000," + + "foo=-9223372032999999999-9223372032000000000," + + "foo=-9223372033999999999-9223372033000000000," + + "foo=-9223372034999999999-9223372034000000000," + + "foo=-9223372035999999999-9223372035000000000," + + "foo=-9223372009999999999-9223372000000000000," + + "foo=-9223372019999999999-9223372010000000000," + + "foo=-9223372029999999999-9223372020000000000," + + "foo=-9223370999999999999-9223370000000000000," + + "foo=-9223371999999999999-9223371000000000000," + + "foo=-9223309999999999999-9223300000000000000," + + "foo=-9223319999999999999-9223310000000000000," + + "foo=-9223329999999999999-9223320000000000000," + + "foo=-9223339999999999999-9223330000000000000," + + "foo=-9223349999999999999-9223340000000000000," + + "foo=-9223359999999999999-9223350000000000000," + + "foo=-9223369999999999999-9223360000000000000," + + "foo=-9223099999999999999-9223000000000000000," + + "foo=-9223199999999999999-9223100000000000000," + + "foo=-9223299999999999999-9223200000000000000," + + "foo=-9220999999999999999-9220000000000000000," + + "foo=-9221999999999999999-9221000000000000000," + + "foo=-9222999999999999999-9222000000000000000," + + "foo=-9209999999999999999-9200000000000000000," + + "foo=-9219999999999999999-9210000000000000000," + + "foo=-9099999999999999999-9000000000000000000," + + "foo=-9199999999999999999-9100000000000000000," + + "foo=-999999999999999999-0," + + "foo=-1999999999999999999-1000000000000000000," + + "foo=-2999999999999999999-2000000000000000000," + + "foo=-3999999999999999999-3000000000000000000," + + "foo=-4999999999999999999-4000000000000000000," + + "foo=-5999999999999999999-5000000000000000000," + + "foo=-6999999999999999999-6000000000000000000," + + "foo=-7999999999999999999-7000000000000000000," + + "foo=-8999999999999999999-8000000000000000000," + + "foo=9223372036854775700-9223372036854775709," + + "foo=9223372036854775710-9223372036854775719," + + "foo=9223372036854775720-9223372036854775729," + + "foo=9223372036854775730-9223372036854775739," + + "foo=9223372036854775740-9223372036854775749," + + "foo=9223372036854775750-9223372036854775759," + + "foo=9223372036854775760-9223372036854775769," + + "foo=9223372036854775770-9223372036854775779," + + "foo=9223372036854775780-9223372036854775789," + + "foo=9223372036854775000-9223372036854775099," + + "foo=9223372036854775100-9223372036854775199," + + "foo=9223372036854775200-9223372036854775299," + + "foo=9223372036854775300-9223372036854775399," + + "foo=9223372036854775400-9223372036854775499," + + "foo=9223372036854775500-9223372036854775599," + + "foo=9223372036854775600-9223372036854775699," + + "foo=9223372036854770000-9223372036854770999," + + "foo=9223372036854771000-9223372036854771999," + + "foo=9223372036854772000-9223372036854772999," + + "foo=9223372036854773000-9223372036854773999," + + "foo=9223372036854774000-9223372036854774999," + + "foo=9223372036854700000-9223372036854709999," + + "foo=9223372036854710000-9223372036854719999," + + "foo=9223372036854720000-9223372036854729999," + + "foo=9223372036854730000-9223372036854739999," + + "foo=9223372036854740000-9223372036854749999," + + "foo=9223372036854750000-9223372036854759999," + + "foo=9223372036854760000-9223372036854769999," + + "foo=9223372036854000000-9223372036854099999," + + "foo=9223372036854100000-9223372036854199999," + + "foo=9223372036854200000-9223372036854299999," + + "foo=9223372036854300000-9223372036854399999," + + "foo=9223372036854400000-9223372036854499999," + + "foo=9223372036854500000-9223372036854599999," + + "foo=9223372036854600000-9223372036854699999," + + "foo=9223372036850000000-9223372036850999999," + + "foo=9223372036851000000-9223372036851999999," + + "foo=9223372036852000000-9223372036852999999," + + "foo=9223372036853000000-9223372036853999999," + + "foo=9223372036800000000-9223372036809999999," + + "foo=9223372036810000000-9223372036819999999," + + "foo=9223372036820000000-9223372036829999999," + + "foo=9223372036830000000-9223372036839999999," + + "foo=9223372036840000000-9223372036849999999," + + "foo=9223372036000000000-9223372036099999999," + + "foo=9223372036100000000-9223372036199999999," + + "foo=9223372036200000000-9223372036299999999," + + "foo=9223372036300000000-9223372036399999999," + + "foo=9223372036400000000-9223372036499999999," + + "foo=9223372036500000000-9223372036599999999," + + "foo=9223372036600000000-9223372036699999999," + + "foo=9223372036700000000-9223372036799999999," + + "foo=9223372030000000000-9223372030999999999," + + "foo=9223372031000000000-9223372031999999999," + + "foo=9223372032000000000-9223372032999999999," + + "foo=9223372033000000000-9223372033999999999," + + "foo=9223372034000000000-9223372034999999999," + + "foo=9223372035000000000-9223372035999999999," + + "foo=9223372000000000000-9223372009999999999," + + "foo=9223372010000000000-9223372019999999999," + + "foo=9223372020000000000-9223372029999999999," + + "foo=9223370000000000000-9223370999999999999," + + "foo=9223371000000000000-9223371999999999999," + + "foo=9223300000000000000-9223309999999999999," + + "foo=9223310000000000000-9223319999999999999," + + "foo=9223320000000000000-9223329999999999999," + + "foo=9223330000000000000-9223339999999999999," + + "foo=9223340000000000000-9223349999999999999," + + "foo=9223350000000000000-9223359999999999999," + + "foo=9223360000000000000-9223369999999999999," + + "foo=9223000000000000000-9223099999999999999," + + "foo=9223100000000000000-9223199999999999999," + + "foo=9223200000000000000-9223299999999999999," + + "foo=9220000000000000000-9220999999999999999," + + "foo=9221000000000000000-9221999999999999999," + + "foo=9222000000000000000-9222999999999999999," + + "foo=9200000000000000000-9209999999999999999," + + "foo=9210000000000000000-9219999999999999999," + + "foo=9000000000000000000-9099999999999999999," + + "foo=9100000000000000000-9199999999999999999," + + "foo=0-999999999999999999," + + "foo=1000000000000000000-1999999999999999999," + + "foo=2000000000000000000-2999999999999999999," + + "foo=3000000000000000000-3999999999999999999," + + "foo=4000000000000000000-4999999999999999999," + + "foo=5000000000000000000-5999999999999999999," + + "foo=6000000000000000000-6999999999999999999," + + "foo=7000000000000000000-7999999999999999999," + + "foo=8000000000000000000-8999999999999999999)]"); + + lowerBound = -922337203685477579L; + upperBound = 922337203685477579L; + testConvert("foo in [-9223372036854775791..9223372036854775792]", + "foo in [-9223372036854775791..9223372036854775792 (foo=-999999999999999999-0,foo=0-999999999999999999)]"); + } + + @Test + public void requireThatExistingPartitionsAreCleared() { + testConvert("foo in [10..19]", "foo in [10..19 (foo=10-19)]"); + Predicate p = Predicate.fromString("foo in [10..19]"); + ((FeatureRange)p).addPartition(new RangePartition("foo", 10000L, 20000L, false)); + ComplexNodeTransformer tranformer = new ComplexNodeTransformer(); + Predicate converted = tranformer.process(p, new PredicateOptions(10, lowerBound, upperBound)); + assertEquals("foo in [10..19 (foo=10-19)]", converted.toString()); + + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/NotNodeReordererTest.java b/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/NotNodeReordererTest.java new file mode 100644 index 00000000000..de61c3e04c6 --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/NotNodeReordererTest.java @@ -0,0 +1,55 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.predicate.optimization; + +import com.yahoo.document.predicate.Predicate; +import org.junit.Test; + +import static com.yahoo.document.predicate.Predicates.and; +import static com.yahoo.document.predicate.Predicates.feature; +import static com.yahoo.document.predicate.Predicates.or; +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:magnarn@yahoo-inc.com">Magnar Nedland</a> + */ +public class NotNodeReordererTest { + @Test + public void requireThatNotChildrenAreMovedAwayFromLastAndChild() { + checkReorder( + and(feature("a").inSet("b"), feature("c").notInSet("d")), + and(feature("c").notInSet("d"), feature("a").inSet("b"))); + + checkReorder( + and(feature("a").inSet("b"), feature("c").notInSet("d"), feature("e").notInSet("f")), + and(feature("c").notInSet("d"), feature("e").notInSet("f"), feature("a").inSet("b"))); + } + + @Test + public void requireThatNotChildrenAreMovedToLastOrChild() { + checkReorder( + or(feature("c").notInSet("d"), feature("a").inSet("b")), + or(feature("a").inSet("b"), feature("c").notInSet("d"))); + + checkReorder( + or(feature("c").notInSet("d"), feature("e").notInSet("f"), feature("a").inSet("b")), + or(feature("a").inSet("b"), feature("c").notInSet("d"), feature("e").notInSet("f"))); + } + + @Test + public void requireThatComplexReorderingWork() { + checkReorder(and(feature("g").inSet("h"), + or(and(feature("a").notInSet("b"), + feature("c").notInSet("d")), + feature("e").inSet("f"))), + and(or(feature("e").inSet("f"), + and(feature("a").notInSet("b"), + feature("c").notInSet("d"))), + feature("g").inSet("h"))); + } + + private static void checkReorder(Predicate input, Predicate expected) { + NotNodeReorderer reorderer = new NotNodeReorderer(); + Predicate actual = reorderer.process(input, new PredicateOptions(10)); + assertEquals(expected, actual); + } +} diff --git a/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/OrSimplifierTest.java b/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/OrSimplifierTest.java new file mode 100644 index 00000000000..6db02c5be57 --- /dev/null +++ b/predicate-search-core/src/test/java/com/yahoo/search/predicate/optimization/OrSimplifierTest.java @@ -0,0 +1,98 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.predicate.optimization; + +import com.yahoo.document.predicate.Predicate; +import org.junit.Test; + +import static com.yahoo.document.predicate.Predicates.and; +import static com.yahoo.document.predicate.Predicates.feature; +import static com.yahoo.document.predicate.Predicates.not; +import static com.yahoo.document.predicate.Predicates.or; +import static org.junit.Assert.assertEquals; + +public class OrSimplifierTest { + + @Test + public void require_that_or_with_feature_sets_of_same_key_is_simplified_to_single_feature_set() { + Predicate p = + or( + feature("key1").inSet("value1", "value4"), + feature("key1").inSet("value2", "value3"), + feature("key1").inSet("value1", "value4")); + Predicate expected = feature("key1").inSet("value1", "value2", "value3", "value4"); + assertConvertedPredicateEquals(expected, p); + } + + @Test + public void require_that_or_with_feature_sets_of_different_keys_is_simplified() { + Predicate p = + or( + feature("key1").inSet("value1", "value3"), + feature("key1").inSet("value2"), + feature("key2").inSet("value1"), + feature("key2").inSet("value2", "value3")); + Predicate expected = + or( + feature("key1").inSet("value1", "value2", "value3"), + feature("key2").inSet("value1", "value2", "value3")); + assertConvertedPredicateEquals(expected, p); + } + + @Test + public void require_that_conversion_is_recursive_and_cascades() { + Predicate p = + or( + feature("key1").inSet("value1", "value4"), + feature("key1").inSet("value2", "value3"), + or( + feature("key1").inSet("value1"), + feature("key1").inSet("value4"))); + Predicate expected = feature("key1").inSet("value1", "value2", "value3", "value4"); + assertConvertedPredicateEquals(expected, p); + } + + @Test + public void require_that_or_below_and_is_converted() { + Predicate p = + and( + or( + feature("key1").inSet("value1"), + feature("key1").inSet("value2")), + feature("key2").inSet("value2")); + Predicate expected = + and( + feature("key1").inSet("value1", "value2"), + feature("key2").inSet("value2")); + assertConvertedPredicateEquals(expected, p); + } + + @Test + public void require_that_or_below_not_is_converted() { + Predicate p = + not( + or( + feature("key1").inSet("value1"), + feature("key1").inSet("value2"))); + Predicate expected = not(feature("key1").inSet("value1", "value2")); + assertConvertedPredicateEquals(expected, p); + } + + @Test + public void require_that_non_feature_set_nodes_are_left_untouched() { + Predicate p = + or( + feature("key1").inSet("value1"), + feature("key1").inSet("value2"), + not(feature("key1").inSet("value3"))); + Predicate expected = + or( + not(feature("key1").inSet("value3")), + feature("key1").inSet("value1", "value2")); + assertConvertedPredicateEquals(expected, p); + } + + private static void assertConvertedPredicateEquals(Predicate expected, Predicate predicate) { + OrSimplifier simplifier = new OrSimplifier(); + assertEquals(expected, simplifier.simplifyTree(predicate)); + } +} |