diff options
Diffstat (limited to 'document/src/tests/predicate')
-rw-r--r-- | document/src/tests/predicate/.gitignore | 7 | ||||
-rw-r--r-- | document/src/tests/predicate/CMakeLists.txt | 28 | ||||
-rw-r--r-- | document/src/tests/predicate/predicate_builder_test.cpp | 81 | ||||
-rw-r--r-- | document/src/tests/predicate/predicate_printer_test.cpp | 122 | ||||
-rw-r--r-- | document/src/tests/predicate/predicate_test.cpp | 248 |
5 files changed, 486 insertions, 0 deletions
diff --git a/document/src/tests/predicate/.gitignore b/document/src/tests/predicate/.gitignore new file mode 100644 index 00000000000..219b478bd6d --- /dev/null +++ b/document/src/tests/predicate/.gitignore @@ -0,0 +1,7 @@ +*.So +*_test +.depend +Makefile +document_predicate_builder_test_app +document_predicate_printer_test_app +document_predicate_test_app diff --git a/document/src/tests/predicate/CMakeLists.txt b/document/src/tests/predicate/CMakeLists.txt new file mode 100644 index 00000000000..445da128498 --- /dev/null +++ b/document/src/tests/predicate/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(document_predicate_test_app + SOURCES + predicate_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_predicate_test_app COMMAND document_predicate_test_app) +vespa_add_executable(document_predicate_builder_test_app + SOURCES + predicate_builder_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_predicate_builder_test_app COMMAND document_predicate_builder_test_app) +vespa_add_executable(document_predicate_printer_test_app + SOURCES + predicate_printer_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_predicate_printer_test_app COMMAND document_predicate_printer_test_app) diff --git a/document/src/tests/predicate/predicate_builder_test.cpp b/document/src/tests/predicate/predicate_builder_test.cpp new file mode 100644 index 00000000000..1e32aa2cc51 --- /dev/null +++ b/document/src/tests/predicate/predicate_builder_test.cpp @@ -0,0 +1,81 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for predicate_builder. + +#include <vespa/log/log.h> +LOG_SETUP("predicate_builder_test"); +#include <vespa/fastos/fastos.h> + +#include <vespa/document/predicate/predicate.h> +#include <vespa/document/predicate/predicate_builder.h> +#include <vespa/vespalib/data/slime/slime.h> +#include <vespa/vespalib/testkit/testapp.h> + +using std::string; +using vespalib::Slime; +using vespalib::slime::Cursor; +using namespace document; + +namespace { + +TEST("require that a predicate tree can be built from a slime object") { + const string feature_name = "feature name"; + Slime input; + Cursor &obj = input.setObject(); + obj.setLong(Predicate::NODE_TYPE, Predicate::TYPE_DISJUNCTION); + Cursor &children = obj.setArray(Predicate::CHILDREN); + { + Cursor &child = children.addObject(); + child.setLong(Predicate::NODE_TYPE, Predicate::TYPE_FEATURE_SET); + child.setString(Predicate::KEY, feature_name); + Cursor &arr = child.setArray(Predicate::SET); + arr.addString("foo"); + arr.addString("bar"); + } + { + Cursor &child = children.addObject(); + child.setLong(Predicate::NODE_TYPE, Predicate::TYPE_CONJUNCTION); + Cursor &and_children = child.setArray(Predicate::CHILDREN); + { + Cursor &and_child = and_children.addObject(); + and_child.setLong(Predicate::NODE_TYPE, + Predicate::TYPE_FEATURE_RANGE); + and_child.setString(Predicate::KEY, feature_name); + and_child.setLong(Predicate::RANGE_MIN, 42); + } + { + Cursor &and_child = and_children.addObject(); + and_child.setLong(Predicate::NODE_TYPE, Predicate::TYPE_NEGATION); + Cursor ¬_child = + and_child.setArray(Predicate::CHILDREN).addObject(); + { + not_child.setLong(Predicate::NODE_TYPE, + Predicate::TYPE_FEATURE_SET); + not_child.setString(Predicate::KEY, feature_name); + Cursor &arr = not_child.setArray(Predicate::SET); + arr.addString("baz"); + arr.addString("qux"); + } + } + } + + PredicateNode::UP node = PredicateBuilder().build(input.get()); + Disjunction *disjunction = dynamic_cast<Disjunction *>(node.get()); + ASSERT_TRUE(disjunction); + ASSERT_EQUAL(2u, disjunction->getSize()); + FeatureSet *feature_set = dynamic_cast<FeatureSet *>((*disjunction)[0]); + ASSERT_TRUE(feature_set); + Conjunction *conjunction = dynamic_cast<Conjunction *>((*disjunction)[1]); + ASSERT_TRUE(conjunction); + ASSERT_EQUAL(2u, conjunction->getSize()); + FeatureRange *feature_range = + dynamic_cast<FeatureRange *>((*conjunction)[0]); + ASSERT_TRUE(feature_range); + Negation *negation = dynamic_cast<Negation *>((*conjunction)[1]); + ASSERT_TRUE(negation); + feature_set = dynamic_cast<FeatureSet *>(&negation->getChild()); + ASSERT_TRUE(feature_set); +} + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/document/src/tests/predicate/predicate_printer_test.cpp b/document/src/tests/predicate/predicate_printer_test.cpp new file mode 100644 index 00000000000..8525fb8e2db --- /dev/null +++ b/document/src/tests/predicate/predicate_printer_test.cpp @@ -0,0 +1,122 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for predicate_printer. + +#include <vespa/log/log.h> +LOG_SETUP("predicate_printer_test"); +#include <vespa/fastos/fastos.h> + +#include <vespa/document/predicate/predicate.h> +#include <vespa/document/predicate/predicate_printer.h> +#include <vespa/document/predicate/predicate_slime_builder.h> +#include <vespa/vespalib/testkit/testapp.h> + +using vespalib::Slime; +using vespalib::slime::Cursor; +using namespace document; + +namespace { + +typedef std::unique_ptr<Slime> SlimeUP; +using namespace document::predicate_slime_builder; + +TEST("require that PredicatePrinter prints FeatureSets") { + PredicateSlimeBuilder builder; + builder.feature("foo").value("bar").value("baz"); + ASSERT_EQUAL("'foo' in ['bar','baz']", + PredicatePrinter::print(*builder.build())); + + builder.feature("foo").value("bar"); + ASSERT_EQUAL("'foo' in ['bar']", + PredicatePrinter::print(*builder.build())); +} + +TEST("require that PredicatePrinter escapes non-ascii characters") { + PredicateSlimeBuilder builder; + builder.feature("\n\t\001'").value("\xc3\xb8"); + ASSERT_EQUAL("'\\n\\t\\x01\\x27' in ['\\xc3\\xb8']", + PredicatePrinter::print(*builder.build())); +} + +TEST("require that PredicatePrinter prints FeatureRanges") { + PredicateSlimeBuilder builder; + builder.feature("foo").range(-10, 42); + ASSERT_EQUAL("'foo' in [-10..42]", + PredicatePrinter::print(*builder.build())); +} + +TEST("require that PredicatePrinter prints open ended FeatureRanges") { + PredicateSlimeBuilder builder; + builder.feature("foo").greaterEqual(-10); + ASSERT_EQUAL("'foo' in [-10..]", + PredicatePrinter::print(*builder.build())); + + builder.feature("foo").lessEqual(42); + ASSERT_EQUAL("'foo' in [..42]", PredicatePrinter::print(*builder.build())); +} + +TEST("require that PredicatePrinter prints NOT_IN FeatureSets") { + PredicateSlimeBuilder builder; + builder.neg().feature("foo").value("bar").value("baz"); + ASSERT_EQUAL("'foo' not in ['bar','baz']", + PredicatePrinter::print(*builder.build())); +} + +TEST("require that PredicatePrinter can negate FeatureRanges") { + auto slime = neg(featureRange("foo", -10, 42)); + ASSERT_EQUAL("'foo' not in [-10..42]", PredicatePrinter::print(*slime)); +} + +TEST("require that PredicatePrinter can negate open ended FeatureRanges") { + auto slime = neg(greaterEqual("foo", 42)); + ASSERT_EQUAL("'foo' not in [42..]", PredicatePrinter::print(*slime)); + + slime = neg(lessEqual("foo", 42)); + ASSERT_EQUAL("'foo' not in [..42]", PredicatePrinter::print(*slime)); +} + +TEST("require that PredicatePrinter can negate double open ended ranges") { + auto slime = neg(emptyRange("foo")); + ASSERT_EQUAL("'foo' not in [..]", PredicatePrinter::print(*slime)); +} + +TEST("require that PredicatePrinter prints AND expressions") { + auto slime = andNode({featureSet("foo", {"bar", "baz"}), + featureSet("foo", {"bar", "baz"})}); + ASSERT_EQUAL("('foo' in ['bar','baz'] and 'foo' in ['bar','baz'])", + PredicatePrinter::print(*slime)); +} + +TEST("require that PredicatePrinter prints OR expressions") { + auto slime = orNode({featureSet("foo", {"bar", "baz"}), + featureSet("foo", {"bar", "baz"})}); + ASSERT_EQUAL("('foo' in ['bar','baz'] or 'foo' in ['bar','baz'])", + PredicatePrinter::print(*slime)); +} + +TEST("require that PredicatePrinter can negate OR expressions") { + auto slime = neg(orNode({featureSet("foo", {"bar", "baz"}), + featureSet("foo", {"bar", "baz"})})); + ASSERT_EQUAL("not ('foo' in ['bar','baz'] or 'foo' in ['bar','baz'])", + PredicatePrinter::print(*slime)); +} + +TEST("require that PredicatePrinter can negate AND expressions") { + auto slime = neg(andNode({featureSet("foo", {"bar", "baz"}), + featureSet("foo", {"bar", "baz"})})); + ASSERT_EQUAL("not ('foo' in ['bar','baz'] and 'foo' in ['bar','baz'])", + PredicatePrinter::print(*slime)); +} + +TEST("require that PredicatePrinter prints True") { + auto slime = truePredicate(); + ASSERT_EQUAL("true", PredicatePrinter::print(*slime)); +} + +TEST("require that PredicatePrinter prints False") { + auto slime = falsePredicate(); + ASSERT_EQUAL("false", PredicatePrinter::print(*slime)); +} + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/document/src/tests/predicate/predicate_test.cpp b/document/src/tests/predicate/predicate_test.cpp new file mode 100644 index 00000000000..3223b628c6a --- /dev/null +++ b/document/src/tests/predicate/predicate_test.cpp @@ -0,0 +1,248 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for predicate. + +#include <vespa/log/log.h> +LOG_SETUP("predicate_test"); +#include <vespa/fastos/fastos.h> + +#include <vespa/document/predicate/predicate.h> +#include <vespa/vespalib/data/slime/slime.h> +#include <vespa/vespalib/testkit/testapp.h> + +#include <vespa/document/predicate/predicate_slime_builder.h> +#include <string> + +using std::string; +using std::vector; +using vespalib::Slime; +using vespalib::slime::Cursor; +using namespace document; + +namespace { + +typedef std::unique_ptr<Slime> SlimeUP; + +TEST("require that predicate feature set slimes can be compared") { + PredicateSlimeBuilder builder; + builder.feature("foo").value("bar").value("baz"); + SlimeUP s1 = builder.build(); + builder.feature("foo").value("baz").value("bar"); + ASSERT_EQUAL(0, Predicate::compare(*s1, *builder.build())); + + builder.feature("bar").value("baz").value("bar"); + ASSERT_EQUAL(1, Predicate::compare(*s1, *builder.build())); + builder.feature("qux").value("baz").value("bar"); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); + + builder.feature("foo").value("baz"); + ASSERT_EQUAL(1, Predicate::compare(*s1, *builder.build())); + builder.feature("foo").value("baz").value("qux").value("quux"); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); + + builder.feature("foo").value("baz").value("qux"); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); + builder.feature("foo").value("baz").value("aaa"); + ASSERT_EQUAL(1, Predicate::compare(*s1, *builder.build())); +} + +TEST("require that predicate feature range slimes can be compared") { + PredicateSlimeBuilder builder; + builder.feature("foo").range(0, 10); + SlimeUP s1 = builder.build(); + builder.feature("foo").range(0, 10); + ASSERT_EQUAL(0, Predicate::compare(*s1, *builder.build())); + + builder.feature("foo").range(-1, 10); + ASSERT_EQUAL(1, Predicate::compare(*s1, *builder.build())); + builder.feature("foo").range(1, 10); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); + + builder.feature("foo").range(0, 9); + ASSERT_EQUAL(1, Predicate::compare(*s1, *builder.build())); + builder.feature("foo").range(0, 11); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); + + builder.feature("foo").greaterEqual(0); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); + builder.feature("foo").lessEqual(10); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); +} + +TEST("require that predicate open feature range slimes can be compared") { + PredicateSlimeBuilder builder; + builder.feature("foo").greaterEqual(10); + SlimeUP s1 = builder.build(); + builder.feature("foo").greaterEqual(10); + ASSERT_EQUAL(0, Predicate::compare(*s1, *builder.build())); + + builder.feature("foo").greaterEqual(9); + ASSERT_EQUAL(1, Predicate::compare(*s1, *builder.build())); + builder.feature("foo").greaterEqual(11); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); + + builder.feature("foo").lessEqual(10); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); +} + +TEST("require that predicate 'not' slimes can be compared") { + PredicateSlimeBuilder builder; + builder.neg().feature("foo").range(0, 10); + SlimeUP s1 = builder.build(); + builder.neg().feature("foo").range(0, 10); + ASSERT_EQUAL(0, Predicate::compare(*s1, *builder.build())); + + builder.neg().feature("foo").range(0, 11); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); + + builder.feature("foo").range(0, 10); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); +} + +TEST("require that predicate 'and' slimes can be compared") { + PredicateSlimeBuilder builder; + SlimeUP s1 = builder.feature("foo").value("bar").value("baz").build(); + SlimeUP s2 = builder.feature("foo").value("bar").value("qux").build(); + SlimeUP and_node = builder.and_node(std::move(s1), std::move(s2)).build(); + + s1 = builder.feature("foo").value("bar").value("baz").build(); + s2 = builder.feature("foo").value("bar").value("qux").build(); + builder.and_node(std::move(s1), std::move(s2)); + ASSERT_EQUAL(0, Predicate::compare(*and_node, *builder.build())); + + s1 = builder.feature("foo").value("bar").value("baz").build(); + s2 = builder.feature("foo").value("bar").value("qux").build(); + builder.and_node(std::move(s2), std::move(s1)); + ASSERT_EQUAL(-1, Predicate::compare(*and_node, *builder.build())); +} + +TEST("require that predicate 'or' slimes can be compared") { + PredicateSlimeBuilder builder; + SlimeUP s1 = builder.feature("foo").value("bar").value("baz").build(); + SlimeUP s2 = builder.feature("foo").value("bar").value("qux").build(); + SlimeUP or_node = builder.or_node(std::move(s1), std::move(s2)).build(); + + s1 = builder.feature("foo").value("bar").value("baz").build(); + s2 = builder.feature("foo").value("bar").value("qux").build(); + builder.or_node(std::move(s1), std::move(s2)); + ASSERT_EQUAL(0, Predicate::compare(*or_node, *builder.build())); + + s1 = builder.feature("foo").value("bar").value("baz").build(); + s2 = builder.feature("foo").value("bar").value("qux").build(); + builder.or_node(std::move(s2), std::move(s1)); + ASSERT_EQUAL(-1, Predicate::compare(*or_node, *builder.build())); +} + +TEST("require that predicate 'true' slimes can be compared") { + PredicateSlimeBuilder builder; + builder.true_predicate(); + SlimeUP s1 = builder.build(); + builder.true_predicate(); + ASSERT_EQUAL(0, Predicate::compare(*s1, *builder.build())); + + builder.false_predicate(); + ASSERT_EQUAL(-1, Predicate::compare(*s1, *builder.build())); +} + +TEST("require that predicate 'false' slimes can be compared") { + PredicateSlimeBuilder builder; + builder.false_predicate(); + SlimeUP s1 = builder.build(); + builder.false_predicate(); + ASSERT_EQUAL(0, Predicate::compare(*s1, *builder.build())); + + builder.true_predicate(); + ASSERT_EQUAL(1, Predicate::compare(*s1, *builder.build())); +} + +TEST("require that feature set can be created") { + const string feature_name = "feature name"; + Slime input; + Cursor &obj = input.setObject(); + obj.setString(Predicate::KEY, feature_name); + Cursor &arr = obj.setArray(Predicate::SET); + arr.addString("foo"); + arr.addString("bar"); + FeatureSet set(input.get()); + EXPECT_EQUAL(feature_name, set.getKey()); + ASSERT_EQUAL(2u, set.getSize()); + EXPECT_EQUAL("foo", set[0]); + EXPECT_EQUAL("bar", set[1]); +} + +TEST("require that feature range can be created") { + const string feature_name = "feature name"; + const long min = 0; + const long max = 42; + Slime input; + Cursor &obj = input.setObject(); + obj.setString(Predicate::KEY, feature_name); + obj.setLong(Predicate::RANGE_MIN, min); + obj.setLong(Predicate::RANGE_MAX, max); + FeatureRange set(input.get()); + EXPECT_EQUAL(feature_name, set.getKey()); + EXPECT_TRUE(set.hasMin()); + EXPECT_TRUE(set.hasMax()); + EXPECT_EQUAL(min, set.getMin()); + EXPECT_EQUAL(max, set.getMax()); +} + +TEST("require that feature range can be open") { + const string feature_name = "feature name"; + Slime input; + Cursor &obj = input.setObject(); + obj.setString(Predicate::KEY, feature_name); + FeatureRange set(input.get()); + EXPECT_EQUAL(feature_name, set.getKey()); + EXPECT_FALSE(set.hasMin()); + EXPECT_FALSE(set.hasMax()); + EXPECT_EQUAL(LLONG_MIN, set.getMin()); + EXPECT_EQUAL(LLONG_MAX, set.getMax()); +} + +PredicateNode::UP getPredicateNode() { + const string feature_name = "feature name"; + Slime input; + Cursor &obj = input.setObject(); + obj.setString(Predicate::KEY, feature_name); + Cursor &arr = obj.setArray(Predicate::SET); + arr.addString("foo"); + arr.addString("bar"); + + PredicateNode::UP node(new FeatureSet(input.get())); + return node; +} + +TEST("require that negation nodes holds a child") { + PredicateNode::UP node(getPredicateNode()); + PredicateNode *expected = node.get(); + Negation neg(std::move(node)); + + EXPECT_EQUAL(expected, &neg.getChild()); +} + +TEST("require that conjunction nodes holds several children") { + vector<PredicateNode *> nodes; + nodes.push_back(getPredicateNode().release()); + nodes.push_back(getPredicateNode().release()); + Conjunction and_node(nodes); + + ASSERT_EQUAL(2u, and_node.getSize()); + EXPECT_EQUAL(nodes[0], and_node[0]); + EXPECT_EQUAL(nodes[1], and_node[1]); +} + +TEST("require that disjunction nodes holds several children") { + vector<PredicateNode *> nodes; + nodes.push_back(getPredicateNode().release()); + nodes.push_back(getPredicateNode().release()); + Disjunction or_node(nodes); + + ASSERT_EQUAL(2u, or_node.getSize()); + EXPECT_EQUAL(nodes[0], or_node[0]); + EXPECT_EQUAL(nodes[1], or_node[1]); +} + + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } |