summaryrefslogtreecommitdiffstats
path: root/document/src/tests/predicate
diff options
context:
space:
mode:
Diffstat (limited to 'document/src/tests/predicate')
-rw-r--r--document/src/tests/predicate/.gitignore7
-rw-r--r--document/src/tests/predicate/CMakeLists.txt28
-rw-r--r--document/src/tests/predicate/predicate_builder_test.cpp81
-rw-r--r--document/src/tests/predicate/predicate_printer_test.cpp122
-rw-r--r--document/src/tests/predicate/predicate_test.cpp248
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 &not_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(); }