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 /document/src/tests |
Publish
Diffstat (limited to 'document/src/tests')
177 files changed, 17410 insertions, 0 deletions
diff --git a/document/src/tests/.gitignore b/document/src/tests/.gitignore new file mode 100644 index 00000000000..3e5d3aea0af --- /dev/null +++ b/document/src/tests/.gitignore @@ -0,0 +1,16 @@ +*.So +*.core +.*.swp +.config.log +.depend +Makefile +cpp-globalids.txt +indexdir +mmapbuffertestfile.txt +mmapq_test +mytestfile +mytestfile_clear +test.vlog +testrunner +*_test +document_testrunner_app diff --git a/document/src/tests/CMakeLists.txt b/document/src/tests/CMakeLists.txt new file mode 100644 index 00000000000..95e6b4b9a18 --- /dev/null +++ b/document/src/tests/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(document_testrunner_app + SOURCES + teststringutil.cpp + testbytebuffer.cpp + stringtokenizertest.cpp + documentcalculatortestcase.cpp + buckettest.cpp + globalidtest.cpp + documentidtest.cpp + documenttypetestcase.cpp + primitivefieldvaluetest.cpp + arrayfieldvaluetest.cpp + weightedsetfieldvaluetest.cpp + structfieldvaluetest.cpp + documenttestcase.cpp + testdocmantest.cpp + documentupdatetestcase.cpp + fieldpathupdatetestcase.cpp + documentselectparsertest.cpp + bucketselectortest.cpp + testxml.cpp + forcelinktest.cpp + orderingselectortest.cpp + testrunner.cpp + heapdebuggerother.cpp + positiontypetest.cpp + urltypetest.cpp + fieldsettest.cpp + gid_filter_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_testrunner_app COMMAND document_testrunner_app) diff --git a/document/src/tests/annotation/.gitignore b/document/src/tests/annotation/.gitignore new file mode 100644 index 00000000000..861906f0e95 --- /dev/null +++ b/document/src/tests/annotation/.gitignore @@ -0,0 +1,6 @@ +*.So +*_test +.depend +Makefile +annotation_test +document_annotation_test_app diff --git a/document/src/tests/annotation/CMakeLists.txt b/document/src/tests/annotation/CMakeLists.txt new file mode 100644 index 00000000000..1ec6a3e3dde --- /dev/null +++ b/document/src/tests/annotation/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(document_annotation_test_app + SOURCES + annotation_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_annotation_test_app COMMAND document_annotation_test_app) diff --git a/document/src/tests/annotation/annotation_test.cpp b/document/src/tests/annotation/annotation_test.cpp new file mode 100644 index 00000000000..c4976485b8b --- /dev/null +++ b/document/src/tests/annotation/annotation_test.cpp @@ -0,0 +1,295 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for annotation. + +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("annotation_test"); + +#include <stdlib.h> +#include <vespa/document/annotation/alternatespanlist.h> +#include <vespa/document/annotation/annotation.h> +#include <vespa/document/annotation/span.h> +#include <vespa/document/annotation/spanlist.h> +#include <vespa/document/annotation/spantree.h> +#include <vespa/document/datatype/annotationreferencedatatype.h> +#include <vespa/document/datatype/annotationtype.h> +#include <vespa/document/datatype/arraydatatype.h> +#include <vespa/document/datatype/primitivedatatype.h> +#include <vespa/document/datatype/structdatatype.h> +#include <vespa/document/fieldvalue/annotationreferencefieldvalue.h> +#include <vespa/document/fieldvalue/arrayfieldvalue.h> +#include <vespa/document/fieldvalue/doublefieldvalue.h> +#include <vespa/document/fieldvalue/structfieldvalue.h> +#include <vespa/vespalib/testkit/testapp.h> +#include <memory> + +using std::unique_ptr; +using namespace document; + +namespace { + +AnnotationType text_type(1, "text"); +AnnotationType begin_tag(2, "begintag"); +AnnotationType end_tag(3, "endtag"); +AnnotationType body_type(4, "body"); +AnnotationType header_type(5, "header"); +AnnotationType city_type(6, "city"); +AnnotationType markup_type(7, "markup"); + +template <typename T> +unique_ptr<T> makeUP(T *p) { return unique_ptr<T>(p); } + +TEST("requireThatSpansHaveOrder") { + Span span(10, 10); + Span before(5, 3); + Span overlap_start(8, 10); + Span contained(12, 3); + Span overlap_end(15, 10); + Span after(21, 10); + Span overlap_complete(5, 20); + Span shorter(10, 5); + Span longer(10, 15); + EXPECT_TRUE(span > before); + EXPECT_TRUE(span > overlap_start); + EXPECT_TRUE(span < contained); + EXPECT_TRUE(span < overlap_end); + EXPECT_TRUE(span < after); + EXPECT_TRUE(span > overlap_complete); + EXPECT_TRUE(span > shorter); + EXPECT_TRUE(span < longer); + EXPECT_TRUE(!(span < span)); +} + +TEST("requireThatSimpleSpanTreeCanBeBuilt") { + SpanList::UP root(new SpanList); + root->add(makeUP(new Span(0, 19))); + root->add(makeUP(new Span(19, 5))); + root->add(makeUP(new Span(24, 21))); + root->add(makeUP(new Span(45, 23))); + root->add(makeUP(new Span(68, 14))); + + EXPECT_EQUAL(5u, root->size()); + SpanList::const_iterator it = root->begin(); + EXPECT_EQUAL(Span(0, 19), *(static_cast<Span *>(*it++))); + EXPECT_EQUAL(Span(19, 5), *(static_cast<Span *>(*it++))); + EXPECT_EQUAL(Span(24, 21), *(static_cast<Span *>(*it++))); + EXPECT_EQUAL(Span(45, 23), *(static_cast<Span *>(*it++))); + EXPECT_EQUAL(Span(68, 14), *(static_cast<Span *>(*it++))); + EXPECT_TRUE(it == root->end()); + + SpanTree tree("html", std::move(root)); +} + +TEST("requireThatSpanTreeCanHaveAnnotations") { + SpanList::UP root_owner(new SpanList); + SpanList *root = root_owner.get(); + SpanTree tree("html", std::move(root_owner)); + + Span &span1 = root->add(makeUP(new Span(0, 19))); + tree.annotate(span1, markup_type); + + Span &span2 = root->add(makeUP(new Span(19, 5))); + tree.annotate(span2, text_type); + + EXPECT_EQUAL(2u, tree.numAnnotations()); + SpanTree::const_iterator it = tree.begin(); + + EXPECT_EQUAL(Annotation(markup_type), *it++); + EXPECT_EQUAL(Annotation(text_type), *it++); + EXPECT_TRUE(it == tree.end()); +} + +TEST("requireThatSpanTreeCanHaveMultipleLevels") { + SpanList::UP root_owner(new SpanList); + SpanList *root = root_owner.get(); + SpanTree tree("html", std::move(root_owner)); + + SpanList::UP header(new SpanList); + tree.annotate(header->add(makeUP(new Span(6, 6))), begin_tag); + tree.annotate(header->add(makeUP(new Span(12, 7))), begin_tag); + tree.annotate(header->add(makeUP(new Span(19, 5))), text_type); + tree.annotate(header->add(makeUP(new Span(24, 8))), end_tag); + tree.annotate(header->add(makeUP(new Span(32, 7))), end_tag); + tree.annotate(*header, header_type); + + SpanList::UP body(new SpanList); + tree.annotate(body->add(makeUP(new Span(39, 6))), begin_tag); + tree.annotate(body->add(makeUP(new Span(45, 23))), text_type); + tree.annotate(body->add(makeUP(new Span(68, 7))), end_tag); + tree.annotate(*body, body_type); + + tree.annotate(root->add(makeUP(new Span(0, 6))), begin_tag); + root->add(std::move(std::move(header))); + root->add(std::move(std::move(body))); + tree.annotate(root->add(makeUP(new Span(75, 7))), end_tag); + + EXPECT_EQUAL(12u, tree.numAnnotations()); + SpanTree::const_iterator it = tree.begin(); + EXPECT_EQUAL(Annotation(begin_tag), *it++); + EXPECT_EQUAL(Annotation(begin_tag), *it++); + EXPECT_EQUAL(Annotation(text_type), *it++); + EXPECT_EQUAL(Annotation(end_tag), *it++); + EXPECT_EQUAL(Annotation(end_tag), *it++); + EXPECT_EQUAL(Annotation(header_type), *it++); + EXPECT_EQUAL(Annotation(begin_tag), *it++); + EXPECT_EQUAL(Annotation(text_type), *it++); + EXPECT_EQUAL(Annotation(end_tag), *it++); + EXPECT_EQUAL(Annotation(body_type), *it++); + EXPECT_EQUAL(Annotation(begin_tag), *it++); + EXPECT_EQUAL(Annotation(end_tag), *it++); + EXPECT_TRUE(it == tree.end()); +} + +TEST("requireThatAnnotationsCanHaveValues") { + PrimitiveDataType double_type(DataType::T_DOUBLE); + StructDataType city_data_type; + city_data_type.addField(Field("latitude", 0, double_type, false)); + city_data_type.addField(Field("longitude", 1, double_type, false)); + + StructFieldValue::UP position(new StructFieldValue(city_data_type)); + position->setValue("latitude", DoubleFieldValue(37.774929)); + position->setValue("longitude", DoubleFieldValue(-122.419415)); + StructFieldValue original(*position); + + Annotation city(city_type, std::move(position)); + + EXPECT_TRUE(*city.getFieldValue() == original); +} + +TEST("requireThatAnnotationsCanReferenceAnnotations") { + SpanList::UP root(new SpanList); + SpanTree tree("html", std::move(root)); + size_t san_index = tree.annotate(makeUP(new Annotation(text_type))); + size_t fran_index = tree.annotate(makeUP(new Annotation(text_type))); + + AnnotationReferenceDataType annotation_ref_type(text_type, 101); + ArrayDataType array_type(annotation_ref_type); + StructDataType city_data_type("name", 42); + city_data_type.addField(Field("references", 0, array_type, false)); + + StructFieldValue::UP city_data(new StructFieldValue(city_data_type)); + ArrayFieldValue ref_list(array_type); + ref_list.add(AnnotationReferenceFieldValue(annotation_ref_type, + san_index)); + ref_list.add(AnnotationReferenceFieldValue(annotation_ref_type, + fran_index)); + city_data->setValue("references", ref_list); + StructFieldValue original(*city_data); + + Annotation city(city_type, std::move(city_data)); + + ASSERT_TRUE(city.getFieldValue()); + EXPECT_TRUE(city.getFieldValue()->isA(original)); + EXPECT_EQUAL(original, *city.getFieldValue()); +} + +const double prob0 = 0.6; +const double prob1 = 0.4; + +TEST("requireThatAlternateSpanListHoldsMultipleLists") { + AlternateSpanList span_list; + span_list.add(0, makeUP(new Span(0, 19))); + span_list.add(0, makeUP(new Span(19, 5))); + span_list.add(1, makeUP(new Span(0, 5))); + span_list.add(1, makeUP(new Span(5, 19))); + span_list.setProbability(0, prob0); + span_list.setProbability(1, prob1); + + EXPECT_EQUAL(2u, span_list.getNumSubtrees()); + EXPECT_EQUAL(2u, span_list.getSubtree(0).size()); + EXPECT_EQUAL(2u, span_list.getSubtree(1).size()); + EXPECT_EQUAL(prob0, span_list.getProbability(0)); + EXPECT_EQUAL(prob1, span_list.getProbability(1)); + + SpanList::const_iterator it = span_list.getSubtree(0).begin(); + EXPECT_EQUAL(Span(0, 19), *(static_cast<Span *>(*it++))); + EXPECT_EQUAL(Span(19, 5), *(static_cast<Span *>(*it++))); + EXPECT_TRUE(it == span_list.getSubtree(0).end()); + + it = span_list.getSubtree(1).begin(); + EXPECT_EQUAL(Span(0, 5), *(static_cast<Span *>(*it++))); + EXPECT_EQUAL(Span(5, 19), *(static_cast<Span *>(*it++))); + EXPECT_TRUE(it == span_list.getSubtree(1).end()); +} + +struct MySpanTreeVisitor : SpanTreeVisitor { + int span_count; + int span_list_count; + int alt_span_list_count; + + MySpanTreeVisitor() + : span_count(0), span_list_count(0), alt_span_list_count(0) {} + + void visitChildren(const SpanList &node) { + for (SpanList::const_iterator + it = node.begin(); it != node.end(); ++it) { + (*it)->accept(*this); + } + } + + void visitChildren(const SimpleSpanList &node) { + for (SimpleSpanList::const_iterator + it = node.begin(); it != node.end(); ++it) { + (*it).accept(*this); + } + } + + void visit(const Span &) override { ++span_count; } + void visit(const SpanList &node) override { + ++span_list_count; + visitChildren(node); + } + void visit(const SimpleSpanList &node) override { + ++span_list_count; + visitChildren(node); + } + void visit(const AlternateSpanList &node) override { + ++alt_span_list_count; + for (size_t i = 0; i < node.getNumSubtrees(); ++i) { + visitChildren(node.getSubtree(i)); + } + } +}; + +TEST("requireThatSpanTreeCanBeVisited") { + SpanList::UP root(new SpanList); + root->add(makeUP(new Span(0, 19))); + AlternateSpanList::UP alt_list(new AlternateSpanList); + alt_list->add(0, makeUP(new Span(19, 5))); + alt_list->add(1, makeUP(new Span(24, 21))); + root->add(std::move(alt_list)); + + SpanTree tree("html", std::move(root)); + + MySpanTreeVisitor visitor; + tree.accept(visitor); + + EXPECT_EQUAL(3, visitor.span_count); + EXPECT_EQUAL(1, visitor.span_list_count); + EXPECT_EQUAL(1, visitor.alt_span_list_count); +} + +TEST("requireThatDefaultAnnotationTypesHaveDefaultDataTypes") { + ASSERT_TRUE(AnnotationType::TERM->getDataType()); + EXPECT_EQUAL(*DataType::STRING, *AnnotationType::TERM->getDataType()); + ASSERT_TRUE(AnnotationType::TOKEN_TYPE->getDataType()); + EXPECT_EQUAL(*DataType::INT, *AnnotationType::TOKEN_TYPE->getDataType()); +} + +TEST("require that SpanTrees can be compared") { + SpanList::UP root(new SpanList); + root->add(makeUP(new Span(0, 19))); + SpanTree tree1("html", std::move(root)); + + root.reset(new SpanList); + root->add(makeUP(new Span(0, 18))); + SpanTree tree2("html", std::move(root)); + + EXPECT_EQUAL(0, tree1.compare(tree1)); + EXPECT_LESS(0, tree1.compare(tree2)); + EXPECT_GREATER(0, tree2.compare(tree1)); +} + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/document/src/tests/arrayfieldvaluetest.cpp b/document/src/tests/arrayfieldvaluetest.cpp new file mode 100644 index 00000000000..136129274b5 --- /dev/null +++ b/document/src/tests/arrayfieldvaluetest.cpp @@ -0,0 +1,247 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <vespa/document/fieldvalue/fieldvalues.h> +#include <vespa/document/serialization/vespadocumentdeserializer.h> +#include <vespa/vdstestlib/cppunit/macros.h> +#include <vespa/vespalib/objects/nbostream.h> + +using vespalib::nbostream; + +namespace document { + +struct ArrayFieldValueTest : public CppUnit::TestFixture { + void setUp() {} + void tearDown() {} + + void testArray(); + + CPPUNIT_TEST_SUITE(ArrayFieldValueTest); + CPPUNIT_TEST(testArray); + CPPUNIT_TEST_SUITE_END(); + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ArrayFieldValueTest); + +namespace { +template <typename T> +void deserialize(const ByteBuffer &buffer, T &value) { + uint16_t version = Document::getNewestSerializationVersion(); + nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining()); + DocumentTypeRepo repo; + VespaDocumentDeserializer deserializer(repo, stream, version); + deserializer.read(value); +} +} // namespace + +void ArrayFieldValueTest::testArray() +{ + ArrayDataType type(*DataType::INT); + ArrayFieldValue value(type); + + // Initially empty + CPPUNIT_ASSERT_EQUAL(size_t(0), value.size()); + CPPUNIT_ASSERT(value.isEmpty()); + CPPUNIT_ASSERT(!value.contains(IntFieldValue(1))); + + CPPUNIT_ASSERT(value.add(IntFieldValue(1))); + + // Not empty + CPPUNIT_ASSERT_EQUAL(size_t(1), value.size()); + CPPUNIT_ASSERT(!value.isEmpty()); + CPPUNIT_ASSERT(value.contains(IntFieldValue(1))); + + // Adding some more + CPPUNIT_ASSERT(value.add(IntFieldValue(2))); + CPPUNIT_ASSERT(value.add(IntFieldValue(3))); + + // Not empty + CPPUNIT_ASSERT_EQUAL(size_t(3), value.size()); + CPPUNIT_ASSERT(!value.isEmpty()); + CPPUNIT_ASSERT_EQUAL(IntFieldValue(1), (IntFieldValue&) value[0]); + CPPUNIT_ASSERT_EQUAL(IntFieldValue(2), (IntFieldValue&) value[1]); + CPPUNIT_ASSERT_EQUAL(IntFieldValue(3), (IntFieldValue&) value[2]); + + // Serialize & equality + std::unique_ptr<ByteBuffer> buffer(value.serialize()); + buffer->flip(); + ArrayFieldValue value2(type); + CPPUNIT_ASSERT(value != value2); + deserialize(*buffer, value2); + CPPUNIT_ASSERT_EQUAL(value, value2); + + // Various ways of removing + { + // By index + buffer->setPos(0); + deserialize(*buffer, value2); + value2.remove(1); + CPPUNIT_ASSERT(!value2.contains(IntFieldValue(2))); + CPPUNIT_ASSERT_EQUAL(size_t(2), value2.size()); + + // By value + buffer->setPos(0); + deserialize(*buffer, value2); + CPPUNIT_ASSERT(value2.remove(IntFieldValue(1))); + CPPUNIT_ASSERT(!value2.contains(IntFieldValue(1))); + CPPUNIT_ASSERT_EQUAL(size_t(2), value2.size()); + + // By value with multiple present + buffer->setPos(0); + deserialize(*buffer, value2); + value2.add(IntFieldValue(1)); + CPPUNIT_ASSERT(value2.remove(IntFieldValue(1))); + CPPUNIT_ASSERT(!value2.contains(IntFieldValue(1))); + CPPUNIT_ASSERT_EQUAL(size_t(2), value2.size()); + + // Clearing all + buffer->setPos(0); + deserialize(*buffer, value2); + value2.clear(); + CPPUNIT_ASSERT(!value2.contains(IntFieldValue(1))); + CPPUNIT_ASSERT_EQUAL(size_t(0), value2.size()); + CPPUNIT_ASSERT(value2.isEmpty()); + } + + // Updating + value2 = value; + CPPUNIT_ASSERT_EQUAL(value, value2); + value2[1].assign(IntFieldValue(5)); + CPPUNIT_ASSERT(!value2.contains(IntFieldValue(2))); + CPPUNIT_ASSERT_EQUAL(IntFieldValue(5), (IntFieldValue&) value2[1]); + CPPUNIT_ASSERT(value != value2); + value2.assign(value); + CPPUNIT_ASSERT_EQUAL(value, value2); + ArrayFieldValue::UP valuePtr(value2.clone()); + CPPUNIT_ASSERT_EQUAL(value, *valuePtr); + + // Iterating + const ArrayFieldValue& constVal(value); + for(ArrayFieldValue::const_iterator it = constVal.begin(); + it != constVal.end(); ++it) + { + const FieldValue& fval1(*it); + (void) fval1; + CPPUNIT_ASSERT_EQUAL((uint32_t) IntFieldValue::classId, + it->getClass().id()); + } + value2 = value; + for(ArrayFieldValue::iterator it = value2.begin(); it != value2.end(); ++it) + { + (*it).assign(IntFieldValue(7)); + it->assign(IntFieldValue(7)); + } + CPPUNIT_ASSERT(value != value2); + CPPUNIT_ASSERT(value2.contains(IntFieldValue(7))); + CPPUNIT_ASSERT(value2.remove(IntFieldValue(7))); + CPPUNIT_ASSERT(value2.isEmpty()); + + // Comparison + value2 = value; + CPPUNIT_ASSERT_EQUAL(0, value.compare(value2)); + value2.remove(1); + CPPUNIT_ASSERT(value.compare(value2) > 0); + CPPUNIT_ASSERT(value2.compare(value) < 0); + value2 = value; + value2[1].assign(IntFieldValue(5)); + CPPUNIT_ASSERT(value.compare(value2) < 0); + CPPUNIT_ASSERT(value2.compare(value) > 0); + + // Output + CPPUNIT_ASSERT_EQUAL(std::string("Array(size: 3,\n 1,\n 2,\n 3\n)"), + value.toString(false)); + CPPUNIT_ASSERT_EQUAL(std::string("Array(size: 3,\n. 1,\n. 2,\n. 3\n.)"), + value.toString(true, ".")); + CPPUNIT_ASSERT_EQUAL(std::string( + "<value>\n" + " <item>1</item>\n" + " <item>2</item>\n" + " <item>3</item>\n" + "</value>" + ), + value.toXml(" ")); + + // Failure situations. + + // Refuse to accept non-array types + try{ + ArrayFieldValue value6(*DataType::TAG); + CPPUNIT_FAIL("Didn't complain about non-array type"); + } catch (std::exception& e) { + fprintf(stderr, "Exception = %s\n", e.what()); + CPPUNIT_ASSERT_CONTAIN( + "Cannot generate an array value with non-array type", + e.what()); + } + + // Verify that datatypes are verified + // Created almost equal types to try to get it to fail + ArrayDataType type1(*DataType::INT); + ArrayDataType type2(*DataType::LONG); + ArrayDataType type3(static_cast<DataType &>(type1)); + ArrayDataType type4(static_cast<DataType &>(type2)); + ArrayFieldValue value3(type3); + ArrayFieldValue value4(type4); + try{ + value3 = value4; + CPPUNIT_FAIL("Failed to check type equality in operator="); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot assign value of type", e.what()); + } + try{ + value3.assign(value4); + CPPUNIT_FAIL("Failed to check type equality in assign()"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot assign value of type", e.what()); + } + try{ + ArrayFieldValue subValue(type2); + subValue.add(LongFieldValue(4)); + value3.add(subValue); + CPPUNIT_FAIL("Failed to check type equality in add()"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot add value of type",e.what()); + } + try{ + ArrayFieldValue subValue(type2); + subValue.add(LongFieldValue(4)); + value3.contains(subValue); + CPPUNIT_FAIL("Failed to check type equality in contains()"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("can't possibly be in array of type", e.what()); + } + try{ + ArrayFieldValue subValue(type2); + subValue.add(LongFieldValue(4)); + value3.remove(subValue); + CPPUNIT_FAIL("Failed to check type equality in remove()"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("can't possibly be in array of type", e.what()); + } + // Verify that compare sees difference + { + ArrayFieldValue subValue1(type1); + ArrayFieldValue subValue2(type2); + subValue1.add(IntFieldValue(3)); + subValue2.add(LongFieldValue(3)); + value3.clear(); + value4.clear(); + value3.add(subValue1); + value4.add(subValue2); + CPPUNIT_ASSERT(value3.compare(value4) != 0); + } + + // Removing non-existing element + value2 = value; + try{ + value2.remove(5); + CPPUNIT_FAIL("Failed to throw out of bounds exception on remove(int)"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot remove index 5 from an array of size 3", + e.what()); + } + CPPUNIT_ASSERT(!value2.remove(IntFieldValue(15))); +} + +} // document diff --git a/document/src/tests/arrayfieldvaluetest.h b/document/src/tests/arrayfieldvaluetest.h new file mode 100644 index 00000000000..8118d57021a --- /dev/null +++ b/document/src/tests/arrayfieldvaluetest.h @@ -0,0 +1,29 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/* $Id$*/ + +#pragma once + +#include <cppunit/extensions/HelperMacros.h> + +class ArrayFieldValue_Test : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE( ArrayFieldValue_Test); + CPPUNIT_TEST(testArray); + CPPUNIT_TEST(testArray2); + CPPUNIT_TEST(testArrayRaw); + CPPUNIT_TEST(testTextSerialize); + CPPUNIT_TEST(testArrayRemove); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + + void tearDown(); +protected: + void testArray(); + void testArray2(); + void testArrayRaw(); + void testTextSerialize(); + void testArrayRemove(); +}; + + diff --git a/document/src/tests/base/.gitignore b/document/src/tests/base/.gitignore new file mode 100644 index 00000000000..bdb07eae1cb --- /dev/null +++ b/document/src/tests/base/.gitignore @@ -0,0 +1,7 @@ +*.So +*_test +*_benchmark +.depend +Makefile +document_documentid_test_app +document_documentid_benchmark_app diff --git a/document/src/tests/base/CMakeLists.txt b/document/src/tests/base/CMakeLists.txt new file mode 100644 index 00000000000..550d77d1340 --- /dev/null +++ b/document/src/tests/base/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(document_documentid_test_app + SOURCES + documentid_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_documentid_test_app COMMAND document_documentid_test_app) +vespa_add_executable(document_documentid_benchmark_app + SOURCES + documentid_benchmark.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_documentid_benchmark_app COMMAND document_documentid_benchmark_app BENCHMARK) diff --git a/document/src/tests/base/documentid_benchmark.cpp b/document/src/tests/base/documentid_benchmark.cpp new file mode 100644 index 00000000000..16f9c9016d7 --- /dev/null +++ b/document/src/tests/base/documentid_benchmark.cpp @@ -0,0 +1,21 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/document/base/documentid.h> + +using namespace document; + +int main(int argc, char *argv[]) +{ + if (argc < 3) { + fprintf(stderr, "Usage %s <docid> <count>\n", argv[0]); + } + vespalib::string s(argv[1]); + uint64_t n = strtoul(argv[2], NULL, 0); + printf("Creating documentid '%s' %ld times\n", s.c_str(), n); + printf("sizeof(IdString)=%ld, sizeof(IdIdString)=%ld\n", sizeof(IdString), sizeof(IdIdString)); + for (uint64_t i=0; i < n; i++) { + IdString::UP id = IdString::createIdString(s); + id.reset(); + } + return 0; +} + diff --git a/document/src/tests/base/documentid_test.cpp b/document/src/tests/base/documentid_test.cpp new file mode 100644 index 00000000000..62ce42c0fb6 --- /dev/null +++ b/document/src/tests/base/documentid_test.cpp @@ -0,0 +1,228 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for documentid. + +#include <vespa/log/log.h> +LOG_SETUP("documentid_test"); +#include <vespa/fastos/fastos.h> + +#include <vespa/document/base/documentid.h> +#include <vespa/vespalib/testkit/testapp.h> + +using namespace document; +using vespalib::string; + +namespace { + +const string ns = "namespace"; +const string ns_id = "namespaceid"; +const string type = "my_type"; + +void checkId(const string &id, IdString::Type t, + const string &ns_str, const string local_id) { + DocumentId doc_id(id); + EXPECT_EQUAL(t, doc_id.getScheme().getType()); + EXPECT_EQUAL(ns_str, doc_id.getScheme().getNamespace()); + EXPECT_EQUAL(local_id, doc_id.getScheme().getNamespaceSpecific()); + EXPECT_EQUAL(id, doc_id.getScheme().toString()); +} + +template <typename IdType> +const IdType &getAs(const DocumentId &doc_id) { + const IdType *id_str = dynamic_cast<const IdType *>(&doc_id.getScheme()); + ASSERT_TRUE(id_str); + return *id_str; +} + +template <typename IdType> +void checkUser(const string &id, int64_t user_id) { + DocumentId doc_id(id); + EXPECT_EQUAL(user_id, getAs<IdType>(doc_id).getUserId()); +} + +void checkOrdering(const string &id, uint64_t ordering) { + DocumentId doc_id(id); + EXPECT_EQUAL(ordering, getAs<OrderDocIdString>(doc_id).getOrdering()); +} + +void checkGroup(const string &id, const string &group) { + DocumentId doc_id(id); + EXPECT_EQUAL(group, getAs<GroupDocIdString>(doc_id).getGroup()); +} + +void checkType(const string &id, const string &doc_type) { + DocumentId doc_id(id); + ASSERT_TRUE(doc_id.hasDocType()); + EXPECT_EQUAL(doc_type, doc_id.getDocType()); +} + +TEST("require that doc id can be parsed") { + const string id = "doc:" + ns + ":" + ns_id; + checkId(id, IdString::DOC, ns, ns_id); +} + +TEST("require that userdoc id can be parsed") { + const string id = "userdoc:" + ns + ":1234:" + ns_id; + checkId(id, IdString::USERDOC, ns, ns_id); + checkUser<UserDocIdString>(id, 1234); +} + +TEST("require that orderdoc id can be parsed") { + const string id = "orderdoc(31,19):" + ns + ":1234:1268182861:" + ns_id; + checkId(id, IdString::ORDERDOC, ns, ns_id); + checkUser<OrderDocIdString>(id, 1234); + checkOrdering(id, 1268182861); +} + +TEST("require that groupdoc id can be parsed") { + const string group = "mygroup"; + const string id = "groupdoc:" + ns + ":" + group + ":" + ns_id; + checkId(id, IdString::GROUPDOC, ns, ns_id); + checkGroup(id, group); +} + +TEST("require that id id can be parsed") { + const string id = "id:" + ns + ":" + type + "::" + ns_id; + checkId(id, IdString::ID, ns, ns_id); + checkType(id, type); +} + +TEST("require that we allow ':' in namespace specific part") { + const string nss=":a:b:c:"; + string id="id:" + ns + ":" + type + "::" + nss; + checkId(id, IdString::ID, ns, nss); + checkType(id, type); + + id="doc:" + ns + ":" + nss; + checkId(id, IdString::DOC, ns, nss); + + id="userdoc:" + ns + ":123:" + nss; + checkId(id, IdString::USERDOC, ns, nss); + checkUser<UserDocIdString>(id, 123); + + id="groupdoc:" + ns + ":123:" + nss; + checkId(id, IdString::GROUPDOC, ns, nss); + checkGroup(id, "123"); + + id="orderdoc(5,2):" + ns + ":42:007:" + nss; + checkId(id, IdString::ORDERDOC, ns, nss); +} + +TEST("require that id id can specify location") { + DocumentId id("id:ns:type:n=12345:foo"); + EXPECT_EQUAL(12345u, id.getScheme().getLocation()); + EXPECT_EQUAL(12345u, getAs<IdIdString>(id).getNumber()); +} + +TEST("require that id id's n key must be a 64-bit number") { + EXPECT_EXCEPTION(DocumentId("id:ns:type:n=abc:foo"), IdParseException, + "'n'-value must be a 64-bit number"); + DocumentId("id:ns:type:n=18446744073709551615:foo"); // ok + EXPECT_EXCEPTION(DocumentId("id:ns:type:n=18446744073709551616:foo"), + IdParseException, "'n'-value out of range"); +} + +TEST("require that id id can specify group") { + DocumentId id1("id:ns:type:g=mygroup:foo"); + DocumentId id2("id:ns:type:g=mygroup:bar"); + DocumentId id3("id:ns:type:g=other group:baz"); + EXPECT_EQUAL(id1.getScheme().getLocation(), id2.getScheme().getLocation()); + EXPECT_NOT_EQUAL(id1.getScheme().getLocation(), + id3.getScheme().getLocation()); + EXPECT_EQUAL("mygroup", getAs<IdIdString>(id1).getGroup()); +} + +TEST("require that id id location is specified by local id only by default") { + DocumentId id1("id:ns:type::locationspec"); + DocumentId id2("id:ns:type:g=locationspec:bar"); + EXPECT_EQUAL("locationspec", id1.getScheme().getNamespaceSpecific()); + EXPECT_EQUAL("bar", id2.getScheme().getNamespaceSpecific()); + EXPECT_EQUAL(id1.getScheme().getLocation(), id2.getScheme().getLocation()); +} + +TEST("require that local id can be empty") { + const string id = "userdoc:" + ns + ":1234:"; + checkId(id, IdString::USERDOC, ns, ""); + checkUser<UserDocIdString>(id, 1234); +} + +TEST("require that document ids can be assigned") { + DocumentId id1("userdoc:" + ns + ":1234:"); + DocumentId id2 = id1; + checkId(id2.toString(), IdString::USERDOC, ns, ""); + checkUser<UserDocIdString>(id2.toString(), 1234); +} + +TEST("require that illegal ids fail") { + EXPECT_EXCEPTION(DocumentId("idg:foo:bar:baz"), IdParseException, + "No scheme separator ':' found"); + EXPECT_EXCEPTION(DocumentId("id:"), IdParseException, "too short"); + EXPECT_EXCEPTION(DocumentId("id:ns"), IdParseException, + "No namespace separator ':' found"); + EXPECT_EXCEPTION(DocumentId("id:ns:type"), IdParseException, + "No document type separator ':' found"); + EXPECT_EXCEPTION(DocumentId("id:ns:type:kv_pair"), IdParseException, + "No key/value-pairs separator ':' found"); + EXPECT_EXCEPTION(DocumentId("id:ns:type:k=foo:bar"), IdParseException, + "Illegal key 'k'"); + EXPECT_EXCEPTION(DocumentId("id:ns:type:n=0,n=1:bar"), IdParseException, + "Illegal key combination in n=0,n=1"); + EXPECT_EXCEPTION(DocumentId("id:ns:type:g=foo,g=ba:bar"), IdParseException, + "Illegal key combination in g=foo,g=ba"); + EXPECT_EXCEPTION(DocumentId("id:ns:type:n=0,g=foo:bar"), IdParseException, + "Illegal key combination in n=0,g=foo"); +} + +TEST("require that key-value pairs in id id are preserved") { + const string id_str1 = "id:ns:type:n=1:foo"; + EXPECT_EQUAL(id_str1, DocumentId(id_str1).toString()); + + const string id_str2 = "id:ns:type:g=mygroup:foo"; + EXPECT_EQUAL(id_str2, DocumentId(id_str2).toString()); +} + +TEST("require that id strings reports features (hasNumber, hasGroup)") { + DocumentId none("id:ns:type::foo"); + EXPECT_FALSE(none.getScheme().hasNumber()); + EXPECT_FALSE(none.getScheme().hasGroup()); + EXPECT_EQUAL("foo", none.getScheme().getNamespaceSpecific()); + + none = DocumentId("doc:ns:foo"); + EXPECT_FALSE(none.getScheme().hasNumber()); + EXPECT_FALSE(none.getScheme().hasGroup()); + EXPECT_EQUAL("foo", none.getScheme().getNamespaceSpecific()); + + DocumentId user("id:ns:type:n=42:foo"); + EXPECT_TRUE(user.getScheme().hasNumber()); + EXPECT_FALSE(user.getScheme().hasGroup()); + EXPECT_EQUAL(42u, user.getScheme().getNumber()); + EXPECT_EQUAL("foo", user.getScheme().getNamespaceSpecific()); + + user = DocumentId("userdoc:ns:42:foo"); + EXPECT_TRUE(user.getScheme().hasNumber()); + EXPECT_FALSE(user.getScheme().hasGroup()); + EXPECT_EQUAL(42u, user.getScheme().getNumber()); + EXPECT_EQUAL("foo", user.getScheme().getNamespaceSpecific()); + + DocumentId group("id:ns:type:g=mygroup:foo"); + EXPECT_FALSE(group.getScheme().hasNumber()); + EXPECT_TRUE(group.getScheme().hasGroup()); + EXPECT_EQUAL("mygroup", group.getScheme().getGroup()); + EXPECT_EQUAL("foo", group.getScheme().getNamespaceSpecific()); + + group = DocumentId("groupdoc:ns:mygroup:foo"); + EXPECT_FALSE(group.getScheme().hasNumber()); + EXPECT_TRUE(group.getScheme().hasGroup()); + EXPECT_EQUAL("mygroup", group.getScheme().getGroup()); + EXPECT_EQUAL("foo", group.getScheme().getNamespaceSpecific()); + + DocumentId order("orderdoc(5,2):ns:42:007:foo"); + EXPECT_TRUE(order.getScheme().hasNumber()); + EXPECT_TRUE(order.getScheme().hasGroup()); + EXPECT_EQUAL(42u, order.getScheme().getNumber()); + EXPECT_EQUAL("42", order.getScheme().getGroup()); + EXPECT_EQUAL("foo", order.getScheme().getNamespaceSpecific()); +} + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/document/src/tests/bucketselectortest.cpp b/document/src/tests/bucketselectortest.cpp new file mode 100644 index 00000000000..f04816cba0d --- /dev/null +++ b/document/src/tests/bucketselectortest.cpp @@ -0,0 +1,102 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <vespa/document/bucket/bucketselector.h> + +#include <vespa/document/bucket/bucketid.h> +#include <vespa/vdstestlib/cppunit/macros.h> +#include <vespa/document/select/parser.h> +#include <vespa/document/base/testdocrepo.h> +#include <memory> + +using document::select::Node; +using document::select::Parser; + +namespace document { + +struct BucketSelectorTest : public CppUnit::TestFixture { + void testSimple(); + + CPPUNIT_TEST_SUITE(BucketSelectorTest); + CPPUNIT_TEST(testSimple); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BucketSelectorTest); + +#define ASSERT_BUCKET_COUNT(expression, count) \ +{ \ + TestDocRepo testRepo; \ + BucketIdFactory idfactory; \ + BucketSelector selector(idfactory); \ + Parser parser(testRepo.getTypeRepo(), idfactory); \ + std::unique_ptr<Node> node(parser.parse(expression)); \ + CPPUNIT_ASSERT(node.get() != 0); \ + std::unique_ptr<BucketSelector::BucketVector> buckets( \ + selector.select(*node)); \ + size_t bcount(buckets.get() ? buckets->size() : 0); \ + std::ostringstream ost; \ + ost << "Expression " << expression << " did not contain " << count \ + << " buckets as expected"; \ + CPPUNIT_ASSERT_EQUAL_MSG(ost.str(), (size_t) count, bcount); \ +} + +#define ASSERT_BUCKET(expression, bucket) \ +{ \ + TestDocRepo testRepo; \ + BucketIdFactory idfactory; \ + BucketSelector selector(idfactory); \ + Parser parser(testRepo.getTypeRepo(), idfactory); \ + std::unique_ptr<Node> node(parser.parse(expression)); \ + CPPUNIT_ASSERT(node.get() != 0); \ + std::unique_ptr<BucketSelector::BucketVector> buckets( \ + selector.select(*node)); \ + std::ostringstream ost; \ + ost << "Expression " << expression << " did not contain bucket " \ + << bucket.toString(); \ + if (buckets.get()) { \ + ost << ". Buckets: " << std::hex << *buckets; \ + } else { \ + ost << ". Matches all buckets"; \ + } \ + CPPUNIT_ASSERT_MSG(ost.str(), buckets.get() && \ + std::find(buckets->begin(), buckets->end(), \ + bucket) != buckets->end()); \ +} + +void BucketSelectorTest::testSimple() +{ + ASSERT_BUCKET_COUNT("id = \"userdoc:ns:123:foobar\"", 1u); + ASSERT_BUCKET_COUNT("id = \"userdoc:ns:123:foo*\"", 0u); + ASSERT_BUCKET_COUNT("id == \"userdoc:ns:123:f?oo*\"", 1u); + ASSERT_BUCKET_COUNT("id =~ \"userdoc:ns:123:foo*\"", 0u); + ASSERT_BUCKET_COUNT("id =~ \"userdoc:ns:123:foo?\"", 0u); + ASSERT_BUCKET_COUNT("id.user = 123", 1u); + ASSERT_BUCKET_COUNT("id.user == 123", 1u); + ASSERT_BUCKET_COUNT("id.group = \"yahoo.com\"", 1u); + ASSERT_BUCKET_COUNT("id.group = \"yahoo.com\" or id.user=123", 2u); + ASSERT_BUCKET_COUNT("id.group = \"yahoo.com\" and id.user=123", 0u); + ASSERT_BUCKET_COUNT( + "id.group = \"yahoo.com\" and testdoctype1.hstringval=\"Doe\"", 1u); + ASSERT_BUCKET_COUNT("not id.group = \"yahoo.com\"", 0u); + ASSERT_BUCKET_COUNT("id.group != \"yahoo.com\"", 0u); + ASSERT_BUCKET_COUNT("id.group <= \"yahoo.com\"", 0u); + + ASSERT_BUCKET_COUNT("id.bucket = 0x4000000000003018", 1u); // Bucket 12312 + ASSERT_BUCKET_COUNT("id.bucket == 0x4000000000000258", 1u); // Bucket 600 + ASSERT_BUCKET_COUNT("(testdoctype1 and id.bucket=0)", 1u); + + ASSERT_BUCKET_COUNT("searchcolumn.3 = 1", 21845u); + + // Check that the correct buckets is found + ASSERT_BUCKET("id = \"userdoc:ns:123:foobar\"", + document::BucketId(58, 123)); + + ASSERT_BUCKET("id.bucket == 0x4000000000000258", document::BucketId(16, 600)); + + ASSERT_BUCKET("id.user = 123", document::BucketId(32, 123)); + ASSERT_BUCKET("id.group = \"yahoo.com\"", + document::BucketId(32, 0x9a1acd50)); +} + +} // document diff --git a/document/src/tests/buckettest.cpp b/document/src/tests/buckettest.cpp new file mode 100644 index 00000000000..2f2f3df9ab6 --- /dev/null +++ b/document/src/tests/buckettest.cpp @@ -0,0 +1,258 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <cppunit/extensions/HelperMacros.h> +#include <vespa/document/bucket/bucketidfactory.h> +#include <vespa/document/base/documentid.h> +#include <vespa/vespalib/util/random.h> + +namespace document { + +class BucketTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(BucketTest); + CPPUNIT_TEST(testBucket); + CPPUNIT_TEST(testBucketGeneration); + CPPUNIT_TEST(testBucketSerialization); + CPPUNIT_TEST(testReverseBucket); + CPPUNIT_TEST(testContains); + CPPUNIT_TEST(testGetBit); + CPPUNIT_TEST_SUITE_END(); + +public: + void testBucket(); + void testBucketGeneration(); + void testBucketSerialization(); + void testReverseBucket(); + void testContains(); + void testGetBit(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BucketTest); + +struct Hex { + BucketId::Type val; + + Hex(BucketId::Type v) : val(v) {} + bool operator==(const Hex& h) const { return val == h.val; } +}; + +inline std::ostream& operator<<(std::ostream& out, const Hex& h) { + out << std::hex << h.val << std::dec; + return out; +} + +void BucketTest::testBucket() +{ + // Test empty (invalid) buckets + BucketId id1; + BucketId id2; + CPPUNIT_ASSERT_EQUAL(id1, id2); + CPPUNIT_ASSERT(!(id1 < id2) && !(id2 < id1)); + CPPUNIT_ASSERT_EQUAL(Hex(0), Hex(id1.getId())); + CPPUNIT_ASSERT_EQUAL(Hex(0), Hex(id1.getRawId())); + CPPUNIT_ASSERT_EQUAL(vespalib::string("BucketId(0x0000000000000000)"), + id1.toString()); + CPPUNIT_ASSERT_EQUAL(0u, id1.getUsedBits()); + + // Test bucket with a value + id2 = BucketId((BucketId::Type(16) << 58) | 0x123); + CPPUNIT_ASSERT(id1 != id2); + CPPUNIT_ASSERT((id1 < id2) && !(id2 < id1)); + CPPUNIT_ASSERT_EQUAL(Hex(0x4000000000000123ull), Hex(id2.getId())); + CPPUNIT_ASSERT_EQUAL(Hex(0x4000000000000123ull), Hex(id2.getRawId())); + CPPUNIT_ASSERT_EQUAL(vespalib::string("BucketId(0x4000000000000123)"), + id2.toString()); + CPPUNIT_ASSERT_EQUAL(16u, id2.getUsedBits()); + + // Test copy constructor and operator= + BucketId id3(id2); + CPPUNIT_ASSERT_EQUAL(id2, id3); + id3 = id1; + CPPUNIT_ASSERT(!(id2 == id3)); + id3 = id2; + CPPUNIT_ASSERT_EQUAL(id2, id3); +} + +void BucketTest::testGetBit() +{ + for (uint32_t i = 0; i < 58; ++i) { + CPPUNIT_ASSERT_EQUAL(0, (int)document::BucketId(16, 0).getBit(i)); + } + + for (uint32_t i = 0; i < 4; ++i) { + CPPUNIT_ASSERT_EQUAL(0, (int)document::BucketId(16, 16).getBit(i)); + } + + CPPUNIT_ASSERT_EQUAL(1, (int)document::BucketId(16, 16).getBit(4)); + + for (uint32_t i = 5; i < 59; ++i) { + CPPUNIT_ASSERT_EQUAL(0, (int)document::BucketId(16, 16).getBit(i)); + } + + CPPUNIT_ASSERT_EQUAL(0, (int)document::BucketId(17, 0x0ffff).getBit(16)); + + for (uint32_t i = 0; i < 16; ++i) { + CPPUNIT_ASSERT_EQUAL(1, (int)document::BucketId(17, 0x0ffff).getBit(i)); + } +} + +void BucketTest::testBucketGeneration() +{ + BucketIdFactory factory; + DocumentId doc1("doc:ns:spec"); + DocumentId doc2("doc:ns2:spec"); + DocumentId doc3("doc:ns:spec2"); + DocumentId userDoc1("userdoc:ns:18:spec"); + DocumentId userDoc2("userdoc:ns2:18:spec2"); + DocumentId userDoc3("userdoc:ns:19:spec"); + DocumentId groupDoc1("groupdoc:ns:yahoo.com:spec"); + DocumentId groupDoc2("groupdoc:ns2:yahoo.com:spec2"); + DocumentId groupDoc3("groupdoc:ns:yahoo:spec"); + DocumentId orderDoc1("orderdoc(31,19):ns:13:1268182861:foo"); + DocumentId orderDoc2("orderdoc(31,19):ns:13:1205110861:foo"); + DocumentId orderDoc3("orderdoc(31,19):ns:13:1205715661:foo"); + DocumentId orderDoc4("orderdoc(4,0):ns:13:2:foo"); + DocumentId orderDoc5("orderdoc(4,0):ns:13:4:foo"); + DocumentId orderDoc6("orderdoc(4,0):ns:13:11:foo"); + + BucketId docBucket1(factory.getBucketId(doc1)); + BucketId docBucket2(factory.getBucketId(doc2)); + BucketId docBucket3(factory.getBucketId(doc3)); + BucketId userDocBucket1(factory.getBucketId(userDoc1)); + BucketId userDocBucket2(factory.getBucketId(userDoc2)); + BucketId userDocBucket3(factory.getBucketId(userDoc3)); + BucketId groupDocBucket1(factory.getBucketId(groupDoc1)); + BucketId groupDocBucket2(factory.getBucketId(groupDoc2)); + BucketId groupDocBucket3(factory.getBucketId(groupDoc3)); + BucketId orderDocBucket1(factory.getBucketId(orderDoc1)); + BucketId orderDocBucket2(factory.getBucketId(orderDoc2)); + BucketId orderDocBucket3(factory.getBucketId(orderDoc3)); + BucketId orderDocBucket4(factory.getBucketId(orderDoc4)); + BucketId orderDocBucket5(factory.getBucketId(orderDoc5)); + BucketId orderDocBucket6(factory.getBucketId(orderDoc6)); + + CPPUNIT_ASSERT_EQUAL(Hex(0xe99703f200000012ull), Hex(userDocBucket1.getRawId())); + CPPUNIT_ASSERT_EQUAL(Hex(0xebfa518a00000012ull), Hex(userDocBucket2.getRawId())); + CPPUNIT_ASSERT_EQUAL(Hex(0xeac1850800000013ull), Hex(userDocBucket3.getRawId())); + + CPPUNIT_ASSERT_EQUAL(Hex(0xeae764e90000000dull), Hex(orderDocBucket1.getRawId())); + CPPUNIT_ASSERT_EQUAL(Hex(0xeacb85f10000000dull), Hex(orderDocBucket2.getRawId())); + CPPUNIT_ASSERT_EQUAL(Hex(0xea68ddf10000000dull), Hex(orderDocBucket3.getRawId())); + + CPPUNIT_ASSERT_EQUAL(Hex(0xe87526540000000dull), Hex(orderDocBucket4.getRawId())); + CPPUNIT_ASSERT_EQUAL(Hex(0xea59f8f20000000dull), Hex(orderDocBucket5.getRawId())); + CPPUNIT_ASSERT_EQUAL(Hex(0xe9eb703d0000000dull), Hex(orderDocBucket6.getRawId())); + + userDocBucket1.setUsedBits(16); + CPPUNIT_ASSERT_EQUAL(Hex(0x4000000000000012ull), Hex(userDocBucket1.getId())); + userDocBucket2.setUsedBits(16); + CPPUNIT_ASSERT_EQUAL(Hex(0x4000000000000012ull), Hex(userDocBucket2.getId())); + userDocBucket3.setUsedBits(16); + CPPUNIT_ASSERT_EQUAL(Hex(0x4000000000000013ull), Hex(userDocBucket3.getId())); + + CPPUNIT_ASSERT_EQUAL(Hex(0xe90ce4b09a1acd50ull), Hex(groupDocBucket1.getRawId())); + CPPUNIT_ASSERT_EQUAL(Hex(0xe9cedaa49a1acd50ull), Hex(groupDocBucket2.getRawId())); + CPPUNIT_ASSERT_EQUAL(Hex(0xe8cdb18bafe81f24ull), Hex(groupDocBucket3.getRawId())); + + groupDocBucket1.setUsedBits(16); + CPPUNIT_ASSERT_EQUAL(Hex(0x400000000000cd50ull), Hex(groupDocBucket1.getId())); + groupDocBucket2.setUsedBits(16); + CPPUNIT_ASSERT_EQUAL(Hex(0x400000000000cd50ull), Hex(groupDocBucket2.getId())); + groupDocBucket3.setUsedBits(16); + CPPUNIT_ASSERT_EQUAL(Hex(0x4000000000001f24ull), Hex(groupDocBucket3.getId())); + + CPPUNIT_ASSERT_EQUAL(Hex(0xe980c9abd5fd8d11ull), Hex(docBucket1.getRawId())); + CPPUNIT_ASSERT_EQUAL(Hex(0xeafe870c5f9c37b9ull), Hex(docBucket2.getRawId())); + CPPUNIT_ASSERT_EQUAL(Hex(0xeaebe9473ecbcd69ull), Hex(docBucket3.getRawId())); + + docBucket1.setUsedBits(16); + CPPUNIT_ASSERT_EQUAL(Hex(0x4000000000008d11ull), Hex(docBucket1.getId())); + docBucket2.setUsedBits(16); + CPPUNIT_ASSERT_EQUAL(Hex(0x40000000000037b9ull), Hex(docBucket2.getId())); + docBucket3.setUsedBits(16); + CPPUNIT_ASSERT_EQUAL(Hex(0x400000000000cd69ull), Hex(docBucket3.getId())); +} + +void BucketTest::testBucketSerialization() +{ + BucketIdFactory factory; + DocumentId doc(DocIdString("ns", "spec")); + BucketId bucket(factory.getBucketId(doc)); + + std::ostringstream ost; + ost << bucket.getRawId(); + CPPUNIT_ASSERT_EQUAL(std::string("16825669947722927377"), + ost.str()); + + BucketId::Type id; + std::istringstream ist(ost.str()); + ist >> id; + BucketId bucket2(id); + + CPPUNIT_ASSERT_EQUAL(bucket, bucket2); +} + +void BucketTest::testReverseBucket() +{ + { + BucketId id(0x3000000000000012ull); + CPPUNIT_ASSERT_EQUAL(Hex(0x480000000000000cull), Hex(id.toKey())); + CPPUNIT_ASSERT_EQUAL(Hex(id.getId()), Hex(document::BucketId::keyToBucketId(id.stripUnused().toKey()))); + } + + { + BucketId id(0x4000000000000012ull); + CPPUNIT_ASSERT_EQUAL(Hex(0x4800000000000010ull), Hex(id.toKey())); + CPPUNIT_ASSERT_EQUAL(Hex(id.getId()), Hex(document::BucketId::keyToBucketId(id.stripUnused().toKey()))); + } + + { + BucketId id(0x600000000000ffffull); + CPPUNIT_ASSERT_EQUAL(Hex(0xffff000000000018ull), Hex(id.toKey())); + CPPUNIT_ASSERT_EQUAL(Hex(id.getId()), Hex(document::BucketId::keyToBucketId(id.stripUnused().toKey()))); + } + + { + BucketId id(0x540000000001ffffull); + CPPUNIT_ASSERT_EQUAL(Hex(0xffff800000000015ull), Hex(id.toKey())); + CPPUNIT_ASSERT_EQUAL(Hex(id.getId()), Hex(document::BucketId::keyToBucketId(id.stripUnused().toKey()))); + } + + { + BucketId id(0xa80000000003ffffull); + CPPUNIT_ASSERT_EQUAL(Hex(0xffffc0000000002aull), Hex(id.toKey())); + CPPUNIT_ASSERT_EQUAL(Hex(id.getId()), Hex(document::BucketId::keyToBucketId(id.stripUnused().toKey()))); + } + + { + BucketId id(0xbc0000000007ffffull); + CPPUNIT_ASSERT_EQUAL(Hex(0xffffe0000000002full), Hex(id.toKey())); + CPPUNIT_ASSERT_EQUAL(Hex(id.getId()), Hex(document::BucketId::keyToBucketId(id.stripUnused().toKey()))); + } + { + BucketId id(0xcc0000000002ffffull); + CPPUNIT_ASSERT_EQUAL(Hex(0xffff400000000033ull), Hex(id.toKey())); + CPPUNIT_ASSERT_EQUAL(Hex(id.getId()), Hex(document::BucketId::keyToBucketId(id.stripUnused().toKey()))); + } + { + BucketId id(0xebffffffffffffffull); + CPPUNIT_ASSERT_EQUAL(Hex(0xfffffffffffffffaull), Hex(id.toKey())); + CPPUNIT_ASSERT_EQUAL(Hex(id.getId()), Hex(document::BucketId::keyToBucketId(id.stripUnused().toKey()))); + } + { + BucketId id(0xeaaaaaaaaaaaaaaaull); + CPPUNIT_ASSERT_EQUAL(Hex(0x555555555555557aull), Hex(id.toKey())); + CPPUNIT_ASSERT_EQUAL(Hex(id.getId()), Hex(document::BucketId::keyToBucketId(id.stripUnused().toKey()))); + } +} + +void BucketTest::testContains() { + BucketId id(18, 0x123456789ULL); + CPPUNIT_ASSERT(id.contains(BucketId(20, 0x123456789ULL))); + CPPUNIT_ASSERT(id.contains(BucketId(18, 0x888f56789ULL))); + CPPUNIT_ASSERT(id.contains(BucketId(24, 0x888456789ULL))); + CPPUNIT_ASSERT(!id.contains(BucketId(24, 0x888886789ULL))); + CPPUNIT_ASSERT(!id.contains(BucketId(16, 0x123456789ULL))); +} + +} // document diff --git a/document/src/tests/cpp-globalidbucketids.txt b/document/src/tests/cpp-globalidbucketids.txt new file mode 100644 index 00000000000..33dca454d3b --- /dev/null +++ b/document/src/tests/cpp-globalidbucketids.txt @@ -0,0 +1,38 @@ +doc:ns:specific - gid(0x2c01a21163cb7d0ce85fddd6) - BucketId(0xeadd5fe811a2012c) +doc:another:specific - gid(0xcd2ba528d1135e40605ce372) - BucketId(0xeae35c6028a52bcd) +doc:ns:another - gid(0x1d5324270601e76a7a1f58b7) - BucketId(0xeb581f7a2724531d) +userdoc:ns:100:specific - gid(0x64000000e7bd2a0d13c71935) - BucketId(0xe919c71300000064) +userdoc:np:100:another - gid(0x64000000102745c9ceec3238) - BucketId(0xe832ecce00000064) +userdoc:ns:101:specific - gid(0x6500000026238735e9cd9bb8) - BucketId(0xe89bcde900000065) +groupdoc:ns:agroup:specific - gid(0x3272feb9fb729a50be795117) - BucketId(0xeb5179beb9fe7232) +groupdoc:np:agroup:another - gid(0x3272feb93ff5e65f3cf4aba9) - BucketId(0xe9abf43cb9fe7232) +groupdoc:ns:another:specific - gid(0xb32d73e51ae730d4fa104396) - BucketId(0xea4310fae5732db3) +doc:ns:0 - gid(0x87817cf2f6d05976505e74be) - BucketId(0xea745e50f27c8187) +doc:ns:1 - gid(0x911a03b253cb5b1c283b2024) - BucketId(0xe8203b28b2031a91) +doc:ns:2 - gid(0x1d82e56be428cda364ed6875) - BucketId(0xe968ed646be5821d) +doc:ns:3 - gid(0xf8d223e4e68e0d571b95a6d8) - BucketId(0xe8a6951be423d2f8) +doc:ns:4 - gid(0xdab024c2e41747dc92a1b8e3) - BucketId(0xebb8a192c224b0da) +doc:ns:5 - gid(0xdb9023e3080c94901734f948) - BucketId(0xe8f93417e32390db) +doc:ns:6 - gid(0xbd84ae30c63f7fdef9edbf69) - BucketId(0xe9bfedf930ae84bd) +doc:ns:7 - gid(0x463977cf070d06e204b8166f) - BucketId(0xeb16b804cf773946) +doc:ns:8 - gid(0x46cf1241cec694a0c07af5e2) - BucketId(0xeaf57ac04112cf46) +doc:ns:9 - gid(0x909b77593ef7b309a06d22ef) - BucketId(0xeb226da059779b90) +doc:ns:10 - gid(0x4888f0b3796031003a8840fb) - BucketId(0xeb40883ab3f08848) +doc:ns:11 - gid(0x18fae4cbc359765470c10fcd) - BucketId(0xe90fc170cbe4fa18) +doc:ns:12 - gid(0xc902059d4ac551616aea5431) - BucketId(0xe954ea6a9d0502c9) +doc:ns:13 - gid(0x639b6aa505018e29ca4e342d) - BucketId(0xe9344ecaa56a9b63) +doc:ns:14 - gid(0x5fa1f02be952a9d0811e8ddd) - BucketId(0xe98d1e812bf0a15f) +doc:ns:15 - gid(0xfbc851f81830365c84229c49) - BucketId(0xe99c2284f851c8fb) +doc:ns:16 - gid(0x06313edc8072f4495329fb5b) - BucketId(0xebfb2953dc3e3106) +doc:ns:17 - gid(0x3d9df3e147de3a5fbd5664e4) - BucketId(0xe86456bde1f39d3d) +doc:ns:18 - gid(0x75512f41a8dbde1c8f86a97d) - BucketId(0xe9a9868f412f5175) +doc:ns:19 - gid(0x15ae3aa9919b2e1e46d84ada) - BucketId(0xea4ad846a93aae15) +id:ns:type::specific - gid(0x2067d966823ebdfb79537da1) - BucketId(0xe97d537966d96720) +id:another:type::specific - gid(0x2067d9664809eb39a3b72218) - BucketId(0xe822b7a366d96720) +id:ns:type::another - gid(0xb32d73e53e034c30c58a01ff) - BucketId(0xeb018ac5e5732db3) +id:ns:type:n=100:specific - gid(0x64000000dd4c4b431749f95f) - BucketId(0xebf9491700000064) +id:np:type:n=100:another - gid(0x6400000031359e4a70e670a8) - BucketId(0xe870e67000000064) +id:ns:type:n=101:specific - gid(0x6500000083f2836ce5883df1) - BucketId(0xe93d88e500000065) +id:ns:type:g=agroup:specific - gid(0x3272feb90fe348ef910b064a) - BucketId(0xea060b91b9fe7232) +id:np:type:g=agroup:another - gid(0x3272feb96d53d40047cb4f23) - BucketId(0xeb4fcb47b9fe7232) +id:ns:type:g=another:specific - gid(0xb32d73e58bd215d1451922cc) - BucketId(0xe8221945e5732db3) diff --git a/document/src/tests/data/compressed.cfg b/document/src/tests/data/compressed.cfg new file mode 100644 index 00000000000..740e56c47ac --- /dev/null +++ b/document/src/tests/data/compressed.cfg @@ -0,0 +1,139 @@ +enablecompression true +datatype[11] +datatype[0].id -260050933 +datatype[0].arraytype[0] +datatype[0].weightedsettype[0] +datatype[0].structtype[1] +datatype[0].structtype[0].name serializetest.header +datatype[0].structtype[0].version 0 +datatype[0].structtype[0].compresstype LZ4 +datatype[0].structtype[0].compresslevel 9 +datatype[0].structtype[0].compressthreshold 99 +datatype[0].structtype[0].compressminsize 0 +datatype[0].structtype[0].field[4] +datatype[0].structtype[0].field[0].name floatfield +datatype[0].structtype[0].field[0].id[0] +datatype[0].structtype[0].field[0].datatype 1 +datatype[0].structtype[0].field[1].name stringfield +datatype[0].structtype[0].field[1].id[0] +datatype[0].structtype[0].field[1].datatype 2 +datatype[0].structtype[0].field[2].name longfield +datatype[0].structtype[0].field[2].id[0] +datatype[0].structtype[0].field[2].datatype 4 +datatype[0].structtype[0].field[3].name urifield +datatype[0].structtype[0].field[3].id[0] +datatype[0].structtype[0].field[3].datatype 10 +datatype[0].documenttype[0] +datatype[1].id 1001 +datatype[1].arraytype[1] +datatype[1].arraytype[0].datatype 1 +datatype[1].weightedsettype[0] +datatype[1].structtype[0] +datatype[1].documenttype[0] +datatype[2].id 2001 +datatype[2].arraytype[1] +datatype[2].arraytype[0].datatype 1001 +datatype[2].weightedsettype[0] +datatype[2].structtype[0] +datatype[2].documenttype[0] +datatype[3].id 1026122976 +datatype[3].arraytype[0] +datatype[3].weightedsettype[0] +datatype[3].structtype[1] +datatype[3].structtype[0].name serializetest.body +datatype[3].structtype[0].version 0 +datatype[3].structtype[0].compresstype LZ4 +datatype[3].structtype[0].compresslevel 9 +datatype[3].structtype[0].compressthreshold 99 +datatype[3].structtype[0].compressminsize 0 +datatype[3].structtype[0].field[8] +datatype[3].structtype[0].field[0].name intfield +datatype[3].structtype[0].field[0].id[0] +datatype[3].structtype[0].field[0].datatype 0 +datatype[3].structtype[0].field[1].name rawfield +datatype[3].structtype[0].field[1].id[0] +datatype[3].structtype[0].field[1].datatype 3 +datatype[3].structtype[0].field[2].name doublefield +datatype[3].structtype[0].field[2].id[0] +datatype[3].structtype[0].field[2].datatype 5 +datatype[3].structtype[0].field[3].name contentfield +datatype[3].structtype[0].field[3].id[0] +datatype[3].structtype[0].field[3].datatype 2 +datatype[3].structtype[0].field[4].name bytefield +datatype[3].structtype[0].field[4].id[0] +datatype[3].structtype[0].field[4].datatype 16 +datatype[3].structtype[0].field[5].name arrayoffloatfield +datatype[3].structtype[0].field[5].id[0] +datatype[3].structtype[0].field[5].datatype 1001 +datatype[3].structtype[0].field[6].name arrayofarrayoffloatfield +datatype[3].structtype[0].field[6].id[0] +datatype[3].structtype[0].field[6].datatype 2001 +datatype[3].structtype[0].field[7].name docfield +datatype[3].structtype[0].field[7].id[0] +datatype[3].structtype[0].field[7].datatype 8 +datatype[3].documenttype[0] +datatype[4].id 1306012852 +datatype[4].arraytype[0] +datatype[4].weightedsettype[0] +datatype[4].structtype[0] +datatype[4].documenttype[1] +datatype[4].documenttype[0].name serializetest +datatype[4].documenttype[0].version 0 +datatype[4].documenttype[0].inherits[0] +datatype[4].documenttype[0].headerstruct -260050933 +datatype[4].documenttype[0].bodystruct 1026122976 +datatype[5].id -1686125086 +datatype[5].arraytype[0] +datatype[5].weightedsettype[0] +datatype[5].structtype[1] +datatype[5].structtype[0].name docindoc.header +datatype[5].structtype[0].version 0 +datatype[5].structtype[0].field[0] +datatype[5].documenttype[0] +datatype[6].id 2030224503 +datatype[6].arraytype[0] +datatype[6].weightedsettype[0] +datatype[6].structtype[1] +datatype[6].structtype[0].name docindoc.body +datatype[6].structtype[0].version 0 +datatype[6].structtype[0].field[0] +datatype[6].documenttype[0] +datatype[7].id 1447635645 +datatype[7].arraytype[0] +datatype[7].weightedsettype[0] +datatype[7].structtype[0] +datatype[7].documenttype[1] +datatype[7].documenttype[0].name docindoc +datatype[7].documenttype[0].version 0 +datatype[7].documenttype[0].inherits[0] +datatype[7].documenttype[0].headerstruct -1686125086 +datatype[7].documenttype[0].bodystruct 2030224503 +datatype[8].id -792441727 +datatype[8].arraytype[0] +datatype[8].weightedsettype[0] +datatype[8].structtype[1] +datatype[8].structtype[0].name mapindocindoc.header +datatype[8].structtype[0].version 0 +datatype[8].structtype[0].field[0] +datatype[8].documenttype[0] +datatype[9].id 145123030 +datatype[9].arraytype[0] +datatype[9].weightedsettype[0] +datatype[9].structtype[1] +datatype[9].structtype[0].name mapindocindoc.body +datatype[9].structtype[0].version 0 +datatype[9].structtype[0].field[1] +datatype[9].structtype[0].field[0].name stringinmap +datatype[9].structtype[0].field[0].id[0] +datatype[9].structtype[0].field[0].datatype 2 +datatype[9].documenttype[0] +datatype[10].id -1456256770 +datatype[10].arraytype[0] +datatype[10].weightedsettype[0] +datatype[10].structtype[0] +datatype[10].documenttype[1] +datatype[10].documenttype[0].name mapindocindoc +datatype[10].documenttype[0].version 0 +datatype[10].documenttype[0].inherits[0] +datatype[10].documenttype[0].headerstruct -792441727 +datatype[10].documenttype[0].bodystruct 145123030 diff --git a/document/src/tests/data/cppdocument.cfg b/document/src/tests/data/cppdocument.cfg new file mode 100644 index 00000000000..e4fcb5cb5bf --- /dev/null +++ b/document/src/tests/data/cppdocument.cfg @@ -0,0 +1,131 @@ +enablecompression false +datatype[11] +datatype[0].id -260050933 +datatype[0].arraytype[0] +datatype[0].weightedsettype[0] +datatype[0].structtype[1] +datatype[0].structtype[0].name serializetest.header +datatype[0].structtype[0].version 0 +datatype[0].structtype[0].field[4] +datatype[0].structtype[0].field[0].name floatfield +datatype[0].structtype[0].field[0].id[0] +datatype[0].structtype[0].field[0].datatype 1 +datatype[0].structtype[0].field[1].name stringfield +datatype[0].structtype[0].field[1].id[0] +datatype[0].structtype[0].field[1].datatype 2 +datatype[0].structtype[0].field[2].name longfield +datatype[0].structtype[0].field[2].id[0] +datatype[0].structtype[0].field[2].datatype 4 +datatype[0].structtype[0].field[3].name urifield +datatype[0].structtype[0].field[3].id[0] +datatype[0].structtype[0].field[3].datatype 10 +datatype[0].documenttype[0] +datatype[1].id 1001 +datatype[1].arraytype[1] +datatype[1].arraytype[0].datatype 1 +datatype[1].weightedsettype[0] +datatype[1].structtype[0] +datatype[1].documenttype[0] +datatype[2].id 2001 +datatype[2].arraytype[1] +datatype[2].arraytype[0].datatype 1001 +datatype[2].weightedsettype[0] +datatype[2].structtype[0] +datatype[2].documenttype[0] +datatype[3].id 1026122976 +datatype[3].arraytype[0] +datatype[3].weightedsettype[0] +datatype[3].structtype[1] +datatype[3].structtype[0].name serializetest.body +datatype[3].structtype[0].version 0 +datatype[3].structtype[0].field[8] +datatype[3].structtype[0].field[0].name intfield +datatype[3].structtype[0].field[0].id[0] +datatype[3].structtype[0].field[0].datatype 0 +datatype[3].structtype[0].field[1].name rawfield +datatype[3].structtype[0].field[1].id[0] +datatype[3].structtype[0].field[1].datatype 3 +datatype[3].structtype[0].field[2].name doublefield +datatype[3].structtype[0].field[2].id[0] +datatype[3].structtype[0].field[2].datatype 5 +datatype[3].structtype[0].field[3].name contentfield +datatype[3].structtype[0].field[3].id[0] +datatype[3].structtype[0].field[3].datatype 2 +datatype[3].structtype[0].field[4].name bytefield +datatype[3].structtype[0].field[4].id[0] +datatype[3].structtype[0].field[4].datatype 16 +datatype[3].structtype[0].field[5].name arrayoffloatfield +datatype[3].structtype[0].field[5].id[0] +datatype[3].structtype[0].field[5].datatype 1001 +datatype[3].structtype[0].field[6].name arrayofarrayoffloatfield +datatype[3].structtype[0].field[6].id[0] +datatype[3].structtype[0].field[6].datatype 2001 +datatype[3].structtype[0].field[7].name docfield +datatype[3].structtype[0].field[7].id[0] +datatype[3].structtype[0].field[7].datatype 8 +datatype[3].documenttype[0] +datatype[4].id 1306012852 +datatype[4].arraytype[0] +datatype[4].weightedsettype[0] +datatype[4].structtype[0] +datatype[4].documenttype[1] +datatype[4].documenttype[0].name serializetest +datatype[4].documenttype[0].version 0 +datatype[4].documenttype[0].inherits[0] +datatype[4].documenttype[0].headerstruct -260050933 +datatype[4].documenttype[0].bodystruct 1026122976 +datatype[5].id -1686125086 +datatype[5].arraytype[0] +datatype[5].weightedsettype[0] +datatype[5].structtype[1] +datatype[5].structtype[0].name docindoc.header +datatype[5].structtype[0].version 0 +datatype[5].structtype[0].field[0] +datatype[5].documenttype[0] +datatype[6].id 2030224503 +datatype[6].arraytype[0] +datatype[6].weightedsettype[0] +datatype[6].structtype[1] +datatype[6].structtype[0].name docindoc.body +datatype[6].structtype[0].version 0 +datatype[6].structtype[0].field[0] +datatype[6].documenttype[0] +datatype[7].id 1447635645 +datatype[7].arraytype[0] +datatype[7].weightedsettype[0] +datatype[7].structtype[0] +datatype[7].documenttype[1] +datatype[7].documenttype[0].name docindoc +datatype[7].documenttype[0].version 0 +datatype[7].documenttype[0].inherits[0] +datatype[7].documenttype[0].headerstruct -1686125086 +datatype[7].documenttype[0].bodystruct 2030224503 +datatype[8].id -792441727 +datatype[8].arraytype[0] +datatype[8].weightedsettype[0] +datatype[8].structtype[1] +datatype[8].structtype[0].name mapindocindoc.header +datatype[8].structtype[0].version 0 +datatype[8].structtype[0].field[0] +datatype[8].documenttype[0] +datatype[9].id 145123030 +datatype[9].arraytype[0] +datatype[9].weightedsettype[0] +datatype[9].structtype[1] +datatype[9].structtype[0].name mapindocindoc.body +datatype[9].structtype[0].version 0 +datatype[9].structtype[0].field[1] +datatype[9].structtype[0].field[0].name stringinmap +datatype[9].structtype[0].field[0].id[0] +datatype[9].structtype[0].field[0].datatype 2 +datatype[9].documenttype[0] +datatype[10].id -1456256770 +datatype[10].arraytype[0] +datatype[10].weightedsettype[0] +datatype[10].structtype[0] +datatype[10].documenttype[1] +datatype[10].documenttype[0].name mapindocindoc +datatype[10].documenttype[0].version 0 +datatype[10].documenttype[0].inherits[0] +datatype[10].documenttype[0].headerstruct -792441727 +datatype[10].documenttype[0].bodystruct 145123030 diff --git a/document/src/tests/data/crossplatform-java-cpp-doctypes.cfg b/document/src/tests/data/crossplatform-java-cpp-doctypes.cfg new file mode 100644 index 00000000000..6d33df468dd --- /dev/null +++ b/document/src/tests/data/crossplatform-java-cpp-doctypes.cfg @@ -0,0 +1,202 @@ +enablecompression false +documenttype[2] +documenttype[0].id 1306012852 +documenttype[0].name "serializetest" +documenttype[0].version 0 +documenttype[0].headerstruct -260050933 +documenttype[0].bodystruct 1026122976 +documenttype[0].inherits[0] +documenttype[0].datatype[6] +documenttype[0].datatype[0].id 9999 +documenttype[0].datatype[0].type MAP +documenttype[0].datatype[0].array.element.id 0 +documenttype[0].datatype[0].map.key.id 2 +documenttype[0].datatype[0].map.value.id 2 +documenttype[0].datatype[0].wset.key.id 0 +documenttype[0].datatype[0].wset.createifnonexistent false +documenttype[0].datatype[0].wset.removeifzero false +documenttype[0].datatype[0].annotationref.annotation.id 0 +documenttype[0].datatype[0].sstruct.name "" +documenttype[0].datatype[0].sstruct.version 0 +documenttype[0].datatype[0].sstruct.compression.type NONE +documenttype[0].datatype[0].sstruct.compression.level 0 +documenttype[0].datatype[0].sstruct.compression.threshold 95 +documenttype[0].datatype[0].sstruct.compression.minsize 200 +documenttype[0].datatype[0].sstruct.field[0] +documenttype[0].datatype[1].id 1001 +documenttype[0].datatype[1].type ARRAY +documenttype[0].datatype[1].array.element.id 1 +documenttype[0].datatype[1].map.key.id 0 +documenttype[0].datatype[1].map.value.id 0 +documenttype[0].datatype[1].wset.key.id 0 +documenttype[0].datatype[1].wset.createifnonexistent false +documenttype[0].datatype[1].wset.removeifzero false +documenttype[0].datatype[1].annotationref.annotation.id 0 +documenttype[0].datatype[1].sstruct.name "" +documenttype[0].datatype[1].sstruct.version 0 +documenttype[0].datatype[1].sstruct.compression.type NONE +documenttype[0].datatype[1].sstruct.compression.level 0 +documenttype[0].datatype[1].sstruct.compression.threshold 95 +documenttype[0].datatype[1].sstruct.compression.minsize 200 +documenttype[0].datatype[1].sstruct.field[0] +documenttype[0].datatype[2].id 2001 +documenttype[0].datatype[2].type ARRAY +documenttype[0].datatype[2].array.element.id 1001 +documenttype[0].datatype[2].map.key.id 0 +documenttype[0].datatype[2].map.value.id 0 +documenttype[0].datatype[2].wset.key.id 0 +documenttype[0].datatype[2].wset.createifnonexistent false +documenttype[0].datatype[2].wset.removeifzero false +documenttype[0].datatype[2].annotationref.annotation.id 0 +documenttype[0].datatype[2].sstruct.name "" +documenttype[0].datatype[2].sstruct.version 0 +documenttype[0].datatype[2].sstruct.compression.type NONE +documenttype[0].datatype[2].sstruct.compression.level 0 +documenttype[0].datatype[2].sstruct.compression.threshold 95 +documenttype[0].datatype[2].sstruct.compression.minsize 200 +documenttype[0].datatype[2].sstruct.field[0] +documenttype[0].datatype[3].id 437829 +documenttype[0].datatype[3].type WSET +documenttype[0].datatype[3].array.element.id 0 +documenttype[0].datatype[3].map.key.id 0 +documenttype[0].datatype[3].map.value.id 0 +documenttype[0].datatype[3].wset.key.id 2 +documenttype[0].datatype[3].wset.createifnonexistent false +documenttype[0].datatype[3].wset.removeifzero false +documenttype[0].datatype[3].annotationref.annotation.id 0 +documenttype[0].datatype[3].sstruct.name "" +documenttype[0].datatype[3].sstruct.version 0 +documenttype[0].datatype[3].sstruct.compression.type NONE +documenttype[0].datatype[3].sstruct.compression.level 0 +documenttype[0].datatype[3].sstruct.compression.threshold 95 +documenttype[0].datatype[3].sstruct.compression.minsize 200 +documenttype[0].datatype[3].sstruct.field[0] +documenttype[0].datatype[4].id -260050933 +documenttype[0].datatype[4].type STRUCT +documenttype[0].datatype[4].array.element.id 0 +documenttype[0].datatype[4].map.key.id 0 +documenttype[0].datatype[4].map.value.id 0 +documenttype[0].datatype[4].wset.key.id 0 +documenttype[0].datatype[4].wset.createifnonexistent false +documenttype[0].datatype[4].wset.removeifzero false +documenttype[0].datatype[4].annotationref.annotation.id 0 +documenttype[0].datatype[4].sstruct.name "serializetest.header" +documenttype[0].datatype[4].sstruct.version 0 +documenttype[0].datatype[4].sstruct.compression.type NONE +documenttype[0].datatype[4].sstruct.compression.level 0 +documenttype[0].datatype[4].sstruct.compression.threshold 90 +documenttype[0].datatype[4].sstruct.compression.minsize 0 +documenttype[0].datatype[4].sstruct.field[4] +documenttype[0].datatype[4].sstruct.field[0].name "floatfield" +documenttype[0].datatype[4].sstruct.field[0].id 1055999199 +documenttype[0].datatype[4].sstruct.field[0].id_v6 657399019 +documenttype[0].datatype[4].sstruct.field[0].datatype 1 +documenttype[0].datatype[4].sstruct.field[1].name "longfield" +documenttype[0].datatype[4].sstruct.field[1].id 1589309697 +documenttype[0].datatype[4].sstruct.field[1].id_v6 477609536 +documenttype[0].datatype[4].sstruct.field[1].datatype 4 +documenttype[0].datatype[4].sstruct.field[2].name "stringfield" +documenttype[0].datatype[4].sstruct.field[2].id 1182460484 +documenttype[0].datatype[4].sstruct.field[2].id_v6 779638844 +documenttype[0].datatype[4].sstruct.field[2].datatype 2 +documenttype[0].datatype[4].sstruct.field[3].name "urifield" +documenttype[0].datatype[4].sstruct.field[3].id 628407450 +documenttype[0].datatype[4].sstruct.field[3].id_v6 756285986 +documenttype[0].datatype[4].sstruct.field[3].datatype 10 +documenttype[0].datatype[5].id 1026122976 +documenttype[0].datatype[5].type STRUCT +documenttype[0].datatype[5].array.element.id 0 +documenttype[0].datatype[5].map.key.id 0 +documenttype[0].datatype[5].map.value.id 0 +documenttype[0].datatype[5].wset.key.id 0 +documenttype[0].datatype[5].wset.createifnonexistent false +documenttype[0].datatype[5].wset.removeifzero false +documenttype[0].datatype[5].annotationref.annotation.id 0 +documenttype[0].datatype[5].sstruct.name "serializetest.body" +documenttype[0].datatype[5].sstruct.version 0 +documenttype[0].datatype[5].sstruct.compression.type NONE +documenttype[0].datatype[5].sstruct.compression.level 0 +documenttype[0].datatype[5].sstruct.compression.threshold 90 +documenttype[0].datatype[5].sstruct.compression.minsize 0 +documenttype[0].datatype[5].sstruct.field[9] +documenttype[0].datatype[5].sstruct.field[0].name "arrayofarrayoffloatfield" +documenttype[0].datatype[5].sstruct.field[0].id 2061447601 +documenttype[0].datatype[5].sstruct.field[0].id_v6 903107040 +documenttype[0].datatype[5].sstruct.field[0].datatype 2001 +documenttype[0].datatype[5].sstruct.field[1].name "arrayoffloatfield" +documenttype[0].datatype[5].sstruct.field[1].id 1870932758 +documenttype[0].datatype[5].sstruct.field[1].id_v6 86189529 +documenttype[0].datatype[5].sstruct.field[1].datatype 1001 +documenttype[0].datatype[5].sstruct.field[2].name "bytefield" +documenttype[0].datatype[5].sstruct.field[2].id 1924064342 +documenttype[0].datatype[5].sstruct.field[2].id_v6 2050488857 +documenttype[0].datatype[5].sstruct.field[2].datatype 16 +documenttype[0].datatype[5].sstruct.field[3].name "docfield" +documenttype[0].datatype[5].sstruct.field[3].id 728739268 +documenttype[0].datatype[5].sstruct.field[3].id_v6 1725514984 +documenttype[0].datatype[5].sstruct.field[3].datatype 8 +documenttype[0].datatype[5].sstruct.field[4].name "doublefield" +documenttype[0].datatype[5].sstruct.field[4].id 421343958 +documenttype[0].datatype[5].sstruct.field[4].id_v6 944141865 +documenttype[0].datatype[5].sstruct.field[4].datatype 5 +documenttype[0].datatype[5].sstruct.field[5].name "intfield" +documenttype[0].datatype[5].sstruct.field[5].id 435380425 +documenttype[0].datatype[5].sstruct.field[5].id_v6 545156740 +documenttype[0].datatype[5].sstruct.field[5].datatype 0 +documenttype[0].datatype[5].sstruct.field[6].name "mapfield" +documenttype[0].datatype[5].sstruct.field[6].id 162466023 +documenttype[0].datatype[5].sstruct.field[6].id_v6 1028589263 +documenttype[0].datatype[5].sstruct.field[6].datatype 9999 +documenttype[0].datatype[5].sstruct.field[7].name "rawfield" +documenttype[0].datatype[5].sstruct.field[7].id 172982133 +documenttype[0].datatype[5].sstruct.field[7].id_v6 84029972 +documenttype[0].datatype[5].sstruct.field[7].datatype 3 +documenttype[0].datatype[5].sstruct.field[8].name "wsfield" +documenttype[0].datatype[5].sstruct.field[8].id 1683714185 +documenttype[0].datatype[5].sstruct.field[8].id_v6 2988081 +documenttype[0].datatype[5].sstruct.field[8].datatype 437829 +documenttype[0].annotationtype[0] +documenttype[1].id 1447635645 +documenttype[1].name "docindoc" +documenttype[1].version 0 +documenttype[1].headerstruct -1686125086 +documenttype[1].bodystruct 2030224503 +documenttype[1].inherits[0] +documenttype[1].datatype[2] +documenttype[1].datatype[0].id -1686125086 +documenttype[1].datatype[0].type STRUCT +documenttype[1].datatype[0].array.element.id 0 +documenttype[1].datatype[0].map.key.id 0 +documenttype[1].datatype[0].map.value.id 0 +documenttype[1].datatype[0].wset.key.id 0 +documenttype[1].datatype[0].wset.createifnonexistent false +documenttype[1].datatype[0].wset.removeifzero false +documenttype[1].datatype[0].annotationref.annotation.id 0 +documenttype[1].datatype[0].sstruct.name "docindoc.header" +documenttype[1].datatype[0].sstruct.version 0 +documenttype[1].datatype[0].sstruct.compression.type NONE +documenttype[1].datatype[0].sstruct.compression.level 0 +documenttype[1].datatype[0].sstruct.compression.threshold 90 +documenttype[1].datatype[0].sstruct.compression.minsize 0 +documenttype[1].datatype[0].sstruct.field[0] +documenttype[1].datatype[1].id 2030224503 +documenttype[1].datatype[1].type STRUCT +documenttype[1].datatype[1].array.element.id 0 +documenttype[1].datatype[1].map.key.id 0 +documenttype[1].datatype[1].map.value.id 0 +documenttype[1].datatype[1].wset.key.id 0 +documenttype[1].datatype[1].wset.createifnonexistent false +documenttype[1].datatype[1].wset.removeifzero false +documenttype[1].datatype[1].annotationref.annotation.id 0 +documenttype[1].datatype[1].sstruct.name "docindoc.body" +documenttype[1].datatype[1].sstruct.version 0 +documenttype[1].datatype[1].sstruct.compression.type NONE +documenttype[1].datatype[1].sstruct.compression.level 0 +documenttype[1].datatype[1].sstruct.compression.threshold 90 +documenttype[1].datatype[1].sstruct.compression.minsize 0 +documenttype[1].datatype[1].sstruct.field[1] +documenttype[1].datatype[1].sstruct.field[0].name "stringindocfield" +documenttype[1].datatype[1].sstruct.field[0].id 102054923 +documenttype[1].datatype[1].sstruct.field[0].id_v6 917294979 +documenttype[1].datatype[1].sstruct.field[0].datatype 2 +documenttype[1].annotationtype[0] diff --git a/document/src/tests/data/crossplatform-java-cpp-document.cfg b/document/src/tests/data/crossplatform-java-cpp-document.cfg new file mode 100644 index 00000000000..f12dae77fc0 --- /dev/null +++ b/document/src/tests/data/crossplatform-java-cpp-document.cfg @@ -0,0 +1,128 @@ +enablecompression false +datatype[10] +datatype[0].id 9999 +datatype[0].arraytype[0] +datatype[0].maptype[1] +datatype[0].maptype[0].keytype 2 +datatype[0].maptype[0].valtype 2 +datatype[0].weightedsettype[0] +datatype[0].structtype[0] +datatype[0].documenttype[0] +datatype[1].id 1001 +datatype[1].arraytype[1] +datatype[1].arraytype[0].datatype 1 +datatype[1].weightedsettype[0] +datatype[1].structtype[0] +datatype[1].documenttype[0] +datatype[2].id 2001 +datatype[2].arraytype[1] +datatype[2].arraytype[0].datatype 1001 +datatype[2].weightedsettype[0] +datatype[2].structtype[0] +datatype[2].documenttype[0] +datatype[3].id 437829 +datatype[3].arraytype[0] +datatype[3].weightedsettype[1] +datatype[3].weightedsettype[0].datatype 2 +datatype[3].weightedsettype[0].createifnonexistant false +datatype[3].weightedsettype[0].removeifzero false +datatype[3].structtype[0] +datatype[3].documenttype[0] +datatype[4].id -1686125086 +datatype[4].arraytype[0] +datatype[4].weightedsettype[0] +datatype[4].structtype[1] +datatype[4].structtype[0].name docindoc.header +datatype[4].structtype[0].version 0 +datatype[4].structtype[0].field[0] +datatype[4].documenttype[0] +datatype[5].id 2030224503 +datatype[5].arraytype[0] +datatype[5].weightedsettype[0] +datatype[5].structtype[1] +datatype[5].structtype[0].name docindoc.body +datatype[5].structtype[0].version 0 +datatype[5].structtype[0].field[1] +datatype[5].structtype[0].field[0].name stringindocfield +datatype[5].structtype[0].field[0].id[0] +datatype[5].structtype[0].field[0].datatype 2 +datatype[5].documenttype[0] +datatype[6].id 1447635645 +datatype[6].arraytype[0] +datatype[6].weightedsettype[0] +datatype[6].structtype[0] +datatype[6].documenttype[1] +datatype[6].documenttype[0].name docindoc +datatype[6].documenttype[0].version 0 +datatype[6].documenttype[0].inherits[0] +datatype[6].documenttype[0].headerstruct -1686125086 +datatype[6].documenttype[0].bodystruct 2030224503 +datatype[7].id -260050933 +datatype[7].arraytype[0] +datatype[7].weightedsettype[0] +datatype[7].structtype[1] +datatype[7].structtype[0].name serializetest.header +datatype[7].structtype[0].version 0 +datatype[7].structtype[0].field[4] +datatype[7].structtype[0].field[0].name floatfield +datatype[7].structtype[0].field[0].id[0] +datatype[7].structtype[0].field[0].datatype 1 +datatype[7].structtype[0].field[1].name stringfield +datatype[7].structtype[0].field[1].id[0] +datatype[7].structtype[0].field[1].datatype 2 +datatype[7].structtype[0].field[2].name longfield +datatype[7].structtype[0].field[2].id[0] +datatype[7].structtype[0].field[2].datatype 4 +datatype[7].structtype[0].field[3].name urifield +datatype[7].structtype[0].field[3].id[0] +datatype[7].structtype[0].field[3].datatype 10 +datatype[7].documenttype[0] +datatype[8].id 1026122976 +datatype[8].arraytype[0] +datatype[8].weightedsettype[0] +datatype[8].structtype[1] +datatype[8].structtype[0].name serializetest.body +datatype[8].structtype[0].version 0 +datatype[8].structtype[0].field[10] +datatype[8].structtype[0].field[0].name intfield +datatype[8].structtype[0].field[0].id[0] +datatype[8].structtype[0].field[0].datatype 0 +datatype[8].structtype[0].field[1].name rawfield +datatype[8].structtype[0].field[1].id[0] +datatype[8].structtype[0].field[1].datatype 3 +datatype[8].structtype[0].field[2].name doublefield +datatype[8].structtype[0].field[2].id[0] +datatype[8].structtype[0].field[2].datatype 5 +datatype[8].structtype[0].field[3].name contentfield +datatype[8].structtype[0].field[3].id[0] +datatype[8].structtype[0].field[3].datatype 2 +datatype[8].structtype[0].field[4].name bytefield +datatype[8].structtype[0].field[4].id[0] +datatype[8].structtype[0].field[4].datatype 16 +datatype[8].structtype[0].field[5].name arrayoffloatfield +datatype[8].structtype[0].field[5].id[0] +datatype[8].structtype[0].field[5].datatype 1001 +datatype[8].structtype[0].field[6].name arrayofarrayoffloatfield +datatype[8].structtype[0].field[6].id[0] +datatype[8].structtype[0].field[6].datatype 2001 +datatype[8].structtype[0].field[7].name docfield +datatype[8].structtype[0].field[7].id[0] +datatype[8].structtype[0].field[7].datatype 8 +datatype[8].structtype[0].field[8].name wsfield +datatype[8].structtype[0].field[8].id[0] +datatype[8].structtype[0].field[8].datatype 437829 +datatype[8].structtype[0].field[9].name mapfield +datatype[8].structtype[0].field[9].id[0] +datatype[8].structtype[0].field[9].datatype 9999 +datatype[8].documenttype[0] +datatype[9].id 1306012852 +datatype[9].arraytype[0] +datatype[9].weightedsettype[0] +datatype[9].structtype[0] +datatype[9].documenttype[1] +datatype[9].documenttype[0].name serializetest +datatype[9].documenttype[0].version 0 +datatype[9].documenttype[0].inherits[0] +datatype[9].documenttype[0].headerstruct -260050933 +datatype[9].documenttype[0].bodystruct 1026122976 + diff --git a/document/src/tests/data/defaultdoctypes.cfg b/document/src/tests/data/defaultdoctypes.cfg new file mode 100644 index 00000000000..8d9f71cfeb9 --- /dev/null +++ b/document/src/tests/data/defaultdoctypes.cfg @@ -0,0 +1,150 @@ +enablecompression false +documenttype[1] +documenttype[0].id -1175657560 +documenttype[0].name "testdoc" +documenttype[0].version 0 +documenttype[0].headerstruct 5000 +documenttype[0].bodystruct 5001 +documenttype[0].inherits[0] +documenttype[0].datatype[5] +documenttype[0].datatype[0].id 1000 +documenttype[0].datatype[0].type ARRAY +documenttype[0].datatype[0].array.element.id 0 +documenttype[0].datatype[0].map.key.id 0 +documenttype[0].datatype[0].map.value.id 0 +documenttype[0].datatype[0].wset.key.id 0 +documenttype[0].datatype[0].wset.createifnonexistent false +documenttype[0].datatype[0].wset.removeifzero false +documenttype[0].datatype[0].annotationref.annotation.id 0 +documenttype[0].datatype[0].sstruct.name "" +documenttype[0].datatype[0].sstruct.version 0 +documenttype[0].datatype[0].sstruct.compression.type NONE +documenttype[0].datatype[0].sstruct.compression.level 0 +documenttype[0].datatype[0].sstruct.compression.threshold 95 +documenttype[0].datatype[0].sstruct.compression.minsize 200 +documenttype[0].datatype[0].sstruct.field[0] +documenttype[0].datatype[1].id 1003 +documenttype[0].datatype[1].type ARRAY +documenttype[0].datatype[1].array.element.id 3 +documenttype[0].datatype[1].map.key.id 0 +documenttype[0].datatype[1].map.value.id 0 +documenttype[0].datatype[1].wset.key.id 0 +documenttype[0].datatype[1].wset.createifnonexistent false +documenttype[0].datatype[1].wset.removeifzero false +documenttype[0].datatype[1].annotationref.annotation.id 0 +documenttype[0].datatype[1].sstruct.name "" +documenttype[0].datatype[1].sstruct.version 0 +documenttype[0].datatype[1].sstruct.compression.type NONE +documenttype[0].datatype[1].sstruct.compression.level 0 +documenttype[0].datatype[1].sstruct.compression.threshold 95 +documenttype[0].datatype[1].sstruct.compression.minsize 200 +documenttype[0].datatype[1].sstruct.field[0] +documenttype[0].datatype[2].id 2002 +documenttype[0].datatype[2].type WSET +documenttype[0].datatype[2].array.element.id 0 +documenttype[0].datatype[2].map.key.id 0 +documenttype[0].datatype[2].map.value.id 0 +documenttype[0].datatype[2].wset.key.id 2 +documenttype[0].datatype[2].wset.createifnonexistent false +documenttype[0].datatype[2].wset.removeifzero false +documenttype[0].datatype[2].annotationref.annotation.id 0 +documenttype[0].datatype[2].sstruct.name "" +documenttype[0].datatype[2].sstruct.version 0 +documenttype[0].datatype[2].sstruct.compression.type NONE +documenttype[0].datatype[2].sstruct.compression.level 0 +documenttype[0].datatype[2].sstruct.compression.threshold 95 +documenttype[0].datatype[2].sstruct.compression.minsize 200 +documenttype[0].datatype[2].sstruct.field[0] +documenttype[0].datatype[3].id 5000 +documenttype[0].datatype[3].type STRUCT +documenttype[0].datatype[3].array.element.id 0 +documenttype[0].datatype[3].map.key.id 0 +documenttype[0].datatype[3].map.value.id 0 +documenttype[0].datatype[3].wset.key.id 0 +documenttype[0].datatype[3].wset.createifnonexistent false +documenttype[0].datatype[3].wset.removeifzero false +documenttype[0].datatype[3].annotationref.annotation.id 0 +documenttype[0].datatype[3].sstruct.name "testdoc.header" +documenttype[0].datatype[3].sstruct.version 0 +documenttype[0].datatype[3].sstruct.compression.type NONE +documenttype[0].datatype[3].sstruct.compression.level 0 +documenttype[0].datatype[3].sstruct.compression.threshold 90 +documenttype[0].datatype[3].sstruct.compression.minsize 0 +documenttype[0].datatype[3].sstruct.field[3] +documenttype[0].datatype[3].sstruct.field[0].name "doubleattr" +documenttype[0].datatype[3].sstruct.field[0].id 149562827 +documenttype[0].datatype[3].sstruct.field[0].id_v6 630428365 +documenttype[0].datatype[3].sstruct.field[0].datatype 5 +documenttype[0].datatype[3].sstruct.field[1].name "floatattr" +documenttype[0].datatype[3].sstruct.field[1].id 1633158509 +documenttype[0].datatype[3].sstruct.field[1].id_v6 1853512789 +documenttype[0].datatype[3].sstruct.field[1].datatype 1 +documenttype[0].datatype[3].sstruct.field[2].name "intattr" +documenttype[0].datatype[3].sstruct.field[2].id 2079212392 +documenttype[0].datatype[3].sstruct.field[2].id_v6 1840069917 +documenttype[0].datatype[3].sstruct.field[2].datatype 0 +documenttype[0].datatype[4].id 5001 +documenttype[0].datatype[4].type STRUCT +documenttype[0].datatype[4].array.element.id 0 +documenttype[0].datatype[4].map.key.id 0 +documenttype[0].datatype[4].map.value.id 0 +documenttype[0].datatype[4].wset.key.id 0 +documenttype[0].datatype[4].wset.createifnonexistent false +documenttype[0].datatype[4].wset.removeifzero false +documenttype[0].datatype[4].annotationref.annotation.id 0 +documenttype[0].datatype[4].sstruct.name "testdoc.body" +documenttype[0].datatype[4].sstruct.version 0 +documenttype[0].datatype[4].sstruct.compression.type NONE +documenttype[0].datatype[4].sstruct.compression.level 0 +documenttype[0].datatype[4].sstruct.compression.threshold 90 +documenttype[0].datatype[4].sstruct.compression.minsize 0 +documenttype[0].datatype[4].sstruct.field[12] +documenttype[0].datatype[4].sstruct.field[0].name "arrayattr" +documenttype[0].datatype[4].sstruct.field[0].id 505184256 +documenttype[0].datatype[4].sstruct.field[0].id_v6 1771774378 +documenttype[0].datatype[4].sstruct.field[0].datatype 1000 +documenttype[0].datatype[4].sstruct.field[1].name "byteattr" +documenttype[0].datatype[4].sstruct.field[1].id 2134674304 +documenttype[0].datatype[4].sstruct.field[1].id_v6 973038129 +documenttype[0].datatype[4].sstruct.field[1].datatype 16 +documenttype[0].datatype[4].sstruct.field[2].name "docfield" +documenttype[0].datatype[4].sstruct.field[2].id 728739268 +documenttype[0].datatype[4].sstruct.field[2].id_v6 1725514984 +documenttype[0].datatype[4].sstruct.field[2].datatype 8 +documenttype[0].datatype[4].sstruct.field[3].name "longattr" +documenttype[0].datatype[4].sstruct.field[3].id 790774460 +documenttype[0].datatype[4].sstruct.field[3].id_v6 192291556 +documenttype[0].datatype[4].sstruct.field[3].datatype 4 +documenttype[0].datatype[4].sstruct.field[4].name "minattr" +documenttype[0].datatype[4].sstruct.field[4].id 1829587385 +documenttype[0].datatype[4].sstruct.field[4].id_v6 741727195 +documenttype[0].datatype[4].sstruct.field[4].datatype 0 +documenttype[0].datatype[4].sstruct.field[5].name "minattr2" +documenttype[0].datatype[4].sstruct.field[5].id 128399609 +documenttype[0].datatype[4].sstruct.field[5].id_v6 920037076 +documenttype[0].datatype[4].sstruct.field[5].datatype 0 +documenttype[0].datatype[4].sstruct.field[6].name "rawarrayattr" +documenttype[0].datatype[4].sstruct.field[6].id 730665199 +documenttype[0].datatype[4].sstruct.field[6].id_v6 1023469062 +documenttype[0].datatype[4].sstruct.field[6].datatype 1003 +documenttype[0].datatype[4].sstruct.field[7].name "rawattr" +documenttype[0].datatype[4].sstruct.field[7].id 141092158 +documenttype[0].datatype[4].sstruct.field[7].id_v6 1247497470 +documenttype[0].datatype[4].sstruct.field[7].datatype 3 +documenttype[0].datatype[4].sstruct.field[8].name "stringattr" +documenttype[0].datatype[4].sstruct.field[8].id 1869816099 +documenttype[0].datatype[4].sstruct.field[8].id_v6 1592475234 +documenttype[0].datatype[4].sstruct.field[8].datatype 2 +documenttype[0].datatype[4].sstruct.field[9].name "stringattr2" +documenttype[0].datatype[4].sstruct.field[9].id 1054720742 +documenttype[0].datatype[4].sstruct.field[9].id_v6 1869816099 +documenttype[0].datatype[4].sstruct.field[9].datatype 2 +documenttype[0].datatype[4].sstruct.field[10].name "stringweightedsetattr" +documenttype[0].datatype[4].sstruct.field[10].id 1791723468 +documenttype[0].datatype[4].sstruct.field[10].id_v6 676317895 +documenttype[0].datatype[4].sstruct.field[10].datatype 2002 +documenttype[0].datatype[4].sstruct.field[11].name "uri" +documenttype[0].datatype[4].sstruct.field[11].id 31928604 +documenttype[0].datatype[4].sstruct.field[11].id_v6 1003424810 +documenttype[0].datatype[4].sstruct.field[11].datatype 2 +documenttype[0].annotationtype[0] diff --git a/document/src/tests/data/defaultdocument.cfg b/document/src/tests/data/defaultdocument.cfg new file mode 100644 index 00000000000..9780f43def6 --- /dev/null +++ b/document/src/tests/data/defaultdocument.cfg @@ -0,0 +1,94 @@ +enablecompression false +datatype[6] +datatype[0].id 1000 +datatype[0].arraytype[1] +datatype[0].arraytype[0].datatype 0 +datatype[0].weightedsettype[0] +datatype[0].structtype[0] +datatype[0].documenttype[0] +datatype[1].id 1003 +datatype[1].arraytype[1] +datatype[1].arraytype[0].datatype 3 +datatype[1].weightedsettype[0] +datatype[1].structtype[0] +datatype[1].documenttype[0] +datatype[2].id 2002 +datatype[2].arraytype[0] +datatype[2].weightedsettype[1] +datatype[2].weightedsettype[0].datatype 2 +datatype[2].weightedsettype[0].createifnonexistant false +datatype[2].weightedsettype[0].removeifzero false +datatype[2].structtype[0] +datatype[2].documenttype[0] +datatype[3].id 5000 +datatype[3].arraytype[0] +datatype[3].weightedsettype[0] +datatype[3].structtype[1] +datatype[3].structtype[0].name testdoc.header +datatype[3].structtype[0].version 0 +datatype[3].structtype[0].field[3] +datatype[3].structtype[0].field[0].name intattr +datatype[3].structtype[0].field[0].id[0] +datatype[3].structtype[0].field[0].datatype 0 +datatype[3].structtype[0].field[1].name doubleattr +datatype[3].structtype[0].field[1].id[0] +datatype[3].structtype[0].field[1].datatype 5 +datatype[3].structtype[0].field[2].name floatattr +datatype[3].structtype[0].field[2].id[0] +datatype[3].structtype[0].field[2].datatype 1 +datatype[3].documenttype[0] +datatype[4].id 5001 +datatype[4].arraytype[0] +datatype[4].weightedsettype[0] +datatype[4].structtype[1] +datatype[4].structtype[0].name testdoc.body +datatype[4].structtype[0].version 0 +datatype[4].structtype[0].field[11] +datatype[4].structtype[0].field[0].name stringattr +datatype[4].structtype[0].field[0].id[0] +datatype[4].structtype[0].field[0].datatype 2 +datatype[4].structtype[0].field[1].name stringattr2 +datatype[4].structtype[0].field[1].id[0] +datatype[4].structtype[0].field[1].datatype 2 +datatype[4].structtype[0].field[2].name longattr +datatype[4].structtype[0].field[2].id[0] +datatype[4].structtype[0].field[2].datatype 4 +datatype[4].structtype[0].field[3].name byteattr +datatype[4].structtype[0].field[3].id[0] +datatype[4].structtype[0].field[3].datatype 16 +datatype[4].structtype[0].field[4].name rawattr +datatype[4].structtype[0].field[4].id[0] +datatype[4].structtype[0].field[4].datatype 3 +datatype[4].structtype[0].field[5].name minattr +datatype[4].structtype[0].field[5].id[0] +datatype[4].structtype[0].field[5].datatype 0 +datatype[4].structtype[0].field[6].name minattr2 +datatype[4].structtype[0].field[6].id[0] +datatype[4].structtype[0].field[6].datatype 0 +datatype[4].structtype[0].field[7].name arrayattr +datatype[4].structtype[0].field[7].id[0] +datatype[4].structtype[0].field[7].datatype 1000 +datatype[4].structtype[0].field[8].name rawarrayattr +datatype[4].structtype[0].field[8].id[0] +datatype[4].structtype[0].field[8].datatype 1003 +datatype[4].structtype[0].field[9].name stringweightedsetattr +datatype[4].structtype[0].field[9].id[0] +datatype[4].structtype[0].field[9].datatype 2002 +datatype[4].structtype[0].field[10].name uri +datatype[4].structtype[0].field[10].id[0] +datatype[4].structtype[0].field[10].datatype 2 +datatype[4].structtype[0].field[11].name docfield +datatype[4].structtype[0].field[11].id[0] +datatype[4].structtype[0].field[11].datatype 8 +datatype[4].documenttype[0] +datatype[5].id 5002 +datatype[5].arraytype[0] +datatype[5].weightedsettype[0] +datatype[5].structtype[0] +datatype[5].documenttype[1] +datatype[5].documenttype[0].name testdoc +datatype[5].documenttype[0].version 0 +datatype[5].documenttype[0].inherits[0] +datatype[5].documenttype[0].headerstruct 5000 +datatype[5].documenttype[0].bodystruct 5001 + diff --git a/document/src/tests/data/docmancfg.txt b/document/src/tests/data/docmancfg.txt new file mode 100644 index 00000000000..9507211edf3 --- /dev/null +++ b/document/src/tests/data/docmancfg.txt @@ -0,0 +1,170 @@ +enablecompression false +datatype[16] +datatype[0].id 101 +datatype[0].arraytype[1] +datatype[0].arraytype[0].datatype 1 +datatype[0].weightedsettype[0] +datatype[0].structtype[0] +datatype[0].documenttype[0] +datatype[1].id 102 +datatype[1].arraytype[1] +datatype[1].arraytype[0].datatype 2 +datatype[1].weightedsettype[0] +datatype[1].structtype[0] +datatype[1].documenttype[0] +datatype[2].id 103 +datatype[2].arraytype[0] +datatype[2].weightedsettype[0] +datatype[2].structtype[1] +datatype[2].structtype[0].name teststruct +datatype[2].structtype[0].version 0 +datatype[2].structtype[0].field[2] +datatype[2].structtype[0].field[0].name Foo +datatype[2].structtype[0].field[0].id[0] +datatype[2].structtype[0].field[0].datatype 2 +datatype[2].structtype[0].field[1].name Bar +datatype[2].structtype[0].field[1].id[0] +datatype[2].structtype[0].field[1].datatype 2 +datatype[2].documenttype[0] +datatype[3].id 104 +datatype[3].arraytype[0] +datatype[3].weightedsettype[0] +datatype[3].structtype[1] +datatype[3].structtype[0].name teststruct +datatype[3].structtype[0].version 1 +datatype[3].structtype[0].field[2] +datatype[3].structtype[0].field[0].name Foooo +datatype[3].structtype[0].field[0].id[0] +datatype[3].structtype[0].field[0].datatype 2 +datatype[3].structtype[0].field[1].name Bar +datatype[3].structtype[0].field[1].id[0] +datatype[3].structtype[0].field[1].datatype 2 +datatype[3].documenttype[0] +datatype[4].id 1000 +datatype[4].arraytype[0] +datatype[4].weightedsettype[0] +datatype[4].structtype[1] +datatype[4].structtype[0].name crawler_type.header +datatype[4].structtype[0].version 0 +datatype[4].structtype[0].field[5] +datatype[4].structtype[0].field[0].name URI +datatype[4].structtype[0].field[0].id[1] +datatype[4].structtype[0].field[0].id[0].id 0 +datatype[4].structtype[0].field[0].datatype 2 +datatype[4].structtype[0].field[1].name CONTEXT +datatype[4].structtype[0].field[1].id[1] +datatype[4].structtype[0].field[1].id[0].id 1 +datatype[4].structtype[0].field[1].datatype 2 +datatype[4].structtype[0].field[2].name CONTENT +datatype[4].structtype[0].field[2].id[1] +datatype[4].structtype[0].field[2].id[0].id 2 +datatype[4].structtype[0].field[2].datatype 3 +datatype[4].structtype[0].field[3].name CONTENT_TYPE +datatype[4].structtype[0].field[3].id[1] +datatype[4].structtype[0].field[3].id[0].id 3 +datatype[4].structtype[0].field[3].datatype 2 +datatype[4].structtype[0].field[4].name LAST_MODIFIED +datatype[4].structtype[0].field[4].id[1] +datatype[4].structtype[0].field[4].id[0].id 4 +datatype[4].structtype[0].field[4].datatype 0 +datatype[4].documenttype[0] +datatype[5].id 1001 +datatype[5].arraytype[0] +datatype[5].weightedsettype[0] +datatype[5].structtype[1] +datatype[5].structtype[0].name crawler_type.body +datatype[5].structtype[0].version 0 +datatype[5].structtype[0].field[0] +datatype[5].documenttype[0] +datatype[6].id 1002 +datatype[6].arraytype[0] +datatype[6].weightedsettype[0] +datatype[6].structtype[0] +datatype[6].documenttype[1] +datatype[6].documenttype[0].name crawler_type +datatype[6].documenttype[0].version 0 +datatype[6].documenttype[0].inherits[0] +datatype[6].documenttype[0].headerstruct 1000 +datatype[6].documenttype[0].bodystruct 1001 +datatype[7].id 2000 +datatype[7].arraytype[0] +datatype[7].weightedsettype[0] +datatype[7].structtype[1] +datatype[7].structtype[0].name multimedia_type.header +datatype[7].structtype[0].version 0 +datatype[7].structtype[0].field[7] +datatype[7].structtype[0].field[0].name URL_KEYWORDS +datatype[7].structtype[0].field[0].id[1] +datatype[7].structtype[0].field[0].id[0].id 147 +datatype[7].structtype[0].field[0].datatype 102 +datatype[7].structtype[0].field[1].name MULTIMEDIA_LINKTYPES +datatype[7].structtype[0].field[1].id[1] +datatype[7].structtype[0].field[1].id[0].id 148 +datatype[7].structtype[0].field[1].datatype 102 +datatype[7].structtype[0].field[2].name THUMBNAIL_URLS +datatype[7].structtype[0].field[2].id[1] +datatype[7].structtype[0].field[2].id[0].id 149 +datatype[7].structtype[0].field[2].datatype 102 +datatype[7].structtype[0].field[3].name FROM_LINK_LIST +datatype[7].structtype[0].field[3].id[1] +datatype[7].structtype[0].field[3].id[0].id 150 +datatype[7].structtype[0].field[3].datatype 101 +datatype[7].structtype[0].field[4].name FROM_LINK_LANGUAGE_LIST +datatype[7].structtype[0].field[4].id[1] +datatype[7].structtype[0].field[4].id[0].id 151 +datatype[7].structtype[0].field[4].datatype 102 +datatype[7].structtype[0].field[5].name TESTSTRUCT1 +datatype[7].structtype[0].field[5].id[1] +datatype[7].structtype[0].field[5].id[0].id 153 +datatype[7].structtype[0].field[5].datatype 103 +datatype[7].structtype[0].field[6].name TESTSTRUCT2 +datatype[7].structtype[0].field[6].id[1] +datatype[7].structtype[0].field[6].id[0].id 154 +datatype[7].structtype[0].field[6].datatype 104 +datatype[7].documenttype[0] +datatype[8].id 2001 +datatype[8].structtype[1] +datatype[8].structtype[0].name multimedia_type.body +datatype[8].structtype[0].version 0 +datatype[8].structtype[0].field[0] +datatype[9].id 2002 +datatype[9].documenttype[1] +datatype[9].documenttype[0].name multimedia_type +datatype[9].documenttype[0].version 0 +datatype[9].documenttype[0].inherits[1] +datatype[9].documenttype[0].inherits[0].name crawler_type +datatype[9].documenttype[0].inherits[0].version 0 +datatype[9].documenttype[0].headerstruct 2000 +datatype[9].documenttype[0].bodystruct 2001 +datatype[10].id 2003 +datatype[10].maptype[1] +datatype[10].maptype[0].keytype 2 +datatype[10].maptype[0].valtype 2 +datatype[11].id 2004 +datatype[11].maptype[1] +datatype[11].maptype[0].keytype 0 +datatype[11].maptype[0].valtype 0 +datatype[12].id 2005 +datatype[12].maptype[1] +datatype[12].maptype[0].keytype 2003 +datatype[12].maptype[0].valtype 2004 +datatype[13].id 2006 +datatype[13].structtype[1] +datatype[13].structtype[0].name map.header +datatype[13].structtype[0].version 0 +datatype[13].structtype[0].field[1] +datatype[13].structtype[0].field[0].name mymap +datatype[13].structtype[0].field[0].id[1] +datatype[13].structtype[0].field[0].id[0].id 155 +datatype[13].structtype[0].field[0].datatype 2005 +datatype[14].id 2007 +datatype[14].structtype[1] +datatype[14].structtype[0].name map.body +datatype[14].structtype[0].version 0 +datatype[14].structtype[0].field[0] +datatype[15].id 2008 +datatype[15].documenttype[1] +datatype[15].documenttype[0].name mapdoc +datatype[15].documenttype[0].version 0 +datatype[15].documenttype[0].headerstruct 2006 +datatype[15].documenttype[0].bodystruct 2007 diff --git a/document/src/tests/data/docselectcfg1.txt b/document/src/tests/data/docselectcfg1.txt new file mode 100644 index 00000000000..aeb20ea42ea --- /dev/null +++ b/document/src/tests/data/docselectcfg1.txt @@ -0,0 +1,16 @@ +documenttype testdoc +documenttypeversion -1 +documentselect[3] +documentselect[0].type and +documentselect[0].subcount 2 +documentselect[0].field "" +documentselect[0].value "" +documentselect[1].type equals +documentselect[1].subcount 0 +documentselect[1].field "stringattr" +documentselect[1].value "hei" +documentselect[2].type equals +documentselect[2].subcount 0 +documentselect[2].field "intattr" +documentselect[2].value "100" + diff --git a/document/src/tests/data/docselectcfg2.txt b/document/src/tests/data/docselectcfg2.txt new file mode 100644 index 00000000000..b96e12331f1 --- /dev/null +++ b/document/src/tests/data/docselectcfg2.txt @@ -0,0 +1,35 @@ +documenttype testdoc +documenttypeversion 0 +documentselect[8] +documentselect[0].type or +documentselect[0].subcount 2 +documentselect[0].field "" +documentselect[0].value "" +documentselect[1].type and +documentselect[1].subcount 2 +documentselect[1].field "" +documentselect[1].value "" +documentselect[2].type not +documentselect[2].subcount 1 +documentselect[2].field "" +documentselect[2].value "" +documentselect[3].type equals +documentselect[3].subcount 0 +documentselect[3].field "stringattr" +documentselect[3].value "hei" +documentselect[4].type equals +documentselect[4].subcount 0 +documentselect[4].field "intattr" +documentselect[4].value "100" +documentselect[5].type and +documentselect[5].subcount 2 +documentselect[5].field "" +documentselect[5].value "" +documentselect[6].type equals +documentselect[6].subcount 0 +documentselect[6].field "URI" +documentselect[6].value "http://www.ntnu.no/" +documentselect[7].type lt +documentselect[7].subcount 0 +documentselect[7].field "floatattr" +documentselect[7].value "100.5" diff --git a/document/src/tests/data/doctypesconfigtest.cfg b/document/src/tests/data/doctypesconfigtest.cfg new file mode 100644 index 00000000000..36666bc7f3e --- /dev/null +++ b/document/src/tests/data/doctypesconfigtest.cfg @@ -0,0 +1,184 @@ +enablecompression false +documenttype[3] +documenttype[0].id -332662462 +documenttype[0].name "base1" +documenttype[0].version 0 +documenttype[0].headerstruct 762677181 +documenttype[0].bodystruct -1448787694 +documenttype[0].inherits[0] +documenttype[0].datatype[3] +documenttype[0].datatype[0].id 1000 +documenttype[0].datatype[0].type ARRAY +documenttype[0].datatype[0].array.element.id 0 +documenttype[0].datatype[0].map.key.id 0 +documenttype[0].datatype[0].map.value.id 0 +documenttype[0].datatype[0].wset.key.id 0 +documenttype[0].datatype[0].wset.createifnonexistent false +documenttype[0].datatype[0].wset.removeifzero false +documenttype[0].datatype[0].annotationref.annotation.id 0 +documenttype[0].datatype[0].sstruct.name "" +documenttype[0].datatype[0].sstruct.version 0 +documenttype[0].datatype[0].sstruct.compression.type NONE +documenttype[0].datatype[0].sstruct.compression.level 0 +documenttype[0].datatype[0].sstruct.compression.threshold 95 +documenttype[0].datatype[0].sstruct.compression.minsize 200 +documenttype[0].datatype[0].sstruct.field[0] +documenttype[0].datatype[1].id 762677181 +documenttype[0].datatype[1].type STRUCT +documenttype[0].datatype[1].array.element.id 0 +documenttype[0].datatype[1].map.key.id 0 +documenttype[0].datatype[1].map.value.id 0 +documenttype[0].datatype[1].wset.key.id 0 +documenttype[0].datatype[1].wset.createifnonexistent false +documenttype[0].datatype[1].wset.removeifzero false +documenttype[0].datatype[1].annotationref.annotation.id 0 +documenttype[0].datatype[1].sstruct.name "base1.header" +documenttype[0].datatype[1].sstruct.version 0 +documenttype[0].datatype[1].sstruct.compression.type NONE +documenttype[0].datatype[1].sstruct.compression.level 0 +documenttype[0].datatype[1].sstruct.compression.threshold 90 +documenttype[0].datatype[1].sstruct.compression.minsize 0 +documenttype[0].datatype[1].sstruct.field[3] +documenttype[0].datatype[1].sstruct.field[0].name "field1" +documenttype[0].datatype[1].sstruct.field[0].id 36262354 +documenttype[0].datatype[1].sstruct.field[0].id_v6 1141913055 +documenttype[0].datatype[1].sstruct.field[0].datatype 0 +documenttype[0].datatype[1].sstruct.field[1].name "field2" +documenttype[0].datatype[1].sstruct.field[1].id 1388988771 +documenttype[0].datatype[1].sstruct.field[1].id_v6 1215205431 +documenttype[0].datatype[1].sstruct.field[1].datatype 0 +documenttype[0].datatype[1].sstruct.field[2].name "fieldarray1" +documenttype[0].datatype[1].sstruct.field[2].id 1030920758 +documenttype[0].datatype[1].sstruct.field[2].id_v6 1073985444 +documenttype[0].datatype[1].sstruct.field[2].datatype 1000 +documenttype[0].datatype[2].id -1448787694 +documenttype[0].datatype[2].type STRUCT +documenttype[0].datatype[2].array.element.id 0 +documenttype[0].datatype[2].map.key.id 0 +documenttype[0].datatype[2].map.value.id 0 +documenttype[0].datatype[2].wset.key.id 0 +documenttype[0].datatype[2].wset.createifnonexistent false +documenttype[0].datatype[2].wset.removeifzero false +documenttype[0].datatype[2].annotationref.annotation.id 0 +documenttype[0].datatype[2].sstruct.name "base1.body" +documenttype[0].datatype[2].sstruct.version 0 +documenttype[0].datatype[2].sstruct.compression.type NONE +documenttype[0].datatype[2].sstruct.compression.level 0 +documenttype[0].datatype[2].sstruct.compression.threshold 90 +documenttype[0].datatype[2].sstruct.compression.minsize 0 +documenttype[0].datatype[2].sstruct.field[0] +documenttype[0].annotationtype[0] +documenttype[1].id -332661500 +documenttype[1].name "base2" +documenttype[1].version 1 +documenttype[1].headerstruct 566163677 +documenttype[1].bodystruct 294022642 +documenttype[1].inherits[0] +documenttype[1].datatype[2] +documenttype[1].datatype[0].id 566163677 +documenttype[1].datatype[0].type STRUCT +documenttype[1].datatype[0].array.element.id 0 +documenttype[1].datatype[0].map.key.id 0 +documenttype[1].datatype[0].map.value.id 0 +documenttype[1].datatype[0].wset.key.id 0 +documenttype[1].datatype[0].wset.createifnonexistent false +documenttype[1].datatype[0].wset.removeifzero false +documenttype[1].datatype[0].annotationref.annotation.id 0 +documenttype[1].datatype[0].sstruct.name "base2.header" +documenttype[1].datatype[0].sstruct.version 1 +documenttype[1].datatype[0].sstruct.compression.type NONE +documenttype[1].datatype[0].sstruct.compression.level 0 +documenttype[1].datatype[0].sstruct.compression.threshold 90 +documenttype[1].datatype[0].sstruct.compression.minsize 0 +documenttype[1].datatype[0].sstruct.field[1] +documenttype[1].datatype[0].sstruct.field[0].name "field4" +documenttype[1].datatype[0].sstruct.field[0].id 1556675886 +documenttype[1].datatype[0].sstruct.field[0].id_v6 1677332587 +documenttype[1].datatype[0].sstruct.field[0].datatype 2 +documenttype[1].datatype[1].id 294022642 +documenttype[1].datatype[1].type STRUCT +documenttype[1].datatype[1].array.element.id 0 +documenttype[1].datatype[1].map.key.id 0 +documenttype[1].datatype[1].map.value.id 0 +documenttype[1].datatype[1].wset.key.id 0 +documenttype[1].datatype[1].wset.createifnonexistent false +documenttype[1].datatype[1].wset.removeifzero false +documenttype[1].datatype[1].annotationref.annotation.id 0 +documenttype[1].datatype[1].sstruct.name "base2.body" +documenttype[1].datatype[1].sstruct.version 1 +documenttype[1].datatype[1].sstruct.compression.type NONE +documenttype[1].datatype[1].sstruct.compression.level 0 +documenttype[1].datatype[1].sstruct.compression.threshold 90 +documenttype[1].datatype[1].sstruct.compression.minsize 0 +documenttype[1].datatype[1].sstruct.field[1] +documenttype[1].datatype[1].sstruct.field[0].name "field3" +documenttype[1].datatype[1].sstruct.field[0].id 722536103 +documenttype[1].datatype[1].sstruct.field[0].id_v6 1036475953 +documenttype[1].datatype[1].sstruct.field[0].datatype 2 +documenttype[1].annotationtype[0] +documenttype[2].id 787712161 +documenttype[2].name "derived" +documenttype[2].version 2 +documenttype[2].headerstruct 296510914 +documenttype[2].bodystruct -1359887401 +documenttype[2].inherits[2] +documenttype[2].inherits[0].id -332662462 +documenttype[2].inherits[1].id -332661500 +documenttype[2].datatype[2] +documenttype[2].datatype[0].id 296510914 +documenttype[2].datatype[0].type STRUCT +documenttype[2].datatype[0].array.element.id 0 +documenttype[2].datatype[0].map.key.id 0 +documenttype[2].datatype[0].map.value.id 0 +documenttype[2].datatype[0].wset.key.id 0 +documenttype[2].datatype[0].wset.createifnonexistent false +documenttype[2].datatype[0].wset.removeifzero false +documenttype[2].datatype[0].annotationref.annotation.id 0 +documenttype[2].datatype[0].sstruct.name "derived.header" +documenttype[2].datatype[0].sstruct.version 2 +documenttype[2].datatype[0].sstruct.compression.type NONE +documenttype[2].datatype[0].sstruct.compression.level 0 +documenttype[2].datatype[0].sstruct.compression.threshold 90 +documenttype[2].datatype[0].sstruct.compression.minsize 0 +documenttype[2].datatype[0].sstruct.field[4] +documenttype[2].datatype[0].sstruct.field[0].name "field1" +documenttype[2].datatype[0].sstruct.field[0].id 36262354 +documenttype[2].datatype[0].sstruct.field[0].id_v6 1141913055 +documenttype[2].datatype[0].sstruct.field[0].datatype 0 +documenttype[2].datatype[0].sstruct.field[1].name "field2" +documenttype[2].datatype[0].sstruct.field[1].id 1388988771 +documenttype[2].datatype[0].sstruct.field[1].id_v6 1215205431 +documenttype[2].datatype[0].sstruct.field[1].datatype 0 +documenttype[2].datatype[0].sstruct.field[2].name "field4" +documenttype[2].datatype[0].sstruct.field[2].id 1556675886 +documenttype[2].datatype[0].sstruct.field[2].id_v6 1677332587 +documenttype[2].datatype[0].sstruct.field[2].datatype 2 +documenttype[2].datatype[0].sstruct.field[3].name "fieldarray1" +documenttype[2].datatype[0].sstruct.field[3].id 1030920758 +documenttype[2].datatype[0].sstruct.field[3].id_v6 1073985444 +documenttype[2].datatype[0].sstruct.field[3].datatype 1000 +documenttype[2].datatype[1].id -1359887401 +documenttype[2].datatype[1].type STRUCT +documenttype[2].datatype[1].array.element.id 0 +documenttype[2].datatype[1].map.key.id 0 +documenttype[2].datatype[1].map.value.id 0 +documenttype[2].datatype[1].wset.key.id 0 +documenttype[2].datatype[1].wset.createifnonexistent false +documenttype[2].datatype[1].wset.removeifzero false +documenttype[2].datatype[1].annotationref.annotation.id 0 +documenttype[2].datatype[1].sstruct.name "derived.body" +documenttype[2].datatype[1].sstruct.version 2 +documenttype[2].datatype[1].sstruct.compression.type NONE +documenttype[2].datatype[1].sstruct.compression.level 0 +documenttype[2].datatype[1].sstruct.compression.threshold 90 +documenttype[2].datatype[1].sstruct.compression.minsize 0 +documenttype[2].datatype[1].sstruct.field[2] +documenttype[2].datatype[1].sstruct.field[0].name "field3" +documenttype[2].datatype[1].sstruct.field[0].id 722536103 +documenttype[2].datatype[1].sstruct.field[0].id_v6 1036475953 +documenttype[2].datatype[1].sstruct.field[0].datatype 2 +documenttype[2].datatype[1].sstruct.field[1].name "field5" +documenttype[2].datatype[1].sstruct.field[1].id 1352306621 +documenttype[2].datatype[1].sstruct.field[1].id_v6 1511574032 +documenttype[2].datatype[1].sstruct.field[1].datatype 1 +documenttype[2].annotationtype[0] diff --git a/document/src/tests/data/document-cpp-currentversion-lz4-9.dat b/document/src/tests/data/document-cpp-currentversion-lz4-9.dat Binary files differnew file mode 100644 index 00000000000..5f31ac0ab78 --- /dev/null +++ b/document/src/tests/data/document-cpp-currentversion-lz4-9.dat diff --git a/document/src/tests/data/document-cpp-currentversion-uncompressed.dat b/document/src/tests/data/document-cpp-currentversion-uncompressed.dat Binary files differnew file mode 100644 index 00000000000..6a5c5e90b84 --- /dev/null +++ b/document/src/tests/data/document-cpp-currentversion-uncompressed.dat diff --git a/document/src/tests/data/document-cpp-v7-uncompressed.dat b/document/src/tests/data/document-cpp-v7-uncompressed.dat Binary files differnew file mode 100644 index 00000000000..5f073e33438 --- /dev/null +++ b/document/src/tests/data/document-cpp-v7-uncompressed.dat diff --git a/document/src/tests/data/document-cpp-v8-uncompressed.dat b/document/src/tests/data/document-cpp-v8-uncompressed.dat Binary files differnew file mode 100644 index 00000000000..429334fc102 --- /dev/null +++ b/document/src/tests/data/document-cpp-v8-uncompressed.dat diff --git a/document/src/tests/data/embeddeddocument.cfg b/document/src/tests/data/embeddeddocument.cfg new file mode 100644 index 00000000000..baefc0d7338 --- /dev/null +++ b/document/src/tests/data/embeddeddocument.cfg @@ -0,0 +1,102 @@ +enablecompression false +datatype[8] +datatype[0].id 1001 +datatype[0].arraytype[1] +datatype[0].arraytype[0].datatype 1 +datatype[0].weightedsettype[0] +datatype[0].structtype[0] +datatype[0].documenttype[0] +datatype[1].id 2001 +datatype[1].arraytype[1] +datatype[1].arraytype[0].datatype 1001 +datatype[1].weightedsettype[0] +datatype[1].structtype[0] +datatype[1].documenttype[0] +datatype[2].id -1686125086 +datatype[2].arraytype[0] +datatype[2].weightedsettype[0] +datatype[2].structtype[1] +datatype[2].structtype[0].name docindoc.header +datatype[2].structtype[0].version 0 +datatype[2].structtype[0].field[0] +datatype[2].documenttype[0] +datatype[3].id 2030224503 +datatype[3].arraytype[0] +datatype[3].weightedsettype[0] +datatype[3].structtype[1] +datatype[3].structtype[0].name docindoc.body +datatype[3].structtype[0].version 0 +datatype[3].structtype[0].field[1] +datatype[3].structtype[0].field[0].name stringindoc +datatype[3].structtype[0].field[0].id[0] +datatype[3].structtype[0].field[0].datatype 2 +datatype[3].documenttype[0] +datatype[4].id 1447635645 +datatype[4].arraytype[0] +datatype[4].weightedsettype[0] +datatype[4].structtype[0] +datatype[4].documenttype[1] +datatype[4].documenttype[0].name docindoc +datatype[4].documenttype[0].version 0 +datatype[4].documenttype[0].inherits[0] +datatype[4].documenttype[0].headerstruct -1686125086 +datatype[4].documenttype[0].bodystruct 2030224503 +datatype[5].id -260050933 +datatype[5].arraytype[0] +datatype[5].weightedsettype[0] +datatype[5].structtype[1] +datatype[5].structtype[0].name serializetest.header +datatype[5].structtype[0].version 0 +datatype[5].structtype[0].field[4] +datatype[5].structtype[0].field[0].name floatfield +datatype[5].structtype[0].field[0].id[0] +datatype[5].structtype[0].field[0].datatype 1 +datatype[5].structtype[0].field[1].name stringfield +datatype[5].structtype[0].field[1].id[0] +datatype[5].structtype[0].field[1].datatype 2 +datatype[5].structtype[0].field[2].name longfield +datatype[5].structtype[0].field[2].id[0] +datatype[5].structtype[0].field[2].datatype 4 +datatype[5].structtype[0].field[3].name urifield +datatype[5].structtype[0].field[3].id[0] +datatype[5].structtype[0].field[3].datatype 10 +datatype[5].documenttype[0] +datatype[6].id 1026122976 +datatype[6].arraytype[0] +datatype[6].weightedsettype[0] +datatype[6].structtype[1] +datatype[6].structtype[0].name serializetest.body +datatype[6].structtype[0].version 0 +datatype[6].structtype[0].field[7] +datatype[6].structtype[0].field[0].name intfield +datatype[6].structtype[0].field[0].id[0] +datatype[6].structtype[0].field[0].datatype 0 +datatype[6].structtype[0].field[1].name rawfield +datatype[6].structtype[0].field[1].id[0] +datatype[6].structtype[0].field[1].datatype 3 +datatype[6].structtype[0].field[2].name doublefield +datatype[6].structtype[0].field[2].id[0] +datatype[6].structtype[0].field[2].datatype 5 +datatype[6].structtype[0].field[3].name bytefield +datatype[6].structtype[0].field[3].id[0] +datatype[6].structtype[0].field[3].datatype 16 +datatype[6].structtype[0].field[4].name arrayoffloatfield +datatype[6].structtype[0].field[4].id[0] +datatype[6].structtype[0].field[4].datatype 1001 +datatype[6].structtype[0].field[5].name arrayofarrayoffloatfield +datatype[6].structtype[0].field[5].id[0] +datatype[6].structtype[0].field[5].datatype 2001 +datatype[6].structtype[0].field[6].name docfield +datatype[6].structtype[0].field[6].id[0] +datatype[6].structtype[0].field[6].datatype 8 +datatype[6].documenttype[0] +datatype[7].id 1306012852 +datatype[7].arraytype[0] +datatype[7].weightedsettype[0] +datatype[7].structtype[0] +datatype[7].documenttype[1] +datatype[7].documenttype[0].name serializetest +datatype[7].documenttype[0].version 0 +datatype[7].documenttype[0].inherits[0] +datatype[7].documenttype[0].headerstruct -260050933 +datatype[7].documenttype[0].bodystruct 1026122976 diff --git a/document/src/tests/data/inheritancetest.cfg b/document/src/tests/data/inheritancetest.cfg new file mode 100644 index 00000000000..1b2a704de92 --- /dev/null +++ b/document/src/tests/data/inheritancetest.cfg @@ -0,0 +1,354 @@ +enablecompression false +documenttype[5] +documenttype[0].bodystruct 380234823 +documenttype[0].headerstruct 1811691954 +documenttype[0].id -602119955 +documenttype[0].name "common" +documenttype[0].version 0 +documenttype[0].annotationtype[0] +documenttype[0].datatype[3] +documenttype[0].datatype[0].id 1811691954 +documenttype[0].datatype[0].type STRUCT +documenttype[0].datatype[0].sstruct.name "common.header" +documenttype[0].datatype[0].sstruct.version 0 +documenttype[0].datatype[0].sstruct.field[3] +documenttype[0].datatype[0].sstruct.field[0].datatype 0 +documenttype[0].datatype[0].sstruct.field[0].id 1501857786 +documenttype[0].datatype[0].sstruct.field[0].id_v6 1023904001 +documenttype[0].datatype[0].sstruct.field[0].name "popularity" +documenttype[0].datatype[0].sstruct.field[1].datatype 2 +documenttype[0].datatype[0].sstruct.field[1].id 567626448 +documenttype[0].datatype[0].sstruct.field[1].id_v6 29129762 +documenttype[0].datatype[0].sstruct.field[1].name "title" +documenttype[0].datatype[0].sstruct.field[2].datatype 0 +documenttype[0].datatype[0].sstruct.field[2].id 624630872 +documenttype[0].datatype[0].sstruct.field[2].id_v6 1508520108 +documenttype[0].datatype[0].sstruct.field[2].name "year" +documenttype[0].datatype[1].id 380234823 +documenttype[0].datatype[1].type STRUCT +documenttype[0].datatype[1].sstruct.name "common.body" +documenttype[0].datatype[1].sstruct.version 0 +documenttype[0].datatype[1].sstruct.field[1] +documenttype[0].datatype[1].sstruct.field[0].datatype 1 +documenttype[0].datatype[1].sstruct.field[0].id 1225171018 +documenttype[0].datatype[1].sstruct.field[0].id_v6 205748629 +documenttype[0].datatype[1].sstruct.field[0].name "price" +documenttype[0].datatype[2].id 874542262 +documenttype[0].datatype[2].type STRUCT +documenttype[0].datatype[2].sstruct.name "address" +documenttype[0].datatype[2].sstruct.version 0 +documenttype[0].datatype[2].sstruct.field[2] +documenttype[0].datatype[2].sstruct.field[0].datatype 2 +documenttype[0].datatype[2].sstruct.field[0].id 725636280 +documenttype[0].datatype[2].sstruct.field[0].id_v6 2127904 +documenttype[0].datatype[2].sstruct.field[0].name "city" +documenttype[0].datatype[2].sstruct.field[1].datatype 2 +documenttype[0].datatype[2].sstruct.field[1].id 1954764240 +documenttype[0].datatype[2].sstruct.field[1].id_v6 1527533866 +documenttype[0].datatype[2].sstruct.field[1].name "street" +documenttype[0].inherits[1] +documenttype[0].inherits[0].id 8 +documenttype[1].bodystruct 993120973 +documenttype[1].headerstruct -1910204744 +documenttype[1].id 1412693671 +documenttype[1].name "music" +documenttype[1].version 0 +documenttype[1].annotationtype[0] +documenttype[1].datatype[5] +documenttype[1].datatype[0].id 1328286588 +documenttype[1].datatype[0].type WSET +documenttype[1].datatype[0].wset.createifnonexistent false +documenttype[1].datatype[0].wset.removeifzero false +documenttype[1].datatype[0].wset.key.id 2 +documenttype[1].datatype[1].id 993120973 +documenttype[1].datatype[1].type STRUCT +documenttype[1].datatype[1].sstruct.name "music.body" +documenttype[1].datatype[1].sstruct.version 0 +documenttype[1].datatype[1].sstruct.field[1] +documenttype[1].datatype[1].sstruct.field[0].datatype 5 +documenttype[1].datatype[1].sstruct.field[0].id 552396752 +documenttype[1].datatype[1].sstruct.field[0].id_v6 713868363 +documenttype[1].datatype[1].sstruct.field[0].name "randomdouble" +documenttype[1].datatype[2].id 874542262 +documenttype[1].datatype[2].type STRUCT +documenttype[1].datatype[2].sstruct.name "address" +documenttype[1].datatype[2].sstruct.version 0 +documenttype[1].datatype[2].sstruct.field[2] +documenttype[1].datatype[2].sstruct.field[0].datatype 2 +documenttype[1].datatype[2].sstruct.field[0].id 725636280 +documenttype[1].datatype[2].sstruct.field[0].id_v6 2127904 +documenttype[1].datatype[2].sstruct.field[0].name "city" +documenttype[1].datatype[2].sstruct.field[1].datatype 2 +documenttype[1].datatype[2].sstruct.field[1].id 1954764240 +documenttype[1].datatype[2].sstruct.field[1].id_v6 1527533866 +documenttype[1].datatype[2].sstruct.field[1].name "street" +documenttype[1].datatype[3].id -1857943347 +documenttype[1].datatype[3].type ARRAY +documenttype[1].datatype[3].array.element.id 874542262 +documenttype[1].datatype[4].id -1910204744 +documenttype[1].datatype[4].type STRUCT +documenttype[1].datatype[4].sstruct.name "music.header" +documenttype[1].datatype[4].sstruct.version 0 +documenttype[1].datatype[4].sstruct.field[6] +documenttype[1].datatype[4].sstruct.field[0].datatype -1857943347 +documenttype[1].datatype[4].sstruct.field[0].id 785651450 +documenttype[1].datatype[4].sstruct.field[0].id_v6 1210851114 +documenttype[1].datatype[4].sstruct.field[0].name "addresses" +documenttype[1].datatype[4].sstruct.field[1].datatype 1328286588 +documenttype[1].datatype[4].sstruct.field[1].id 1692189119 +documenttype[1].datatype[4].sstruct.field[1].id_v6 866139998 +documenttype[1].datatype[4].sstruct.field[1].name "albums" +documenttype[1].datatype[4].sstruct.field[2].datatype 2 +documenttype[1].datatype[4].sstruct.field[2].id 51903611 +documenttype[1].datatype[4].sstruct.field[2].id_v6 1854279480 +documenttype[1].datatype[4].sstruct.field[2].name "artist" +documenttype[1].datatype[4].sstruct.field[3].datatype 2 +documenttype[1].datatype[4].sstruct.field[3].id 306708139 +documenttype[1].datatype[4].sstruct.field[3].id_v6 1047427207 +documenttype[1].datatype[4].sstruct.field[3].name "phrase" +documenttype[1].datatype[4].sstruct.field[4].datatype 874542262 +documenttype[1].datatype[4].sstruct.field[4].id 776401886 +documenttype[1].datatype[4].sstruct.field[4].id_v6 1741595836 +documenttype[1].datatype[4].sstruct.field[4].name "randomAddr" +documenttype[1].datatype[4].sstruct.field[5].datatype 10 +documenttype[1].datatype[4].sstruct.field[5].id 2095970198 +documenttype[1].datatype[4].sstruct.field[5].id_v6 1826424031 +documenttype[1].datatype[4].sstruct.field[5].name "url" +documenttype[1].inherits[2] +documenttype[1].inherits[0].id -602119955 +documenttype[1].inherits[1].id 8 +documenttype[2].bodystruct -727249584 +documenttype[2].headerstruct -1623901061 +documenttype[2].id 1722744388 +documenttype[2].name "music_search" +documenttype[2].version 0 +documenttype[2].annotationtype[0] +documenttype[2].datatype[5] +documenttype[2].datatype[0].id 1328286588 +documenttype[2].datatype[0].type WSET +documenttype[2].datatype[0].wset.createifnonexistent false +documenttype[2].datatype[0].wset.removeifzero false +documenttype[2].datatype[0].wset.key.id 2 +documenttype[2].datatype[1].id 874542262 +documenttype[2].datatype[1].type STRUCT +documenttype[2].datatype[1].sstruct.name "address" +documenttype[2].datatype[1].sstruct.version 0 +documenttype[2].datatype[1].sstruct.field[2] +documenttype[2].datatype[1].sstruct.field[0].datatype 2 +documenttype[2].datatype[1].sstruct.field[0].id 725636280 +documenttype[2].datatype[1].sstruct.field[0].id_v6 2127904 +documenttype[2].datatype[1].sstruct.field[0].name "city" +documenttype[2].datatype[1].sstruct.field[1].datatype 2 +documenttype[2].datatype[1].sstruct.field[1].id 1954764240 +documenttype[2].datatype[1].sstruct.field[1].id_v6 1527533866 +documenttype[2].datatype[1].sstruct.field[1].name "street" +documenttype[2].datatype[2].id -727249584 +documenttype[2].datatype[2].type STRUCT +documenttype[2].datatype[2].sstruct.name "music_search.body" +documenttype[2].datatype[2].sstruct.version 0 +documenttype[2].datatype[2].sstruct.field[2] +documenttype[2].datatype[2].sstruct.field[0].datatype 1 +documenttype[2].datatype[2].sstruct.field[0].id 1225171018 +documenttype[2].datatype[2].sstruct.field[0].id_v6 205748629 +documenttype[2].datatype[2].sstruct.field[0].name "price" +documenttype[2].datatype[2].sstruct.field[1].datatype 5 +documenttype[2].datatype[2].sstruct.field[1].id 552396752 +documenttype[2].datatype[2].sstruct.field[1].id_v6 713868363 +documenttype[2].datatype[2].sstruct.field[1].name "randomdouble" +documenttype[2].datatype[3].id -1857943347 +documenttype[2].datatype[3].type ARRAY +documenttype[2].datatype[3].array.element.id 874542262 +documenttype[2].datatype[4].id -1623901061 +documenttype[2].datatype[4].type STRUCT +documenttype[2].datatype[4].sstruct.name "music_search.header" +documenttype[2].datatype[4].sstruct.version 0 +documenttype[2].datatype[4].sstruct.field[11] +documenttype[2].datatype[4].sstruct.field[00].datatype -1857943347 +documenttype[2].datatype[4].sstruct.field[00].id 785651450 +documenttype[2].datatype[4].sstruct.field[00].id_v6 1210851114 +documenttype[2].datatype[4].sstruct.field[00].name "addresses" +documenttype[2].datatype[4].sstruct.field[01].datatype 1328286588 +documenttype[2].datatype[4].sstruct.field[01].id 1692189119 +documenttype[2].datatype[4].sstruct.field[01].id_v6 866139998 +documenttype[2].datatype[4].sstruct.field[01].name "albums" +documenttype[2].datatype[4].sstruct.field[02].datatype 2 +documenttype[2].datatype[4].sstruct.field[02].id 51903611 +documenttype[2].datatype[4].sstruct.field[02].id_v6 1854279480 +documenttype[2].datatype[4].sstruct.field[02].name "artist" +documenttype[2].datatype[4].sstruct.field[03].datatype 2 +documenttype[2].datatype[4].sstruct.field[03].id 306708139 +documenttype[2].datatype[4].sstruct.field[03].id_v6 1047427207 +documenttype[2].datatype[4].sstruct.field[03].name "phrase" +documenttype[2].datatype[4].sstruct.field[04].datatype 0 +documenttype[2].datatype[4].sstruct.field[04].id 1501857786 +documenttype[2].datatype[4].sstruct.field[04].id_v6 1023904001 +documenttype[2].datatype[4].sstruct.field[04].name "popularity" +documenttype[2].datatype[4].sstruct.field[05].datatype 874542262 +documenttype[2].datatype[4].sstruct.field[05].id 776401886 +documenttype[2].datatype[4].sstruct.field[05].id_v6 1741595836 +documenttype[2].datatype[4].sstruct.field[05].name "randomAddr" +documenttype[2].datatype[4].sstruct.field[06].datatype 2 +documenttype[2].datatype[4].sstruct.field[06].id 1883197392 +documenttype[2].datatype[4].sstruct.field[06].id_v6 699950698 +documenttype[2].datatype[4].sstruct.field[06].name "rankfeatures" +documenttype[2].datatype[4].sstruct.field[07].datatype 2 +documenttype[2].datatype[4].sstruct.field[07].id 1840337115 +documenttype[2].datatype[4].sstruct.field[07].id_v6 1981648971 +documenttype[2].datatype[4].sstruct.field[07].name "summaryfeatures" +documenttype[2].datatype[4].sstruct.field[08].datatype 2 +documenttype[2].datatype[4].sstruct.field[08].id 567626448 +documenttype[2].datatype[4].sstruct.field[08].id_v6 29129762 +documenttype[2].datatype[4].sstruct.field[08].name "title" +documenttype[2].datatype[4].sstruct.field[09].datatype 111553393 +documenttype[2].datatype[4].sstruct.field[09].id 2119414873 +documenttype[2].datatype[4].sstruct.field[09].id_v6 1826424031 +documenttype[2].datatype[4].sstruct.field[09].name "url" +documenttype[2].datatype[4].sstruct.field[10].datatype 0 +documenttype[2].datatype[4].sstruct.field[10].id 624630872 +documenttype[2].datatype[4].sstruct.field[10].id_v6 1508520108 +documenttype[2].datatype[4].sstruct.field[10].name "year" +documenttype[2].inherits[1] +documenttype[2].inherits[0].id 8 +documenttype[3].bodystruct 1051291304 +documenttype[3].headerstruct -1843091501 +documenttype[3].id 64693740 +documenttype[3].name "books" +documenttype[3].version 0 +documenttype[3].annotationtype[0] +documenttype[3].datatype[5] +documenttype[3].datatype[0].id 2125154557 +documenttype[3].datatype[0].type MAP +documenttype[3].datatype[0].map.key.id 2 +documenttype[3].datatype[0].map.value.id 1 +documenttype[3].datatype[1].id 1051291304 +documenttype[3].datatype[1].type STRUCT +documenttype[3].datatype[1].sstruct.name "books.body" +documenttype[3].datatype[1].sstruct.version 0 +documenttype[3].datatype[1].sstruct.field[3] +documenttype[3].datatype[1].sstruct.field[0].datatype 2125154557 +documenttype[3].datatype[1].sstruct.field[0].id 492235657 +documenttype[3].datatype[1].sstruct.field[0].id_v6 1647256015 +documenttype[3].datatype[1].sstruct.field[0].name "bindPrice" +documenttype[3].datatype[1].sstruct.field[1].datatype 16 +documenttype[3].datatype[1].sstruct.field[1].id 1891624676 +documenttype[3].datatype[1].sstruct.field[1].id_v6 531118233 +documenttype[3].datatype[1].sstruct.field[1].name "grade" +documenttype[3].datatype[1].sstruct.field[2].datatype 4 +documenttype[3].datatype[1].sstruct.field[2].id 782940634 +documenttype[3].datatype[1].sstruct.field[2].id_v6 698610188 +documenttype[3].datatype[1].sstruct.field[2].name "pages" +documenttype[3].datatype[2].id 874542262 +documenttype[3].datatype[2].type STRUCT +documenttype[3].datatype[2].sstruct.name "address" +documenttype[3].datatype[2].sstruct.version 0 +documenttype[3].datatype[2].sstruct.field[2] +documenttype[3].datatype[2].sstruct.field[0].datatype 2 +documenttype[3].datatype[2].sstruct.field[0].id 725636280 +documenttype[3].datatype[2].sstruct.field[0].id_v6 2127904 +documenttype[3].datatype[2].sstruct.field[0].name "city" +documenttype[3].datatype[2].sstruct.field[1].datatype 2 +documenttype[3].datatype[2].sstruct.field[1].id 1954764240 +documenttype[3].datatype[2].sstruct.field[1].id_v6 1527533866 +documenttype[3].datatype[2].sstruct.field[1].name "street" +documenttype[3].datatype[3].id -1857943347 +documenttype[3].datatype[3].type ARRAY +documenttype[3].datatype[3].array.element.id 874542262 +documenttype[3].datatype[4].id -1843091501 +documenttype[3].datatype[4].type STRUCT +documenttype[3].datatype[4].sstruct.name "books.header" +documenttype[3].datatype[4].sstruct.version 0 +documenttype[3].datatype[4].sstruct.field[2] +documenttype[3].datatype[4].sstruct.field[0].datatype -1857943347 +documenttype[3].datatype[4].sstruct.field[0].id 785651450 +documenttype[3].datatype[4].sstruct.field[0].id_v6 1210851114 +documenttype[3].datatype[4].sstruct.field[0].name "addresses" +documenttype[3].datatype[4].sstruct.field[1].datatype 2 +documenttype[3].datatype[4].sstruct.field[1].id 644499292 +documenttype[3].datatype[4].sstruct.field[1].id_v6 177126295 +documenttype[3].datatype[4].sstruct.field[1].name "author" +documenttype[3].inherits[2] +documenttype[3].inherits[0].id -602119955 +documenttype[3].inherits[1].id 8 +documenttype[4].bodystruct -2128841451 +documenttype[4].headerstruct 66045696 +documenttype[4].id 1789857631 +documenttype[4].name "books_search" +documenttype[4].version 0 +documenttype[4].annotationtype[0] +documenttype[4].datatype[5] +documenttype[4].datatype[0].id 2125154557 +documenttype[4].datatype[0].type MAP +documenttype[4].datatype[0].map.key.id 2 +documenttype[4].datatype[0].map.value.id 1 +documenttype[4].datatype[1].id -2128841451 +documenttype[4].datatype[1].type STRUCT +documenttype[4].datatype[1].sstruct.name "books_search.body" +documenttype[4].datatype[1].sstruct.version 0 +documenttype[4].datatype[1].sstruct.field[3] +documenttype[4].datatype[1].sstruct.field[0].datatype 2125154557 +documenttype[4].datatype[1].sstruct.field[0].id 492235657 +documenttype[4].datatype[1].sstruct.field[0].id_v6 1647256015 +documenttype[4].datatype[1].sstruct.field[0].name "bindPrice" +documenttype[4].datatype[1].sstruct.field[1].datatype 16 +documenttype[4].datatype[1].sstruct.field[1].id 1891624676 +documenttype[4].datatype[1].sstruct.field[1].id_v6 531118233 +documenttype[4].datatype[1].sstruct.field[1].name "grade" +documenttype[4].datatype[1].sstruct.field[2].datatype 1 +documenttype[4].datatype[1].sstruct.field[2].id 1225171018 +documenttype[4].datatype[1].sstruct.field[2].id_v6 205748629 +documenttype[4].datatype[1].sstruct.field[2].name "price" +documenttype[4].datatype[2].id 874542262 +documenttype[4].datatype[2].type STRUCT +documenttype[4].datatype[2].sstruct.name "address" +documenttype[4].datatype[2].sstruct.version 0 +documenttype[4].datatype[2].sstruct.field[2] +documenttype[4].datatype[2].sstruct.field[0].datatype 2 +documenttype[4].datatype[2].sstruct.field[0].id 725636280 +documenttype[4].datatype[2].sstruct.field[0].id_v6 2127904 +documenttype[4].datatype[2].sstruct.field[0].name "city" +documenttype[4].datatype[2].sstruct.field[1].datatype 2 +documenttype[4].datatype[2].sstruct.field[1].id 1954764240 +documenttype[4].datatype[2].sstruct.field[1].id_v6 1527533866 +documenttype[4].datatype[2].sstruct.field[1].name "street" +documenttype[4].datatype[3].id -1857943347 +documenttype[4].datatype[3].type ARRAY +documenttype[4].datatype[3].array.element.id 874542262 +documenttype[4].datatype[4].id 66045696 +documenttype[4].datatype[4].type STRUCT +documenttype[4].datatype[4].sstruct.name "books_search.header" +documenttype[4].datatype[4].sstruct.version 0 +documenttype[4].datatype[4].sstruct.field[8] +documenttype[4].datatype[4].sstruct.field[0].datatype -1857943347 +documenttype[4].datatype[4].sstruct.field[0].id 785651450 +documenttype[4].datatype[4].sstruct.field[0].id_v6 1210851114 +documenttype[4].datatype[4].sstruct.field[0].name "addresses" +documenttype[4].datatype[4].sstruct.field[1].datatype 2 +documenttype[4].datatype[4].sstruct.field[1].id 644499292 +documenttype[4].datatype[4].sstruct.field[1].id_v6 177126295 +documenttype[4].datatype[4].sstruct.field[1].name "author" +documenttype[4].datatype[4].sstruct.field[2].datatype 4 +documenttype[4].datatype[4].sstruct.field[2].id 782940634 +documenttype[4].datatype[4].sstruct.field[2].id_v6 698610188 +documenttype[4].datatype[4].sstruct.field[2].name "pages" +documenttype[4].datatype[4].sstruct.field[3].datatype 0 +documenttype[4].datatype[4].sstruct.field[3].id 1501857786 +documenttype[4].datatype[4].sstruct.field[3].id_v6 1023904001 +documenttype[4].datatype[4].sstruct.field[3].name "popularity" +documenttype[4].datatype[4].sstruct.field[4].datatype 2 +documenttype[4].datatype[4].sstruct.field[4].id 1883197392 +documenttype[4].datatype[4].sstruct.field[4].id_v6 699950698 +documenttype[4].datatype[4].sstruct.field[4].name "rankfeatures" +documenttype[4].datatype[4].sstruct.field[5].datatype 2 +documenttype[4].datatype[4].sstruct.field[5].id 1840337115 +documenttype[4].datatype[4].sstruct.field[5].id_v6 1981648971 +documenttype[4].datatype[4].sstruct.field[5].name "summaryfeatures" +documenttype[4].datatype[4].sstruct.field[6].datatype 2 +documenttype[4].datatype[4].sstruct.field[6].id 567626448 +documenttype[4].datatype[4].sstruct.field[6].id_v6 29129762 +documenttype[4].datatype[4].sstruct.field[6].name "title" +documenttype[4].datatype[4].sstruct.field[7].datatype 0 +documenttype[4].datatype[4].sstruct.field[7].id 624630872 +documenttype[4].datatype[4].sstruct.field[7].id_v6 1508520108 +documenttype[4].datatype[4].sstruct.field[7].name "year" +documenttype[4].inherits[1] +documenttype[4].inherits[0].id 8 diff --git a/document/src/tests/data/serialize-fieldpathupdate-cpp.dat b/document/src/tests/data/serialize-fieldpathupdate-cpp.dat Binary files differnew file mode 100644 index 00000000000..cf5f20f7a0c --- /dev/null +++ b/document/src/tests/data/serialize-fieldpathupdate-cpp.dat diff --git a/document/src/tests/data/serialize-fieldpathupdate-java.dat b/document/src/tests/data/serialize-fieldpathupdate-java.dat Binary files differnew file mode 100644 index 00000000000..cf5f20f7a0c --- /dev/null +++ b/document/src/tests/data/serialize-fieldpathupdate-java.dat diff --git a/document/src/tests/data/serializejava-compressed.dat b/document/src/tests/data/serializejava-compressed.dat Binary files differnew file mode 100644 index 00000000000..5c8721c097c --- /dev/null +++ b/document/src/tests/data/serializejava-compressed.dat diff --git a/document/src/tests/data/serializejava.dat b/document/src/tests/data/serializejava.dat Binary files differnew file mode 100644 index 00000000000..3dd9f8fcd52 --- /dev/null +++ b/document/src/tests/data/serializejava.dat diff --git a/document/src/tests/data/serializejavawithannotations.dat b/document/src/tests/data/serializejavawithannotations.dat Binary files differnew file mode 100644 index 00000000000..a648b72404f --- /dev/null +++ b/document/src/tests/data/serializejavawithannotations.dat diff --git a/document/src/tests/data/serializeupdatecpp.dat b/document/src/tests/data/serializeupdatecpp.dat Binary files differnew file mode 100644 index 00000000000..73d83b2ab45 --- /dev/null +++ b/document/src/tests/data/serializeupdatecpp.dat diff --git a/document/src/tests/data/serializeupdatejava.dat b/document/src/tests/data/serializeupdatejava.dat Binary files differnew file mode 100644 index 00000000000..e2a98d42fb1 --- /dev/null +++ b/document/src/tests/data/serializeupdatejava.dat diff --git a/document/src/tests/data/serializev6.dat b/document/src/tests/data/serializev6.dat Binary files differnew file mode 100644 index 00000000000..555acb9ecc7 --- /dev/null +++ b/document/src/tests/data/serializev6.dat diff --git a/document/src/tests/data/variablesizedocument.cfg b/document/src/tests/data/variablesizedocument.cfg new file mode 100644 index 00000000000..959f6b7b12d --- /dev/null +++ b/document/src/tests/data/variablesizedocument.cfg @@ -0,0 +1,34 @@ +enablecompression false +datatype[3] +datatype[0].id -1633334123 +datatype[0].arraytype[0] +datatype[0].weightedsettype[0] +datatype[0].structtype[1] +datatype[0].structtype[0].name varsize.header +datatype[0].structtype[0].version 0 +datatype[0].structtype[0].field[1] +datatype[0].structtype[0].field[0].name headerstring +datatype[0].structtype[0].field[0].id[0] +datatype[0].structtype[0].field[0].datatype 2 +datatype[0].documenttype[0] +datatype[1].id -785359894 +datatype[1].arraytype[0] +datatype[1].weightedsettype[0] +datatype[1].structtype[1] +datatype[1].structtype[0].name varsize.body +datatype[1].structtype[0].version 0 +datatype[1].structtype[0].field[1] +datatype[1].structtype[0].field[0].name bodystring +datatype[1].structtype[0].field[0].id[0] +datatype[1].structtype[0].field[0].datatype 2 +datatype[1].documenttype[0] +datatype[2].id 211908458 +datatype[2].arraytype[0] +datatype[2].weightedsettype[0] +datatype[2].structtype[0] +datatype[2].documenttype[1] +datatype[2].documenttype[0].name varsize +datatype[2].documenttype[0].version 0 +datatype[2].documenttype[0].inherits[0] +datatype[2].documenttype[0].headerstruct -1633334123 +datatype[2].documenttype[0].bodystruct -785359894 diff --git a/document/src/tests/data/versionscfg.txt b/document/src/tests/data/versionscfg.txt new file mode 100644 index 00000000000..a79e6040bc4 --- /dev/null +++ b/document/src/tests/data/versionscfg.txt @@ -0,0 +1,176 @@ +enablecompression false +datatype[14] +datatype[0].id 101 +datatype[0].arraytype[1] +datatype[0].arraytype[0].datatype 1 +datatype[0].weightedsettype[0] +datatype[0].structtype[0] +datatype[0].documenttype[0] +datatype[1].id 102 +datatype[1].arraytype[1] +datatype[1].arraytype[0].datatype 2 +datatype[1].weightedsettype[0] +datatype[1].structtype[0] +datatype[1].documenttype[0] +datatype[2].id 1000 +datatype[2].arraytype[0] +datatype[2].weightedsettype[0] +datatype[2].structtype[1] +datatype[2].structtype[0].name crawler_type.header +datatype[2].structtype[0].version 0 +datatype[2].structtype[0].field[5] +datatype[2].structtype[0].field[0].name URI +datatype[2].structtype[0].field[0].id[0] +datatype[2].structtype[0].field[0].datatype 2 +datatype[2].structtype[0].field[1].name CONTEXT +datatype[2].structtype[0].field[1].id[0] +datatype[2].structtype[0].field[1].datatype 2 +datatype[2].structtype[0].field[2].name CONTENT +datatype[2].structtype[0].field[2].id[0] +datatype[2].structtype[0].field[2].datatype 3 +datatype[2].structtype[0].field[3].name CONTENT_TYPE +datatype[2].structtype[0].field[3].id[0] +datatype[2].structtype[0].field[3].datatype 2 +datatype[2].structtype[0].field[4].name LAST_MODIFIED +datatype[2].structtype[0].field[4].id[0] +datatype[2].structtype[0].field[4].datatype 0 +datatype[2].documenttype[0] +datatype[3].id 1001 +datatype[3].arraytype[0] +datatype[3].weightedsettype[0] +datatype[3].structtype[1] +datatype[3].structtype[0].name crawler_type.body +datatype[3].structtype[0].version 0 +datatype[3].structtype[0].field[0] +datatype[3].documenttype[0] +datatype[4].id 1002 +datatype[4].arraytype[0] +datatype[4].weightedsettype[0] +datatype[4].structtype[0] +datatype[4].documenttype[1] +datatype[4].documenttype[0].name crawler_type +datatype[4].documenttype[0].version 0 +datatype[4].documenttype[0].inherits[0] +datatype[4].documenttype[0].headerstruct 1000 +datatype[4].documenttype[0].bodystruct 1001 +datatype[5].id 2000 +datatype[5].arraytype[0] +datatype[5].weightedsettype[0] +datatype[5].structtype[1] +datatype[5].structtype[0].name crawler_type.header +datatype[5].structtype[0].version 2 +datatype[5].structtype[0].field[3] +datatype[5].structtype[0].field[0].name URI +datatype[5].structtype[0].field[0].id[0] +datatype[5].structtype[0].field[0].datatype 2 +datatype[5].structtype[0].field[1].name CONTEXT +datatype[5].structtype[0].field[1].id[0] +datatype[5].structtype[0].field[1].datatype 2 +datatype[5].structtype[0].field[2].name CONTENT +datatype[5].structtype[0].field[2].id[0] +datatype[5].structtype[0].field[2].datatype 3 +datatype[5].documenttype[0] +datatype[6].id 2001 +datatype[6].arraytype[0] +datatype[6].weightedsettype[0] +datatype[6].structtype[1] +datatype[6].structtype[0].name crawler_type.body +datatype[6].structtype[0].version 2 +datatype[6].structtype[0].field[0] +datatype[6].documenttype[0] +datatype[7].id 2002 +datatype[7].arraytype[0] +datatype[7].weightedsettype[0] +datatype[7].structtype[0] +datatype[7].documenttype[1] +datatype[7].documenttype[0].name crawler_type +datatype[7].documenttype[0].version 2 +datatype[7].documenttype[0].inherits[0] +datatype[7].documenttype[0].headerstruct 2000 +datatype[7].documenttype[0].bodystruct 2001 +datatype[8].id 3000 +datatype[8].arraytype[0] +datatype[8].weightedsettype[0] +datatype[8].structtype[1] +datatype[8].structtype[0].name multimedia_type.header +datatype[8].structtype[0].version 0 +datatype[8].structtype[0].field[5] +datatype[8].structtype[0].field[0].name URL_KEYWORDS +datatype[8].structtype[0].field[0].id[0] +datatype[8].structtype[0].field[0].datatype 102 +datatype[8].structtype[0].field[1].name MULTIMEDIA_LINKTYPES +datatype[8].structtype[0].field[1].id[0] +datatype[8].structtype[0].field[1].datatype 102 +datatype[8].structtype[0].field[2].name THUMBNAIL_URLS +datatype[8].structtype[0].field[2].id[0] +datatype[8].structtype[0].field[2].datatype 102 +datatype[8].structtype[0].field[3].name FROM_LINK_LIST +datatype[8].structtype[0].field[3].id[0] +datatype[8].structtype[0].field[3].datatype 101 +datatype[8].structtype[0].field[4].name FROM_LINK_LANGUAGE_LIST +datatype[8].structtype[0].field[4].id[0] +datatype[8].structtype[0].field[4].datatype 102 +datatype[8].documenttype[0] +datatype[9].id 3001 +datatype[9].arraytype[0] +datatype[9].weightedsettype[0] +datatype[9].structtype[1] +datatype[9].structtype[0].name multimedia_type.body +datatype[9].structtype[0].version 0 +datatype[9].structtype[0].field[0] +datatype[9].documenttype[0] +datatype[10].id 3002 +datatype[10].arraytype[0] +datatype[10].weightedsettype[0] +datatype[10].structtype[0] +datatype[10].documenttype[1] +datatype[10].documenttype[0].name multimedia_type +datatype[10].documenttype[0].version 0 +datatype[10].documenttype[0].inherits[1] +datatype[10].documenttype[0].inherits[0].name crawler_type +datatype[10].documenttype[0].inherits[0].version 0 +datatype[10].documenttype[0].headerstruct 3000 +datatype[10].documenttype[0].bodystruct 3001 +datatype[11].id 4000 +datatype[11].arraytype[0] +datatype[11].weightedsettype[0] +datatype[11].structtype[1] +datatype[11].structtype[0].name multimedia_type.header +datatype[11].structtype[0].version 2 +datatype[11].structtype[0].field[5] +datatype[11].structtype[0].field[0].name URL_KEYWORDS +datatype[11].structtype[0].field[0].id[0] +datatype[11].structtype[0].field[0].datatype 102 +datatype[11].structtype[0].field[1].name MULTIMEDIA_LINKTYPES +datatype[11].structtype[0].field[1].id[0] +datatype[11].structtype[0].field[1].datatype 102 +datatype[11].structtype[0].field[2].name THUMBNAIL_URIS +datatype[11].structtype[0].field[2].id[0] +datatype[11].structtype[0].field[2].datatype 102 +datatype[11].structtype[0].field[3].name FROM_LINK_LIST +datatype[11].structtype[0].field[3].id[0] +datatype[11].structtype[0].field[3].datatype 101 +datatype[11].structtype[0].field[4].name FROM_LINK_LANGUAGE_LIST +datatype[11].structtype[0].field[4].id[0] +datatype[11].structtype[0].field[4].datatype 102 +datatype[11].documenttype[0] +datatype[12].id 4001 +datatype[12].arraytype[0] +datatype[12].weightedsettype[0] +datatype[12].structtype[1] +datatype[12].structtype[0].name multimedia_type.body +datatype[12].structtype[0].version 2 +datatype[12].structtype[0].field[0] +datatype[12].documenttype[0] +datatype[13].id 4002 +datatype[13].arraytype[0] +datatype[13].weightedsettype[0] +datatype[13].structtype[0] +datatype[13].documenttype[1] +datatype[13].documenttype[0].name multimedia_type +datatype[13].documenttype[0].version 2 +datatype[13].documenttype[0].inherits[1] +datatype[13].documenttype[0].inherits[0].name crawler_type +datatype[13].documenttype[0].inherits[0].version 2 +datatype[13].documenttype[0].headerstruct 4000 +datatype[13].documenttype[0].bodystruct 4001 diff --git a/document/src/tests/datatype/.gitignore b/document/src/tests/datatype/.gitignore new file mode 100644 index 00000000000..bbfc05cb12e --- /dev/null +++ b/document/src/tests/datatype/.gitignore @@ -0,0 +1,5 @@ +*.So +*_test +.depend +Makefile +document_datatype_test_app diff --git a/document/src/tests/datatype/CMakeLists.txt b/document/src/tests/datatype/CMakeLists.txt new file mode 100644 index 00000000000..399bccdf691 --- /dev/null +++ b/document/src/tests/datatype/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(document_datatype_test_app + SOURCES + datatype_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_datatype_test_app COMMAND document_datatype_test_app) diff --git a/document/src/tests/datatype/datatype_test.cpp b/document/src/tests/datatype/datatype_test.cpp new file mode 100644 index 00000000000..d084e4f90ef --- /dev/null +++ b/document/src/tests/datatype/datatype_test.cpp @@ -0,0 +1,64 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for datatype. + +#include <vespa/log/log.h> +LOG_SETUP("datatype_test"); +#include <vespa/fastos/fastos.h> + +#include <vespa/document/base/field.h> +#include <vespa/document/datatype/arraydatatype.h> +#include <vespa/document/datatype/structdatatype.h> +#include <vespa/document/fieldvalue/longfieldvalue.h> +#include <vespa/vespalib/testkit/testapp.h> + +using namespace document; + +namespace { + +TEST("require that ArrayDataType can be assigned to.") { + ArrayDataType type1(*DataType::STRING); + ArrayDataType type2(*DataType::INT); + type1 = type1; + EXPECT_EQUAL(*DataType::STRING, type1.getNestedType()); + type1 = type2; + EXPECT_EQUAL(*DataType::INT, type1.getNestedType()); +} + +TEST("require that ArrayDataType can be cloned.") { + ArrayDataType type1(*DataType::STRING); + std::unique_ptr<ArrayDataType> type2(type1.clone()); + ASSERT_TRUE(type2.get()); + EXPECT_EQUAL(*DataType::STRING, type2->getNestedType()); +} + +TEST("require that assignment operator works for LongFieldValue") { + LongFieldValue val; + val = "1"; + EXPECT_EQUAL(1, val.getValue()); + val = 2; + EXPECT_EQUAL(2, val.getValue()); + val = static_cast<int64_t>(3); + EXPECT_EQUAL(3, val.getValue()); + val = 4.0f; + EXPECT_EQUAL(4, val.getValue()); + val = 5.0; + EXPECT_EQUAL(5, val.getValue()); +} + +TEST("require that StructDataType can redeclare identical fields.") { + StructDataType s("foo"); + Field field1("field1", 42, *DataType::STRING, true); + Field field2("field2", 42, *DataType::STRING, true); + + s.addField(field1); + s.addField(field1); // ok + s.addInheritedField(field1); // ok + EXPECT_EXCEPTION(s.addField(field2), vespalib::IllegalArgumentException, + "Field id in use by field Field(field1"); + s.addInheritedField(field2); + EXPECT_FALSE(s.hasField(field2.getName())); +} + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/document/src/tests/documentcalculatortestcase.cpp b/document/src/tests/documentcalculatortestcase.cpp new file mode 100644 index 00000000000..00de5427961 --- /dev/null +++ b/document/src/tests/documentcalculatortestcase.cpp @@ -0,0 +1,202 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + + +#include <vespa/fastos/fastos.h> +#include <vespa/document/base/testdocrepo.h> +#include <vespa/document/fieldvalue/fieldvalues.h> +#include <vespa/vespalib/io/fileutil.h> +#include <vespa/vdstestlib/cppunit/macros.h> +#include <vespa/document/base/documentcalculator.h> +#include <vespa/document/fieldvalue/intfieldvalue.h> +#include <vespa/document/fieldvalue/longfieldvalue.h> +#include <vespa/document/fieldvalue/floatfieldvalue.h> + +#include <fstream> + +namespace document { + +class DocumentCalculatorTest : public CppUnit::TestFixture { + TestDocRepo _testRepo; + +public: + const DocumentTypeRepo &getRepo() { return _testRepo.getTypeRepo(); } + + void setUp() {} + void tearDown() {} + + void testConstant(); + void testSimple(); + void testVariables(); + void testFields(); + void testDivideByZero(); + void testModByZero(); + void testFieldsDivZero(); + void testFieldNotSet(); + void testFieldNotFound(); + void testByteSubtractionZeroResult(); + + CPPUNIT_TEST_SUITE(DocumentCalculatorTest); + CPPUNIT_TEST(testConstant); + CPPUNIT_TEST(testSimple); + CPPUNIT_TEST(testVariables); + CPPUNIT_TEST(testFields); + CPPUNIT_TEST(testDivideByZero); + CPPUNIT_TEST(testModByZero); + CPPUNIT_TEST(testFieldsDivZero); + CPPUNIT_TEST(testFieldNotSet); + CPPUNIT_TEST(testFieldNotFound); + CPPUNIT_TEST(testByteSubtractionZeroResult); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(DocumentCalculatorTest); + + +void +DocumentCalculatorTest::testConstant() { + DocumentCalculator::VariableMap variables; + DocumentCalculator calc(getRepo(), "4.0"); + + Document doc(*_testRepo.getDocumentType("testdoctype1"), + DocumentId("doc:test:foo")); + CPPUNIT_ASSERT_EQUAL(4.0, calc.evaluate(doc, variables)); +} + +void +DocumentCalculatorTest::testSimple() { + DocumentCalculator::VariableMap variables; + DocumentCalculator calc(getRepo(), "(3 + 5) / 2"); + + Document doc(*_testRepo.getDocumentType("testdoctype1"), + DocumentId("doc:test:foo")); + CPPUNIT_ASSERT_EQUAL(4.0, calc.evaluate(doc, variables)); +} + +void +DocumentCalculatorTest::testVariables() { + DocumentCalculator::VariableMap variables; + variables["x"] = 3.0; + variables["y"] = 5.0; + DocumentCalculator calc(getRepo(), "($x + $y) / 2"); + + Document doc(*_testRepo.getDocumentType("testdoctype1"), + DocumentId("doc:test:foo")); + CPPUNIT_ASSERT_EQUAL(4.0, calc.evaluate(doc, variables)); +} + +void +DocumentCalculatorTest::testFields() { + DocumentCalculator::VariableMap variables; + variables["x"] = 3.0; + variables["y"] = 5.0; + DocumentCalculator calc(getRepo(), "(testdoctype1.headerval + testdoctype1" + ".hfloatval) / testdoctype1.headerlongval"); + + Document doc(*_testRepo.getDocumentType("testdoctype1"), + DocumentId("doc:test:foo")); + doc.setValue(doc.getField("headerval"), IntFieldValue(5)); + doc.setValue(doc.getField("hfloatval"), FloatFieldValue(3.0)); + doc.setValue(doc.getField("headerlongval"), LongFieldValue(2)); + CPPUNIT_ASSERT_EQUAL(4.0, calc.evaluate(doc, variables)); +} + +void +DocumentCalculatorTest::testFieldsDivZero() { + DocumentCalculator::VariableMap variables; + variables["x"] = 3.0; + variables["y"] = 5.0; + DocumentCalculator calc(getRepo(), "(testdoctype1.headerval + testdoctype1" + ".hfloatval) / testdoctype1.headerlongval"); + + Document doc(*_testRepo.getDocumentType("testdoctype1"), + DocumentId("doc:test:foo")); + doc.setValue(doc.getField("headerval"), IntFieldValue(5)); + doc.setValue(doc.getField("hfloatval"), FloatFieldValue(3.0)); + doc.setValue(doc.getField("headerlongval"), LongFieldValue(0)); + try { + calc.evaluate(doc, variables); + CPPUNIT_ASSERT(false); + } catch (const vespalib::IllegalArgumentException& e) { + // OK + } +} + +void +DocumentCalculatorTest::testDivideByZero() { + DocumentCalculator::VariableMap variables; + DocumentCalculator calc(getRepo(), "(3 + 5) / 0"); + + Document doc(*_testRepo.getDocumentType("testdoctype1"), + DocumentId("doc:test:foo")); + try { + calc.evaluate(doc, variables); + CPPUNIT_ASSERT(false); + } catch (const vespalib::IllegalArgumentException& e) { + // OK + } +} + +void +DocumentCalculatorTest::testModByZero() { + DocumentCalculator::VariableMap variables; + DocumentCalculator calc(getRepo(), "(3 + 5) % 0"); + + Document doc(*_testRepo.getDocumentType("testdoctype1"), + DocumentId("doc:test:foo")); + try { + calc.evaluate(doc, variables); + CPPUNIT_ASSERT(false); + } catch (const vespalib::IllegalArgumentException& e) { + // OK + } +} + +void +DocumentCalculatorTest::testFieldNotSet() { + DocumentCalculator::VariableMap variables; + DocumentCalculator calc(getRepo(), "(testdoctype1.headerval + testdoctype1" + ".hfloatval) / testdoctype1.headerlongval"); + + Document doc(*_testRepo.getDocumentType("testdoctype1"), + DocumentId("doc:test:foo")); + doc.setValue(doc.getField("hfloatval"), FloatFieldValue(3.0)); + doc.setValue(doc.getField("headerlongval"), LongFieldValue(2)); + try { + calc.evaluate(doc, variables); + CPPUNIT_ASSERT(false); + } catch (const vespalib::IllegalArgumentException&) { + // OK + } +} + +void +DocumentCalculatorTest::testFieldNotFound() { + DocumentCalculator::VariableMap variables; + DocumentCalculator calc(getRepo(), + "(testdoctype1.mynotfoundfield + testdoctype1" + ".hfloatval) / testdoctype1.headerlongval"); + + Document doc(*_testRepo.getDocumentType("testdoctype1"), + DocumentId("doc:test:foo")); + doc.setValue(doc.getField("hfloatval"), FloatFieldValue(3.0)); + doc.setValue(doc.getField("headerlongval"), LongFieldValue(2)); + try { + calc.evaluate(doc, variables); + CPPUNIT_ASSERT(false); + } catch (const vespalib::IllegalArgumentException&) { + // OK + } +} + +void +DocumentCalculatorTest::testByteSubtractionZeroResult() { + DocumentCalculator::VariableMap variables; + DocumentCalculator calc(getRepo(), "testdoctype1.byteval - 3"); + + Document doc(*_testRepo.getDocumentType("testdoctype1"), + DocumentId("doc:test:foo")); + doc.setValue(doc.getField("byteval"), ByteFieldValue(3)); + CPPUNIT_ASSERT_EQUAL(0.0, calc.evaluate(doc, variables)); +} + +} diff --git a/document/src/tests/documentidtest.cpp b/document/src/tests/documentidtest.cpp new file mode 100644 index 00000000000..fd60c6c436b --- /dev/null +++ b/document/src/tests/documentidtest.cpp @@ -0,0 +1,183 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <cppunit/extensions/HelperMacros.h> +#include <vespa/document/serialization/vespadocumentdeserializer.h> +#include <vespa/document/serialization/vespadocumentserializer.h> +#include <vespa/vespalib/objects/nbostream.h> +#include <sstream> +#include <string> +#include <vespa/document/bucket/bucketidfactory.h> +#include <vespa/document/base/documentid.h> +#include <vespa/document/datatype/documenttype.h> +#include <vespa/vespalib/util/md5.h> + +using document::VespaDocumentDeserializer; +using document::VespaDocumentSerializer; +using vespalib::nbostream; + +namespace document { + +struct DocumentIdTest : public CppUnit::TestFixture { + void generateJavaComplianceFile(); + void testOutput(); + void testEqualityOperator(); + void testCopying(); + void testParseId(); + void checkNtnuGlobalId(); + void testDocGlobalId(); + void freestandingLocationFromGroupNameFuncMatchesIdLocation(); + + CPPUNIT_TEST_SUITE(DocumentIdTest); + CPPUNIT_TEST(testEqualityOperator); + CPPUNIT_TEST(testOutput); + CPPUNIT_TEST(testCopying); + CPPUNIT_TEST(generateJavaComplianceFile); + CPPUNIT_TEST(testParseId); + CPPUNIT_TEST(checkNtnuGlobalId); + CPPUNIT_TEST(testDocGlobalId); + CPPUNIT_TEST(freestandingLocationFromGroupNameFuncMatchesIdLocation); + CPPUNIT_TEST_SUITE_END(); + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(DocumentIdTest); + +namespace { + void writeGlobalIdBucketId(std::ostream& out, const std::string& id) { + BucketIdFactory factory; + out << id << " - " << document::DocumentId(id).getGlobalId() + << " - " << factory.getBucketId(document::DocumentId(id)).toString() + << "\n"; + } +} + +void DocumentIdTest::generateJavaComplianceFile() +{ + { // Generate file with globalids and bucket ID of various document ids, + // which java will use to ensure equal implementations. + std::ostringstream ost; + writeGlobalIdBucketId(ost, "doc:ns:specific"); + writeGlobalIdBucketId(ost, "doc:another:specific"); + writeGlobalIdBucketId(ost, "doc:ns:another"); + writeGlobalIdBucketId(ost, "userdoc:ns:100:specific"); + writeGlobalIdBucketId(ost, "userdoc:np:100:another"); + writeGlobalIdBucketId(ost, "userdoc:ns:101:specific"); + writeGlobalIdBucketId(ost, "groupdoc:ns:agroup:specific"); + writeGlobalIdBucketId(ost, "groupdoc:np:agroup:another"); + writeGlobalIdBucketId(ost, "groupdoc:ns:another:specific"); + for (uint32_t i=0; i<20; ++i) { + std::ostringstream ost2; + ost2 << i; + writeGlobalIdBucketId(ost, "doc:ns:"+ost2.str()); + } + writeGlobalIdBucketId(ost, "id:ns:type::specific"); + writeGlobalIdBucketId(ost, "id:another:type::specific"); + writeGlobalIdBucketId(ost, "id:ns:type::another"); + writeGlobalIdBucketId(ost, "id:ns:type:n=100:specific"); + writeGlobalIdBucketId(ost, "id:np:type:n=100:another"); + writeGlobalIdBucketId(ost, "id:ns:type:n=101:specific"); + writeGlobalIdBucketId(ost, "id:ns:type:g=agroup:specific"); + writeGlobalIdBucketId(ost, "id:np:type:g=agroup:another"); + writeGlobalIdBucketId(ost, "id:ns:type:g=another:specific"); + FastOS_File file; + CPPUNIT_ASSERT(file.OpenWriteOnlyTruncate("cpp-globalidbucketids.txt")); + std::string content(ost.str()); + CPPUNIT_ASSERT(file.CheckedWrite(content.c_str(), content.size())); + CPPUNIT_ASSERT(file.Close()); + } +} + + +void DocumentIdTest::testOutput() +{ + DocumentId id(DocIdString("crawler", "http://www.yahoo.com")); + + std::ostringstream ost; + ost << id; + std::string expected("doc:crawler:http://www.yahoo.com"); + CPPUNIT_ASSERT_EQUAL(expected, ost.str()); + + CPPUNIT_ASSERT_EQUAL(vespalib::string(expected), id.toString()); + + expected = "DocumentId(id = doc:crawler:http://www.yahoo.com, " + "gid(0x928baffb39cf32004542fb60))"; + CPPUNIT_ASSERT_EQUAL(expected, static_cast<Printable&>(id).toString(true)); +} + +namespace { + template<class T> + std::string getNotEqualMessage(const T& t1, const T& t2) { + std::ostringstream ost; + ost << "Expected instances to be different. This was not the case:\n" + << t1 << "\n" << t2 << "\n"; + return ost.str(); + } +} + +void DocumentIdTest::testEqualityOperator() +{ + std::string uri(DocIdString("crawler", "http://www.yahoo.com").toString()); + + DocumentId id1(uri); + DocumentId id2(uri); + DocumentId id3("doc:crawler:http://www.yahoo.no/"); + + CPPUNIT_ASSERT_EQUAL(id1, id2); + CPPUNIT_ASSERT_MESSAGE(getNotEqualMessage(id1, id3), !(id1 == id3)); +} + +void DocumentIdTest::testCopying() +{ + std::string uri(DocIdString("crawler", "http://www.yahoo.com/").toString()); + + DocumentId id1(uri); + DocumentId id2(id1); + DocumentId id3("doc:ns:foo"); + id3 = id2; + + CPPUNIT_ASSERT_EQUAL(id1, id2); + CPPUNIT_ASSERT_EQUAL(id1, id3); +} + +void +DocumentIdTest::testParseId() +{ + // Moved to base/documentid_test.cpp +} + +void +DocumentIdTest::checkNtnuGlobalId() +{ + DocumentId id("doc:crawler:http://www.ntnu.no/"); + CPPUNIT_ASSERT_EQUAL(vespalib::string("gid(0xb8863740be14221c0ac77896)"), + id.getGlobalId().toString()); +} + +void +DocumentIdTest::testDocGlobalId() +{ + // Test that location of doc scheme documents are set correctly, such + // that the location is the first bytes of the original GID. + std::string id("doc:crawler:http://www.ntnu.no/"); + DocumentId did(id); + + unsigned char key[16]; + fastc_md5sum(reinterpret_cast<const unsigned char*>(id.c_str()), + id.size(), key); + + CPPUNIT_ASSERT_EQUAL(GlobalId(key), did.getGlobalId()); +} + +void +DocumentIdTest::freestandingLocationFromGroupNameFuncMatchesIdLocation() +{ + CPPUNIT_ASSERT_EQUAL( + DocumentId("id::foo:g=zoid:bar").getScheme().getLocation(), + GroupDocIdString::locationFromGroupName("zoid")); + CPPUNIT_ASSERT_EQUAL( + DocumentId("id::bar:g=doink:baz").getScheme().getLocation(), + GroupDocIdString::locationFromGroupName("doink")); +} + +} // document diff --git a/document/src/tests/documentselectparsertest.cpp b/document/src/tests/documentselectparsertest.cpp new file mode 100644 index 00000000000..357e6eb2a68 --- /dev/null +++ b/document/src/tests/documentselectparsertest.cpp @@ -0,0 +1,1278 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + + +//#include <boost/regex/icu.hpp> +#include <vespa/fastos/fastos.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <vespa/document/repo/configbuilder.h> +#include <vespa/document/repo/documenttyperepo.h> +#include <iostream> +#include <memory> +#include <vespa/document/update/assignvalueupdate.h> +#include <vespa/document/base/testdocman.h> +#include <vespa/document/base/testdocrepo.h> +#include <vespa/document/select/parser.h> +#include <vespa/document/select/visitor.h> +#include <vespa/document/select/bodyfielddetector.h> +#include <vespa/document/select/valuenode.h> +#include <vespa/document/select/branch.h> +#include <vespa/document/select/simpleparser.h> +#include <vespa/document/select/constant.h> +#include <vespa/document/select/invalidconstant.h> +#include <vespa/document/select/doctype.h> +#include <vespa/document/select/compare.h> + +using namespace document::config_builder; + +namespace document { + +class DocumentSelectParserTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(DocumentSelectParserTest); + CPPUNIT_TEST(testParseTerminals); + CPPUNIT_TEST(testParseBranches); + CPPUNIT_TEST(testOperators); + CPPUNIT_TEST(testVisitor); + CPPUNIT_TEST(testUtf8); + CPPUNIT_TEST(testBodyFieldDetection); + CPPUNIT_TEST(testDocumentUpdates); + CPPUNIT_TEST_SUITE_END(); + + BucketIdFactory _bucketIdFactory; + std::unique_ptr<select::Parser> _parser; + std::vector<Document::SP > _doc; + std::vector<DocumentUpdate::SP > _update; + + Document::SP createDoc( + const std::string& doctype, const std::string& id, uint32_t hint, + double hfloat, const std::string& hstr, const std::string& cstr, + uint64_t hlong = 0); + + DocumentUpdate::SP createUpdate( + const std::string& doctype, const std::string& id, uint32_t hint, + const std::string& hstr); + + template <typename ContainsType> + select::ResultList doParse(const vespalib::stringref& expr, + const ContainsType& t); +public: + + DocumentSelectParserTest() + : _bucketIdFactory() {} + + void setUp(); + void tearDown() {} + void createDocs(); + + void testParseTerminals(); + void testParseBranches(); + void testOperators(); + void testOperators0(); + void testOperators1(); + void testOperators2(); + void testOperators3(); + void testOperators4(); + void testOperators5(); + void testOperators6(); + void testOperators7(); + void testOperators8(); + void testOperators9(); + void testVisitor(); + void testUtf8(); + void testBodyFieldDetection(); + void testDocumentUpdates(); + void testDocumentUpdates0(); + void testDocumentUpdates1(); + void testDocumentUpdates2(); + void testDocumentUpdates3(); + void testDocumentUpdates4(); + void testDocumentUpdates5(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(DocumentSelectParserTest); + +namespace { + DocumentTypeRepo::SP _repo; +} + +void DocumentSelectParserTest::setUp() +{ + DocumenttypesConfigBuilderHelper builder(TestDocRepo::getDefaultConfig()); + builder.document(535424777, "notandor", + Struct("notandor.header"), Struct("notandor.body")); + builder.document(1348665801, "ornotand", + Struct("ornotand.header"), Struct("ornotand.body")); + builder.document(-1848670693, "andornot", + Struct("andornot.header"), Struct("andornot.body")); + builder.document(-1193328712, "idid", + Struct("idid.header"), Struct("idid.body")); + builder.document(-1673092522, "usergroup", + Struct("usergroup.header"), + Struct("usergroup.body")); + _repo.reset(new DocumentTypeRepo(builder.config())); + + _parser.reset(new select::Parser(*_repo, _bucketIdFactory)); +} + +Document::SP DocumentSelectParserTest::createDoc( + const std::string& doctype, const std::string& id, uint32_t hint, + double hfloat, const std::string& hstr, const std::string& cstr, + uint64_t hlong) +{ + const DocumentType* type = _repo->getDocumentType(doctype); + Document::SP doc(new Document(*type, DocumentId(id))); + doc->setValue(doc->getField("headerval"), IntFieldValue(hint)); + + if (hlong != 0) { + doc->setValue(doc->getField("headerlongval"), LongFieldValue(hlong)); + } + doc->setValue(doc->getField("hfloatval"), FloatFieldValue(hfloat)); + doc->setValue(doc->getField("hstringval"), StringFieldValue(hstr.c_str())); + doc->setValue(doc->getField("content"), StringFieldValue(cstr.c_str())); + return doc; +} + +DocumentUpdate::SP DocumentSelectParserTest::createUpdate( + const std::string& doctype, const std::string& id, uint32_t hint, + const std::string& hstr) +{ + const DocumentType* type = _repo->getDocumentType(doctype); + DocumentUpdate::SP doc( + new DocumentUpdate(*type, DocumentId(id))); + doc->addUpdate(FieldUpdate(doc->getType().getField("headerval")) + .addUpdate(AssignValueUpdate(IntFieldValue(hint)))); + doc->addUpdate(FieldUpdate(doc->getType().getField("hstringval")) + .addUpdate(AssignValueUpdate(StringFieldValue(hstr)))); + return doc; +} + +void +DocumentSelectParserTest::createDocs() +{ + _doc.clear(); + _doc.push_back(createDoc( + "testdoctype1", "doc:myspace:anything", 24, 2.0, "foo", "bar", 0)); // DOC 0 + _doc.push_back(createDoc( + "testdoctype1", "doc:anotherspace:foo", 13, 4.1, "bar", "foo", 0)); // DOC 1 + // Add some arrays and structs to doc 1 + { + StructFieldValue sval(_doc.back()->getField("mystruct").getDataType()); + sval.set("key", 14); + sval.set("value", "structval"); + _doc.back()->setValue("mystruct", sval); + ArrayFieldValue + aval(_doc.back()->getField("structarray").getDataType()); + { + StructFieldValue sval1(aval.getNestedType()); + sval1.set("key", 15); + sval1.set("value", "structval1"); + StructFieldValue sval2(aval.getNestedType()); + sval2.set("key", 16); + sval2.set("value", "structval2"); + aval.add(sval1); + aval.add(sval2); + } + _doc.back()->setValue("structarray", aval); + + MapFieldValue mval(_doc.back()->getField("mymap").getDataType()); + mval.put(document::IntFieldValue(3), document::StringFieldValue("a")); + mval.put(document::IntFieldValue(5), document::StringFieldValue("b")); + mval.put(document::IntFieldValue(7), document::StringFieldValue("c")); + _doc.back()->setValue("mymap", mval); + + MapFieldValue + amval(_doc.back()->getField("structarrmap").getDataType()); + amval.put(StringFieldValue("foo"), aval); + + ArrayFieldValue + abval(_doc.back()->getField("structarray").getDataType()); + { + StructFieldValue sval1(aval.getNestedType()); + sval1.set("key", 17); + sval1.set("value", "structval3"); + StructFieldValue sval2(aval.getNestedType()); + sval2.set("key", 18); + sval2.set("value", "structval4"); + abval.add(sval1); + abval.add(sval2); + } + + amval.put(StringFieldValue("bar"), abval); + _doc.back()->setValue("structarrmap", amval); + + WeightedSetFieldValue wsval( + _doc.back()->getField("stringweightedset").getDataType()); + wsval.add("foo"); + wsval.add("val1"); + wsval.add("val2"); + wsval.add("val3"); + wsval.add("val4"); + _doc.back()->setValue("stringweightedset", wsval); + + WeightedSetFieldValue wsbytes( + _doc.back()->getField("byteweightedset").getDataType()); + wsbytes.add(ByteFieldValue(5)); + wsbytes.add(ByteFieldValue(75)); + wsbytes.add(ByteFieldValue(255)); + wsbytes.add(ByteFieldValue(0)); + _doc.back()->setValue("byteweightedset", wsbytes); + } + + _doc.push_back(createDoc( + "testdoctype1", "userdoc:myspace:1234:footype1", 15, 1.0, "some", "some", 0)); // DOC 2 + // Add empty struct and array + { + StructFieldValue sval(_doc.back()->getField("mystruct").getDataType()); + _doc.back()->setValue("mystruct", sval); + ArrayFieldValue aval( + _doc.back()->getField("structarray").getDataType()); + _doc.back()->setValue("structarray", aval); + } + _doc.push_back(createDoc( + "testdoctype1", "groupdoc:myspace:yahoo:bar", 14, 2.4, "Yet", "\xE4\xB8\xBA\xE4\xBB\x80", 0)); // DOC 3 + _doc.push_back(createDoc( + "testdoctype2", "doc:myspace:inheriteddoc", 10, 1.4, "inherited", "")); // DOC 4 + _doc.push_back(createDoc( + "testdoctype1", "userdoc:footype:123456789:aardvark", + 10, 1.4, "inherited", "", 0)); // DOC 5 + _doc.push_back(createDoc( + "testdoctype1", "userdoc:footype:1234:highlong", + 10, 1.4, "inherited", "", 2651257743)); // DOC 6 + _doc.push_back(createDoc( + "testdoctype1", "userdoc:footype:1234:highlong", + 10, 1.4, "inherited", "", -2651257743)); // DOC 7 + _doc.push_back(createDoc( // DOC 8 + "testdoctype1", "orderdoc(4,4):footype:1234:12:highlong", + 10, 1.4, "inherited", "", -2651257743)); + _doc.push_back(createDoc( // DOC 9 + "testdoctype1", "orderdoc(4,4):footype:mygroup:12:highlong", + 10, 1.4, "inherited", "", -2651257743)); + _doc.push_back(createDoc( // DOC 10. As DOC 0 but with version 2. + "testdoctype1", "doc:myspace:anything", 24, 2.0, "foo", "bar", 0)); + _doc.push_back(createDoc( + "testdoctype1", "id:footype:testdoctype1:n=12345:foo", + 10, 1.4, "inherited", "", 42)); // DOC 11 + _doc.push_back(createDoc( + "testdoctype1", "id:myspace:testdoctype1:g=xyzzy:foo", + 10, 1.4, "inherited", "", 42)); // DOC 12 + + _update.clear(); + _update.push_back(createUpdate( + "testdoctype1", "doc:myspace:anything", 20, "hmm")); + _update.push_back(createUpdate( + "testdoctype1", "doc:anotherspace:foo", 10, "foo")); + _update.push_back(createUpdate( + "testdoctype1", "userdoc:myspace:1234:footype1", 0, "foo")); + _update.push_back(createUpdate( + "testdoctype1", "groupdoc:myspace:yahoo:bar", 3, "\xE4\xBA\xB8\xE4\xBB\x80")); + _update.push_back(createUpdate( + "testdoctype2", "doc:myspace:inheriteddoc", 10, "bar")); +} + +namespace { +void doVerifyParse(select::Node *node, const std::string &query, + const char *expected) { + std::string message("Query "+query+" failed to parse."); + CPPUNIT_ASSERT_MESSAGE(message, node != 0); + std::ostringstream actual; + actual << *node; + + std::string exp(expected != 0 ? std::string(expected) : query); + CPPUNIT_ASSERT_EQUAL(exp, actual.str()); + // Test that cloning gives the same result + std::unique_ptr<select::Node> clonedNode(node->clone()); + std::ostringstream clonedStr; + clonedStr << *clonedNode; + CPPUNIT_ASSERT_EQUAL(exp, clonedStr.str()); +} + +void verifySimpleParse(const std::string& query, const char* expected = 0) { + BucketIdFactory factory; + select::simple::SelectionParser parser(factory); + std::string message("Query "+query+" failed to parse."); + CPPUNIT_ASSERT_MESSAGE(message, parser.parse(query)); + std::unique_ptr<select::Node> node(parser.getNode()); + doVerifyParse(node.get(), query, expected); +} + +void verifyParse(const std::string& query, const char* expected = 0) { + BucketIdFactory factory; + select::Parser parser(*_repo, factory); + std::unique_ptr<select::Node> node(parser.parse(query)); + doVerifyParse(node.get(), query, expected); +} + + void verifyFailedParse(const std::string& query, const std::string& error) { + try{ + BucketIdFactory factory; + TestDocRepo test_repo; + select::Parser parser(test_repo.getTypeRepo(), factory); + std::unique_ptr<select::Node> node(parser.parse(query)); + CPPUNIT_FAIL("Expected exception parsing query '"+query+"'"); + } catch (select::ParsingFailedException& e) { + std::string message(e.what()); + if (message.size() > error.size()) + message = message.substr(0, error.size()); + std::string failure("Expected: " + error + "\n- Actual : " + + std::string(e.what())); + CPPUNIT_ASSERT_MESSAGE(failure, error == message); + } + } +} + +void DocumentSelectParserTest::testParseTerminals() +{ + createDocs(); + + // Test number value + verifyParse("", "true"); + verifyParse("testdoctype1.headerval == 123"); + verifyParse("testdoctype1.headerval == +123.53", "testdoctype1.headerval == 123.53"); + verifyParse("testdoctype1.headerval == -123.5"); + verifyParse("testdoctype1.headerval == 234123.523e3", + "testdoctype1.headerval == 2.34124e+08"); + verifyParse("testdoctype1.headerval == -234123.523E-3", + "testdoctype1.headerval == -234.124"); + verifyFailedParse("testdoctype1.headerval == aaa", "ParsingFailedException: " + "Unexpected token at position 23 ('== aaa') in query " + "'testdoctype1.headerval == aaa', at fullParse in "); + // Test string value + verifyParse("testdoctype1.headerval == \"test\""); + std::unique_ptr<select::Node> node( + _parser->parse("testdoctype1.headerval == \"test\"")); + const select::Compare& compnode( + dynamic_cast<const select::Compare&>(*node)); + const select::FieldValueNode& fnode( + dynamic_cast<const select::FieldValueNode&>(compnode.getLeft())); + const select::StringValueNode& vnode( + dynamic_cast<const select::StringValueNode&>(compnode.getRight())); + /* + CPPUNIT_ASSERT_EQUAL(vespalib::string("testdoctype1"), + fnode.getDocType()->getName()); + */ + CPPUNIT_ASSERT_EQUAL(vespalib::string("headerval"), fnode.getFieldName()); + CPPUNIT_ASSERT_EQUAL(vespalib::string("test"), vnode.getValue()); + // Test whitespace + verifyParse("testdoctype1.headerval == \"te st \""); + verifyParse(" \t testdoctype1.headerval\t== \t \"test\"\t", + "testdoctype1.headerval == \"test\""); + // Test escaping + verifyParse("testdoctype1.headerval == \"tab\\ttest\""); + verifyParse("testdoctype1.headerval == \"tab\\x09test\"", + "testdoctype1.headerval == \"tab\\ttest\""); + verifyParse("testdoctype1.headerval == \"tab\\x055test\""); + verifyFailedParse("testdoctype1.headerval == \"tab\\x0notcomplete\"", + "ParsingFailedException: Unexpected token at position 23 " + "('== \"tab\\x0') in query 'testdoctype1.headerval == \"tab\\x0notcomplete\"', " + "at fullParse in "); + verifyFailedParse("testdoctype1.headerval == \"tab\\ysf\"", + "ParsingFailedException: Unexpected token at position 23 " + "('== \"tab\\ys') in query 'testdoctype1.headerval == \"tab\\ysf\"', " + "at fullParse in "); + node = _parser->parse("testdoctype1.headerval == \"\\tt\\x48 \\n\""); + select::Compare& escapednode(dynamic_cast<select::Compare&>(*node)); + const select::StringValueNode& escval( + dynamic_cast<const select::StringValueNode&>(escapednode.getRight())); + CPPUNIT_ASSERT_EQUAL(vespalib::string("\ttH \n"), escval.getValue()); + // Test illegal operator + verifyFailedParse("testdoctype1.headerval <> 12", "ParsingFailedException: Unexpected" + " token at position 23 ('<> 12') in query 'testdoctype1.headerval <> 12', at"); + // Test <= <, > >= + verifyParse("testdoctype1.headerval >= 123"); + verifyParse("testdoctype1.headerval > 123"); + verifyParse("testdoctype1.headerval <= 123"); + verifyParse("testdoctype1.headerval < 123"); + verifyParse("testdoctype1.headerval != 123"); + + // Test defined + verifyParse("testdoctype1.headerval", "testdoctype1.headerval != null"); + + // Test bools + verifyParse("TRUE"); + verifyParse("FALSE"); + verifyParse("true"); + verifyParse("false"); + verifyParse("faLSe"); + verifyFailedParse("fal se", "ParsingFailedException: Unexpected token at " + "position 4 ('se') in query 'fal se', at"); + + // Test document types + verifyParse("testdoctype1"); + verifyFailedParse("mytype", "ParsingFailedException: Document type mytype " + "not found"); + verifyParse("_test_doctype3_"); + verifyParse("_test_doctype3_._only_in_child_ == 0"); + + // Test document id with simple parser. + verifySimpleParse("id == \"userdoc:ns:mytest\""); + verifySimpleParse("id.namespace == \"myspace\""); + verifySimpleParse("id.scheme == \"userdoc\""); + verifySimpleParse("id.type == \"testdoctype1\""); + verifySimpleParse("id.group == \"yahoo.com\""); + verifySimpleParse("id.user == 1234"); + verifySimpleParse("id.user == 0x12456ab", "id.user == 19158699"); + + // Test document id + verifyParse("id == \"userdoc:ns:mytest\""); + verifyParse("id.namespace == \"myspace\""); + verifyParse("id.scheme == \"userdoc\""); + verifyParse("id.type == \"testdoctype1\""); + verifyParse("id.user == 1234"); + verifyParse("id.user == 0x12456ab", "id.user == 19158699"); + verifyParse("id.group == \"yahoo.com\""); + verifyParse("id.order(10,5) < 100"); + + verifyParse("id.specific == \"mypart\""); + verifyParse("id.bucket == 1234"); + verifyParse("id.bucket == 0x800000", "id.bucket == 8388608"); + verifyParse("id.bucket == 0x80a000", "id.bucket == 8429568"); + verifyParse("id.bucket == 0x80000000000000f2", + "id.bucket == -9223372036854775566"); + verifyParse("id.gid == \"gid(0xd755743aea262650274d70f0)\""); + + // Test search column + verifyParse("searchcolumn.10 == 2"); + + // Test other operators + verifyParse("id.scheme = \"*doc\""); + verifyParse("testdoctype1.hstringval =~ \"(john|barry|shrek)\""); + + // Verify functions + verifyParse("id.hash() == 124"); + verifyParse("id.specific.hash() == 124"); + verifyParse("testdoctype1.hstringval.lowercase() == \"chang\""); + verifyParse("testdoctype1.hstringval.lowercase().hash() == 124"); + verifyFailedParse("testdoctype1 == 8", "ParsingFailedException: Unexpected token" + " at position 13 ('== 8') in query 'testdoctype1 == 8', at fullParse in "); + verifyParse("testdoctype1.hintval > now()"); + verifyParse("testdoctype1.hintval > now().abs()"); + + // Value grouping + verifyParse("(123) < (200)"); + verifyParse("(\"hmm\") < (id.scheme)"); + + // Arithmetics + verifyParse("1 + 2 > 1"); + verifyParse("1 - 2 > 1"); + verifyParse("1 * 2 > 1"); + verifyParse("1 / 2 > 1"); + verifyParse("1 % 2 > 1"); + verifyParse("(1 + 2) * (4 - 2) == 1"); + verifyParse("23 + 643 / 34 % 10 > 34"); + + // CJK stuff + verifyParse("testdoctype1.hstringval = \"\xE4\xB8\xBA\xE4\xBB\x80\"", + "testdoctype1.hstringval = \"\\xe4\\xb8\\xba\\xe4\\xbb\\x80\""); + + // Strange doctype names + verifyParse("notandor"); + verifyParse("ornotand"); + verifyParse("andornot"); + verifyParse("idid"); + verifyParse("usergroup"); +} + +void DocumentSelectParserTest::testParseBranches() +{ + createDocs(); + + verifyParse("TRUE or FALSE aNd FALSE oR TRUE"); + verifyParse("TRUE and FALSE or FALSE and TRUE"); + verifyParse("TRUE or FALSE and FALSE or TRUE"); + verifyParse("(TRUE or FALSE) and (FALSE or TRUE)"); + verifyParse("true or (not false) and not true"); + + // Test number branching with node branches + verifyParse("((243) < 300 and (\"FOO\").lowercase() == (\"foo\"))"); + + // Strange doctype names + verifyParse("notandor and ornotand"); + verifyParse("ornotand or andornot"); + verifyParse("not andornot"); + verifyParse("idid or not usergroup"); + verifyParse("not(andornot or idid)", "not (andornot or idid)"); +} + +template <typename ContainsType> +select::ResultList +DocumentSelectParserTest::doParse(const vespalib::stringref& expr, + const ContainsType& t) +{ + std::unique_ptr<select::Node> root(_parser->parse(expr)); + select::ResultList result(root->contains(t)); + + std::unique_ptr<select::Node> cloned(root->clone()); + select::ResultList clonedResult(cloned->contains(t)); + + std::unique_ptr<select::Node> traced(_parser->parse(expr)); + std::ostringstream oss; + oss << "for expr: " << expr << "\n"; + select::ResultList tracedResult(root->trace(t, oss)); + + CPPUNIT_ASSERT_EQUAL_MESSAGE(expr, result, clonedResult); + CPPUNIT_ASSERT_EQUAL_MESSAGE(oss.str(), result, tracedResult); + + return result; +} + +#define PARSE(expr, doc, result) \ + CPPUNIT_ASSERT_EQUAL_MESSAGE(expr, select::ResultList(select::Result::result), \ + doParse(expr, (doc))); + +#define PARSEI(expr, doc, result) \ + CPPUNIT_ASSERT_EQUAL_MESSAGE(std::string("Doc: ") + expr, \ + select::ResultList(select::Result::result), \ + doParse(expr, (doc))); \ + CPPUNIT_ASSERT_EQUAL_MESSAGE(std::string("Doc id: ") + expr, \ + select::ResultList(select::Result::result), \ + doParse(expr, (doc).getId())); + +void DocumentSelectParserTest::testOperators() +{ + testOperators0(); + testOperators1(); + testOperators2(); + testOperators3(); + testOperators4(); + testOperators5(); + testOperators6(); + testOperators7(); + testOperators8(); + testOperators9(); +} + +void DocumentSelectParserTest::testOperators0() +{ + createDocs(); + + /* Code for tracing result to see what went wrong + { + std::ostringstream ost; + _parser->parse("id.specific.hash() % 10 = 8")->trace(*_doc[0], ost); + ost << "\n\n"; + _parser->parse("id.specific.hash() % 10 = 8") + ->trace(_doc[0]->getId(), ost); + std::cerr << ost.str() << "\n"; + } // */ + + // Check that comparison operators work. + PARSE("", *_doc[0], True); + PARSE("30 < 10", *_doc[0], False); + PARSE("10 < 30", *_doc[0], True); + PARSE("30 < 10", *_doc[0], False); + PARSE("10 < 30", *_doc[0], True); + PARSE("30 <= 10", *_doc[0], False); + PARSE("10 <= 30", *_doc[0], True); + PARSE("30 <= 30", *_doc[0], True); + PARSE("10 >= 30", *_doc[0], False); + PARSE("30 >= 10", *_doc[0], True); + PARSE("30 >= 30", *_doc[0], True); + + PARSE("10 > 30", *_doc[0], False); + PARSE("30 > 10", *_doc[0], True); + PARSE("30 == 10", *_doc[0], False); + PARSE("30 == 30", *_doc[0], True); + PARSE("30 != 10", *_doc[0], True); + PARSE("30 != 30", *_doc[0], False); + PARSE("\"foo\" != \"bar\"", *_doc[0], True); + PARSE("\"foo\" != \"foo\"", *_doc[0], False); + PARSE("\"foo\" == 'bar'", *_doc[0], False); + PARSE("\"foo\" == 'foo'", *_doc[0], True); + PARSE("\"bar\" = \"a\"", *_doc[0], False); + PARSE("\"bar\" = \"*a*\"", *_doc[0], True); + PARSE("\"bar\" = \"\"", *_doc[0], False); + PARSE("\"\" = \"\"", *_doc[0], True); + PARSE("\"bar\" =~ \"^a$\"", *_doc[0], False); + PARSE("\"bar\" =~ \"a\"", *_doc[0], True); + PARSE("\"bar\" =~ \"\"", *_doc[0], True); + PARSE("\"\" =~ \"\"", *_doc[0], True); + PARSE("30 = 10", *_doc[0], False); + PARSE("30 = 30", *_doc[0], True); +} + +void DocumentSelectParserTest::testOperators1() +{ + createDocs(); + + // Mix of types should within numbers, but otherwise not match + PARSE("30 < 10.2", *_doc[0], False); + PARSE("10.2 < 30", *_doc[0], True); + PARSE("30 < \"foo\"", *_doc[0], Invalid); + PARSE("30 > \"foo\"", *_doc[0], Invalid); + PARSE("30 != \"foo\"", *_doc[0], Invalid); + PARSE("14.2 <= \"foo\"", *_doc[0], Invalid); + PARSE("null == null", *_doc[0], True); + PARSE("null = null", *_doc[0], True); + PARSE("\"bar\" == null", *_doc[0], False); + PARSE("14.3 == null", *_doc[0], False); + PARSE("null = 0", *_doc[0], False); + + // Field values + PARSE("testdoctype1.headerval = 24", *_doc[0], True); + PARSE("testdoctype1.headerval = 24", *_doc[1], False); + PARSE("testdoctype1.headerval = 13", *_doc[0], False); + PARSE("testdoctype1.headerval = 13", *_doc[1], True); + PARSE("testdoctype1.hfloatval = 2.0", *_doc[0], True); + PARSE("testdoctype1.hfloatval = 1.0", *_doc[1], False); + PARSE("testdoctype1.hfloatval = 4.1", *_doc[0], False); + PARSE("testdoctype1.hfloatval > 4.09 and testdoctype1.hfloatval < 4.11", + *_doc[1], True); + PARSE("testdoctype1.content = \"bar\"", *_doc[0], True); + PARSE("testdoctype1.content = \"bar\"", *_doc[1], False); + PARSE("testdoctype1.content = \"foo\"", *_doc[0], False); + PARSE("testdoctype1.content = \"foo\"", *_doc[1], True); + PARSE("testdoctype1.hstringval == testdoctype1.content", *_doc[0], False); + PARSE("testdoctype1.hstringval == testdoctype1.content", *_doc[2], True); + PARSE("testdoctype1.byteweightedset == 7", *_doc[1], False); + PARSE("testdoctype1.byteweightedset == 5", *_doc[1], True); + + // Document types + PARSE("testdoctype1", *_doc[0], True); + PARSE("testdoctype2", *_doc[0], False); + + // Inherited doctypes + PARSE("testdoctype2", *_doc[4], True); + PARSE("testdoctype2", *_doc[3], False); + PARSE("testdoctype1", *_doc[4], True); + PARSE("testdoctype1.headerval = 10", *_doc[4], True); +} + +void DocumentSelectParserTest::testOperators2() +{ + createDocs(); + + // Id values + PARSEI("id == \"doc:myspace:anything\"", *_doc[0], True); + PARSEI(" iD== \"doc:myspace:anything\" ", *_doc[0], True); + PARSEI("id == \"doc:myspa:nything\"", *_doc[0], False); + PARSEI("Id.scHeme == \"doc\"", *_doc[0], True); + PARSEI("id.scheme == \"userdoc\"", *_doc[0], False); + PARSEI("id.type == \"testdoctype1\"", *_doc[11], True); + PARSEI("id.type == \"wrong_type\"", *_doc[11], False); + PARSEI("id.type == \"unknown\"", *_doc[0], Invalid); + PARSEI("Id.namespaCe == \"myspace\"", *_doc[0], True); + PARSEI("id.NaMespace == \"pace\"", *_doc[0], False); + PARSEI("id.specific == \"anything\"", *_doc[0], True); + PARSEI("id.user=1234", *_doc[2], True); + PARSEI("id.user == 1234", *_doc[0], Invalid); + PARSEI("id.group == 1234", *_doc[3], Invalid); + PARSEI("id.group == \"yahoo\"", *_doc[3], True); + PARSEI("id.bucket == 1234", *_doc[0], False); + PARSEI("id.order(4,4) == 12", *_doc[8], True); + PARSEI("id.order(4,4) < 20", *_doc[8], True); + PARSEI("id.order(4,4) > 12", *_doc[8], False); + PARSEI("id.order(5,5) <= 12", *_doc[8], Invalid); + PARSEI("id.order(4,4) <= 12", *_doc[8], True); + PARSEI("id.order(4,4) == 12", *_doc[0], Invalid); + PARSEI("id.user=12345", *_doc[11], True); + PARSEI("id.group == \"xyzzy\"", *_doc[12], True); +} + +void DocumentSelectParserTest::testOperators3() +{ + createDocs(); + { + std::ostringstream ost; + ost << "id.bucket == " << BucketId(16, 4006).getId() ; + PARSEI(ost.str(), *_doc[0], True); + } + { + std::ostringstream ost; + ost << "id.bucket == " << BucketId(17, 4006).getId() ; + PARSEI(ost.str(), *_doc[0], False); + } + { + std::ostringstream ost; + ost << "id.bucket == " << BucketId(17, 69542).getId() ; + PARSEI(ost.str(), *_doc[0], True); + } + { + std::ostringstream ost; + ost << "id.bucket == " << BucketId(16, 1234).getId() ; + PARSEI(ost.str(), *_doc[0], False); + } + + PARSEI("id.bucket == \"foo\"", *_doc[0], Invalid); + + std::string gidmatcher = "id.gid == \"" + _doc[0]->getId().getGlobalId().toString() + "\""; + PARSEI(gidmatcher, *_doc[0], True); + + PARSEI("id.user=123456789 and id = \"userdoc:footype:123456789:aardvark\"", *_doc[5], True); + PARSEI("id == \"userdoc:footype:123456789:badger\"", *_doc[5], False); + + PARSEI("id.user = 1234", *_doc[8], True); + PARSEI("id.group == \"1234\"", *_doc[8], True); + PARSEI("id.group == \"mygroup\"", *_doc[9], True); + + // Searchcolumn policy + PARSE("searchcolumn.10 == 8", *_doc[0], True); +} + +void DocumentSelectParserTest::testOperators4() +{ + createDocs(); + + // Branch operators + PARSEI("true and false", *_doc[0], False); + PARSEI("true and true", *_doc[0], True); + PARSEI("true or false", *_doc[0], True); + PARSEI("false or false", *_doc[0], False); + PARSEI("false and true or true and true", *_doc[0], True); + PARSEI("false or true and true or false", *_doc[0], True); + PARSEI("not false", *_doc[0], True); + PARSEI("not true", *_doc[0], False); + PARSEI("true and not false or false", *_doc[0], True); + PARSEI("((243 < 300) and (\"FOO\".lowercase() == \"foo\"))", *_doc[0], True); + + // Invalid branching. testdoctype1.content = 1 is invalid + PARSE("testdoctype1.content = 1 and true", *_doc[0], Invalid); + PARSE("testdoctype1.content = 1 or true", *_doc[0], True); + PARSE("testdoctype1.content = 1 and false", *_doc[0], False); + PARSE("testdoctype1.content = 1 or false", *_doc[0], Invalid); + PARSE("true and testdoctype1.content = 1", *_doc[0], Invalid); + PARSE("true or testdoctype1.content = 1", *_doc[0], True); + PARSE("false and testdoctype1.content = 1", *_doc[0], False); + PARSE("false or testdoctype1.content = 1", *_doc[0], Invalid); +} + +void DocumentSelectParserTest::testOperators5() +{ + createDocs(); + + // Functions + PARSE("testdoctype1.hstringval.lowercase() == \"Yet\"", *_doc[3], False); + PARSE("testdoctype1.hstringval.lowercase() == \"yet\"", *_doc[3], True); + PARSE("testdoctype1.hfloatval.lowercase() == \"yet\"", *_doc[3], Invalid); + PARSEI("\"bar\".hash() == -2012135647395072713", *_doc[0], True); + PARSEI("\"bar\".hash().abs() == 2012135647395072713", *_doc[0], True); + PARSEI("null.hash() == 123", *_doc[0], Invalid); + PARSEI("(0.234).hash() == 123", *_doc[0], False); + PARSEI("(0.234).lowercase() == 123", *_doc[0], Invalid); + PARSE("\"foo\".hash() == 123", *_doc[0], False); + PARSEI("(234).hash() == 123", *_doc[0], False); + PARSE("now() > 1311862500", *_doc[10], True); + PARSE("now() < 1611862500", *_doc[10], True); + PARSE("now() < 1311862500", *_doc[10], False); + PARSE("now() > 1611862500", *_doc[10], False); + + // Arithmetics + PARSEI("id.specific.hash() % 10 = 8", *_doc[0], True); + PARSEI("id.specific.hash() % 10 = 2", *_doc[0], False); + PARSEI("\"foo\" + \"bar\" = \"foobar\"", *_doc[0], True); + PARSEI("\"foo\" + 4 = 25", *_doc[0], Invalid); + PARSEI("34.0 % 4 = 4", *_doc[0], Invalid); + PARSEI("-6 % 10 = -6", *_doc[0], True); +} + +void DocumentSelectParserTest::testOperators6() +{ + createDocs(); + + // CJK + // Assuming the characters " \ ? * is not used as part of CJK tokens + PARSE("testdoctype1.content=\"\xE4\xB8\xBA\xE4\xBB\x80\"", *_doc[3], True); + PARSE("testdoctype1.content=\"\xE4\xB7\xBA\xE4\xBB\x80\"", *_doc[3], False); + + // Structs and arrays + PARSE("testdoctype1.mystruct", *_doc[0], False); + PARSE("testdoctype1.mystruct", *_doc[1], True); + PARSE("testdoctype1.mystruct", *_doc[2], False); + PARSE("testdoctype1.mystruct == testdoctype1.mystruct", *_doc[0], True); + PARSE("testdoctype1.mystruct == testdoctype1.mystruct", *_doc[1], True); + PARSE("testdoctype1.mystruct != testdoctype1.mystruct", *_doc[0], False); + PARSE("testdoctype1.mystruct != testdoctype1.mystruct", *_doc[1], False); + PARSE("testdoctype1.mystruct < testdoctype1.mystruct", *_doc[0], Invalid); + PARSE("testdoctype1.mystruct < testdoctype1.mystruct", *_doc[1], False); + PARSE("testdoctype1.mystruct < 5", *_doc[1], False); + // PARSE("testdoctype1.mystruct == \"foo\"", *_doc[1], Invalid); + PARSE("testdoctype1.mystruct.key == 14", *_doc[0], False); + PARSE("testdoctype1.mystruct.value == \"structval\"", *_doc[0], False); + PARSE("testdoctype1.mystruct.key == 14", *_doc[1], True); + PARSE("testdoctype1.mystruct.value == \"structval\"", *_doc[1], True); + PARSE("testdoctype1.structarray", *_doc[0], False); + PARSE("testdoctype1.structarray", *_doc[1], True); + PARSE("testdoctype1.structarray", *_doc[2], False); + PARSE("testdoctype1.structarray == testdoctype1.structarray", + *_doc[0], True); + PARSE("testdoctype1.structarray < testdoctype1.structarray", + *_doc[0], Invalid); + PARSE("testdoctype1.structarray == testdoctype1.structarray", + *_doc[1], True); + PARSE("testdoctype1.structarray < testdoctype1.structarray", + *_doc[1], False); + PARSE("testdoctype1.headerlongval<0", *_doc[6], False); + PARSE("testdoctype1.headerlongval<0", *_doc[7], True); +} + +void DocumentSelectParserTest::testOperators7() +{ + createDocs(); + + PARSE("testdoctype1.structarray.key == 15", *_doc[0], False); + PARSE("testdoctype1.structarray[4].key == 15", *_doc[0], False); + PARSE("testdoctype1.structarray", *_doc[1], True); + PARSE("testdoctype1.structarray.key == 15", *_doc[1], True); + PARSE("testdoctype1.structarray[1].key == 16", *_doc[1], True); + PARSE("testdoctype1.structarray[1].key = 16", *_doc[1], True); + PARSE("testdoctype1.structarray.value == \"structval1\"", *_doc[0], False); + PARSE("testdoctype1.structarray[4].value == \"structval1\"", *_doc[0], False); + PARSE("testdoctype1.structarray.value == \"structval1\"", *_doc[1], True); + PARSE("testdoctype1.structarray[0].value == \"structval1\"", *_doc[1], True); + // Globbing of array-of-struct fields + PARSE("testdoctype1.structarray.key = 15", *_doc[0], False); + PARSE("testdoctype1.structarray.key = 15", *_doc[2], False); + PARSE("testdoctype1.structarray.key = 15", *_doc[1], True); + PARSE("testdoctype1.structarray.value = \"structval2\"", *_doc[2], Invalid); // Invalid due to lhs being NullValue + PARSE("testdoctype1.structarray.value = \"*ctval*\"", *_doc[1], True); + PARSE("testdoctype1.structarray[1].value = \"structval2\"", *_doc[1], True); + PARSE("testdoctype1.structarray[1].value = \"batman\"", *_doc[1], False); + // Regexp of array-of-struct fields + PARSE("testdoctype1.structarray.value =~ \"structval[1-9]\"", *_doc[1], True); + PARSE("testdoctype1.structarray.value =~ \"structval[a-z]\"", *_doc[1], False); + // Globbing/regexp of struct fields + PARSE("testdoctype1.mystruct.value = \"struc?val\"", *_doc[0], Invalid); // Invalid due to lhs being NullValue + PARSE("testdoctype1.mystruct.value = \"struc?val\"", *_doc[1], True); + PARSE("testdoctype1.mystruct.value =~ \"struct.*\"", *_doc[0], Invalid); // Ditto here + PARSE("testdoctype1.mystruct.value =~ \"struct.*\"", *_doc[1], True); + + PARSE("testdoctype1.structarray[$x].key == 15 AND testdoctype1.structarray[$x].value == \"structval1\"", *_doc[1], True); + PARSE("testdoctype1.structarray[$x].key == 15 AND testdoctype1.structarray[$x].value == \"structval2\"", *_doc[1], False); + PARSE("testdoctype1.structarray[$x].key == 15 AND testdoctype1.structarray[$y].value == \"structval2\"", *_doc[1], True); +} + +void DocumentSelectParserTest::testOperators8() +{ + createDocs(); + + PARSE("testdoctype1.mymap", *_doc[0], False); + PARSE("testdoctype1.mymap", *_doc[1], True); + PARSE("testdoctype1.mymap{3}", *_doc[1], True); + PARSE("testdoctype1.mymap{9}", *_doc[1], False); + PARSE("testdoctype1.mymap{3} == \"a\"", *_doc[1], True); + PARSE("testdoctype1.mymap{3} == \"b\"", *_doc[1], False); + PARSE("testdoctype1.mymap{9} == \"b\"", *_doc[1], False); + PARSE("testdoctype1.mymap.value == \"a\"", *_doc[1], True); + PARSE("testdoctype1.mymap.value == \"d\"", *_doc[1], False); + PARSE("testdoctype1.mymap{3} = \"a\"", *_doc[1], True); + PARSE("testdoctype1.mymap{3} = \"b\"", *_doc[1], False); + PARSE("testdoctype1.mymap{3} =~ \"a\"", *_doc[1], True); + PARSE("testdoctype1.mymap{3} =~ \"b\"", *_doc[1], False); + PARSE("testdoctype1.mymap.value = \"a\"", *_doc[1], True); + PARSE("testdoctype1.mymap.value = \"d\"", *_doc[1], False); + PARSE("testdoctype1.mymap.value =~ \"a\"", *_doc[1], True); + PARSE("testdoctype1.mymap.value =~ \"d\"", *_doc[1], False); + PARSE("testdoctype1.mymap == 3", *_doc[1], True); + PARSE("testdoctype1.mymap == 4", *_doc[1], False); + PARSE("testdoctype1.mymap = 3", *_doc[1], True); // Fallback to == + PARSE("testdoctype1.mymap = 4", *_doc[1], False); // Fallback to == + + PARSE("testdoctype1.structarrmap{$x}[$y].key == 15 AND testdoctype1.structarrmap{$x}[$y].value == \"structval1\"", *_doc[1], True); + PARSE("testdoctype1.structarrmap.value[$y].key == 15 AND testdoctype1.structarrmap.value[$y].value == \"structval1\"", *_doc[1], True); + PARSE("testdoctype1.structarrmap{$x}[$y].key == 15 AND testdoctype1.structarrmap{$x}[$y].value == \"structval2\"", *_doc[1], False); + PARSE("testdoctype1.structarrmap.value[$y].key == 15 AND testdoctype1.structarrmap.value[$y].value == \"structval2\"", *_doc[1], False); + PARSE("testdoctype1.structarrmap{$x}[$y].key == 15 AND testdoctype1.structarrmap{$y}[$x].value == \"structval2\"", *_doc[1], False); +} + +void DocumentSelectParserTest::testOperators9() +{ + createDocs(); + + PARSE("testdoctype1.stringweightedset", *_doc[1], True); + PARSE("testdoctype1.stringweightedset{val1}", *_doc[1], True); + PARSE("testdoctype1.stringweightedset{val1} == 1", *_doc[1], True); + PARSE("testdoctype1.stringweightedset{val1} == 2", *_doc[1], False); + PARSE("testdoctype1.stringweightedset == \"val1\"", *_doc[1], True); + PARSE("testdoctype1.stringweightedset = \"val*\"", *_doc[1], True); + PARSE("testdoctype1.stringweightedset =~ \"val[0-9]\"", *_doc[1], True); + PARSE("testdoctype1.stringweightedset == \"val5\"", *_doc[1], False); + PARSE("testdoctype1.stringweightedset = \"val5\"", *_doc[1], False); + PARSE("testdoctype1.stringweightedset =~ \"val5\"", *_doc[1], False); + + PARSE("testdoctype1.structarrmap{$x}.key == 15 AND testdoctype1.stringweightedset{$x}", *_doc[1], True); + PARSE("testdoctype1.structarrmap{$x}.key == 17 AND testdoctype1.stringweightedset{$x}", *_doc[1], False); + + PARSE("testdoctype1.structarray.key < 16", *_doc[1], True); + PARSE("testdoctype1.structarray.key < 15", *_doc[1], False); + PARSE("testdoctype1.structarray.key > 15", *_doc[1], True); + PARSE("testdoctype1.structarray.key > 16", *_doc[1], False); + PARSE("testdoctype1.structarray.key <= 15", *_doc[1], True); + PARSE("testdoctype1.structarray.key <= 14", *_doc[1], False); + PARSE("testdoctype1.structarray.key >= 16", *_doc[1], True); + PARSE("testdoctype1.structarray.key >= 17", *_doc[1], False); +} + +namespace { + + class TestVisitor : public select::Visitor { + private: + std::ostringstream data; + + public: + virtual ~TestVisitor() {} + + void visitConstant(const select::Constant& node) + { + data << "CONSTANT(" << node << ")"; + } + + virtual void + visitInvalidConstant(const select::InvalidConstant& node) + { + data << "INVALIDCONSTANT(" << node << ")"; + } + + void visitDocumentType(const select::DocType& node) + { + data << "DOCTYPE(" << node << ")"; + } + + void visitComparison(const select::Compare& node) + { + data << "COMPARE(" << node.getLeft() << " " + << node.getOperator() << " " << node.getRight() << ")"; + } + + void visitAndBranch(const select::And& node) + { + data << "AND("; + node.getLeft().visit(*this); + data << ", "; + node.getRight().visit(*this); + data << ")"; + } + + void visitOrBranch(const select::Or& node) + { + data << "OR("; + node.getLeft().visit(*this); + data << ", "; + node.getRight().visit(*this); + data << ")"; + } + + void visitNotBranch(const select::Not& node) + { + data << "NOT("; + node.getChild().visit(*this); + data << ")"; + } + + virtual void + visitArithmeticValueNode(const select::ArithmeticValueNode &) + { + } + + virtual void + visitFunctionValueNode(const select::FunctionValueNode &) + { + } + + virtual void + visitIdValueNode(const select::IdValueNode &) + { + } + + virtual void + visitSearchColumnValueNode(const select::SearchColumnValueNode &) + { + } + + virtual void + visitFieldValueNode(const select::FieldValueNode &) + { + } + + virtual void + visitFloatValueNode(const select::FloatValueNode &) + { + } + + virtual void + visitVariableValueNode(const select::VariableValueNode &) + { + } + + virtual void + visitIntegerValueNode(const select::IntegerValueNode &) + { + } + + virtual void + visitCurrentTimeValueNode(const select::CurrentTimeValueNode &) + { + } + + virtual void + visitStringValueNode(const select::StringValueNode &) + { + } + + virtual void + visitNullValueNode(const select::NullValueNode &) + { + } + + virtual void + visitInvalidValueNode(const select::InvalidValueNode &) + { + } + + std::string getVisitString() { return data.str(); } + }; + +} + +void DocumentSelectParserTest::testVisitor() +{ + createDocs(); + + std::unique_ptr<select::Node> root(_parser->parse( + "true or testdoctype1 and (not id.user = 12 or testdoctype1.hstringval = \"ola\") and " + "testdoctype1.headerval")); + + TestVisitor v; + root->visit(v); + std::string expected = + "OR(CONSTANT(true), " + "AND(DOCTYPE(testdoctype1), " + "AND(OR(NOT(COMPARE(id.user = 12)), " + "COMPARE(testdoctype1.hstringval = \"ola\")), " + "COMPARE(testdoctype1.headerval != null)" + ")" + ")" + ")"; + CPPUNIT_ASSERT_EQUAL(expected, v.getVisitString()); +} + +void DocumentSelectParserTest::testBodyFieldDetection() +{ + + { + std::unique_ptr<select::Node> root(_parser->parse("testdoctype1")); + select::BodyFieldDetector detector(*_repo); + + root->visit(detector); + CPPUNIT_ASSERT(!detector.foundBodyField); + CPPUNIT_ASSERT(detector.foundHeaderField); + } + + { + std::unique_ptr<select::Node> root(_parser->parse("testdoctype1 AND id.user=1234")); + select::BodyFieldDetector detector(*_repo); + + root->visit(detector); + CPPUNIT_ASSERT(!detector.foundBodyField); + CPPUNIT_ASSERT(detector.foundHeaderField); + } + + { + std::unique_ptr<select::Node> root(_parser->parse("testdoctype1.headerval=123")); + select::BodyFieldDetector detector(*_repo); + + root->visit(detector); + CPPUNIT_ASSERT(!detector.foundBodyField); + CPPUNIT_ASSERT(detector.foundHeaderField); + } + + { + std::unique_ptr<select::Node> root(_parser->parse("testdoctype1.content")); + select::BodyFieldDetector detector(*_repo); + + root->visit(detector); + CPPUNIT_ASSERT(detector.foundBodyField); + } + + { + std::unique_ptr<select::Node> root(_parser->parse( + "true or testdoctype1 and (not id.user = 12 or testdoctype1.hstringval = \"ola\") and " + "testdoctype1.headerval")); + + select::BodyFieldDetector detector(*_repo); + + root->visit(detector); + CPPUNIT_ASSERT(!detector.foundBodyField); + } + +} + +void DocumentSelectParserTest::testDocumentUpdates() +{ + testDocumentUpdates0(); + testDocumentUpdates1(); + testDocumentUpdates2(); + testDocumentUpdates3(); + testDocumentUpdates4(); +} + +void DocumentSelectParserTest::testDocumentUpdates0() +{ + createDocs(); + + /* Code for tracing result to see what went wrong + { + std::ostringstream ost; + _parser->parse("id.bucket == 4006")->trace(*_update[0], ost); + ost << "\n\n"; + _parser->parse("id.bucket == 4006") + ->trace(_update[0]->getId(), ost); + std::cerr << ost.str() << "\n"; + } // */ + PARSEI("", *_update[0], True); + PARSEI("30 < 10", *_update[0], False); + PARSEI("10 < 30", *_update[0], True); + PARSEI("30 < 10", *_update[0], False); + PARSEI("10 < 30", *_update[0], True); + PARSEI("30 <= 10", *_update[0], False); + PARSEI("10 <= 30", *_update[0], True); + PARSEI("30 <= 30", *_update[0], True); + PARSEI("10 >= 30", *_update[0], False); + PARSEI("30 >= 10", *_update[0], True); + PARSEI("30 >= 30", *_update[0], True); + PARSEI("10 > 30", *_update[0], False); + PARSEI("30 > 10", *_update[0], True); + PARSEI("30 == 10", *_update[0], False); + PARSEI("30 == 30", *_update[0], True); + PARSEI("30 != 10", *_update[0], True); + PARSEI("30 != 30", *_update[0], False); + PARSEI("\"foo\" != \"bar\"", *_update[0], True); + PARSEI("\"foo\" != \"foo\"", *_update[0], False); + PARSEI("\"foo\" == \"bar\"", *_update[0], False); + PARSEI("\"foo\" == \"foo\"", *_update[0], True); + PARSEI("\"bar\" = \"a\"", *_update[0], False); + PARSEI("\"bar\" = \"*a*\"", *_update[0], True); + PARSEI("\"bar\" = \"\"", *_update[0], False); + PARSEI("\"\" = \"\"", *_update[0], True); + PARSEI("\"bar\" =~ \"^a$\"", *_update[0], False); + PARSEI("\"bar\" =~ \"a\"", *_update[0], True); + PARSEI("\"bar\" =~ \"\"", *_update[0], True); + PARSEI("\"\" =~ \"\"", *_update[0], True); + PARSEI("30 = 10", *_update[0], False); + PARSEI("30 = 30", *_update[0], True); +} + +void DocumentSelectParserTest::testDocumentUpdates1() +{ + createDocs(); + + // Mix of types should within numbers, but otherwise not match + PARSEI("30 < 10.2", *_update[0], False); + PARSEI("10.2 < 30", *_update[0], True); + PARSEI("30 < \"foo\"", *_update[0], Invalid); + PARSEI("30 > \"foo\"", *_update[0], Invalid); + PARSEI("30 != \"foo\"", *_update[0], Invalid); + PARSEI("14.2 <= \"foo\"", *_update[0], Invalid); + PARSEI("null == null", *_update[0], True); + PARSEI("null = null", *_update[0], True); + PARSEI("\"bar\" == null", *_update[0], False); + PARSEI("14.3 == null", *_update[0], False); + PARSEI("null = 0", *_update[0], False); + + // Field values + PARSE("testdoctype1.headerval = 24", *_update[0], Invalid); + PARSE("testdoctype1.hfloatval = 2.0", *_update[0], Invalid); + PARSE("testdoctype1.content = \"bar\"", *_update[0], Invalid); + PARSE("testdoctype1.hstringval == testdoctype1.content", *_update[0], Invalid); + + // Document types + PARSE("testdoctype1", *_update[0], True); + PARSE("testdoctype2", *_update[0], False); + + // Inherited doctypes + PARSE("testdoctype2", *_update[4], True); + PARSE("testdoctype2", *_update[3], False); + PARSE("testdoctype1", *_update[4], True); + PARSE("testdoctype1.headerval = 10", *_update[4], Invalid); +} + +void DocumentSelectParserTest::testDocumentUpdates2() +{ + createDocs(); + + // Id values + PARSEI("id == \"doc:myspace:anything\"", *_update[0], True); + PARSEI(" iD== \"doc:myspace:anything\" ", *_update[0], True); + PARSEI("id == \"doc:myspa:nything\"", *_update[0], False); + PARSEI("Id.scHeme == \"doc\"", *_update[0], True); + PARSEI("id.scheme == \"userdoc\"", *_update[0], False); + PARSEI("Id.namespaCe == \"myspace\"", *_update[0], True); + PARSEI("id.NaMespace == \"pace\"", *_update[0], False); + PARSEI("id.specific == \"anything\"", *_update[0], True); + PARSEI("id.user=1234", *_update[2], True); + PARSEI("id.user == 1234", *_update[0], Invalid); + PARSEI("id.group == 1234", *_update[3], Invalid); + PARSEI("id.group == \"yahoo\"", *_update[3], True); + PARSEI("id.bucket == 1234", *_update[0], False); + { + std::ostringstream ost; + ost << "id.bucket == " << BucketId(16, 4006).getId(); + PARSEI(ost.str(), *_update[0], True); + } + PARSEI("id.bucket == \"foo\"", *_update[0], Invalid); +} + +void DocumentSelectParserTest::testDocumentUpdates3() +{ + createDocs(); + + // Branch operators + PARSEI("true and false", *_update[0], False); + PARSEI("true and true", *_update[0], True); + PARSEI("true or false", *_update[0], True); + PARSEI("false or false", *_update[0], False); + PARSEI("false and true or true and true", *_update[0], True); + PARSEI("false or true and true or false", *_update[0], True); + PARSEI("not false", *_update[0], True); + PARSEI("not true", *_update[0], False); + PARSEI("true and not false or false", *_update[0], True); + PARSEI("((243 < 300) and (\"FOO\".lowercase() == \"foo\"))", *_update[0], True); + + // Invalid branching. testdoctype1.content = 1 is invalid + PARSE("testdoctype1.content = 1 and true", *_update[0], Invalid); + PARSE("testdoctype1.content = 1 or true", *_update[0], True); + PARSE("testdoctype1.content = 1 and false", *_update[0], False); + PARSE("testdoctype1.content = 1 or false", *_update[0], Invalid); + PARSE("true and testdoctype1.content = 1", *_update[0], Invalid); + PARSE("true or testdoctype1.content = 1", *_update[0], True); + PARSE("false and testdoctype1.content = 1", *_update[0], False); + PARSE("false or testdoctype1.content = 1", *_update[0], Invalid); +} + +void DocumentSelectParserTest::testDocumentUpdates4() +{ + createDocs(); + + // Functions + PARSEI("\"bar\".hash() == -2012135647395072713", *_update[0], True); + PARSEI("\"bar\".hash().abs() == 2012135647395072713", *_update[0], True); + PARSEI("null.hash() == 123", *_update[0], Invalid); + PARSEI("(0.234).hash() == 123", *_update[0], False); + PARSEI("(0.234).lowercase() == 123", *_update[0], Invalid); + PARSEI("\"foo\".hash() == 123", *_update[0], False); + PARSEI("(234).hash() == 123", *_update[0], False); + + // Arithmetics + PARSEI("id.specific.hash() % 10 = 8", *_update[0], True); + PARSEI("id.specific.hash() % 10 = 2", *_update[0], False); + PARSEI("\"foo\" + \"bar\" = \"foobar\"", *_update[0], True); + PARSEI("\"foo\" + 4 = 25", *_update[0], Invalid); + PARSEI("34.0 % 4 = 4", *_update[0], Invalid); + PARSEI("-6 % 10 = -6", *_update[0], True); +} + +void DocumentSelectParserTest::testUtf8() +{ + createDocs(); + std::string utf8name(u8"H\u00e5kon"); + CPPUNIT_ASSERT_EQUAL(size_t(6), utf8name.size()); + + /// \todo TODO (was warning): UTF8 test for glob/regex support in selection language disabled. Known not to work +// boost::u32regex rx = boost::make_u32regex("H.kon"); +// CPPUNIT_ASSERT_EQUAL(true, boost::u32regex_match(utf8name, rx)); + + _doc.push_back(createDoc( + "testdoctype1", "doc:myspace:utf8doc", 24, 2.0, utf8name, "bar")); +// PARSE("testdoctype1.hstringval = \"H?kon\"", *_doc[_doc.size()-1], True); +// PARSE("testdoctype1.hstringval =~ \"H.kon\"", *_doc[_doc.size()-1], True); +} + +} // document diff --git a/document/src/tests/documentselecttest.h b/document/src/tests/documentselecttest.h new file mode 100644 index 00000000000..d8cb4d427e1 --- /dev/null +++ b/document/src/tests/documentselecttest.h @@ -0,0 +1,34 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/* $Id$*/ +#pragma once + +#include <cppunit/extensions/HelperMacros.h> + +class DocumentSelect_Test : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE( DocumentSelect_Test); + CPPUNIT_TEST(testEquals); + CPPUNIT_TEST(testLt); + CPPUNIT_TEST(testGt); + CPPUNIT_TEST(testAnd); + CPPUNIT_TEST(testOr); + CPPUNIT_TEST(testNot); + CPPUNIT_TEST(testConfig1); + CPPUNIT_TEST(testConfig2); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + + void tearDown(); +protected: + void testEquals(); + void testLt(); + void testGt(); + void testAnd(); + void testOr(); + void testNot(); + void testConfig1(); + void testConfig2(); +}; + + diff --git a/document/src/tests/documenttestcase.cpp b/document/src/tests/documenttestcase.cpp new file mode 100644 index 00000000000..475cf64128e --- /dev/null +++ b/document/src/tests/documenttestcase.cpp @@ -0,0 +1,1434 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <vespa/document/base/testdocman.h> +#include <vespa/document/base/testdocrepo.h> +#include <vespa/document/fieldvalue/fieldvalues.h> +#include <vespa/vespalib/io/fileutil.h> +#include <vespa/vdstestlib/cppunit/macros.h> + +#include <vespa/document/annotation/spantree.h> +#include <vespa/document/config/config-documenttypes.h> +#include <vespa/document/datatype/annotationreferencedatatype.h> +#include <vespa/document/repo/configbuilder.h> +#include <vespa/document/repo/documenttyperepo.h> +#include <vespa/document/serialization/vespadocumentdeserializer.h> +#include <vespa/document/serialization/vespadocumentserializer.h> +#include <vespa/vespalib/objects/nbostream.h> +#include <fstream> + +using vespalib::nbostream; + +using namespace document::config_builder; + +namespace document { + +struct DocumentTest : public CppUnit::TestFixture { + void testTraversing(); + void testFieldPath(); + void testModifyDocument(); + void testVariables(); + void testSimpleUsage(); + void testReadSerializedFile(); + void testReadSerializedFileCompressed(); + void testReadSerializedAllVersions(); + void testGenerateSerializedFile(); + void testGetURIFromSerialized(); + void testBogusserialize(); + void testCRC32(); + void testHasChanged(); + void testSplitSerialization(); + void testSliceSerialize(); + void testCompression(); + void testCompressionConfigured(); + void testUnknownEntries(); + void testAnnotationDeserialization(); + void testGetSerializedSize(); + void testDeserializeMultiple(); + void testSizeOf(); + + CPPUNIT_TEST_SUITE(DocumentTest); + CPPUNIT_TEST(testFieldPath); + CPPUNIT_TEST(testTraversing); + CPPUNIT_TEST(testModifyDocument); + CPPUNIT_TEST(testVariables); + CPPUNIT_TEST(testSimpleUsage); + CPPUNIT_TEST(testReadSerializedFile); + CPPUNIT_TEST(testReadSerializedFileCompressed); + CPPUNIT_TEST(testReadSerializedAllVersions); + CPPUNIT_TEST(testGenerateSerializedFile); + CPPUNIT_TEST(testGetURIFromSerialized); + CPPUNIT_TEST(testBogusserialize); + CPPUNIT_TEST(testCRC32); + CPPUNIT_TEST(testHasChanged); + CPPUNIT_TEST(testSplitSerialization); + CPPUNIT_TEST(testSliceSerialize); + CPPUNIT_TEST(testCompression); + CPPUNIT_TEST(testCompressionConfigured); + CPPUNIT_TEST(testUnknownEntries); + CPPUNIT_TEST(testAnnotationDeserialization); + CPPUNIT_TEST(testGetSerializedSize); + CPPUNIT_TEST(testDeserializeMultiple); + CPPUNIT_TEST(testSizeOf); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(DocumentTest); + +void DocumentTest::testSizeOf() +{ + CPPUNIT_ASSERT_EQUAL(120ul, sizeof(Document)); + CPPUNIT_ASSERT_EQUAL(64ul, sizeof(StructFieldValue)); + CPPUNIT_ASSERT_EQUAL(16ul, sizeof(StructuredFieldValue)); + CPPUNIT_ASSERT_EQUAL(112ul, sizeof(SerializableArray)); +} + +void DocumentTest::testFieldPath() +{ + FieldPathEntry empty; + empty.asString(); + const vespalib::string testValues[] = { "{}", "", "", + "{}r", "", "r", + "{{}}", "{", "}", + "{{}}r", "{", "}r", + "{\"{}\"}", "{}", "", + "{\"{}\"}r", "{}", "r", + "{\"{\\a}\"}r", "{a}", "r", + "{\"{\\\"}\"}r", "{\"}", "r", + "{\"{\\\\}\"}r", "{\\}", "r", + "{$x}", "$x", "", + "{$x}[$y]", "$x", "[$y]", + "{$x}.ss", "$x", ".ss", + "{\"\"}", "", "" + }; + for (size_t i(0); i < sizeof(testValues)/sizeof(testValues[0]); i+=3) { + vespalib::string tmp = testValues[i]; + vespalib::string key = FieldPathEntry::parseKey(tmp); + CPPUNIT_ASSERT_EQUAL(testValues[i+1], key); + CPPUNIT_ASSERT_EQUAL(testValues[i+2], tmp); + } +} + +void DocumentTest::testTraversing() +{ + class Handler : public FieldValue::IteratorHandler { + public: + const std::string & getResult() const { return _result; } + private: + virtual void onPrimitive(const Content&) { _result += 'P'; } + virtual void onCollectionStart(const Content&) { _result += '['; } + virtual void onCollectionEnd(const Content&) { _result += ']'; } + virtual void onStructStart(const Content&) { _result += '<'; } + virtual void onStructEnd(const Content&) { _result += '>'; } + std::string _result; + }; + + Field primitive1("primitive1", 1, *DataType::INT, true); + Field primitive2("primitive2", 2, *DataType::INT, true); + StructDataType struct1("struct1"); + struct1.addField(primitive1); + struct1.addField(primitive2); + + ArrayDataType iarr(*DataType::INT); + ArrayDataType sarr(struct1); + Field iarrF("iarray", 21, iarr, true); + Field sarrF("sarray", 22, sarr, true); + + StructDataType struct2("struct2"); + struct2.addField(primitive1); + struct2.addField(primitive2); + struct2.addField(iarrF); + struct2.addField(sarrF); + Field s2("ss", 12, struct2, true); + + StructDataType struct3("struct3"); + struct3.addField(primitive1); + struct3.addField(s2); + + Field structl1s1("l1s1", 11, struct3, true); + + DocumentType type("test"); + type.addField(primitive1); + type.addField(structl1s1); + + Document doc(type, DocumentId("doc::testdoc")); + doc.setValue(primitive1, IntFieldValue(1)); + + StructFieldValue l1s1(struct3); + l1s1.setValue(primitive1, IntFieldValue(2)); + + StructFieldValue l2s1(struct2); + l2s1.setValue(primitive1, IntFieldValue(3)); + l2s1.setValue(primitive2, IntFieldValue(4)); + ArrayFieldValue iarr1(iarr); + iarr1.add(IntFieldValue(11)); + iarr1.add(IntFieldValue(12)); + iarr1.add(IntFieldValue(13)); + ArrayFieldValue sarr1(sarr); + StructFieldValue l3s1(struct1); + l3s1.setValue(primitive1, IntFieldValue(1)); + l3s1.setValue(primitive2, IntFieldValue(2)); + sarr1.add(l3s1); + sarr1.add(l3s1); + l2s1.setValue(iarrF, iarr1); + l2s1.setValue(sarrF, sarr1); + + l1s1.setValue(s2, l2s1); + doc.setValue(structl1s1, l1s1); + + Handler fullTraverser; + FieldPath empty; + doc.iterateNested(empty.begin(), empty.end(), fullTraverser); + CPPUNIT_ASSERT_EQUAL(fullTraverser.getResult(), + std::string("<P<P<PP[PPP][<PP><PP>]>>>")); +} + +class VariableIteratorHandler : public FieldValue::IteratorHandler { +public: + std::string retVal; + + virtual void onPrimitive(const Content & fv) { + for (VariableMap::iterator iter = getVariables().begin(); iter != getVariables().end(); iter++) { + retVal += vespalib::make_string("%s: %s,", iter->first.c_str(), iter->second.toString().c_str()); + } + retVal += " - " + fv.getValue().toString() + "\n"; + }; +}; + +void +DocumentTest::testVariables() +{ + ArrayDataType iarr(*DataType::INT); + ArrayDataType iiarr(static_cast<DataType &>(iarr)); + ArrayDataType iiiarr(static_cast<DataType &>(iiarr)); + + Field iiiarrF("iiiarray", 1, iiiarr, true); + DocumentType type("test"); + type.addField(iiiarrF); + + ArrayFieldValue iiiaV(iiiarr); + for (int i = 1; i < 4; i++) { + ArrayFieldValue iiaV(iiarr); + for (int j = 1; j < 4; j++) { + ArrayFieldValue iaV(iarr); + for (int k = 1; k < 4; k++) { + iaV.add(IntFieldValue(i * j * k)); + } + iiaV.add(iaV); + } + iiiaV.add(iiaV); + } + + Document doc(type, DocumentId("doc::testdoc")); + doc.setValue(iiiarrF, iiiaV); + + { + VariableIteratorHandler handler; + FieldPath::UP path = type.buildFieldPath("iiiarray[$x][$y][$z]"); + doc.iterateNested(path->begin(), path->end(), handler); + + std::string fasit = + "x: 0,y: 0,z: 0, - 1\n" + "x: 0,y: 0,z: 1, - 2\n" + "x: 0,y: 0,z: 2, - 3\n" + "x: 0,y: 1,z: 0, - 2\n" + "x: 0,y: 1,z: 1, - 4\n" + "x: 0,y: 1,z: 2, - 6\n" + "x: 0,y: 2,z: 0, - 3\n" + "x: 0,y: 2,z: 1, - 6\n" + "x: 0,y: 2,z: 2, - 9\n" + "x: 1,y: 0,z: 0, - 2\n" + "x: 1,y: 0,z: 1, - 4\n" + "x: 1,y: 0,z: 2, - 6\n" + "x: 1,y: 1,z: 0, - 4\n" + "x: 1,y: 1,z: 1, - 8\n" + "x: 1,y: 1,z: 2, - 12\n" + "x: 1,y: 2,z: 0, - 6\n" + "x: 1,y: 2,z: 1, - 12\n" + "x: 1,y: 2,z: 2, - 18\n" + "x: 2,y: 0,z: 0, - 3\n" + "x: 2,y: 0,z: 1, - 6\n" + "x: 2,y: 0,z: 2, - 9\n" + "x: 2,y: 1,z: 0, - 6\n" + "x: 2,y: 1,z: 1, - 12\n" + "x: 2,y: 1,z: 2, - 18\n" + "x: 2,y: 2,z: 0, - 9\n" + "x: 2,y: 2,z: 1, - 18\n" + "x: 2,y: 2,z: 2, - 27\n"; + + CPPUNIT_ASSERT_EQUAL(fasit, handler.retVal); + } + +} + +class ModifyIteratorHandler : public FieldValue::IteratorHandler { +public: + ModificationStatus doModify(FieldValue& fv) { + StringFieldValue* sfv = dynamic_cast<StringFieldValue*>(&fv); + if (sfv != NULL) { + *sfv = std::string("newvalue"); + return MODIFIED; + } + + return NOT_MODIFIED; + }; + + bool onComplex(const Content&) { + return false; + } +}; + +void +DocumentTest::testModifyDocument() +{ + // Create test document type and content + Field primitive1("primitive1", 1, *DataType::INT, true); + Field primitive2("primitive2", 2, *DataType::INT, true); + StructDataType struct1("struct1"); + struct1.addField(primitive1); + struct1.addField(primitive2); + + ArrayDataType iarr(*DataType::INT); + ArrayDataType sarr(struct1); + Field iarrF("iarray", 21, iarr, true); + Field sarrF("sarray", 22, sarr, true); + + MapDataType smap(*DataType::STRING, *DataType::STRING); + Field smapF("smap", 23, smap, true); + + StructDataType struct2("struct2"); + struct2.addField(primitive1); + struct2.addField(primitive2); + struct2.addField(iarrF); + struct2.addField(sarrF); + struct2.addField(smapF); + Field s2("ss", 12, struct2, true); + + MapDataType structmap(*DataType::STRING, struct2); + Field structmapF("structmap", 24, structmap, true); + + WeightedSetDataType wset(*DataType::STRING, false, false); + Field wsetF("wset", 25, wset, true); + + WeightedSetDataType structwset(struct2, false, false); + Field structwsetF("structwset", 26, structwset, true); + + StructDataType struct3("struct3"); + struct3.addField(primitive1); + struct3.addField(s2); + struct3.addField(structmapF); + struct3.addField(wsetF); + struct3.addField(structwsetF); + + Field structl1s1("l1s1", 11, struct3, true); + + DocumentType type("test"); + type.addField(primitive1); + type.addField(structl1s1); + + Document::UP doc(new Document(type, DocumentId("doc::testdoc"))); + doc->setValue(primitive1, IntFieldValue(1)); + + StructFieldValue l1s1(struct3); + l1s1.setValue(primitive1, IntFieldValue(2)); + + StructFieldValue l2s1(struct2); + l2s1.setValue(primitive1, IntFieldValue(3)); + l2s1.setValue(primitive2, IntFieldValue(4)); + StructFieldValue l2s2(struct2); + l2s2.setValue(primitive1, IntFieldValue(5)); + l2s2.setValue(primitive2, IntFieldValue(6)); + ArrayFieldValue iarr1(iarr); + iarr1.add(IntFieldValue(11)); + iarr1.add(IntFieldValue(12)); + iarr1.add(IntFieldValue(13)); + ArrayFieldValue sarr1(sarr); + StructFieldValue l3s1(struct1); + l3s1.setValue(primitive1, IntFieldValue(1)); + l3s1.setValue(primitive2, IntFieldValue(2)); + sarr1.add(l3s1); + sarr1.add(l3s1); + MapFieldValue smap1(smap); + smap1.put(StringFieldValue("leonardo"), StringFieldValue("dicaprio")); + smap1.put(StringFieldValue("ellen"), StringFieldValue("page")); + smap1.put(StringFieldValue("joseph"), StringFieldValue("gordon-levitt")); + l2s1.setValue(smapF, smap1); + l2s1.setValue(iarrF, iarr1); + l2s1.setValue(sarrF, sarr1); + + l1s1.setValue(s2, l2s1); + MapFieldValue structmap1(structmap); + structmap1.put(StringFieldValue("test"), l2s1); + l1s1.setValue(structmapF, structmap1); + + WeightedSetFieldValue wset1(wset); + wset1.add("foo"); + wset1.add("bar"); + wset1.add("zoo"); + l1s1.setValue(wsetF, wset1); + + WeightedSetFieldValue wset2(structwset); + wset2.add(l2s1); + wset2.add(l2s2); + l1s1.setValue(structwsetF, wset2); + + doc->setValue(structl1s1, l1s1); + + // Begin test proper + + doc->print(std::cerr, true, ""); + + ModifyIteratorHandler handler; + + FieldPath::UP path + = doc->getDataType()->buildFieldPath("l1s1.structmap.value.smap{leonardo}"); + doc->iterateNested(path->begin(), path->end(), handler); + + doc->print(std::cerr, true, ""); +} + +void DocumentTest::testSimpleUsage() +{ + DocumentType::SP type(new DocumentType("test")); + Field intF("int", 1, *DataType::INT, true); + Field longF("long", 2, *DataType::LONG, true); + Field strF("content", 4, *DataType::STRING, false); + + type->addField(intF); + type->addField(longF); + type->addField(strF); + + DocumentTypeRepo repo(*type); + Document value(*repo.getDocumentType("test"), DocumentId("doc::testdoc")); + + // Initially empty + CPPUNIT_ASSERT_EQUAL(size_t(0), value.getSetFieldCount()); + CPPUNIT_ASSERT(!value.hasValue(intF)); + + value.setValue(intF, IntFieldValue(1)); + + // Not empty + CPPUNIT_ASSERT_EQUAL(size_t(1), value.getSetFieldCount()); + CPPUNIT_ASSERT(value.hasValue(intF)); + + // Adding some more + value.setValue(longF, LongFieldValue(2)); + + // Not empty + CPPUNIT_ASSERT_EQUAL(size_t(2), value.getSetFieldCount()); + CPPUNIT_ASSERT_EQUAL(1, value.getValue(intF)->getAsInt()); + CPPUNIT_ASSERT_EQUAL(2, value.getValue(longF)->getAsInt()); + + // Serialize & equality + std::unique_ptr<ByteBuffer> buffer(value.serialize()); + buffer->flip(); + Document value2(*repo.getDocumentType("test"), + DocumentId("userdoc::3:foo")); + CPPUNIT_ASSERT(value != value2); + value2.deserialize(repo, *buffer); + CPPUNIT_ASSERT(value2.hasValue(intF)); + CPPUNIT_ASSERT_EQUAL(value, value2); + CPPUNIT_ASSERT_EQUAL(DocumentId("doc::testdoc"), value2.getId()); + + // Various ways of removing + { + // By value + buffer->setPos(0); + value2.deserialize(repo, *buffer); + value2.remove(intF); + CPPUNIT_ASSERT(!value2.hasValue(intF)); + CPPUNIT_ASSERT_EQUAL(size_t(1), value2.getSetFieldCount()); + + // Clearing all + buffer->setPos(0); + value2.deserialize(repo, *buffer); + value2.clear(); + CPPUNIT_ASSERT(!value2.hasValue(intF)); + CPPUNIT_ASSERT_EQUAL(size_t(0), value2.getSetFieldCount()); + } + + // Updating + value2 = value; + CPPUNIT_ASSERT_EQUAL(value, value2); + value2.setValue(strF, StringFieldValue("foo")); + CPPUNIT_ASSERT(value2.hasValue(strF)); + CPPUNIT_ASSERT_EQUAL(vespalib::string("foo"), + value2.getValue(strF)->getAsString()); + CPPUNIT_ASSERT(value != value2); + value2.assign(value); + CPPUNIT_ASSERT_EQUAL(value, value2); + Document::UP valuePtr(value2.clone()); + CPPUNIT_ASSERT_EQUAL(value, *valuePtr); + + // Iterating + const Document& constVal(value); + for(Document::const_iterator it = constVal.begin(); + it != constVal.end(); ++it) + { + constVal.getValue(it.field()); + } + + // Comparison + value2 = value; + CPPUNIT_ASSERT_EQUAL(0, value.compare(value2)); + value2.remove(intF); + CPPUNIT_ASSERT(value.compare(value2) < 0); + CPPUNIT_ASSERT(value2.compare(value) > 0); + value2 = value; + value2.setValue(intF, IntFieldValue(5)); + CPPUNIT_ASSERT(value.compare(value2) < 0); + CPPUNIT_ASSERT(value2.compare(value) > 0); + + // Output + CPPUNIT_ASSERT_EQUAL( + std::string("Document(doc::testdoc, DocumentType(test))"), + value.toString(false)); + CPPUNIT_ASSERT_EQUAL( + std::string( +" Document(doc::testdoc\n" +" DocumentType(test, id -877171244)\n" +" : DocumentType(document) {\n" +" StructDataType(test.header, id 306916075) {\n" +" Field(content, id 4, PrimitiveDataType(String, id 2), body)\n" +" Field(int, id 1, NumericDataType(Int, id 0), header)\n" +" Field(long, id 2, NumericDataType(Long, id 4), header)\n" +" }\n" +" }\n" +" int: 1\n" +" long: 2\n" +" )"), + " " + value.toString(true, " ")); + CPPUNIT_ASSERT_EQUAL( + std::string( + "<document documenttype=\"test\" documentid=\"doc::testdoc\">\n" + " <int>1</int>\n" + " <long>2</long>\n" + "</document>"), + value.toXml(" ")); + + // Failure situations. + + // Fetch a field not existing in type + // (Would be nice if this failed, but whole idea to fetch by field + // objects is to improve performance.) + Field anotherIntF("int", 17, *DataType::INT, true); + CPPUNIT_ASSERT(!value.hasValue(anotherIntF)); + CPPUNIT_ASSERT(value.getValue(anotherIntF).get() == 0); + + // Refuse to accept non-document types + try{ + StructDataType otherType("foo", 4); + Document value6(otherType, DocumentId("doc::")); + CPPUNIT_FAIL("Didn't complain about non-document type"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot generate a document with " + "non-document type", e.what()); + } + + // Refuse to set wrong types + try{ + value2.setValue(intF, StringFieldValue("bar")); + CPPUNIT_FAIL("Failed to check type equality in setValue"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot assign value of type", e.what()); + } +} + +void verifyJavaDocument(Document& doc) +{ + IntFieldValue intVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("intfield"), intVal)); + CPPUNIT_ASSERT_EQUAL(5, intVal.getAsInt()); + + FloatFieldValue floatVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("floatfield"), floatVal)); + CPPUNIT_ASSERT(floatVal.getAsFloat() == (float) -9.23); + + StringFieldValue stringVal(""); + CPPUNIT_ASSERT(doc.getValue(doc.getField("stringfield"), stringVal)); + CPPUNIT_ASSERT_EQUAL(vespalib::string("This is a string."), + stringVal.getAsString()); + + LongFieldValue longVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("longfield"), longVal)); + CPPUNIT_ASSERT_EQUAL((int64_t)398420092938472983LL, longVal.getAsLong()); + + DoubleFieldValue doubleVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("doublefield"), doubleVal)); + CPPUNIT_ASSERT_EQUAL(doubleVal.getAsDouble(), 98374532.398820); + + ByteFieldValue byteVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("bytefield"), byteVal)); + CPPUNIT_ASSERT_EQUAL(-2, byteVal.getAsInt()); + + RawFieldValue rawVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("rawfield"), rawVal)); + CPPUNIT_ASSERT(memcmp(rawVal.getAsRaw().first, "RAW DATA", 8) == 0); + + Document embedDocVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("docfield"), embedDocVal)); + + ArrayFieldValue array(doc.getField("arrayoffloatfield").getDataType()); + CPPUNIT_ASSERT(doc.getValue(doc.getField("arrayoffloatfield"), array)); + CPPUNIT_ASSERT_EQUAL((float)1.0, array[0].getAsFloat()); + CPPUNIT_ASSERT_EQUAL((float)2.0, array[1].getAsFloat()); + + WeightedSetFieldValue wset(doc.getField("wsfield").getDataType()); + CPPUNIT_ASSERT(doc.getValue(doc.getField("wsfield"), wset)); + CPPUNIT_ASSERT_EQUAL(50, wset.get(StringFieldValue("Weighted 0"))); + CPPUNIT_ASSERT_EQUAL(199, wset.get(StringFieldValue("Weighted 1"))); + + MapFieldValue map(doc.getField("mapfield").getDataType()); + CPPUNIT_ASSERT(doc.getValue(doc.getField("mapfield"), map)); + CPPUNIT_ASSERT(map.get(StringFieldValue("foo1")).get()); + CPPUNIT_ASSERT(map.get(StringFieldValue("foo2")).get()); + CPPUNIT_ASSERT_EQUAL(StringFieldValue("bar1"), dynamic_cast<StringFieldValue&>(*map.get(StringFieldValue("foo1")))); + CPPUNIT_ASSERT_EQUAL(StringFieldValue("bar2"), dynamic_cast<StringFieldValue&>(*map.get(StringFieldValue("foo2")))); +} + +void DocumentTest::testReadSerializedFile() +{ + // Reads a file serialized from java + const char file_name[] = "data/crossplatform-java-cpp-doctypes.cfg"; + DocumentTypeRepo repo(readDocumenttypesConfig(file_name)); + + int fd = open("data/serializejava.dat", O_RDONLY); + + size_t len = lseek(fd,0,SEEK_END); + ByteBuffer buf(len); + lseek(fd,0,SEEK_SET); + read(fd, buf.getBuffer(), len); + + close(fd); + + Document doc(repo, buf); + verifyJavaDocument(doc); + + std::unique_ptr<ByteBuffer> buf2 = doc.serialize(); + buf2->flip(); + + Document doc2(repo, *buf2); + verifyJavaDocument(doc2); + + CPPUNIT_ASSERT_EQUAL(len, buf2->getPos()); + CPPUNIT_ASSERT(memcmp(buf2->getBuffer(), buf.getBuffer(), buf2->getPos()) == 0); + + doc2.setValue("stringfield", StringFieldValue("hei")); + + std::unique_ptr<ByteBuffer> buf3 = doc2.serialize(); + CPPUNIT_ASSERT(len != buf3->getPos()); +} + +void DocumentTest::testReadSerializedFileCompressed() +{ + // Reads a file serialized from java + const char file_name[] = "data/crossplatform-java-cpp-doctypes.cfg"; + DocumentTypeRepo repo(readDocumenttypesConfig(file_name)); + + int fd = open("data/serializejava-compressed.dat", O_RDONLY); + + int len = lseek(fd,0,SEEK_END); + ByteBuffer buf(len); + lseek(fd,0,SEEK_SET); + read(fd, buf.getBuffer(), len); + + close(fd); + + Document doc(repo, buf); + verifyJavaDocument(doc); +} + +namespace { + struct TestDoc { + std::string _dataFile; + /** + * We may add or remove types as we create new versions. If we do so, + * we can use the created version to know what types we no longer + * should check, or what fields these old documents does not contain. + */ + uint32_t _createdVersion; + + TestDoc(const std::string& dataFile, uint32_t version) + : _dataFile(dataFile), + _createdVersion(version) + { + } + }; +} + +/** + * Tests serialization of all versions. + * + * This test tests serialization and deserialization of documents of all + * supported types. + * + * Serialization is only supported in newest format. Deserialization should work + * for all formats supported, but only the part that makes sense in the new + * format. Thus, if new format deprecates a datatype, that datatype, when + * serializing old versions, must either just be dropped or converted. + * + * Thus, we create document type programmatically, because all old versions need + * to make sense with current config. + * + * When we create a document programmatically. This is serialized into current + * version files. When altering the format, after the alteration, copy the + * current version files to a specific version file and add those to list of + * files this test checks. + * + * When adding new fields to the documents, use the version tagged with each + * file to ignore these field for old types. + */ +void DocumentTest::testReadSerializedAllVersions() +{ + const int array_id = 1650586661; + const int wset_id = 1328286588; + // Create the datatype used for serialization test + DocumenttypesConfigBuilderHelper builder; + builder.document(1306012852, "serializetest", + Struct("serializetest.header") + .addField("floatfield", DataType::T_FLOAT) + .addField("stringfield", DataType::T_STRING) + .addField("longfield", DataType::T_LONG) + .addField("urifield", DataType::T_URI), + Struct("serializetest.body") + .addField("intfield", DataType::T_INT) + .addField("rawfield", DataType::T_RAW) + .addField("doublefield", DataType::T_DOUBLE) + .addField("bytefield", DataType::T_BYTE) + .addField("arrayoffloatfield", + Array(DataType::T_FLOAT).setId(array_id)) + .addField("docfield", DataType::T_DOCUMENT) + .addField("wsfield", + Wset(DataType::T_STRING).setId(wset_id))); + builder.document(1447635645, "docindoc", Struct("docindoc.header"), + Struct("docindoc.body") + .addField("stringindocfield", DataType::T_STRING)); + DocumentTypeRepo repo(builder.config()); + + const DocumentType* docType(repo.getDocumentType("serializetest")); + const DocumentType* docInDocType(repo.getDocumentType("docindoc")); + const DataType* arrayOfFloatDataType(repo.getDataType(*docType, array_id)); + const DataType* weightedSetDataType(repo.getDataType(*docType, wset_id)); + + // Create a memory instance of document + { + Document doc(*docType, + DocumentId("doc:serializetest:http://test.doc.id/")); + doc.set("intfield", 5); + doc.set("floatfield", -9.23); + doc.set("stringfield", "This is a string."); + doc.set("longfield", static_cast<int64_t>(398420092938472983LL)); + doc.set("doublefield", 98374532.398820); + doc.set("bytefield", -2); + doc.setValue("rawfield", RawFieldValue("RAW DATA", 8)); + Document docInDoc(*docInDocType, + DocumentId("doc:serializetest:http://doc.in.doc/")); + docInDoc.set("stringindocfield", "Elvis is dead"); + //docInDoc.setCompression(CompressionConfig(CompressionConfig::NONE, 0, 0)); + doc.setValue("docfield", docInDoc); + ArrayFieldValue floatArray(*arrayOfFloatDataType); + floatArray.add(1.0); + floatArray.add(2.0); + doc.setValue("arrayoffloatfield", floatArray); + WeightedSetFieldValue weightedSet(*weightedSetDataType); + weightedSet.add(StringFieldValue("Weighted 0"), 50); + weightedSet.add(StringFieldValue("Weighted 1"), 199); + doc.setValue("wsfield", weightedSet); + + // Write document to disk, (when you bump version and alter stuff, + // you can copy this current to new test for new version) + { + //doc.setCompression(CompressionConfig(CompressionConfig::NONE, 0, 0)); + std::unique_ptr<ByteBuffer> buf = doc.serialize(); + CPPUNIT_ASSERT_EQUAL(buf->getLength(), buf->getPos()); + int fd = open("data/document-cpp-currentversion-uncompressed.dat", + O_WRONLY | O_CREAT | O_TRUNC, 0644); + CPPUNIT_ASSERT(fd > 0); + size_t len = write(fd, buf->getBuffer(), buf->getPos()); + CPPUNIT_ASSERT_EQUAL(buf->getPos(), len); + close(fd); + } + { + CompressionConfig oldCfg(doc.getType().getFieldsType().getCompressionConfig()); + CompressionConfig newCfg(CompressionConfig::LZ4, 9, 95); + const_cast<StructDataType &>(doc.getType().getFieldsType()).setCompressionConfig(newCfg); + std::unique_ptr<ByteBuffer> buf = doc.serialize(); + CPPUNIT_ASSERT(buf->getPos() <= buf->getLength()); + int fd = open("data/document-cpp-currentversion-lz4-9.dat", + O_WRONLY | O_CREAT | O_TRUNC, 0644); + CPPUNIT_ASSERT(fd > 0); + size_t len = write(fd, buf->getBuffer(), buf->getPos()); + CPPUNIT_ASSERT_EQUAL(buf->getPos(), len); + close(fd); + const_cast<StructDataType &>(doc.getType().getFieldsType()).setCompressionConfig(oldCfg); + } + } + + std::string jpath = "../test/serializeddocuments/"; + + std::vector<TestDoc> tests; + tests.push_back(TestDoc("data/document-cpp-v8-uncompressed.dat", 8)); + tests.push_back(TestDoc("data/document-cpp-v7-uncompressed.dat", 7)); + tests.push_back(TestDoc("data/serializev6.dat", 6)); + tests.push_back(TestDoc(jpath + "document-java-v8-uncompressed.dat", 8)); + for (uint32_t i=0; i<tests.size(); ++i) { + int version = tests[i]._createdVersion; + std::string name = tests[i]._dataFile; + std::cerr << name << std::endl; + if (!vespalib::fileExists(name)) { + CPPUNIT_FAIL("File " + name + " does not exist."); + } + int fd = open(tests[i]._dataFile.c_str(), O_RDONLY); + int len = lseek(fd,0,SEEK_END); + ByteBuffer buf(len); + lseek(fd,0,SEEK_SET); + read(fd, buf.getBuffer(), len); + close(fd); + + Document doc(repo, buf); + + IntFieldValue intVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("intfield"), intVal)); + CPPUNIT_ASSERT_EQUAL(5, intVal.getAsInt()); + + FloatFieldValue floatVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("floatfield"), floatVal)); + CPPUNIT_ASSERT(floatVal.getAsFloat() == (float) -9.23); + + StringFieldValue stringVal(""); + CPPUNIT_ASSERT(doc.getValue(doc.getField("stringfield"), stringVal)); + CPPUNIT_ASSERT_EQUAL(vespalib::string("This is a string."), + stringVal.getAsString()); + + LongFieldValue longVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("longfield"), longVal)); + CPPUNIT_ASSERT_EQUAL(static_cast<int64_t>(398420092938472983LL), + longVal.getAsLong()); + + DoubleFieldValue doubleVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("doublefield"), doubleVal)); + CPPUNIT_ASSERT_EQUAL(doubleVal.getAsDouble(), 98374532.398820); + + ByteFieldValue byteVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("bytefield"), byteVal)); + CPPUNIT_ASSERT_EQUAL(-2, byteVal.getAsInt()); + + RawFieldValue rawVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("rawfield"), rawVal)); + CPPUNIT_ASSERT(memcmp(rawVal.getAsRaw().first, "RAW DATA", 8) == 0); + + if (version > 6) { + Document docInDoc; + CPPUNIT_ASSERT(doc.getValue(doc.getField("docfield"), docInDoc)); + + CPPUNIT_ASSERT(docInDoc.getValue( + docInDoc.getField("stringindocfield"), stringVal)); + CPPUNIT_ASSERT_EQUAL(vespalib::string("Elvis is dead"), + stringVal.getAsString()); + } + + ArrayFieldValue array(doc.getField("arrayoffloatfield").getDataType()); + CPPUNIT_ASSERT(doc.getValue(doc.getField("arrayoffloatfield"), array)); + CPPUNIT_ASSERT_EQUAL((float)1.0, array[0].getAsFloat()); + CPPUNIT_ASSERT_EQUAL((float)2.0, array[1].getAsFloat()); + + WeightedSetFieldValue wset(doc.getField("wsfield").getDataType()); + CPPUNIT_ASSERT(doc.getValue(doc.getField("wsfield"), wset)); + CPPUNIT_ASSERT_EQUAL(50, wset.get(StringFieldValue("Weighted 0"))); + CPPUNIT_ASSERT_EQUAL(199, wset.get(StringFieldValue("Weighted 1"))); + + // Check that serialization doesn't cause any problems. + std::unique_ptr<ByteBuffer> buf2 = doc.serialize(); + buf2->flip(); + + Document doc2(repo, *buf2); + } +} + +size_t getSerializedSize(const Document &doc) { + return doc.serialize()->getLength(); +} + +size_t getSerializedSizeHeader(const Document &doc) { + nbostream stream; + doc.serializeHeader(stream); + return stream.size(); +} + +size_t getSerializedSizeBody(const Document &doc) { + nbostream stream; + doc.serializeBody(stream); + return stream.size(); +} + +void DocumentTest::testGenerateSerializedFile() +{ + const char file_name[] = "data/crossplatform-java-cpp-doctypes.cfg"; + DocumentTypeRepo repo(readDocumenttypesConfig(file_name)); + Document doc(*repo.getDocumentType("serializetest"), + DocumentId(DocIdString("serializetest", + "http://test.doc.id/"))); + + doc.set("intfield", 5); + doc.set("floatfield", -9.23); + doc.set("stringfield", "This is a string."); + doc.set("longfield", (int64_t) 398420092938472983ll); + doc.set("doublefield", 98374532.398820); + doc.set("urifield", "http://this.is.a.test/"); + doc.set("bytefield", -2); + doc.set("rawfield", "RAW DATA"); + + const DocumentType *docindoc_type = repo.getDocumentType("docindoc"); + CPPUNIT_ASSERT(docindoc_type); + Document embedDoc(*docindoc_type, + DocumentId(DocIdString("docindoc", "http://embedded"))); + + doc.setValue("docfield", embedDoc); + + WeightedSetFieldValue wset(doc.getField("wsfield").getDataType()); + wset.add(StringFieldValue("Weighted 0"), 50); + wset.add(StringFieldValue("Weighted 1"), 199); + doc.setValue("wsfield", wset); + + ArrayFieldValue array(doc.getField("arrayoffloatfield").getDataType()); + array.add(FloatFieldValue(1.0)); + array.add(FloatFieldValue(2.0)); + doc.setValue("arrayoffloatfield", array); + + MapFieldValue map(doc.getField("mapfield").getDataType()); + map.put(StringFieldValue("foo1"), StringFieldValue("bar1")); + map.put(StringFieldValue("foo2"), StringFieldValue("bar2")); + doc.setValue("mapfield", map); + + std::unique_ptr<ByteBuffer> buf = doc.serialize(); + +#define SERIALIZED_DIR "../../test/document/" + int fd = open(SERIALIZED_DIR "/serializecpp.dat", + O_WRONLY | O_TRUNC | O_CREAT, 0644); + write(fd, buf->getBuffer(), buf->getPos()); + close(fd); + + ByteBuffer hBuf(getSerializedSizeHeader(doc)); + doc.serializeHeader(hBuf); + fd = open(SERIALIZED_DIR "/serializecppsplit_header.dat", + O_WRONLY | O_TRUNC | O_CREAT, 0644); + write(fd, hBuf.getBuffer(), hBuf.getPos()); + close(fd); + + ByteBuffer bBuf(getSerializedSizeBody(doc)); + doc.serializeBody(bBuf); + fd = open(SERIALIZED_DIR "/serializecppsplit_body.dat", + O_WRONLY | O_TRUNC | O_CREAT, 0644); + write(fd, bBuf.getBuffer(), bBuf.getPos()); + close(fd); + + + CompressionConfig newCfg(CompressionConfig::LZ4, 9, 95); + const_cast<StructDataType &>(doc.getType().getFieldsType()).setCompressionConfig(newCfg); + + ByteBuffer lz4buf(getSerializedSize(doc)); + + doc.serialize(lz4buf); + lz4buf.flip(); + + fd = open(SERIALIZED_DIR "/serializecpp-lz4-level9.dat", + O_WRONLY | O_TRUNC | O_CREAT, 0644); + write(fd, lz4buf.getBufferAtPos(), lz4buf.getRemaining()); + close(fd); +} +void DocumentTest::testGetURIFromSerialized() +{ + TestDocRepo test_repo; + Document doc(*test_repo.getDocumentType("testdoctype1"), + DocumentId("doc:ns:testdoc")); + + { + std::unique_ptr<ByteBuffer> serialized = doc.serialize(); + serialized->flip(); + + CPPUNIT_ASSERT_EQUAL( + vespalib::string(DocIdString("ns", "testdoc").toString()), + Document::getIdFromSerialized(*serialized).toString()); + + CPPUNIT_ASSERT_EQUAL(vespalib::string("testdoctype1"), + Document::getDocTypeFromSerialized( + test_repo.getTypeRepo(), + *serialized)->getName()); + } + + { + std::unique_ptr<ByteBuffer> serialized = doc.serialize(); + serialized->flip(); + + Document doc2(test_repo.getTypeRepo(), *serialized, false, NULL); + CPPUNIT_ASSERT_EQUAL( + vespalib::string(DocIdString("ns", "testdoc").toString()), doc2.getId().toString()); + CPPUNIT_ASSERT_EQUAL(vespalib::string("testdoctype1"), doc2.getType().getName()); + } +}; + +void DocumentTest::testBogusserialize() +{ + TestDocRepo test_repo; + try { + std::unique_ptr<ByteBuffer> buf( + new ByteBuffer("aoifjweprjwoejr203r+2+4r823++!",100)); + Document doc(test_repo.getTypeRepo(), *buf); + CPPUNIT_ASSERT(false); + } catch (DeserializeException& e) { + CPPUNIT_ASSERT_CONTAIN("Unrecognized serialization version", e.what()); + } + + try { + std::unique_ptr<ByteBuffer> buf(new ByteBuffer("",0)); + Document doc(test_repo.getTypeRepo(), *buf); + CPPUNIT_ASSERT(false); + } catch (DeserializeException& e) { + CPPUNIT_ASSERT_CONTAIN("Buffer out of bounds", e.what()); + } +} + +void DocumentTest::testCRC32() +{ + TestDocRepo test_repo; + Document doc(*test_repo.getDocumentType("testdoctype1"), + DocumentId(DocIdString("crawler", "http://www.ntnu.no/"))); + + doc.setValue(doc.getField("hstringval"), + StringFieldValue("bla bla bla bla bla")); + + uint32_t crc = doc.calculateChecksum(); + CPPUNIT_ASSERT_EQUAL(277496115u, crc); + + std::unique_ptr<ByteBuffer> buf = doc.serialize(); + buf->flip(); + + int pos = 20; + + // Corrupt serialization. + buf->getBuffer()[pos] ^= 72; + // Create document. Byte corrupted above is in data area and + // shouldn't fail deserialization. + try { + Document doc2(test_repo.getTypeRepo(), *buf); + buf->setPos(0); + CPPUNIT_ASSERT(crc != doc2.calculateChecksum()); + } catch (document::DeserializeException& e) { + CPPUNIT_ASSERT(false); + } + // Return original value and retry + buf->getBuffer()[pos] ^= 72; + + /// \todo TODO (was warning): Cannot test for in memory representation altered, as there is no syntax for getting internal refs to data from document. Add test when this is added. +} + +void +DocumentTest::testHasChanged() +{ + TestDocRepo test_repo; + Document doc(*test_repo.getDocumentType("testdoctype1"), + DocumentId(DocIdString("crawler", "http://www.ntnu.no/"))); + // Before deserialization we are changed. + CPPUNIT_ASSERT(doc.hasChanged()); + + doc.setValue(doc.getField("hstringval"), + StringFieldValue("bla bla bla bla bla")); + // Still changed after setting a value of course. + CPPUNIT_ASSERT(doc.hasChanged()); + + std::unique_ptr<ByteBuffer> buf = doc.serialize(); + buf->flip(); + + // Setting a value in doc tags us changed. + { + buf->setPos(0); + Document doc2(test_repo.getTypeRepo(), *buf); + CPPUNIT_ASSERT(!doc2.hasChanged()); + + doc2.set("headerval", 13); + CPPUNIT_ASSERT(doc2.hasChanged()); + } + // Overwriting a value in doc tags us changed. + { + buf->setPos(0); + Document doc2(test_repo.getTypeRepo(), *buf); + + doc2.set("hstringval", "bla bla bla bla bla"); + CPPUNIT_ASSERT(doc2.hasChanged()); + } + // Clearing value tags us changed. + { + buf->setPos(0); + Document doc2(test_repo.getTypeRepo(), *buf); + + doc2.clear(); + CPPUNIT_ASSERT(doc2.hasChanged()); + } + // Add more tests here when we allow non-const refs to internals +} + +void +DocumentTest::testSplitSerialization() +{ + TestDocMan testDocMan; + Document::UP doc = testDocMan.createDocument(); + doc->set("headerval", 50); + + ByteBuffer buf(getSerializedSizeHeader(*doc)); + doc->serializeHeader(buf); + buf.flip(); + + ByteBuffer buf2(getSerializedSizeBody(*doc)); + doc->serializeBody(buf2); + buf2.flip(); + + CPPUNIT_ASSERT_EQUAL(size_t(65), buf.getLength()); + CPPUNIT_ASSERT_EQUAL(size_t(73), buf2.getLength()); + + Document headerDoc(testDocMan.getTypeRepo(), buf); + CPPUNIT_ASSERT(headerDoc.hasValue("headerval")); + CPPUNIT_ASSERT(!headerDoc.hasValue("content")); + + buf.setPos(0); + Document fullDoc(testDocMan.getTypeRepo(), buf, buf2); + CPPUNIT_ASSERT(fullDoc.hasValue("headerval")); + CPPUNIT_ASSERT(fullDoc.hasValue("content")); + + CPPUNIT_ASSERT_EQUAL(*doc, fullDoc); +} + +void DocumentTest::testSliceSerialize() +{ + // Test that document doesn't need its own bytebuffer, such that we + // can serialize multiple documents after each other in the same + // bytebuffer. + TestDocMan testDocMan; + Document::UP doc = testDocMan.createDocument(); + Document::UP doc2 = testDocMan.createDocument( + "Some other content", "doc:test:anotherdoc"); + + ArrayFieldValue val(doc2->getField("rawarray").getDataType()); + val.add(RawFieldValue("hei", 3)); + val.add(RawFieldValue("hallo", 5)); + val.add(RawFieldValue("hei der", 7)); + doc2->setValue(doc2->getField("rawarray"), val); + + ByteBuffer buf(getSerializedSize(*doc) + getSerializedSize(*doc2)); + doc->serialize(buf); + CPPUNIT_ASSERT_EQUAL(getSerializedSize(*doc), buf.getPos()); + doc2->serialize(buf); + CPPUNIT_ASSERT_EQUAL(getSerializedSize(*doc) + getSerializedSize(*doc2), + buf.getPos()); + buf.flip(); + + Document doc3(testDocMan.getTypeRepo(), buf); + CPPUNIT_ASSERT_EQUAL(getSerializedSize(*doc), buf.getPos()); + Document doc4(testDocMan.getTypeRepo(), buf); + CPPUNIT_ASSERT_EQUAL(getSerializedSize(*doc) + getSerializedSize(*doc2), + buf.getPos()); + + CPPUNIT_ASSERT_EQUAL(*doc, doc3); + CPPUNIT_ASSERT_EQUAL(*doc2, doc4); +} + +void DocumentTest::testCompression() +{ + TestDocMan testDocMan; + Document::UP doc = testDocMan.createDocument(); + + std::string bigString("compress me"); + for (int i = 0; i < 8; ++i) { bigString += bigString; } + + doc->setValue("hstringval", StringFieldValue(bigString)); + + std::unique_ptr<ByteBuffer> buf_uncompressed = doc->serialize(); + buf_uncompressed->flip(); + + CompressionConfig oldCfg(doc->getType().getFieldsType().getCompressionConfig()); + + CompressionConfig newCfg(CompressionConfig::LZ4, 9, 95); + const_cast<StructDataType &>(doc->getType().getFieldsType()).setCompressionConfig(newCfg); + std::unique_ptr<ByteBuffer> buf_lz4 = doc->serialize(); + buf_lz4->flip(); + + const_cast<StructDataType &>(doc->getType().getFieldsType()).setCompressionConfig(oldCfg); + + CPPUNIT_ASSERT(buf_lz4->getRemaining() < buf_uncompressed->getRemaining()); + + Document doc_lz4(testDocMan.getTypeRepo(), *buf_lz4); + + CPPUNIT_ASSERT_EQUAL(*doc, doc_lz4); +} + +void DocumentTest::testCompressionConfigured() +{ + DocumenttypesConfigBuilderHelper builder; + builder.document(43, "serializetest", + Struct("serializetest.header").setId(44), + Struct("serializetest.body").setId(45) + .addField("stringfield", DataType::T_STRING)); + DocumentTypeRepo repo(builder.config()); + Document doc_uncompressed( + *repo.getDocumentType("serializetest"), + DocumentId("doc:test:test")); + + std::string bigString("compress me"); + for (int i = 0; i < 8; ++i) { bigString += bigString; } + + doc_uncompressed.setValue("stringfield", StringFieldValue(bigString)); + std::unique_ptr<ByteBuffer> buf_uncompressed = doc_uncompressed.serialize(); + buf_uncompressed->flip(); + + size_t uncompressedSize = buf_uncompressed->getRemaining(); + + DocumenttypesConfigBuilderHelper builder2; + builder2.document(43, "serializetest", + Struct("serializetest.header").setId(44), + Struct("serializetest.body").setId(45) + .addField("stringfield", DataType::T_STRING) + .setCompression(DocumenttypesConfig::Documenttype:: + Datatype::Sstruct::Compression::LZ4, + 9, 99, 0)); + DocumentTypeRepo repo2(builder2.config()); + + Document doc(repo2, *buf_uncompressed); + + std::unique_ptr<ByteBuffer> buf_compressed = doc.serialize(); + buf_compressed->flip(); + size_t compressedSize = buf_compressed->getRemaining(); + + CPPUNIT_ASSERT(compressedSize < uncompressedSize); + + Document doc2(repo2, *buf_compressed); + + std::unique_ptr<ByteBuffer> buf_compressed2 = doc2.serialize(); + buf_compressed2->flip(); + + CPPUNIT_ASSERT_EQUAL(compressedSize, buf_compressed2->getRemaining()); + + Document doc3(repo2, *buf_compressed2); + + CPPUNIT_ASSERT_EQUAL(doc2, doc_uncompressed); + CPPUNIT_ASSERT_EQUAL(doc2, doc3); +} + +void +DocumentTest::testUnknownEntries() +{ + // We should be able to deserialize a document with unknown values in it. + DocumentType type1("test", 0); + DocumentType type2("test", 0); + Field field1("int1", *DataType::INT, true); + Field field2("int2", *DataType::INT, true); + Field field3("int3", *DataType::INT, false); + Field field4("int4", *DataType::INT, false); + + type1.addField(field1); + type1.addField(field2); + type1.addField(field3); + type1.addField(field4); + + type2.addField(field3); + type2.addField(field4); + + DocumentTypeRepo repo(type2); + + Document doc1(type1, DocumentId("doc::testdoc")); + doc1.setValue(field1, IntFieldValue(1)); + doc1.setValue(field2, IntFieldValue(2)); + doc1.setValue(field3, IntFieldValue(3)); + doc1.setValue(field4, IntFieldValue(4)); + + uint32_t headerLen = getSerializedSizeHeader(doc1); + document::ByteBuffer header(headerLen); + doc1.serializeHeader(header); + header.flip(); + + uint32_t bodyLen = getSerializedSizeBody(doc1); + document::ByteBuffer body(bodyLen); + doc1.serializeBody(body); + body.flip(); + + uint32_t totalLen = getSerializedSize(doc1); + document::ByteBuffer total(totalLen); + doc1.serialize(total); + total.flip(); + + Document doc2; + doc2.deserialize(repo, total); + + Document doc3; + doc3.deserializeHeader(repo, header); + doc3.deserializeBody(repo, body); + + CPPUNIT_ASSERT_EQUAL(std::string( + "<document documenttype=\"test\" documentid=\"doc::testdoc\">\n" + "<int3>3</int3>\n" + "<int4>4</int4>\n" + "</document>"), doc2.toXml()); + CPPUNIT_ASSERT_EQUAL(std::string( + "<document documenttype=\"test\" documentid=\"doc::testdoc\">\n" + "<int3>3</int3>\n" + "<int4>4</int4>\n" + "</document>"), doc3.toXml()); + + CPPUNIT_ASSERT_EQUAL(3, doc2.getValue(field3)->getAsInt()); + CPPUNIT_ASSERT_EQUAL(4, doc2.getValue(field4)->getAsInt()); + CPPUNIT_ASSERT_EQUAL(3, doc3.getValue(field3)->getAsInt()); + CPPUNIT_ASSERT_EQUAL(4, doc3.getValue(field4)->getAsInt()); + + // The fields are actually accessible as long as you ask with field of + // correct type. + + CPPUNIT_ASSERT(doc2.hasValue(field1)); + CPPUNIT_ASSERT(doc2.hasValue(field2)); + CPPUNIT_ASSERT(doc3.hasValue(field1)); + CPPUNIT_ASSERT(doc3.hasValue(field2)); + + CPPUNIT_ASSERT_EQUAL(1, doc2.getValue(field1)->getAsInt()); + CPPUNIT_ASSERT_EQUAL(2, doc2.getValue(field2)->getAsInt()); + CPPUNIT_ASSERT_EQUAL(1, doc3.getValue(field1)->getAsInt()); + CPPUNIT_ASSERT_EQUAL(2, doc3.getValue(field2)->getAsInt()); + + CPPUNIT_ASSERT_EQUAL(size_t(2), doc2.getSetFieldCount()); + CPPUNIT_ASSERT_EQUAL(size_t(2), doc3.getSetFieldCount()); + + // Copy paste of above test to read an old version document and + // deserialize it with some fields lacking to see that it doesn't + // report failure. (Had a bug on this earlier) + int fd = open("data/serializev6.dat", O_RDONLY); + int len = lseek(fd,0,SEEK_END); + ByteBuffer buf(len); + lseek(fd,0,SEEK_SET); + read(fd, buf.getBuffer(), len); + close(fd); + + DocumenttypesConfigBuilderHelper builder; + builder.document(42, "docindoc", Struct("docindoc.header"), + Struct("docindoc.body") + .addField("stringindocfield", DataType::T_STRING)); + builder.document(43, "serializetest", + Struct("serializetest.header") + .addField("floatfield", DataType::T_FLOAT), + Struct("serializetest.body") + .addField("rawfield", DataType::T_RAW)); + DocumentTypeRepo repo2(builder.config()); + + Document doc(repo2, buf); + doc.toXml(); +} + +void DocumentTest::testAnnotationDeserialization() +{ + DocumenttypesConfigBuilderHelper builder; + builder.document(-1326249427, "dokk", Struct("dokk.header"), + Struct("dokk.body") + .addField("age", DataType::T_BYTE) + .addField("story", DataType::T_STRING) + .addField("date", DataType::T_INT) + .addField("friend", DataType::T_LONG)) + .annotationType(609952424, "person", Struct("person") + .addField("firstname", DataType::T_STRING) + .addField("lastname", DataType::T_STRING) + .addField("birthyear", DataType::T_INT) + .setId(443162583)) + .annotationType(-1695443536, "dummy", 0) + .annotationType(-427420193, "number", DataType::T_INT) + .annotationType(1616020615, "relative", Struct("relative") + .addField("title", DataType::T_STRING) + .addField("related", AnnotationRef(609952424)) + .setId(-236946034)) + .annotationType(-269517759, "banana", 0) + .annotationType(-513687143, "grape", 0) + .annotationType(1730712959, "apple", 0); + DocumentTypeRepo repo(builder.config()); + + int fd = open("data/serializejavawithannotations.dat", O_RDONLY); + int len = lseek(fd,0,SEEK_END); + ByteBuffer buf(len); + lseek(fd,0,SEEK_SET); + read(fd, buf.getBuffer(), len); + close(fd); + + Document doc(repo, buf); + StringFieldValue strVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("story"), strVal)); + + vespalib::nbostream stream; + VespaDocumentSerializer serializer(stream); + serializer.write(strVal); + + FixedTypeRepo fixedRepo(repo, doc.getType()); + VespaDocumentDeserializer deserializer(fixedRepo, stream, 8); + StringFieldValue strVal2; + deserializer.read(strVal2); + CPPUNIT_ASSERT_EQUAL(strVal.toString(), strVal2.toString()); + CPPUNIT_ASSERT_EQUAL(strVal.toString(true), strVal2.toString(true)); + + CPPUNIT_ASSERT_EQUAL(vespalib::string("help me help me i'm stuck inside a computer!"), + strVal.getAsString()); + StringFieldValue::SpanTrees trees = strVal.getSpanTrees(); + const SpanTree *span_tree = StringFieldValue::findTree(trees, "fruits"); + CPPUNIT_ASSERT(span_tree); + CPPUNIT_ASSERT_EQUAL(size_t(8), span_tree->numAnnotations()); + span_tree = StringFieldValue::findTree(trees, "ballooo"); + CPPUNIT_ASSERT(span_tree); + CPPUNIT_ASSERT_EQUAL(size_t(8), span_tree->numAnnotations()); + + + ByteFieldValue byteVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("age"), byteVal)); + CPPUNIT_ASSERT_EQUAL( 123, byteVal.getAsInt()); + + IntFieldValue intVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("date"), intVal)); + CPPUNIT_ASSERT_EQUAL(13829297, intVal.getAsInt()); + + LongFieldValue longVal; + CPPUNIT_ASSERT(doc.getValue(doc.getField("friend"), longVal)); + CPPUNIT_ASSERT_EQUAL((int64_t)2384LL, longVal.getAsLong()); +} + +void +DocumentTest::testGetSerializedSize() +{ + TestDocMan testDocMan; + Document::UP doc = testDocMan.createDocument(); + + CPPUNIT_ASSERT_EQUAL(getSerializedSize(*doc), doc->getSerializedSize()); +} + +void +DocumentTest::testDeserializeMultiple() +{ + TestDocRepo testDocRepo; + const DocumentTypeRepo& repo(testDocRepo.getTypeRepo()); + const DocumentType& docType(*repo.getDocumentType("testdoctype1")); + CPPUNIT_ASSERT(&docType != 0); + + StructFieldValue sv1(docType.getField("mystruct").getDataType()); + + StructFieldValue sv2(docType.getField("mystruct").getDataType()); + + sv1.setValue(sv1.getField("key"), IntFieldValue(1234)); + sv2.setValue(sv2.getField("value"), StringFieldValue("badger")); + + StructFieldValue sv3(docType.getField("mystruct").getDataType()); + + vespalib::nbostream stream; + VespaDocumentSerializer serializer(stream); + serializer.write(sv1); + serializer.write(sv2); + + VespaDocumentDeserializer deserializer(repo, stream, 8); + deserializer.readStructNoReset(sv3); + deserializer.readStructNoReset(sv3); + + StructFieldValue correct(docType.getField("mystruct").getDataType()); + + correct.setValue(correct.getField("key"), IntFieldValue(1234)); + correct.setValue(correct.getField("value"), StringFieldValue("badger")); + CPPUNIT_ASSERT_EQUAL(correct, sv3); +} + +} // document diff --git a/document/src/tests/documenttypetestcase.cpp b/document/src/tests/documenttypetestcase.cpp new file mode 100644 index 00000000000..e946844e867 --- /dev/null +++ b/document/src/tests/documenttypetestcase.cpp @@ -0,0 +1,254 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> + +#include <vespa/document/config/config-documenttypes.h> +#include <vespa/document/datatype/documenttype.h> +#include <vespa/document/fieldvalue/fieldvalues.h> +#include <vespa/document/repo/configbuilder.h> +#include <vespa/document/repo/documenttyperepo.h> +#include <vespa/vdstestlib/cppunit/macros.h> +#include <sstream> +#include <string> + +using document::config_builder::Struct; +using document::config_builder::Wset; +using document::config_builder::Array; +using document::config_builder::Map; + + +namespace document { + +struct DocumentTypeTest : public CppUnit::TestFixture { + void setUp(); + void tearDown(); + + void testSetGet(); + void testHeaderContent(); + void testFieldSet(); + void testInheritance(); + void testInheritanceConfig(); + void testMultipleInheritance(); + void testOutputOperator(); + + CPPUNIT_TEST_SUITE( DocumentTypeTest); + CPPUNIT_TEST(testSetGet); + CPPUNIT_TEST(testFieldSet); + CPPUNIT_TEST(testInheritance); + CPPUNIT_TEST(testInheritanceConfig); + CPPUNIT_TEST(testMultipleInheritance); + CPPUNIT_TEST(testOutputOperator); + CPPUNIT_TEST(testHeaderContent); + CPPUNIT_TEST_SUITE_END(); + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(DocumentTypeTest); + +void DocumentTypeTest::setUp() +{ +} + +void DocumentTypeTest::tearDown() +{ +} + +void DocumentTypeTest::testSetGet() { + DocumentType docType("doctypetestdoc", 0); + + docType.addField(Field("stringattr", 3, *DataType::STRING, true)); + docType.addField(Field("nalle", 0, *DataType::INT, false)); + + const Field& fetch1=docType.getField("stringattr"); + const Field& fetch2=docType.getField("stringattr"); + + CPPUNIT_ASSERT(fetch1==fetch2); + CPPUNIT_ASSERT(fetch1.getName() == "stringattr"); + + const Field& fetch3 = docType.getField(3, Document::getNewestSerializationVersion()); + + CPPUNIT_ASSERT(fetch1==fetch3); + + const Field& fetch4=docType.getField(0, Document::getNewestSerializationVersion()); + + CPPUNIT_ASSERT(fetch4!=fetch1); +} + +void +categorizeFields(const Field::Set& fields, + std::vector<const Field*>& headers, + std::vector<const Field*>& bodies) +{ + for (Field::Set::const_iterator it(fields.begin()), e(fields.end()); + it != e; ++it) + { + if ((*it)->isHeaderField()) { + headers.push_back(*it); + } else { + bodies.push_back(*it); + } + } +} + +void +DocumentTypeTest::testInheritanceConfig() +{ + DocumentTypeRepo + repo(readDocumenttypesConfig("data/inheritancetest.cfg")); + + { + const DocumentType* type(repo.getDocumentType("music")); + CPPUNIT_ASSERT(type != NULL); + } + + { + const DocumentType* type(repo.getDocumentType("books")); + CPPUNIT_ASSERT(type != NULL); + } +} + +void +DocumentTypeTest::testHeaderContent() +{ + DocumentTypeRepo + repo(readDocumenttypesConfig("data/doctypesconfigtest.cfg")); + + const DocumentType* type(repo.getDocumentType("derived")); + + Field::Set fields = type->getFieldsType().getFieldSet(); + + std::vector<const Field*> headers; + std::vector<const Field*> bodies; + categorizeFields(fields, headers, bodies); + + CPPUNIT_ASSERT(headers.size() == 4); + CPPUNIT_ASSERT(headers[0]->getName() == "field1"); + CPPUNIT_ASSERT(headers[1]->getName() == "field2"); + CPPUNIT_ASSERT(headers[2]->getName() == "field4"); + CPPUNIT_ASSERT(headers[3]->getName() == "fieldarray1"); + + CPPUNIT_ASSERT(bodies.size() == 2); + CPPUNIT_ASSERT(bodies[0]->getName() == "field3"); + CPPUNIT_ASSERT(bodies[1]->getName() == "field5"); +} + +void DocumentTypeTest::testMultipleInheritance() +{ + config_builder::DocumenttypesConfigBuilderHelper builder; + builder.document(42, "test1", Struct("test1.header"), + Struct("test1.body") + .addField("stringattr", DataType::T_STRING) + .addField("nalle", DataType::T_INT)); + builder.document(43, "test2", Struct("test2.header"), + Struct("test2.body") + .addField("stringattr", DataType::T_STRING) + .addField("tmp", DataType::T_STRING) + .addField("tall", DataType::T_INT)); + builder.document(44, "test3", + Struct("test3.header"), Struct("test3.body")) + .inherit(42).inherit(43); + DocumentTypeRepo repo(builder.config()); + const DocumentType* docType3(repo.getDocumentType("test3")); + + CPPUNIT_ASSERT(docType3->hasField("stringattr")); + CPPUNIT_ASSERT(docType3->hasField("nalle")); + CPPUNIT_ASSERT(docType3->hasField("tmp")); + CPPUNIT_ASSERT(docType3->hasField("tall")); + + Document doc(*docType3, DocumentId(DocIdString("test", "test"))); + + IntFieldValue intVal(3); + doc.setValue(doc.getField("nalle"), intVal); + + StringFieldValue stringVal("tmp"); + doc.setValue(doc.getField("tmp"), stringVal); + + CPPUNIT_ASSERT(doc.getValue(doc.getField("nalle"))->getAsInt()==3); + CPPUNIT_ASSERT_EQUAL(vespalib::string("tmp"), + doc.getValue(doc.getField("tmp"))->getAsString()); +} + +void DocumentTypeTest::testFieldSet() +{ + DocumentType docType("test1"); + docType.addField(Field("stringattr", 3, *DataType::STRING, false)); + docType.addField(Field("nalle", 0, *DataType::INT, false)); + DocumentType::FieldSet::Fields tmp; + tmp.insert("nalle"); + tmp.insert("nulle"); + try { + docType.addFieldSet("a", tmp); + CPPUNIT_ASSERT(false); + } catch (const vespalib::IllegalArgumentException & e) { + CPPUNIT_ASSERT_EQUAL(vespalib::string("Fieldset 'a': No field with name 'nulle' in document type 'test1'."), e.getMessage()); + } +} + +void DocumentTypeTest::testInheritance() +{ + // Inheritance of conflicting but equal datatype ok + DocumentType docType("test1"); + docType.addField(Field("stringattr", 3, *DataType::STRING, false)); + docType.addField(Field("nalle", 0, *DataType::INT, false)); + + DocumentType docType2("test2"); + docType2.addField(Field("stringattr", 3, *DataType::STRING, false)); + docType2.addField(Field("tmp", 5, *DataType::STRING, false)); + docType2.addField(Field("tall", 10, *DataType::INT, false)); + + docType.inherit(docType2); + CPPUNIT_ASSERT(docType.hasField("stringattr")); + CPPUNIT_ASSERT(docType.hasField("nalle")); + CPPUNIT_ASSERT(docType.hasField("tmp")); + CPPUNIT_ASSERT(docType.hasField("tall")); + + DocumentType docType3("test3"); + docType3.addField(Field("stringattr", 3, *DataType::RAW, false)); + docType3.addField(Field("tall", 10, *DataType::INT, false)); + + try{ + docType2.inherit(docType3); + //CPPUNIT_FAIL("Supposed to fail"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_EQUAL(std::string("foo"), std::string(e.what())); + } + + try{ + docType.inherit(docType3); + //CPPUNIT_FAIL("Supposed to fail"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_EQUAL(std::string("foo"), std::string(e.what())); + } + + DocumentType docType4("test4"); + docType4.inherit(docType3); + + CPPUNIT_ASSERT(docType4.hasField("stringattr")); + CPPUNIT_ASSERT(docType4.hasField("tall")); + + try{ + docType3.inherit(docType4); + CPPUNIT_FAIL("Accepted cyclic inheritance"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot add cyclic dependencies", e.what()); + } + + DocumentType docType5("test5"); + docType5.addField(Field("stringattr", 20, *DataType::RAW, false)); + + try{ + docType4.inherit(docType5); + //CPPUNIT_FAIL("Supposed to fail"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_EQUAL(std::string("foo"), std::string(e.what())); + } +} + +void DocumentTypeTest::testOutputOperator() { + DocumentType docType("test1"); + std::ostringstream ost; + ost << docType; + std::string expected("DocumentType(test1)"); + CPPUNIT_ASSERT_EQUAL(expected, ost.str()); +} + +} // document diff --git a/document/src/tests/documentupdatetestcase.cpp b/document/src/tests/documentupdatetestcase.cpp new file mode 100644 index 00000000000..be619b0f35e --- /dev/null +++ b/document/src/tests/documentupdatetestcase.cpp @@ -0,0 +1,1030 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <vespa/document/fieldvalue/fieldvalues.h> +#include <vespa/document/update/documentupdate.h> +#include <vespa/document/base/testdocman.h> + +#include <vespa/document/update/addvalueupdate.h> +#include <vespa/document/update/arithmeticvalueupdate.h> +#include <vespa/document/update/assignvalueupdate.h> +#include <vespa/document/update/clearvalueupdate.h> +#include <vespa/document/update/documentupdateflags.h> +#include <vespa/document/update/fieldupdate.h> +#include <vespa/document/update/mapvalueupdate.h> +#include <vespa/document/update/removevalueupdate.h> +#include <vespa/document/update/valueupdate.h> +#include <vespa/document/serialization/vespadocumentserializer.h> + +#include <vespa/document/repo/configbuilder.h> +#include <vespa/document/repo/documenttyperepo.h> +#include <vespa/vdstestlib/cppunit/macros.h> + +using namespace document::config_builder; + +namespace document { + +struct DocumentUpdateTest : public CppUnit::TestFixture { + void setUp() {} + void tearDown() {} + + void testSimpleUsage(); + void testUpdateApplySingleValue(); + void testClearField(); + void testUpdateArray(); + void testUpdateWeightedSet(); + void testIncrementNonExistingAutoCreateWSetField(); + void testIncrementExistingWSetField(); + void testIncrementWithZeroResultWeightIsRemoved(); + void testReadSerializedFile(); + void testGenerateSerializedFile(); + void testSetBadFieldTypes(); + void testUpdateApplyNoParams(); + void testUpdateApplyNoArrayValues(); + void testUpdateArrayEmptyParamValue(); + void testUpdateWeightedSetEmptyParamValue(); + void testUpdateArrayWrongSubtype(); + void testUpdateWeightedSetWrongSubtype(); + void testMapValueUpdate(); + void testThatDocumentUpdateFlagsIsWorking(); + void testThatCreateIfNonExistentFlagIsSerialized50AndDeserialized50(); + void testThatCreateIfNonExistentFlagIsSerializedAndDeserialized(); + + CPPUNIT_TEST_SUITE(DocumentUpdateTest); + CPPUNIT_TEST(testSimpleUsage); + CPPUNIT_TEST(testUpdateApplySingleValue); + CPPUNIT_TEST(testClearField); + CPPUNIT_TEST(testUpdateArray); + CPPUNIT_TEST(testUpdateWeightedSet); + CPPUNIT_TEST(testIncrementNonExistingAutoCreateWSetField); + CPPUNIT_TEST(testIncrementExistingWSetField); + CPPUNIT_TEST(testIncrementWithZeroResultWeightIsRemoved); + CPPUNIT_TEST(testReadSerializedFile); + CPPUNIT_TEST(testGenerateSerializedFile); + CPPUNIT_TEST(testSetBadFieldTypes); + CPPUNIT_TEST(testUpdateApplyNoParams); + CPPUNIT_TEST(testUpdateApplyNoArrayValues); + CPPUNIT_TEST(testUpdateArrayEmptyParamValue); + CPPUNIT_TEST(testUpdateWeightedSetEmptyParamValue); + CPPUNIT_TEST(testUpdateArrayWrongSubtype); + CPPUNIT_TEST(testUpdateWeightedSetWrongSubtype); + CPPUNIT_TEST(testMapValueUpdate); + CPPUNIT_TEST(testThatDocumentUpdateFlagsIsWorking); + CPPUNIT_TEST(testThatCreateIfNonExistentFlagIsSerialized50AndDeserialized50); + CPPUNIT_TEST(testThatCreateIfNonExistentFlagIsSerializedAndDeserialized); + CPPUNIT_TEST_SUITE_END(); + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(DocumentUpdateTest); + +namespace { + +ByteBuffer::UP serializeHEAD(const DocumentUpdate & update) +{ + vespalib::nbostream stream; + VespaDocumentSerializer serializer(stream); + serializer.writeHEAD(update); + ByteBuffer::UP retVal(new ByteBuffer(stream.size())); + retVal->putBytes(stream.peek(), stream.size()); + return retVal; +} + +ByteBuffer::UP serialize42(const DocumentUpdate & update) +{ + vespalib::nbostream stream; + VespaDocumentSerializer serializer(stream); + serializer.write42(update); + ByteBuffer::UP retVal(new ByteBuffer(stream.size())); + retVal->putBytes(stream.peek(), stream.size()); + return retVal; +} + +ByteBuffer::UP serialize(const ValueUpdate & update) +{ + vespalib::nbostream stream; + VespaDocumentSerializer serializer(stream); + serializer.write(update); + ByteBuffer::UP retVal(new ByteBuffer(stream.size())); + retVal->putBytes(stream.peek(), stream.size()); + return retVal; +} + +ByteBuffer::UP serialize(const FieldUpdate & update) +{ + vespalib::nbostream stream; + VespaDocumentSerializer serializer(stream); + serializer.write(update); + ByteBuffer::UP retVal(new ByteBuffer(stream.size())); + retVal->putBytes(stream.peek(), stream.size()); + return retVal; +} + +template<typename UpdateType> +void testValueUpdate(const UpdateType& update, const DataType &type) { + try{ + DocumentTypeRepo repo; + ByteBuffer::UP buf = serialize(update); + buf->flip(); + typename UpdateType::UP copy(dynamic_cast<UpdateType*>( + ValueUpdate::createInstance(repo, type, *buf, + Document::getNewestSerializationVersion()) + .release())); + CPPUNIT_ASSERT_EQUAL(update, *copy); + } catch (std::exception& e) { + std::cerr << "Failed while processing update " << update << "\n"; + throw; + } +} + + +} // namespace + +void +DocumentUpdateTest::testSimpleUsage() { + DocumenttypesConfigBuilderHelper builder; + builder.document(42, "test", + Struct("test.header") + .addField("bytef", DataType::T_BYTE) + .addField("intf", DataType::T_INT), + Struct("test.body") + .addField("intarr", Array(DataType::T_INT))); + DocumentTypeRepo repo(builder.config()); + const DocumentType* docType(repo.getDocumentType("test")); + const DataType *arrayType = repo.getDataType(*docType, "Array<Int>"); + + // Test that primitive value updates can be serialized + testValueUpdate(ClearValueUpdate(), *DataType::INT); + testValueUpdate(AssignValueUpdate(IntFieldValue(1)), *DataType::INT); + testValueUpdate(ArithmeticValueUpdate(ArithmeticValueUpdate::Div, 4.3), + *DataType::FLOAT); + testValueUpdate(AddValueUpdate(IntFieldValue(1), 4), *arrayType); + testValueUpdate(RemoveValueUpdate(IntFieldValue(1)), *arrayType); + + FieldUpdate fieldUpdate(docType->getField("intf")); + fieldUpdate.addUpdate(AssignValueUpdate(IntFieldValue(1))); + ByteBuffer::UP fieldBuf = serialize(fieldUpdate); + fieldBuf->flip(); + FieldUpdate fieldUpdateCopy(repo, *docType, *fieldBuf, + Document::getNewestSerializationVersion()); + CPPUNIT_ASSERT_EQUAL(fieldUpdate, fieldUpdateCopy); + + // Test that a document update can be serialized + DocumentUpdate docUpdate(*docType, DocumentId("doc::testdoc")); + docUpdate.addUpdate(fieldUpdateCopy); + ByteBuffer::UP docBuf = serialize42(docUpdate); + docBuf->flip(); + DocumentUpdate::UP docUpdateCopy(DocumentUpdate::create42(repo, *docBuf)); + CPPUNIT_ASSERT(!docUpdate.affectsDocumentBody()); + CPPUNIT_ASSERT(!docUpdateCopy->affectsDocumentBody()); + + // Create a test document + Document doc(*docType, DocumentId("doc::testdoc")); + doc.set("bytef", 0); + doc.set("intf", 5); + ArrayFieldValue array(*arrayType); + array.add(IntFieldValue(3)); + array.add(IntFieldValue(7)); + doc.setValue("intarr", array); + + // Verify that we can apply simple updates to it + { + Document updated(doc); + DocumentUpdate upd(*docType, DocumentId("doc::testdoc")); + upd.addUpdate(FieldUpdate(docType->getField("intf")) + .addUpdate(ClearValueUpdate())); + upd.applyTo(updated); + CPPUNIT_ASSERT(doc != updated); + CPPUNIT_ASSERT(! updated.getValue("intf")); + } + { + Document updated(doc); + DocumentUpdate upd(*docType, DocumentId("doc::testdoc")); + upd.addUpdate(FieldUpdate(docType->getField("intf")) + .addUpdate(AssignValueUpdate(IntFieldValue(15)))); + upd.applyTo(updated); + CPPUNIT_ASSERT(doc != updated); + CPPUNIT_ASSERT_EQUAL(15, updated.getValue("intf")->getAsInt()); + } + { + Document updated(doc); + DocumentUpdate upd(*docType, DocumentId("doc::testdoc")); + upd.addUpdate(FieldUpdate(docType->getField("intf")) + .addUpdate(ArithmeticValueUpdate( + ArithmeticValueUpdate::Add, 15))); + upd.applyTo(updated); + CPPUNIT_ASSERT(doc != updated); + CPPUNIT_ASSERT_EQUAL(20, updated.getValue("intf")->getAsInt()); + } + { + Document updated(doc); + DocumentUpdate upd(*docType, DocumentId("doc::testdoc")); + upd.addUpdate(FieldUpdate(docType->getField("intarr")) + .addUpdate(AddValueUpdate(IntFieldValue(4)))); + upd.applyTo(updated); + CPPUNIT_ASSERT(doc != updated); + std::unique_ptr<ArrayFieldValue> val(dynamic_cast<ArrayFieldValue*>( + updated.getValue("intarr").release())); + CPPUNIT_ASSERT_EQUAL(size_t(3), val->size()); + CPPUNIT_ASSERT_EQUAL(4, (*val)[2].getAsInt()); + CPPUNIT_ASSERT(upd.affectsDocumentBody()); + } + { + Document updated(doc); + DocumentUpdate upd(*docType, DocumentId("doc::testdoc")); + upd.addUpdate(FieldUpdate(docType->getField("intarr")) + .addUpdate(RemoveValueUpdate(IntFieldValue(3)))); + upd.applyTo(updated); + CPPUNIT_ASSERT(doc != updated); + std::unique_ptr<ArrayFieldValue> val(dynamic_cast<ArrayFieldValue*>( + updated.getValue("intarr").release())); + CPPUNIT_ASSERT_EQUAL(size_t(1), val->size()); + CPPUNIT_ASSERT_EQUAL(7, (*val)[0].getAsInt()); + } + { + Document updated(doc); + DocumentUpdate upd(*docType, DocumentId("doc::testdoc")); + upd.addUpdate(FieldUpdate(docType->getField("bytef")) + .addUpdate(ArithmeticValueUpdate( + ArithmeticValueUpdate::Add, 15))); + upd.applyTo(updated); + CPPUNIT_ASSERT(doc != updated); + CPPUNIT_ASSERT_EQUAL(15, (int) updated.getValue("bytef")->getAsByte()); + } +} + +void +DocumentUpdateTest::testClearField() +{ + // Create a document. + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + doc->setValue(doc->getField("headerval"), IntFieldValue(4)); + CPPUNIT_ASSERT_EQUAL(4, doc->getValue("headerval")->getAsInt()); + + // Apply an update. + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(doc->getField("headerval")) + .addUpdate(AssignValueUpdate())) + .applyTo(*doc); + CPPUNIT_ASSERT(!doc->getValue("headerval")); +} + +void +DocumentUpdateTest::testUpdateApplySingleValue() +{ + // Create a document. + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + doc->setValue(doc->getField("headerval"), IntFieldValue(4)); + CPPUNIT_ASSERT_EQUAL(4, doc->getValue("headerval")->getAsInt()); + + // Apply an update. + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(doc->getField("headerval")) + .addUpdate(AssignValueUpdate(IntFieldValue(9)))) + .applyTo(*doc); + CPPUNIT_ASSERT_EQUAL(9, doc->getValue("headerval")->getAsInt()); +} + +void +DocumentUpdateTest::testUpdateArray() +{ + // Create a document. + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + CPPUNIT_ASSERT_EQUAL((document::FieldValue*)NULL, + doc->getValue(doc->getField("tags")).get()); + + // Assign array field. + ArrayFieldValue myarray(doc->getType().getField("tags").getDataType()); + myarray.add(StringFieldValue("foo")); + myarray.add(StringFieldValue("bar")); + + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(doc->getField("tags")) + .addUpdate(AssignValueUpdate(myarray))) + .applyTo(*doc); + std::unique_ptr<ArrayFieldValue> + fval1(doc->getAs<ArrayFieldValue>(doc->getField("tags"))); + CPPUNIT_ASSERT_EQUAL((size_t) 2, fval1->size()); + CPPUNIT_ASSERT_EQUAL(std::string("foo"), + std::string((*fval1)[0].getAsString())); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), + std::string((*fval1)[1].getAsString())); + + // Append array field + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(doc->getField("tags")) + .addUpdate(AddValueUpdate(StringFieldValue("another"))) + .addUpdate(AddValueUpdate(StringFieldValue("tag")))) + .applyTo(*doc); + std::unique_ptr<ArrayFieldValue> + fval2(doc->getAs<ArrayFieldValue>(doc->getField("tags"))); + CPPUNIT_ASSERT_EQUAL((size_t) 4, fval2->size()); + CPPUNIT_ASSERT_EQUAL(std::string("foo"), + std::string((*fval2)[0].getAsString())); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), + std::string((*fval2)[1].getAsString())); + CPPUNIT_ASSERT_EQUAL(std::string("another"), + std::string((*fval2)[2].getAsString())); + CPPUNIT_ASSERT_EQUAL(std::string("tag"), + std::string((*fval2)[3].getAsString())); + + // Append single value. + try { + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(doc->getField("tags")) + .addUpdate(AssignValueUpdate( + StringFieldValue("THROW MEH!")))) + .applyTo(*doc); + CPPUNIT_FAIL("Expected exception when assinging a string value to an " + "array field."); + } + catch (std::exception& e) { + ; // fprintf(stderr, "Got exception => OK: %s\n", e.what()); + } + + // Remove array field. + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(doc->getField("tags")) + .addUpdate(RemoveValueUpdate(StringFieldValue("foo"))) + .addUpdate(RemoveValueUpdate(StringFieldValue("tag")))) + .applyTo(*doc); + std::unique_ptr<ArrayFieldValue> + fval3(doc->getAs<ArrayFieldValue>(doc->getField("tags"))); + CPPUNIT_ASSERT_EQUAL((size_t) 2, fval3->size()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), + std::string((*fval3)[0].getAsString())); + CPPUNIT_ASSERT_EQUAL(std::string("another"), + std::string((*fval3)[1].getAsString())); + + // Remove array from array. + ArrayFieldValue myarray2(doc->getType().getField("tags").getDataType()); + myarray2.add(StringFieldValue("foo")); + myarray2.add(StringFieldValue("bar")); + try { + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(doc->getField("tags")) + .addUpdate(RemoveValueUpdate(myarray2))) + .applyTo(*doc); + CPPUNIT_FAIL("Expected exception when removing an array from a " + "string array."); + } + catch (std::exception& e) { + ; // fprintf(stderr, "Got exception => OK: %s\n", e.what()); + } +} + +void +DocumentUpdateTest::testUpdateWeightedSet() +{ + // Create a test document + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + const Field& field(doc->getType().getField("stringweightedset")); + CPPUNIT_ASSERT_EQUAL((FieldValue*) 0, doc->getValue(field).get()); + + // Assign weightedset field + WeightedSetFieldValue wset(field.getDataType()); + wset.add(StringFieldValue("foo"), 3); + wset.add(StringFieldValue("bar"), 14); + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(field) + .addUpdate(AssignValueUpdate(wset))) + .applyTo(*doc); + std::unique_ptr<WeightedSetFieldValue> + fval1(doc->getAs<WeightedSetFieldValue>(field)); + CPPUNIT_ASSERT_EQUAL((size_t) 2, fval1->size()); + CPPUNIT_ASSERT(fval1->contains(StringFieldValue("foo"))); + CPPUNIT_ASSERT(fval1->find(StringFieldValue("foo")) != fval1->end()); + CPPUNIT_ASSERT_EQUAL(3, fval1->get(StringFieldValue("foo"), 0)); + CPPUNIT_ASSERT(fval1->contains(StringFieldValue("bar"))); + CPPUNIT_ASSERT(fval1->find(StringFieldValue("bar")) != fval1->end()); + CPPUNIT_ASSERT_EQUAL(14, fval1->get(StringFieldValue("bar"), 0)); + + // Do a second assign + WeightedSetFieldValue wset2(field.getDataType()); + wset2.add(StringFieldValue("foo"), 16); + wset2.add(StringFieldValue("bar"), 24); + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(field) + .addUpdate(AssignValueUpdate(wset2))) + .applyTo(*doc); + std::unique_ptr<WeightedSetFieldValue> + fval2(doc->getAs<WeightedSetFieldValue>(field)); + CPPUNIT_ASSERT_EQUAL((size_t) 2, fval2->size()); + CPPUNIT_ASSERT(fval2->contains(StringFieldValue("foo"))); + CPPUNIT_ASSERT(fval2->find(StringFieldValue("foo")) != fval1->end()); + CPPUNIT_ASSERT_EQUAL(16, fval2->get(StringFieldValue("foo"), 0)); + CPPUNIT_ASSERT(fval2->contains(StringFieldValue("bar"))); + CPPUNIT_ASSERT(fval2->find(StringFieldValue("bar")) != fval1->end()); + CPPUNIT_ASSERT_EQUAL(24, fval2->get(StringFieldValue("bar"), 0)); + + // Append weighted field + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(field) + .addUpdate(AddValueUpdate(StringFieldValue("foo")) + .setWeight(3)) + .addUpdate(AddValueUpdate(StringFieldValue("too")) + .setWeight(14))) + .applyTo(*doc); + std::unique_ptr<WeightedSetFieldValue> + fval3(doc->getAs<WeightedSetFieldValue>(field)); + CPPUNIT_ASSERT_EQUAL((size_t) 3, fval3->size()); + CPPUNIT_ASSERT(fval3->contains(StringFieldValue("foo"))); + CPPUNIT_ASSERT_EQUAL(3, fval3->get(StringFieldValue("foo"), 0)); + CPPUNIT_ASSERT(fval3->contains(StringFieldValue("bar"))); + CPPUNIT_ASSERT_EQUAL(24, fval3->get(StringFieldValue("bar"), 0)); + CPPUNIT_ASSERT(fval3->contains(StringFieldValue("too"))); + CPPUNIT_ASSERT_EQUAL(14, fval3->get(StringFieldValue("too"), 0)); + + // Remove weighted field + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(field) + .addUpdate(RemoveValueUpdate(StringFieldValue("foo"))) + .addUpdate(RemoveValueUpdate(StringFieldValue("too")))) + .applyTo(*doc); + std::unique_ptr<WeightedSetFieldValue> + fval4(doc->getAs<WeightedSetFieldValue>(field)); + CPPUNIT_ASSERT_EQUAL((size_t) 1, fval4->size()); + CPPUNIT_ASSERT(!fval4->contains(StringFieldValue("foo"))); + CPPUNIT_ASSERT(fval4->contains(StringFieldValue("bar"))); + CPPUNIT_ASSERT_EQUAL(24, fval4->get(StringFieldValue("bar"), 0)); + CPPUNIT_ASSERT(!fval4->contains(StringFieldValue("too"))); +} + +namespace { + +struct WeightedSetAutoCreateFixture +{ + DocumentTypeRepo repo; + const DocumentType* docType; + Document doc; + const Field& field; + DocumentUpdate update; + + WeightedSetAutoCreateFixture() + : repo(makeConfig()), + docType(repo.getDocumentType("test")), + doc(*docType, DocumentId("doc::testdoc")), + field(docType->getField("strwset")), + update(*docType, DocumentId("doc::testdoc")) + { + update.addUpdate(FieldUpdate(field) + .addUpdate(MapValueUpdate(StringFieldValue("foo"), + ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 1)))); + } + + void applyUpdateToDocument() { + update.applyTo(doc); + } + + static DocumenttypesConfig makeConfig() { + DocumenttypesConfigBuilderHelper builder; + // T_TAG is an alias for a weighted set with create-if-non-existing + // and remove-if-zero attributes set. Attempting to explicitly create + // a field matching those characteristics will in fact fail with a + // redefinition error. + builder.document(42, "test", + Struct("test.header") + .addField("strwset", DataType::T_TAG), + Struct("test.body")); + return builder.config(); + } +}; + +} // anon ns + +void +DocumentUpdateTest::testIncrementNonExistingAutoCreateWSetField() +{ + WeightedSetAutoCreateFixture fixture; + + fixture.applyUpdateToDocument(); + + std::unique_ptr<WeightedSetFieldValue> ws( + fixture.doc.getAs<WeightedSetFieldValue>(fixture.field)); + CPPUNIT_ASSERT_EQUAL(size_t(1), ws->size()); + CPPUNIT_ASSERT(ws->contains(StringFieldValue("foo"))); + CPPUNIT_ASSERT_EQUAL(1, ws->get(StringFieldValue("foo"), 0)); +} + +void +DocumentUpdateTest::testIncrementExistingWSetField() +{ + WeightedSetAutoCreateFixture fixture; + { + WeightedSetFieldValue wset(fixture.field.getDataType()); + wset.add(StringFieldValue("bar"), 14); + fixture.doc.setValue(fixture.field, wset); + } + fixture.applyUpdateToDocument(); + + std::unique_ptr<WeightedSetFieldValue> ws( + fixture.doc.getAs<WeightedSetFieldValue>(fixture.field)); + CPPUNIT_ASSERT_EQUAL(size_t(2), ws->size()); + CPPUNIT_ASSERT(ws->contains(StringFieldValue("foo"))); + CPPUNIT_ASSERT_EQUAL(1, ws->get(StringFieldValue("foo"), 0)); +} + +void +DocumentUpdateTest::testIncrementWithZeroResultWeightIsRemoved() +{ + WeightedSetAutoCreateFixture fixture; + fixture.update.addUpdate(FieldUpdate(fixture.field) + .addUpdate(MapValueUpdate(StringFieldValue("baz"), + ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 0)))); + + fixture.applyUpdateToDocument(); + + std::unique_ptr<WeightedSetFieldValue> ws( + fixture.doc.getAs<WeightedSetFieldValue>(fixture.field)); + CPPUNIT_ASSERT_EQUAL(size_t(1), ws->size()); + CPPUNIT_ASSERT(ws->contains(StringFieldValue("foo"))); + CPPUNIT_ASSERT(!ws->contains(StringFieldValue("baz"))); +} + +void DocumentUpdateTest::testReadSerializedFile() +{ + // Reads a file serialized from java + const char file_name[] = "data/crossplatform-java-cpp-doctypes.cfg"; + DocumentTypeRepo repo(readDocumenttypesConfig(file_name)); + + int fd = open("data/serializeupdatejava.dat", O_RDONLY); + + int len = lseek(fd,0,SEEK_END); + ByteBuffer buf(len); + lseek(fd,0,SEEK_SET); + read(fd, buf.getBuffer(), len); + + close(fd); + + DocumentUpdate::UP updp(DocumentUpdate::create42(repo, buf)); + DocumentUpdate& upd(*updp); + + const DocumentType *type = repo.getDocumentType("serializetest"); + CPPUNIT_ASSERT_EQUAL(DocumentId(DocIdString("update", "test")), + upd.getId()); + CPPUNIT_ASSERT_EQUAL(*type, upd.getType()); + + // Verify assign value update. + FieldUpdate serField = upd[0]; + CPPUNIT_ASSERT_EQUAL(serField.getField().getId(Document::getNewestSerializationVersion()), + type->getField("intfield").getId(Document::getNewestSerializationVersion())); + + const ValueUpdate* serValue = &serField[0]; + CPPUNIT_ASSERT_EQUAL(serValue->getType(), ValueUpdate::Assign); + + const AssignValueUpdate* assign( + static_cast<const AssignValueUpdate*>(serValue)); + CPPUNIT_ASSERT_EQUAL(IntFieldValue(4), + static_cast<const IntFieldValue&>(assign->getValue())); + + // Verify clear field update. + serField = upd[1]; + CPPUNIT_ASSERT_EQUAL(serField.getField().getId(Document::getNewestSerializationVersion()), + type->getField("floatfield").getId(Document::getNewestSerializationVersion())); + + serValue = &serField[0]; + CPPUNIT_ASSERT_EQUAL(serValue->getType(), ValueUpdate::Clear); + CPPUNIT_ASSERT(serValue->inherits(ClearValueUpdate::classId)); + + // Verify add value update. + serField = upd[2]; + CPPUNIT_ASSERT_EQUAL(serField.getField().getId(Document::getNewestSerializationVersion()), + type->getField("arrayoffloatfield").getId(Document::getNewestSerializationVersion())); + + serValue = &serField[0]; + CPPUNIT_ASSERT_EQUAL(serValue->getType(), ValueUpdate::Add); + + const AddValueUpdate* add = static_cast<const AddValueUpdate*>(serValue); + const FieldValue* value = &add->getValue(); + CPPUNIT_ASSERT(value->inherits(FloatFieldValue::classId)); + CPPUNIT_ASSERT_EQUAL(value->getAsFloat(), 5.00f); + + serValue = &serField[1]; + CPPUNIT_ASSERT_EQUAL(serValue->getType(), ValueUpdate::Add); + + add = static_cast<const AddValueUpdate*>(serValue); + value = &add->getValue(); + CPPUNIT_ASSERT(value->inherits(FloatFieldValue::classId)); + CPPUNIT_ASSERT_EQUAL(value->getAsFloat(), 4.23f); + + serValue = &serField[2]; + CPPUNIT_ASSERT_EQUAL(serValue->getType(), ValueUpdate::Add); + + add = static_cast<const AddValueUpdate*>(serValue); + value = &add->getValue(); + CPPUNIT_ASSERT(value->inherits(FloatFieldValue::classId)); + CPPUNIT_ASSERT_EQUAL(value->getAsFloat(), -1.00f); + +} + +void DocumentUpdateTest::testGenerateSerializedFile() +{ + // Tests nothing, only generates a file for java test + const char file_name[] = "data/crossplatform-java-cpp-doctypes.cfg"; + DocumentTypeRepo repo(readDocumenttypesConfig(file_name)); + + const DocumentType *type(repo.getDocumentType("serializetest")); + DocumentUpdate upd(*type, DocumentId(DocIdString("update", "test"))); + upd.addUpdate(FieldUpdate(type->getField("intfield")) + .addUpdate(AssignValueUpdate(IntFieldValue(4)))); + upd.addUpdate(FieldUpdate(type->getField("floatfield")) + .addUpdate(AssignValueUpdate(FloatFieldValue(1.00f)))); + upd.addUpdate(FieldUpdate(type->getField("arrayoffloatfield")) + .addUpdate(AddValueUpdate(FloatFieldValue(5.00f))) + .addUpdate(AddValueUpdate(FloatFieldValue(4.23f))) + .addUpdate(AddValueUpdate(FloatFieldValue(-1.00f)))); + upd.addUpdate(FieldUpdate(type->getField("intfield")) + .addUpdate(ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 3))); + upd.addUpdate(FieldUpdate(type->getField("wsfield")) + .addUpdate(MapValueUpdate(StringFieldValue("foo"), + ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 2))) + .addUpdate(MapValueUpdate(StringFieldValue("foo"), + ArithmeticValueUpdate(ArithmeticValueUpdate::Mul, 2)))); + ByteBuffer::UP buf(serialize42(upd)); + + int fd = open("data/serializeupdatecpp.dat", + O_WRONLY | O_TRUNC | O_CREAT, 0644); + write(fd, buf->getBuffer(), buf->getPos()); + close(fd); +} + + +void DocumentUpdateTest::testSetBadFieldTypes() +{ + // Create a test document + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + CPPUNIT_ASSERT_EQUAL((document::FieldValue*)NULL, + doc->getValue(doc->getField("headerval")).get()); + + // Assign a float value to an int field. + DocumentUpdate update(*doc->getDataType(), doc->getId()); + try { + update.addUpdate(FieldUpdate(doc->getField("headerval")) + .addUpdate(AssignValueUpdate(FloatFieldValue(4.00f)))); + CPPUNIT_FAIL("Expected exception when adding a float to an int field."); + } catch (std::exception& e) { + ; // fprintf(stderr, "Got exception => OK: %s\n", e.what()); + } + + // Apply update + update.applyTo(*doc); + + // Verify that the field is NOT set in the document. + CPPUNIT_ASSERT_EQUAL((document::FieldValue*)NULL, + doc->getValue(doc->getField("headerval")).get()); +} + +void +DocumentUpdateTest::testUpdateApplyNoParams() +{ + // Create a test document + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + CPPUNIT_ASSERT_EQUAL((document::FieldValue*)NULL, + doc->getValue(doc->getField("tags")).get()); + + // Assign array field with no parameters - illegal. + DocumentUpdate update(*doc->getDataType(), doc->getId()); + try { + update.addUpdate(FieldUpdate(doc->getField("tags")) + .addUpdate(AssignValueUpdate())); + CPPUNIT_FAIL("Expected exception when assign a NULL value."); + } catch (std::exception& e) { + ; // fprintf(stderr, "Got exception => OK: %s\n", e.what()); + } + + // Apply update + update.applyTo(*doc); + + // Verify that the field was cleared in the document. + CPPUNIT_ASSERT(!doc->hasValue(doc->getField("tags"))); +} + +void +DocumentUpdateTest::testUpdateApplyNoArrayValues() +{ + // Create a test document + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + const Field &field(doc->getType().getField("tags")); + CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, + doc->getValue(field).get()); + + // Assign array field with no array values = empty array + DocumentUpdate update(*doc->getDataType(), doc->getId()); + update.addUpdate(FieldUpdate(field) + .addUpdate(AssignValueUpdate( + ArrayFieldValue(field.getDataType())))); + + // Apply update + update.applyTo(*doc); + + // Verify that the field was set in the document + std::unique_ptr<ArrayFieldValue> fval(doc->getAs<ArrayFieldValue>(field)); + CPPUNIT_ASSERT(fval.get()); + CPPUNIT_ASSERT_EQUAL((size_t) 0, fval->size()); +} + +void +DocumentUpdateTest::testUpdateArrayEmptyParamValue() +{ + // Create a test document. + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + const Field &field(doc->getType().getField("tags")); + CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, + doc->getValue(field).get()); + + // Assign array field with no array values = empty array. + DocumentUpdate update(*doc->getDataType(), doc->getId()); + update.addUpdate(FieldUpdate(field) + .addUpdate(AssignValueUpdate( + ArrayFieldValue(field.getDataType())))); + update.applyTo(*doc); + + // Verify that the field was set in the document. + std::unique_ptr<ArrayFieldValue> fval1(doc->getAs<ArrayFieldValue>(field)); + CPPUNIT_ASSERT(fval1.get()); + CPPUNIT_ASSERT_EQUAL((size_t) 0, fval1->size()); + + // Remove array field. + update.clear(); + update.addUpdate(FieldUpdate(field) + .addUpdate(ClearValueUpdate())); + update.applyTo(*doc); + + // Verify that the field was cleared in the document. + std::unique_ptr<ArrayFieldValue> fval2(doc->getAs<ArrayFieldValue>(field)); + CPPUNIT_ASSERT(!fval2); +} + +void +DocumentUpdateTest::testUpdateWeightedSetEmptyParamValue() +{ + // Create a test document + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + const Field &field(doc->getType().getField("stringweightedset")); + CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, + doc->getValue(field).get()); + + // Assign weighted set with no items = empty set. + DocumentUpdate update(*doc->getDataType(), doc->getId()); + update.addUpdate(FieldUpdate(field) + .addUpdate(AssignValueUpdate( + WeightedSetFieldValue(field.getDataType())))); + update.applyTo(*doc); + + // Verify that the field was set in the document. + std::unique_ptr<WeightedSetFieldValue> + fval1(doc->getAs<WeightedSetFieldValue>(field)); + CPPUNIT_ASSERT(fval1.get()); + CPPUNIT_ASSERT_EQUAL((size_t) 0, fval1->size()); + + // Remove weighted set field. + update.clear(); + update.addUpdate(FieldUpdate(field) + .addUpdate(ClearValueUpdate())); + update.applyTo(*doc); + + // Verify that the field was cleared in the document. + std::unique_ptr<WeightedSetFieldValue> + fval2(doc->getAs<WeightedSetFieldValue>(field)); + CPPUNIT_ASSERT(!fval2); +} + +void +DocumentUpdateTest::testUpdateArrayWrongSubtype() +{ + // Create a test document + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + const Field &field(doc->getType().getField("tags")); + CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, + doc->getValue(field).get()); + + // Assign int values to string array. + DocumentUpdate update(*doc->getDataType(), doc->getId()); + try { + update.addUpdate(FieldUpdate(field) + .addUpdate(AddValueUpdate(IntFieldValue(123))) + .addUpdate(AddValueUpdate(IntFieldValue(456)))); + CPPUNIT_FAIL("Expected exception when adding wrong type."); + } catch (std::exception& e) { + ; // fprintf(stderr, "Got exception => OK: %s\n", e.what()); + } + + // Apply update + update.applyTo(*doc); + + // Verify that the field was NOT set in the document + FieldValue::UP fval(doc->getValue(field)); + CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, fval.get()); +} + +void +DocumentUpdateTest::testUpdateWeightedSetWrongSubtype() +{ + // Create a test document + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + const Field &field(doc->getType().getField("stringweightedset")); + CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, + doc->getValue(field).get()); + + // Assign int values to string array. + DocumentUpdate update(*doc->getDataType(), doc->getId()); + try { + update.addUpdate(FieldUpdate(field) + .addUpdate(AddValueUpdate(IntFieldValue(123)) + .setWeight(1000)) + .addUpdate(AddValueUpdate(IntFieldValue(456)) + .setWeight(2000))); + CPPUNIT_FAIL("Expected exception when adding wrong type."); + } catch (std::exception& e) { + ; // fprintf(stderr, "Got exception => OK: %s\n", e.what()); + } + + // Apply update + update.applyTo(*doc); + + // Verify that the field was NOT set in the document + FieldValue::UP fval(doc->getValue(field)); + CPPUNIT_ASSERT_EQUAL((document::FieldValue*) 0, fval.get()); +} + +void +DocumentUpdateTest::testMapValueUpdate() +{ + // Create a test document + TestDocMan docMan; + Document::UP doc(docMan.createDocument()); + const Field &field1 = doc->getField("stringweightedset"); + const Field &field2 = doc->getField("stringweightedset2"); + WeightedSetFieldValue wsval1(field1.getDataType()); + WeightedSetFieldValue wsval2(field2.getDataType()); + doc->setValue(field1, wsval1); + doc->setValue(field2, wsval2); + + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(field1) + .addUpdate(MapValueUpdate( + StringFieldValue("banana"), + ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 1.0) + ))) + .applyTo(*doc); + std::unique_ptr<WeightedSetFieldValue> fv1 = + doc->getAs<WeightedSetFieldValue>(field1); + CPPUNIT_ASSERT(fv1->size() == 0); + + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(field2) + .addUpdate(MapValueUpdate( + StringFieldValue("banana"), + ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 1.0) + ))) + .applyTo(*doc); + std::unique_ptr<WeightedSetFieldValue> fv2 = + doc->getAs<WeightedSetFieldValue>(field2); + CPPUNIT_ASSERT(fv2->size() == 1); + + CPPUNIT_ASSERT(fv1->find(StringFieldValue("apple")) == fv1->end()); + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(field1) + .addUpdate(ClearValueUpdate())) + .applyTo(*doc); + + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(field1) + .addUpdate(AddValueUpdate(StringFieldValue("apple")) + .setWeight(1))) + .applyTo(*doc); + + std::unique_ptr<WeightedSetFieldValue> + fval3(doc->getAs<WeightedSetFieldValue>(field1)); + CPPUNIT_ASSERT(fval3->find(StringFieldValue("apple")) != fval3->end()); + CPPUNIT_ASSERT_EQUAL(1, fval3->get(StringFieldValue("apple"))); + + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(field2) + .addUpdate(AddValueUpdate(StringFieldValue("apple")) + .setWeight(1))) + .applyTo(*doc); + + std::unique_ptr<WeightedSetFieldValue> + fval3b(doc->getAs<WeightedSetFieldValue>(field2)); + CPPUNIT_ASSERT(fval3b->find(StringFieldValue("apple")) != fval3b->end()); + CPPUNIT_ASSERT_EQUAL(1, fval3b->get(StringFieldValue("apple"))); + + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(field1) + .addUpdate(MapValueUpdate( + StringFieldValue("apple"), + ArithmeticValueUpdate(ArithmeticValueUpdate::Sub, 1.0) + ))) + .applyTo(*doc); + + std::unique_ptr<WeightedSetFieldValue> fv3 = + doc->getAs<WeightedSetFieldValue>(field1); + CPPUNIT_ASSERT(fv3->find(StringFieldValue("apple")) != fv3->end()); + CPPUNIT_ASSERT_EQUAL(0, fv3->get(StringFieldValue("apple"))); + + DocumentUpdate(*doc->getDataType(), doc->getId()) + .addUpdate(FieldUpdate(field2) + .addUpdate(MapValueUpdate( + StringFieldValue("apple"), + ArithmeticValueUpdate(ArithmeticValueUpdate::Sub, 1.0) + ))) + .applyTo(*doc); + + std::unique_ptr<WeightedSetFieldValue> fv4 = + doc->getAs<WeightedSetFieldValue>(field2); + CPPUNIT_ASSERT(fv4->find(StringFieldValue("apple")) == fv4->end()); +} + + +void +assertDocumentUpdateFlag(bool createIfNonExistent, int value) +{ + DocumentUpdateFlags f1; + f1.setCreateIfNonExistent(createIfNonExistent); + CPPUNIT_ASSERT_EQUAL(createIfNonExistent, f1.getCreateIfNonExistent()); + int combined = f1.injectInto(value); + std::cout << "createIfNonExistent=" << createIfNonExistent << ", value=" << value << ", combined=" << combined << std::endl; + + DocumentUpdateFlags f2 = DocumentUpdateFlags::extractFlags(combined); + int extractedValue = DocumentUpdateFlags::extractValue(combined); + CPPUNIT_ASSERT_EQUAL(createIfNonExistent, f2.getCreateIfNonExistent()); + CPPUNIT_ASSERT_EQUAL(value, extractedValue); +} + +void +DocumentUpdateTest::testThatDocumentUpdateFlagsIsWorking() +{ + { // create-if-non-existent = true + assertDocumentUpdateFlag(true, 0); + assertDocumentUpdateFlag(true, 1); + assertDocumentUpdateFlag(true, 2); + assertDocumentUpdateFlag(true, 9999); + assertDocumentUpdateFlag(true, 0xFFFFFFE); + assertDocumentUpdateFlag(true, 0xFFFFFFF); + } + { // create-if-non-existent = false + assertDocumentUpdateFlag(false, 0); + assertDocumentUpdateFlag(false, 1); + assertDocumentUpdateFlag(false, 2); + assertDocumentUpdateFlag(false, 9999); + assertDocumentUpdateFlag(false, 0xFFFFFFE); + assertDocumentUpdateFlag(false, 0xFFFFFFF); + } +} + +struct CreateIfNonExistentFixture +{ + TestDocMan docMan; + Document::UP document; + DocumentUpdate::UP update; + CreateIfNonExistentFixture() + : docMan(), + document(docMan.createDocument()), + update(new DocumentUpdate(*document->getDataType(), + document->getId())) + { + update->addUpdate(FieldUpdate(document->getField("headerval")) + .addUpdate(AssignValueUpdate(IntFieldValue(1)))); + update->setCreateIfNonExistent(true); + } +}; + +void +DocumentUpdateTest::testThatCreateIfNonExistentFlagIsSerialized50AndDeserialized50() +{ + CreateIfNonExistentFixture f; + + ByteBuffer::UP buf(serializeHEAD(*f.update)); + buf->flip(); + + DocumentUpdate::UP deserialized = DocumentUpdate::createHEAD(f.docMan.getTypeRepo(), *buf); + CPPUNIT_ASSERT_EQUAL(*f.update, *deserialized); + CPPUNIT_ASSERT(deserialized->getCreateIfNonExistent()); +} + +void +DocumentUpdateTest::testThatCreateIfNonExistentFlagIsSerializedAndDeserialized() +{ + CreateIfNonExistentFixture f; + + ByteBuffer::UP buf(serialize42(*f.update)); + buf->flip(); + + DocumentUpdate::UP deserialized = DocumentUpdate::create42(f.docMan.getTypeRepo(), *buf); + CPPUNIT_ASSERT_EQUAL(*f.update, *deserialized); + CPPUNIT_ASSERT(deserialized->getCreateIfNonExistent()); +} + +} // namespace document diff --git a/document/src/tests/documentupdatetestcase.h b/document/src/tests/documentupdatetestcase.h new file mode 100644 index 00000000000..b8d8294ec0d --- /dev/null +++ b/document/src/tests/documentupdatetestcase.h @@ -0,0 +1,50 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/* $Id$*/ + +#pragma once + +#include <cppunit/extensions/HelperMacros.h> + +class DocumentUpdate_Test : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(DocumentUpdate_Test); + CPPUNIT_TEST(testUpdateApplySingleValue); + CPPUNIT_TEST(testUpdateArray); + CPPUNIT_TEST(testUpdateWeightedSet); + CPPUNIT_TEST(testReadSerializedFile); + CPPUNIT_TEST(testGenerateSerializedFile); + CPPUNIT_TEST(testSetBadFieldTypes); + CPPUNIT_TEST(testUpdateApplyNoParams); + CPPUNIT_TEST(testUpdateApplyNoArrayValues); + CPPUNIT_TEST(testUpdateArrayEmptyParamValue); + CPPUNIT_TEST(testUpdateWeightedSetEmptyParamValue); + CPPUNIT_TEST(testUpdateArrayWrongSubtype); + CPPUNIT_TEST(testUpdateWeightedSetWrongSubtype); + CPPUNIT_TEST(testMapValueUpdate); + CPPUNIT_TEST(testThatDocumentUpdateFlagsIsWorking); + CPPUNIT_TEST(testThatCreateIfNonExistentFlagIsSerialized50AndDeserialized50); + CPPUNIT_TEST(testThatCreateIfNonExistentFlagIsSerializedAndDeserialized); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + void tearDown(); + +protected: + void testUpdateApplySingleValue(); + void testUpdateArray(); + void testUpdateWeightedSet(); + void testReadSerializedFile(); + void testGenerateSerializedFile(); + void testSetBadFieldTypes(); + void testUpdateApplyNoParams(); + void testUpdateApplyNoArrayValues(); + void testUpdateArrayEmptyParamValue(); + void testUpdateWeightedSetEmptyParamValue(); + void testUpdateArrayWrongSubtype(); + void testUpdateWeightedSetWrongSubtype(); + void testMapValueUpdate(); + void testThatDocumentUpdateFlagsIsWorking(); + void testThatCreateIfNonExistentFlagIsSerialized50AndDeserialized50(); + void testThatCreateIfNonExistentFlagIsSerializedAndDeserialized(); +}; + diff --git a/document/src/tests/fieldpathupdatetestcase.cpp b/document/src/tests/fieldpathupdatetestcase.cpp new file mode 100644 index 00000000000..ec999d0dfe9 --- /dev/null +++ b/document/src/tests/fieldpathupdatetestcase.cpp @@ -0,0 +1,1310 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <vespa/document/base/testdocman.h> +#include <vespa/document/fieldvalue/fieldvalues.h> +#include <vespa/document/select/node.h> +#include <vespa/vespalib/io/fileutil.h> +#include <vespa/vdstestlib/cppunit/macros.h> +#include <vespa/document/update/fieldpathupdates.h> +#include <vespa/document/update/documentupdate.h> + +#include <vespa/document/repo/configbuilder.h> +#include <vespa/vespalib/objects/identifiable.h> +#include <vespa/document/serialization/vespadocumentserializer.h> +#include <fstream> +#include <sstream> + +using vespalib::Identifiable; +using namespace document::config_builder; + +namespace document { + +struct FieldPathUpdateTestCase : public CppUnit::TestFixture { + DocumentTypeRepo::SP _repo; + DocumentType _foobar_type; + + void setUp(); + void tearDown(); + + void testWhereClause(); + void testNoIterateMapValues(); + void testRemoveField(); + void testApplyRemoveEntireListField(); + void testApplyRemoveMultiList(); + void testApplyRemoveMultiWset(); + void testApplyAssignSingle(); + void testApplyAssignMath(); + void testApplyAssignMathDivZero(); + void testApplyAssignMathByteToZero(); + void testApplyAssignMathNotModifiedOnUnderflow(); + void testApplyAssignMathNotModifiedOnOverflow(); + void testApplyAssignFieldNotExistingInExpression(); + void testApplyAssignFieldNotExistingInPath(); + void testApplyAssignTargetNotExisting(); + void testAssignSimpleMapValueWithVariable(); + void testApplyAssignMathRemoveIfZero(); + void testApplyAssignMultiList(); + void testApplyAssignMultiWset(); + void testAssignWsetRemoveIfZero(); + void testApplyAddMultiList(); + void testAddAndAssignList(); + void testAssignMap(); + void testAssignMapStruct(); + void testAssignMapStructVariable(); + void testAssignMapNoExist(); + void testAssignMapNoExistNoCreate(); + void testQuotedStringKey(); + void testEqualityComparison(); + void testAffectsDocumentBody(); + void testIncompatibleDataTypeFails(); + void testSerializeAssign(); + void testSerializeAdd(); + void testSerializeRemove(); + void testSerializeAssignMath(); + void testReadSerializedFile(); + void testGenerateSerializedFile(); + + CPPUNIT_TEST_SUITE(FieldPathUpdateTestCase); + CPPUNIT_TEST(testWhereClause); + CPPUNIT_TEST(testNoIterateMapValues); + CPPUNIT_TEST(testRemoveField); + CPPUNIT_TEST(testApplyRemoveEntireListField); + CPPUNIT_TEST(testApplyRemoveMultiList); + CPPUNIT_TEST(testApplyRemoveMultiWset); + CPPUNIT_TEST(testApplyAssignSingle); + CPPUNIT_TEST(testApplyAssignMath); + CPPUNIT_TEST(testApplyAssignMathDivZero); + CPPUNIT_TEST(testApplyAssignMathByteToZero); + CPPUNIT_TEST(testApplyAssignMathNotModifiedOnUnderflow); + CPPUNIT_TEST(testApplyAssignMathNotModifiedOnOverflow); + CPPUNIT_TEST(testApplyAssignFieldNotExistingInExpression); + CPPUNIT_TEST(testApplyAssignFieldNotExistingInPath); + CPPUNIT_TEST(testApplyAssignTargetNotExisting); + CPPUNIT_TEST(testAssignSimpleMapValueWithVariable); + CPPUNIT_TEST(testApplyAssignMathRemoveIfZero); + CPPUNIT_TEST(testApplyAssignMultiList); + CPPUNIT_TEST(testApplyAssignMultiWset); + CPPUNIT_TEST(testAssignWsetRemoveIfZero); + CPPUNIT_TEST(testApplyAddMultiList); + CPPUNIT_TEST(testAddAndAssignList); + CPPUNIT_TEST(testAssignMap); + CPPUNIT_TEST(testAssignMapStruct); + CPPUNIT_TEST(testAssignMapStructVariable); + CPPUNIT_TEST(testAssignMapNoExist); + CPPUNIT_TEST(testAssignMapNoExistNoCreate); + CPPUNIT_TEST(testQuotedStringKey); + CPPUNIT_TEST(testEqualityComparison); + CPPUNIT_TEST(testAffectsDocumentBody); + CPPUNIT_TEST(testIncompatibleDataTypeFails); + CPPUNIT_TEST(testSerializeAssign); + CPPUNIT_TEST(testSerializeAdd); + CPPUNIT_TEST(testSerializeRemove); + CPPUNIT_TEST(testSerializeAssignMath); + CPPUNIT_TEST(testReadSerializedFile); + CPPUNIT_TEST(testGenerateSerializedFile); + CPPUNIT_TEST_SUITE_END(); +private: + DocumentUpdate::UP + createDocumentUpdateForSerialization(const DocumentTypeRepo& repo); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(FieldPathUpdateTestCase); + +namespace { + +document::DocumenttypesConfig getRepoConfig() { + const int struct2_id = 64; + DocumenttypesConfigBuilderHelper builder; + builder.document( + 42, "test", + Struct("test.header") + .addField("primitive1", DataType::T_INT) + .addField("l1s1", Struct("struct3") + .addField("primitive1", DataType::T_INT) + .addField("ss", Struct("struct2") + .setId(struct2_id) + .addField("primitive1", DataType::T_INT) + .addField("primitive2", DataType::T_INT) + .addField("iarray", Array(DataType::T_INT)) + .addField("sarray", Array( + Struct("struct1") + .addField("primitive1", + DataType::T_INT) + .addField("primitive2", + DataType::T_INT))) + .addField("smap", Map(DataType::T_STRING, + DataType::T_STRING))) + .addField("structmap", + Map(DataType::T_STRING, struct2_id)) + .addField("wset", Wset(DataType::T_STRING)) + .addField("structwset", Wset(struct2_id))), + Struct("test.body")); + return builder.config(); +} + +Document::UP +createTestDocument(const DocumentTypeRepo &repo) +{ + const DocumentType* type(repo.getDocumentType("test")); + const DataType* struct3(repo.getDataType(*type, "struct3")); + const DataType* struct2(repo.getDataType(*type, "struct2")); + const DataType* iarr(repo.getDataType(*type, "Array<Int>")); + const DataType* sarr(repo.getDataType(*type, "Array<struct1>")); + const DataType* struct1(repo.getDataType(*type, "struct1")); + const DataType* smap(repo.getDataType(*type, "Map<String,String>")); + const DataType* structmap(repo.getDataType(*type, "Map<String,struct2>")); + const DataType* wset(repo.getDataType(*type, "WeightedSet<String>")); + const DataType* structwset(repo.getDataType(*type, "WeightedSet<struct2>")); + Document::UP + doc(new Document(*type, DocumentId("doc::testdoc"))); + doc->setValue("primitive1", IntFieldValue(1)); + StructFieldValue l1s1(*struct3); + l1s1.setValue("primitive1", IntFieldValue(2)); + + StructFieldValue l2s1(*struct2); + l2s1.setValue("primitive1", IntFieldValue(3)); + l2s1.setValue("primitive2", IntFieldValue(4)); + StructFieldValue l2s2(*struct2); + l2s2.setValue("primitive1", IntFieldValue(5)); + l2s2.setValue("primitive2", IntFieldValue(6)); + ArrayFieldValue iarr1(*iarr); + iarr1.add(IntFieldValue(11)); + iarr1.add(IntFieldValue(12)); + iarr1.add(IntFieldValue(13)); + ArrayFieldValue sarr1(*sarr); + StructFieldValue l3s1(*struct1); + l3s1.setValue("primitive1", IntFieldValue(1)); + l3s1.setValue("primitive2", IntFieldValue(2)); + sarr1.add(l3s1); + sarr1.add(l3s1); + MapFieldValue smap1(*smap); + smap1.put(StringFieldValue("leonardo"), StringFieldValue("dicaprio")); + smap1.put(StringFieldValue("ellen"), StringFieldValue("page")); + smap1.put(StringFieldValue("joseph"), StringFieldValue("gordon-levitt")); + l2s1.setValue("smap", smap1); + l2s1.setValue("iarray", iarr1); + l2s1.setValue("sarray", sarr1); + + l1s1.setValue("ss", l2s1); + MapFieldValue structmap1(*structmap); + structmap1.put(StringFieldValue("test"), l2s1); + l1s1.setValue("structmap", structmap1); + + WeightedSetFieldValue wset1(*wset); + wset1.add("foo"); + wset1.add("bar"); + wset1.add("zoo"); + l1s1.setValue("wset", wset1); + + WeightedSetFieldValue wset2(*structwset); + wset2.add(l2s1); + wset2.add(l2s2); + l1s1.setValue("structwset", wset2); + + doc->setValue("l1s1", l1s1); + return doc; +} + +ByteBuffer::UP serializeHEAD(const DocumentUpdate & update) +{ + vespalib::nbostream stream; + VespaDocumentSerializer serializer(stream); + serializer.writeHEAD(update); + ByteBuffer::UP retVal(new ByteBuffer(stream.size())); + retVal->putBytes(stream.peek(), stream.size()); + return retVal; +} + +void testSerialize(const DocumentTypeRepo& repo, const DocumentUpdate& a) { + try{ + bool affectsBody = a.affectsDocumentBody(); + ByteBuffer::UP bb(serializeHEAD(a)); + bb->flip(); + DocumentUpdate::UP b(DocumentUpdate::createHEAD(repo, *bb)); + + CPPUNIT_ASSERT_EQUAL(affectsBody, b->affectsDocumentBody()); + CPPUNIT_ASSERT_EQUAL(size_t(0), bb->getRemaining()); + CPPUNIT_ASSERT_EQUAL(a.getId().toString(), b->getId().toString()); + CPPUNIT_ASSERT_EQUAL(a.getUpdates().size(), b->getUpdates().size()); + for (size_t i(0); i < a.getUpdates().size(); i++) { + const FieldUpdate & ua = a.getUpdates()[i]; + const FieldUpdate & ub = b->getUpdates()[i]; + + CPPUNIT_ASSERT_EQUAL(&ua.getField(), &ub.getField()); + CPPUNIT_ASSERT_EQUAL(ua.getUpdates().size(), + ub.getUpdates().size()); + for (size_t j(0); j < ua.getUpdates().size(); j++) { + CPPUNIT_ASSERT_EQUAL(ua.getUpdates()[j]->getType(), + ub.getUpdates()[j]->getType()); + } + } + CPPUNIT_ASSERT_EQUAL(a.getFieldPathUpdates().size(), b->getFieldPathUpdates().size()); + for (size_t i(0); i < a.getFieldPathUpdates().size(); i++) { + const FieldPathUpdate::CP& ua = a.getFieldPathUpdates()[i]; + const FieldPathUpdate::CP& ub = b->getFieldPathUpdates()[i]; + + CPPUNIT_ASSERT_EQUAL(*ua, *ub); + } + CPPUNIT_ASSERT_EQUAL(a, *b); + } catch (std::exception& e) { + std::cerr << "Failed while testing document field path update:\n" + << a.toString(true) << "\n"; + throw; + } +} + +} // anon ns + +struct TestFieldPathUpdate : FieldPathUpdate +{ + struct TestIteratorHandler : FieldValue::IteratorHandler + { + TestIteratorHandler(std::string& str) + : _str(str) {} + + ModificationStatus doModify(FieldValue& value) + { + std::ostringstream ss; + value.print(ss, false, ""); + if (!_str.empty()) { + _str += ';'; + } + _str += ss.str(); + return NOT_MODIFIED; + } + + bool onComplex(const Content&) { return false; } + + std::string& _str; + }; + + mutable std::string _str; + + TestFieldPathUpdate(const DocumentTypeRepo& repo, + const DataType *type, + const std::string& fieldPath, + const std::string& whereClause) + : FieldPathUpdate(repo, *type, fieldPath, whereClause) + { + } + + TestFieldPathUpdate(const TestFieldPathUpdate& other) + : FieldPathUpdate(other) + { + } + + std::unique_ptr<FieldValue::IteratorHandler> getIteratorHandler(Document&) const + { + return std::unique_ptr<FieldValue::IteratorHandler>( + new TestIteratorHandler(_str)); + } + + TestFieldPathUpdate* clone() const { return new TestFieldPathUpdate(*this); } + + void print(std::ostream& out, bool, const std::string&) const + { + out << "TestFieldPathUpdate()"; + } + + void accept(UpdateVisitor & visitor) const override { (void) visitor; } + uint8_t getSerializedType() const override { assert(false); return 7; } +}; + +void +FieldPathUpdateTestCase::setUp() +{ + DocumenttypesConfigBuilderHelper builder; + builder.document(42, "foobar", + Struct("foobar.header") + .addField("num", DataType::T_INT) + .addField("byteval", DataType::T_BYTE) + .addField("strfoo", DataType::T_STRING) + .addField("strarray", Array(DataType::T_STRING)), + Struct("foobar.body") + .addField("strwset", Wset(DataType::T_STRING)) + .addField("structmap", + Map(DataType::T_STRING, Struct("mystruct") + .addField("title", DataType::T_STRING) + .addField("rating", DataType::T_INT))) + .addField("strmap", + Map(DataType::T_STRING, DataType::T_STRING))); + _repo.reset(new DocumentTypeRepo(builder.config())); + + _foobar_type = *_repo->getDocumentType("foobar"); +} + +void +FieldPathUpdateTestCase::tearDown() +{ +} + +void +FieldPathUpdateTestCase::testWhereClause() +{ + DocumentTypeRepo repo(getRepoConfig()); + Document::UP doc(createTestDocument(repo)); + std::string where = "test.l1s1.structmap.value.smap{$x} == \"dicaprio\""; + TestFieldPathUpdate update(repo, doc->getDataType(), + "l1s1.structmap.value.smap{$x}", where); + update.applyTo(*doc); + CPPUNIT_ASSERT_EQUAL(std::string("dicaprio"), update._str); +} + +void +FieldPathUpdateTestCase::testNoIterateMapValues() +{ + DocumentTypeRepo repo(getRepoConfig()); + Document::UP doc(createTestDocument(repo)); + TestFieldPathUpdate update(repo, doc->getDataType(), + "l1s1.structwset.primitive1", "true"); + update.applyTo(*doc); + CPPUNIT_ASSERT_EQUAL(std::string("3;5"), update._str); +} + +void +FieldPathUpdateTestCase::testRemoveField() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:things:thangs"))); + CPPUNIT_ASSERT(doc->hasValue("strfoo") == false); + doc->setValue("strfoo", StringFieldValue("cocacola")); + CPPUNIT_ASSERT_EQUAL(vespalib::string("cocacola"), doc->getValue("strfoo")->getAsString()); + //doc->print(std::cerr, true, ""); + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate(FieldPathUpdate::CP(new RemoveFieldPathUpdate(*_repo, *doc->getDataType(), "strfoo"))); + docUp.applyTo(*doc); + CPPUNIT_ASSERT(doc->hasValue("strfoo") == false); +} + +void +FieldPathUpdateTestCase::testApplyRemoveMultiList() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:things:thangs"))); + CPPUNIT_ASSERT(doc->hasValue("strarray") == false); + { + ArrayFieldValue + strArray(doc->getType().getField("strarray").getDataType()); + strArray.add(StringFieldValue("crouching tiger, hidden field")); + strArray.add(StringFieldValue("remove val 1")); + strArray.add(StringFieldValue("hello hello")); + doc->setValue("strarray", strArray); + } + CPPUNIT_ASSERT(doc->hasValue("strarray")); + //doc->print(std::cerr, true, ""); + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new RemoveFieldPathUpdate(*_repo, *doc->getDataType(), + "strarray[$x]", "foobar.strarray[$x] == \"remove val 1\""))); + docUp.applyTo(*doc); + { + std::unique_ptr<ArrayFieldValue> strArray = + doc->getAs<ArrayFieldValue>(doc->getField("strarray")); + CPPUNIT_ASSERT_EQUAL(std::size_t(2), strArray->size()); + CPPUNIT_ASSERT_EQUAL(vespalib::string("crouching tiger, hidden field"), (*strArray)[0].getAsString()); + CPPUNIT_ASSERT_EQUAL(vespalib::string("hello hello"), (*strArray)[1].getAsString()); + } +} + +void +FieldPathUpdateTestCase::testApplyRemoveEntireListField() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:things:thangs"))); + CPPUNIT_ASSERT(doc->hasValue("strarray") == false); + { + ArrayFieldValue + strArray(doc->getType().getField("strarray").getDataType()); + strArray.add(StringFieldValue("this list")); + strArray.add(StringFieldValue("should be")); + strArray.add(StringFieldValue("totally removed")); + doc->setValue("strarray", strArray); + } + //doc->print(std::cerr, true, ""); + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new RemoveFieldPathUpdate(*_repo, *doc->getDataType(), "strarray", ""))); + docUp.applyTo(*doc); + CPPUNIT_ASSERT(!doc->hasValue("strarray")); +} + +void +FieldPathUpdateTestCase::testApplyRemoveMultiWset() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:helan:halvan"))); + CPPUNIT_ASSERT(doc->hasValue("strwset") == false); + { + WeightedSetFieldValue + strWset(doc->getType().getField("strwset").getDataType()); + strWset.add(StringFieldValue("hello hello"), 10); + strWset.add(StringFieldValue("remove val 1"), 20); + doc->setValue("strwset", strWset); + } + CPPUNIT_ASSERT(doc->hasValue("strwset")); + //doc->print(std::cerr, true, ""); + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new RemoveFieldPathUpdate(*_repo, *doc->getDataType(), + "strwset{remove val 1}"))); + docUp.applyTo(*doc); + { + std::unique_ptr<WeightedSetFieldValue> strWset = + doc->getAs<WeightedSetFieldValue>(doc->getField("strwset")); + CPPUNIT_ASSERT_EQUAL(std::size_t(1), strWset->size()); + CPPUNIT_ASSERT_EQUAL(10, strWset->get(StringFieldValue("hello hello"))); + } +} + +void +FieldPathUpdateTestCase::testApplyAssignSingle() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:drekka:karsk"))); + CPPUNIT_ASSERT(doc->hasValue("strfoo") == false); + // Test assignment of non-existing + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "strfoo", std::string(), StringFieldValue("himert")))); + docUp.applyTo(*doc); + CPPUNIT_ASSERT(doc->hasValue("strfoo")); + CPPUNIT_ASSERT_EQUAL(vespalib::string("himert"), doc->getValue("strfoo")->getAsString()); + // Test overwriting existing + DocumentUpdate docUp2(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp2.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "strfoo", std::string(), StringFieldValue("wunderbaum")))); + docUp2.applyTo(*doc); + CPPUNIT_ASSERT_EQUAL(vespalib::string("wunderbaum"), doc->getValue("strfoo")->getAsString()); +} + +void +FieldPathUpdateTestCase::testApplyAssignMath() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:bat:man"))); + doc->setValue("num", IntFieldValue(34)); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "num", "", "($value * 2) / $value"))); + docUp.applyTo(*doc); + CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(IntFieldValue(2)), *doc->getValue("num")); +} + +void +FieldPathUpdateTestCase::testApplyAssignMathByteToZero() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:bat:man"))); + doc->setValue("byteval", ByteFieldValue(3)); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "byteval", "", "$value - 3"))); + docUp.applyTo(*doc); + CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(ByteFieldValue(0)), *doc->getValue("byteval")); +} + +void +FieldPathUpdateTestCase::testApplyAssignMathNotModifiedOnUnderflow() +{ + int low_value = -126; + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:bat:man"))); + doc->setValue("byteval", ByteFieldValue(low_value)); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "byteval", "", "$value - 4"))); + docUp.applyTo(*doc); + // Over/underflow will happen. You must have control of your data types. + CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(ByteFieldValue((char)(low_value - 4))), *doc->getValue("byteval")); +} + +void +FieldPathUpdateTestCase::testApplyAssignMathNotModifiedOnOverflow() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:bat:man"))); + doc->setValue("byteval", ByteFieldValue(127)); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "byteval", "", "$value + 200"))); + docUp.applyTo(*doc); + // Over/underflow will happen. You must have control of your data types. + CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(ByteFieldValue(static_cast<char>(static_cast<int>(127+200)))), *doc->getValue("byteval")); +} + +void +FieldPathUpdateTestCase::testApplyAssignMathDivZero() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:bat:man"))); + CPPUNIT_ASSERT(doc->hasValue("num") == false); + doc->setValue("num", IntFieldValue(10)); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "num", "", "$value / ($value - 10)"))); + docUp.applyTo(*doc); + CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(IntFieldValue(10)), *doc->getValue("num")); +} + +void +FieldPathUpdateTestCase::testApplyAssignFieldNotExistingInExpression() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:bat:man"))); + CPPUNIT_ASSERT(doc->hasValue("num") == false); + doc->setValue("num", IntFieldValue(10)); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "num", "", "foobar.num2 + $value"))); + docUp.applyTo(*doc); + CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(IntFieldValue(10)), *doc->getValue("num")); +} + +void +FieldPathUpdateTestCase::testApplyAssignFieldNotExistingInPath() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:bat:man"))); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + try { + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "nosuchnum", "", "foobar.num + $value"))); + CPPUNIT_ASSERT(false); + } catch (const vespalib::IllegalArgumentException&) { + } +} + +void +FieldPathUpdateTestCase::testApplyAssignTargetNotExisting() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:bat:man"))); + CPPUNIT_ASSERT(doc->hasValue("num") == false); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "num", "", "$value + 5"))); + docUp.applyTo(*doc); + CPPUNIT_ASSERT_EQUAL(static_cast<const FieldValue&>(IntFieldValue(5)), *doc->getValue("num")); +} + +void +FieldPathUpdateTestCase::testAssignSimpleMapValueWithVariable() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:bug:hunter"))); + + MapFieldValue mfv(doc->getType().getField("strmap").getDataType()); + mfv.put(StringFieldValue("foo"), StringFieldValue("bar")); + mfv.put(StringFieldValue("baz"), StringFieldValue("bananas")); + doc->setValue("strmap", mfv); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + // Select on value, not key + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "strmap{$x}", "foobar.strmap{$x} == \"bar\"", StringFieldValue("shinyvalue")))); + docUp.applyTo(*doc); + + std::unique_ptr<MapFieldValue> valueNow( + doc->getAs<MapFieldValue>(doc->getField("strmap"))); + + CPPUNIT_ASSERT_EQUAL(std::size_t(2), valueNow->size()); + CPPUNIT_ASSERT_EQUAL( + static_cast<const FieldValue&>(StringFieldValue("shinyvalue")), + *valueNow->get(StringFieldValue("foo"))); + CPPUNIT_ASSERT_EQUAL( + static_cast<const FieldValue&>(StringFieldValue("bananas")), + *valueNow->get(StringFieldValue("baz"))); +} + +void +FieldPathUpdateTestCase::testApplyAssignMathRemoveIfZero() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:bat:man"))); + CPPUNIT_ASSERT(doc->hasValue("num") == false); + doc->setValue("num", IntFieldValue(34)); + CPPUNIT_ASSERT(doc->hasValue("num") == true); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + FieldPathUpdate::CP up1(new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "num", "", "($value * 2) / $value - 2")); + static_cast<AssignFieldPathUpdate&>(*up1).setRemoveIfZero(true); + docUp.addFieldPathUpdate(up1); + + docUp.applyTo(*doc); + CPPUNIT_ASSERT(doc->hasValue("num") == false); +} + +void +FieldPathUpdateTestCase::testApplyAssignMultiList() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:fest:skinnvest"))); + CPPUNIT_ASSERT(doc->hasValue("strarray") == false); + + { + ArrayFieldValue + strArray(doc->getType().getField("strarray").getDataType()); + strArray.add(StringFieldValue("hello hello")); + strArray.add(StringFieldValue("blah blargh")); + doc->setValue("strarray", strArray); + CPPUNIT_ASSERT(doc->hasValue("strarray")); + } + + ArrayFieldValue + updateArray(doc->getType().getField("strarray").getDataType()); + updateArray.add(StringFieldValue("assigned val 0")); + updateArray.add(StringFieldValue("assigned val 1")); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "strarray", std::string(), updateArray))); + docUp.applyTo(*doc); + { + std::unique_ptr<ArrayFieldValue> strArray = + doc->getAs<ArrayFieldValue>(doc->getField("strarray")); + CPPUNIT_ASSERT_EQUAL(std::size_t(2), strArray->size()); + CPPUNIT_ASSERT_EQUAL(vespalib::string("assigned val 0"), (*strArray)[0].getAsString()); + CPPUNIT_ASSERT_EQUAL(vespalib::string("assigned val 1"), (*strArray)[1].getAsString()); + } +} + + +void +FieldPathUpdateTestCase::testApplyAssignMultiWset() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:fest:skinnvest"))); + CPPUNIT_ASSERT(doc->hasValue("strarray") == false); + + { + WeightedSetFieldValue + strWset(doc->getType().getField("strwset").getDataType()); + strWset.add(StringFieldValue("hello gentlemen"), 10); + strWset.add(StringFieldValue("what you say"), 20); + doc->setValue("strwset", strWset); + CPPUNIT_ASSERT(doc->hasValue("strwset")); + } + + WeightedSetFieldValue + assignWset(doc->getType().getField("strwset").getDataType()); + assignWset.add(StringFieldValue("assigned val 0"), 5); + assignWset.add(StringFieldValue("assigned val 1"), 10); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "strwset", std::string(), assignWset))); + //doc->print(std::cerr, true, ""); + docUp.applyTo(*doc); + //doc->print(std::cerr, true, ""); + { + std::unique_ptr<WeightedSetFieldValue> strWset = + doc->getAs<WeightedSetFieldValue>(doc->getField("strwset")); + CPPUNIT_ASSERT_EQUAL(std::size_t(2), strWset->size()); + CPPUNIT_ASSERT_EQUAL(5, strWset->get(StringFieldValue("assigned val 0"))); + CPPUNIT_ASSERT_EQUAL(10, strWset->get(StringFieldValue("assigned val 1"))); + } +} + +void +FieldPathUpdateTestCase::testAssignWsetRemoveIfZero() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:tronder:bataljon"))); + CPPUNIT_ASSERT(doc->hasValue("strarray") == false); + + { + WeightedSetFieldValue + strWset(doc->getType().getField("strwset").getDataType()); + strWset.add(StringFieldValue("you say goodbye"), 164); + strWset.add(StringFieldValue("but i say hello"), 243); + doc->setValue("strwset", strWset); + CPPUNIT_ASSERT(doc->hasValue("strwset")); + } + + { + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + IntFieldValue zeroWeight(0); + FieldPathUpdate::CP assignUpdate( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "strwset{you say goodbye}", std::string(), zeroWeight)); + static_cast<AssignFieldPathUpdate&>(*assignUpdate).setRemoveIfZero(true); + docUp.addFieldPathUpdate(assignUpdate); + //doc->print(std::cerr, true, ""); + docUp.applyTo(*doc); + //doc->print(std::cerr, true, ""); + { + std::unique_ptr<WeightedSetFieldValue> strWset = + doc->getAs<WeightedSetFieldValue>(doc->getField("strwset")); + CPPUNIT_ASSERT_EQUAL(std::size_t(1), strWset->size()); + CPPUNIT_ASSERT_EQUAL(243, strWset->get(StringFieldValue("but i say hello"))); + } + } +} + +void +FieldPathUpdateTestCase::testApplyAddMultiList() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:george:costanza"))); + CPPUNIT_ASSERT(doc->hasValue("strarray") == false); + + ArrayFieldValue adds(doc->getType().getField("strarray").getDataType()); + adds.add(StringFieldValue("serenity now")); + adds.add(StringFieldValue("a festivus for the rest of us")); + adds.add(StringFieldValue("george is getting upset!")); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AddFieldPathUpdate(*_repo, *doc->getDataType(), + "strarray", std::string(), adds))); + //doc->print(std::cerr, true, ""); + docUp.applyTo(*doc); + //doc->print(std::cerr, true, ""); + CPPUNIT_ASSERT(doc->hasValue("strarray")); +} + +void +FieldPathUpdateTestCase::testAddAndAssignList() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:fancy:pants"))); + CPPUNIT_ASSERT(doc->hasValue("strarray") == false); + + { + ArrayFieldValue + strArray(doc->getType().getField("strarray").getDataType()); + strArray.add(StringFieldValue("hello hello")); + strArray.add(StringFieldValue("blah blargh")); + doc->setValue("strarray", strArray); + CPPUNIT_ASSERT(doc->hasValue("strarray")); + } + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "strarray[1]", std::string(), StringFieldValue("assigned val 1")))); + + ArrayFieldValue adds(doc->getType().getField("strarray").getDataType()); + adds.add(StringFieldValue("new value")); + + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AddFieldPathUpdate(*_repo, *doc->getDataType(), + "strarray", std::string(), adds))); + //doc->print(std::cerr, true, ""); + docUp.applyTo(*doc); + //doc->print(std::cerr, true, ""); + { + std::unique_ptr<ArrayFieldValue> strArray = + doc->getAs<ArrayFieldValue>(doc->getField("strarray")); + CPPUNIT_ASSERT_EQUAL(std::size_t(3), strArray->size()); + CPPUNIT_ASSERT_EQUAL(vespalib::string("hello hello"), (*strArray)[0].getAsString()); + CPPUNIT_ASSERT_EQUAL(vespalib::string("assigned val 1"), (*strArray)[1].getAsString()); + CPPUNIT_ASSERT_EQUAL(vespalib::string("new value"), (*strArray)[2].getAsString()); + } +} + +namespace { +struct Keys { + vespalib::string key1; + vespalib::string key2; + vespalib::string key3; + Keys() : key1("foo"), key2("bar"), key3("zoo") {} +}; + +struct Fixture { + Document::UP doc; + MapFieldValue mfv; + StructFieldValue fv1, fv2, fv3, fv4; + + const MapDataType &getMapType(const DocumentType &doc_type) { + return static_cast<const MapDataType &>( + doc_type.getField("structmap").getDataType()); + } + + Fixture(const DocumentType &doc_type, const Keys &k) + : doc(new Document(doc_type, DocumentId("doc:planet:express"))), + mfv(getMapType(doc_type)), + fv1(getMapType(doc_type).getValueType()), + fv2(getMapType(doc_type).getValueType()), + fv3(getMapType(doc_type).getValueType()), + fv4(getMapType(doc_type).getValueType()) { + + fv1.setValue("title", StringFieldValue("fry")); + fv1.setValue("rating", IntFieldValue(30)); + mfv.put(StringFieldValue(k.key1), fv1); + + fv2.setValue("title", StringFieldValue("farnsworth")); + fv2.setValue("rating", IntFieldValue(60)); + mfv.put(StringFieldValue(k.key2), fv2); + + fv3.setValue("title", StringFieldValue("zoidberg")); + fv3.setValue("rating", IntFieldValue(-20)); + mfv.put(StringFieldValue(k.key3), fv3); + + doc->setValue("structmap", mfv); + + fv4.setValue("title", StringFieldValue("farnsworth")); + fv4.setValue("rating", IntFieldValue(48)); + } +}; + +} // namespace + +void +FieldPathUpdateTestCase::testAssignMap() +{ + Keys k; + Fixture f(_foobar_type, k); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *f.doc->getDataType(), + "structmap{" + k.key2 + "}", std::string(), + f.fv4))); + docUp.applyTo(*f.doc); + + std::unique_ptr<MapFieldValue> valueNow = + f.doc->getAs<MapFieldValue>(f.doc->getField("structmap")); + CPPUNIT_ASSERT_EQUAL(std::size_t(3), valueNow->size()); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv1), + *valueNow->get(StringFieldValue(k.key1))); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv4), + *valueNow->get(StringFieldValue(k.key2))); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv3), + *valueNow->get(StringFieldValue(k.key3))); +} + +void +FieldPathUpdateTestCase::testAssignMapStruct() +{ + Keys k; + Fixture f(_foobar_type, k); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *f.doc->getDataType(), + "structmap{" + k.key2 + "}.rating", std::string(), + IntFieldValue(48)))); + docUp.applyTo(*f.doc); + + std::unique_ptr<MapFieldValue> valueNow = + f.doc->getAs<MapFieldValue>(f.doc->getField("structmap")); + CPPUNIT_ASSERT_EQUAL(std::size_t(3), valueNow->size()); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv1), + *valueNow->get(StringFieldValue(k.key1))); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv4), + *valueNow->get(StringFieldValue(k.key2))); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv3), + *valueNow->get(StringFieldValue(k.key3))); +} + +void +FieldPathUpdateTestCase::testAssignMapStructVariable() +{ + Keys k; + Fixture f(_foobar_type, k); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *f.doc->getDataType(), + "structmap{$x}.rating", + "foobar.structmap{$x}.title == \"farnsworth\"", + IntFieldValue(48)))); + docUp.applyTo(*f.doc); + + std::unique_ptr<MapFieldValue> valueNow = + f.doc->getAs<MapFieldValue>(f.doc->getField("structmap")); + CPPUNIT_ASSERT_EQUAL(std::size_t(3), valueNow->size()); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv1), + *valueNow->get(StringFieldValue(k.key1))); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv4), + *valueNow->get(StringFieldValue(k.key2))); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv3), + *valueNow->get(StringFieldValue(k.key3))); +} + +void +FieldPathUpdateTestCase::testAssignMapNoExist() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:planet:express"))); + MapFieldValue mfv(doc->getType().getField("structmap").getDataType()); + + StructFieldValue fv1(dynamic_cast<const MapDataType&>(*mfv.getDataType()) + .getValueType()); + fv1.setValue("title", StringFieldValue("fry")); + fv1.setValue("rating", IntFieldValue(30)); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "structmap{foo}", std::string(), fv1))); + //doc->print(std::cerr, true, ""); + docUp.applyTo(*doc); + //doc->print(std::cerr, true, ""); + + std::unique_ptr<MapFieldValue> valueNow = + doc->getAs<MapFieldValue>(doc->getField("structmap")); + CPPUNIT_ASSERT_EQUAL(std::size_t(1), valueNow->size()); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(fv1), *valueNow->get(StringFieldValue("foo"))); +} + +void +FieldPathUpdateTestCase::testAssignMapNoExistNoCreate() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:planet:express"))); + MapFieldValue mfv(doc->getType().getField("structmap").getDataType()); + + StructFieldValue fv1(dynamic_cast<const MapDataType&>(*mfv.getDataType()) + .getValueType()); + fv1.setValue("title", StringFieldValue("fry")); + fv1.setValue("rating", IntFieldValue(30)); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + FieldPathUpdate::CP assignUpdate( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "structmap{foo}", std::string(), fv1)); + static_cast<AssignFieldPathUpdate&>(*assignUpdate).setCreateMissingPath(false); + docUp.addFieldPathUpdate(assignUpdate); + + //doc->print(std::cerr, true, ""); + docUp.applyTo(*doc); + //doc->print(std::cerr, true, ""); + + std::unique_ptr<MapFieldValue> valueNow = + doc->getAs<MapFieldValue>(doc->getField("structmap")); + CPPUNIT_ASSERT(valueNow.get() == 0); +} + +void +FieldPathUpdateTestCase::testQuotedStringKey() +{ + Keys k; + k.key2 = "here is a \"fancy\" 'map' :-} key :-{"; + const char field_path[] = + "structmap{\"here is a \\\"fancy\\\" 'map' :-} key :-{\"}"; + Fixture f(_foobar_type, k); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *f.doc->getDataType(), + field_path, std::string(), f.fv4))); + docUp.applyTo(*f.doc); + + std::unique_ptr<MapFieldValue> valueNow = + f.doc->getAs<MapFieldValue>(f.doc->getField("structmap")); + CPPUNIT_ASSERT_EQUAL(std::size_t(3), valueNow->size()); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv1), + *valueNow->get(StringFieldValue(k.key1))); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv4), + *valueNow->get(StringFieldValue(k.key2))); + CPPUNIT_ASSERT_EQUAL(static_cast<FieldValue&>(f.fv3), + *valueNow->get(StringFieldValue(k.key3))); +} + +void +FieldPathUpdateTestCase::testEqualityComparison() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:foo:zoo"))); + MapFieldValue mfv(doc->getType().getField("structmap").getDataType()); + + StructFieldValue fv4(dynamic_cast<const MapDataType&>(*mfv.getDataType()) + .getValueType()); + fv4.setValue("title", StringFieldValue("tasty cake")); + fv4.setValue("rating", IntFieldValue(95)); + + { + DocumentUpdate docUp1(_foobar_type, DocumentId("doc:barbar:foofoo")); + DocumentUpdate docUp2(_foobar_type, DocumentId("doc:barbar:foofoo")); + CPPUNIT_ASSERT(docUp1 == docUp2); + + FieldPathUpdate::CP assignUp1(new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "structmap{here be dragons}", std::string(), fv4)); + docUp1.addFieldPathUpdate(assignUp1); + CPPUNIT_ASSERT(docUp1 != docUp2); + docUp2.addFieldPathUpdate(assignUp1); + CPPUNIT_ASSERT(docUp1 == docUp2); + } + { + DocumentUpdate docUp1(_foobar_type, DocumentId("doc:barbar:foofoo")); + DocumentUpdate docUp2(_foobar_type, DocumentId("doc:barbar:foofoo")); + // where-clause diff + FieldPathUpdate::CP assignUp1(new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "structmap{here be dragons}", std::string(), fv4)); + FieldPathUpdate::CP assignUp2(new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "structmap{here be dragons}", "false", fv4)); + docUp1.addFieldPathUpdate(assignUp1); + docUp2.addFieldPathUpdate(assignUp2); + CPPUNIT_ASSERT(docUp1 != docUp2); + } + { + DocumentUpdate docUp1(_foobar_type, DocumentId("doc:barbar:foofoo")); + DocumentUpdate docUp2(_foobar_type, DocumentId("doc:barbar:foofoo")); + // fieldpath diff + FieldPathUpdate::CP assignUp1(new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "structmap{here be dragons}", std::string(), fv4)); + FieldPathUpdate::CP assignUp2(new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "structmap{here be kittens}", std::string(), fv4)); + docUp1.addFieldPathUpdate(assignUp1); + docUp2.addFieldPathUpdate(assignUp2); + CPPUNIT_ASSERT(docUp1 != docUp2); + } + +} + +void +FieldPathUpdateTestCase::testAffectsDocumentBody() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:things:stuff"))); + MapFieldValue mfv(doc->getType().getField("structmap").getDataType()); + + StructFieldValue fv4(dynamic_cast<const MapDataType&>(*mfv.getDataType()) + .getValueType()); + fv4.setValue("title", StringFieldValue("scruffy")); + fv4.setValue("rating", IntFieldValue(90)); + + // structmap is body field + { + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + CPPUNIT_ASSERT(!docUp.affectsDocumentBody()); + + FieldPathUpdate::CP update1(new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "structmap{janitor}", std::string(), fv4)); + static_cast<AssignFieldPathUpdate&>(*update1).setCreateMissingPath(true); + docUp.addFieldPathUpdate(update1); + CPPUNIT_ASSERT(docUp.affectsDocumentBody()); + } + + // strfoo is header field + { + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + FieldPathUpdate::CP update1(new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "strfoo", std::string(), StringFieldValue("helloworld"))); + static_cast<AssignFieldPathUpdate&>(*update1).setCreateMissingPath(true); + docUp.addFieldPathUpdate(update1); + CPPUNIT_ASSERT(!docUp.affectsDocumentBody()); + } + +} + +void +FieldPathUpdateTestCase::testIncompatibleDataTypeFails() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:things:stuff"))); + MapFieldValue mfv(doc->getType().getField("structmap").getDataType()); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + + try { + FieldPathUpdate::CP update1( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "structmap{foo}", std::string(), + StringFieldValue("bad things"))); + CPPUNIT_ASSERT(false); + } catch (const vespalib::IllegalArgumentException& e) { + // OK + } +} + +void +FieldPathUpdateTestCase::testSerializeAssign() +{ + Document::UP doc( + new Document(_foobar_type, + DocumentId("doc:weloveto:serializestuff"))); + MapFieldValue mfv(doc->getType().getField("structmap").getDataType()); + + StructFieldValue val(dynamic_cast<const MapDataType&>(*mfv.getDataType()) + .getValueType()); + val.setValue("title", StringFieldValue("cool frog")); + val.setValue("rating", IntFieldValue(100)); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + CPPUNIT_ASSERT(!docUp.affectsDocumentBody()); + + FieldPathUpdate::CP update1(new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "structmap{ribbit}", "true", val)); + static_cast<AssignFieldPathUpdate&>(*update1).setCreateMissingPath(true); + docUp.addFieldPathUpdate(update1); + + testSerialize(*_repo, docUp); + +} + +void +FieldPathUpdateTestCase::testSerializeAdd() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:george:costanza"))); + CPPUNIT_ASSERT(doc->hasValue("strarray") == false); + + ArrayFieldValue adds(doc->getType().getField("strarray").getDataType()); + adds.add(StringFieldValue("serenity now")); + adds.add(StringFieldValue("a festivus for the rest of us")); + adds.add(StringFieldValue("george is getting upset!")); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + CPPUNIT_ASSERT(!docUp.affectsDocumentBody()); + + FieldPathUpdate::CP update1(new AddFieldPathUpdate(*_repo, *doc->getDataType(), + "strarray", std::string(), adds)); + docUp.addFieldPathUpdate(update1); + + testSerialize(*_repo, docUp); +} + +void +FieldPathUpdateTestCase::testSerializeRemove() +{ + Document::UP doc( + new Document(_foobar_type, + DocumentId("doc:weloveto:serializestuff"))); + MapFieldValue mfv(doc->getType().getField("structmap").getDataType()); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + CPPUNIT_ASSERT(!docUp.affectsDocumentBody()); + + FieldPathUpdate::CP update1(new RemoveFieldPathUpdate(*_repo, *doc->getDataType(), + "structmap{ribbit}", std::string())); + docUp.addFieldPathUpdate(update1); + + testSerialize(*_repo, docUp); +} + +void +FieldPathUpdateTestCase::testSerializeAssignMath() +{ + Document::UP doc( + new Document(_foobar_type, DocumentId("doc:bat:man"))); + CPPUNIT_ASSERT(doc->hasValue("num") == false); + doc->setValue("num", IntFieldValue(34)); + + DocumentUpdate docUp(_foobar_type, DocumentId("doc:barbar:foofoo")); + docUp.addFieldPathUpdate( + FieldPathUpdate::CP( + new AssignFieldPathUpdate(*_repo, *doc->getDataType(), + "num", "", "($value * 2) / $value"))); + testSerialize(*_repo, docUp); +} + +DocumentUpdate::UP +FieldPathUpdateTestCase::createDocumentUpdateForSerialization(const DocumentTypeRepo& repo) +{ + const DocumentType *docType(repo.getDocumentType("serializetest")); + DocumentUpdate::UP docUp(new DocumentUpdate( + *docType, DocumentId("doc:serialization:xlanguage"))); + + FieldPathUpdate::CP + assign(new AssignFieldPathUpdate(repo, *docType, "intfield", "", "3")); + static_cast<AssignFieldPathUpdate&>(*assign).setRemoveIfZero(true); + static_cast<AssignFieldPathUpdate&>(*assign).setCreateMissingPath(false); + docUp->addFieldPathUpdate(assign); + + ArrayFieldValue + fArray(docType->getField("arrayoffloatfield").getDataType()); + fArray.add(FloatFieldValue(12.0)); + fArray.add(FloatFieldValue(5.0)); + + docUp->addFieldPathUpdate( + FieldPathUpdate::CP( + new AddFieldPathUpdate(repo, *docType, "arrayoffloatfield", "", fArray))); + + docUp->addFieldPathUpdate( + FieldPathUpdate::CP( + new RemoveFieldPathUpdate(repo, *docType, "intfield", "serializetest.intfield > 0"))); + + return docUp; +} + +void +FieldPathUpdateTestCase::testReadSerializedFile() +{ + // Reads a file serialized from java + const char cfg_file_name[] = "data/crossplatform-java-cpp-doctypes.cfg"; + DocumentTypeRepo repo(readDocumenttypesConfig(cfg_file_name)); + + int fd = open("data/serialize-fieldpathupdate-java.dat", O_RDONLY); + + int len = lseek(fd,0,SEEK_END); + ByteBuffer buf(len); + lseek(fd,0,SEEK_SET); + read(fd, buf.getBuffer(), len); + + close(fd); + + DocumentUpdate::UP updp(DocumentUpdate::createHEAD(repo, buf)); + DocumentUpdate& upd(*updp); + + DocumentUpdate::UP compare( + createDocumentUpdateForSerialization(repo)); + CPPUNIT_ASSERT_EQUAL(*compare, upd); +} + +void +FieldPathUpdateTestCase::testGenerateSerializedFile() +{ + const char cfg_file_name[] = "data/crossplatform-java-cpp-doctypes.cfg"; + DocumentTypeRepo repo(readDocumenttypesConfig(cfg_file_name)); + // Tests nothing, only generates a file for java test + DocumentUpdate::UP upd( + createDocumentUpdateForSerialization(repo)); + + ByteBuffer::UP buf(serializeHEAD(*upd)); + + int fd = open("data/serialize-fieldpathupdate-cpp.dat", + O_WRONLY | O_TRUNC | O_CREAT, 0644); + write(fd, buf->getBuffer(), buf->getPos()); + close(fd); +} + +} // ns document diff --git a/document/src/tests/fieldsettest.cpp b/document/src/tests/fieldsettest.cpp new file mode 100644 index 00000000000..41a530c4b17 --- /dev/null +++ b/document/src/tests/fieldsettest.cpp @@ -0,0 +1,348 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <vespa/document/base/testdocman.h> +#include <vespa/document/fieldset/fieldsetrepo.h> +#include <vespa/document/fieldvalue/fieldvalues.h> +#include <vespa/vespalib/io/fileutil.h> +#include <vespa/vdstestlib/cppunit/macros.h> + +#include <vespa/document/annotation/spantree.h> +#include <vespa/document/config/config-documenttypes.h> +#include <vespa/document/datatype/annotationreferencedatatype.h> +#include <vespa/document/repo/configbuilder.h> +#include <vespa/document/repo/documenttyperepo.h> +#include <vespa/vespalib/objects/nbostream.h> +#include <fstream> + +using vespalib::nbostream; + +using namespace document::config_builder; + +namespace document { + +struct FieldSetTest : public CppUnit::TestFixture { + void testParsing(); + void testContains(); + void testCopyDocumentFields(); + void testDocumentSubsetCopy(); + void testStripFields(); + void testSerialize(); + + CPPUNIT_TEST_SUITE(FieldSetTest); + CPPUNIT_TEST(testParsing); + CPPUNIT_TEST(testSerialize); + CPPUNIT_TEST(testContains); + CPPUNIT_TEST(testCopyDocumentFields); + CPPUNIT_TEST(testDocumentSubsetCopy); + CPPUNIT_TEST(testStripFields); + CPPUNIT_TEST_SUITE_END(); + + std::string stringifyFields(const Document& doc) const; + std::string doCopyFields(const Document& src, + const DocumentTypeRepo& docRepo, + const std::string& fieldSetStr, + Document* dest = 0) const; + std::string doCopyDocument(const Document& src, + const DocumentTypeRepo& docRepo, + const std::string& fieldSetStr); + std::string doStripFields(const Document& doc, + const DocumentTypeRepo& docRepo, + const std::string& fieldSetStr); + Document::UP createTestDocument(const TestDocMan& testDocMan) const; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(FieldSetTest); + +void FieldSetTest::testParsing() +{ + TestDocMan testDocMan; + const DocumentTypeRepo& docRepo = testDocMan.getTypeRepo(); + + FieldSetRepo repo; + + dynamic_cast<AllFields&>(*repo.parse(docRepo, "[all]")); + dynamic_cast<NoFields&>(*repo.parse(docRepo, "[none]")); + dynamic_cast<DocIdOnly&>(*repo.parse(docRepo, "[id]")); + dynamic_cast<HeaderFields&>(*repo.parse(docRepo, "[header]")); + dynamic_cast<BodyFields&>(*repo.parse(docRepo, "[body]")); + + FieldSet::UP set = repo.parse(docRepo, "testdoctype1:headerval,content"); + FieldCollection& coll = dynamic_cast<FieldCollection&>(*set); + + std::ostringstream ost; + for (Field::Set::const_iterator iter = coll.getFields().begin(); + iter != coll.getFields().end(); + ++iter) { + ost << (*iter)->getName() << " "; + } + + CPPUNIT_ASSERT_EQUAL(std::string("content headerval "), ost.str()); +} + +namespace { + +bool checkContains(FieldSetRepo& r, const DocumentTypeRepo& repo, + const std::string& str1, const std::string str2) { + FieldSet::UP set1 = r.parse(repo, str1); + FieldSet::UP set2 = r.parse(repo, str2); + + return set1->contains(*set2); +} + +bool checkError(FieldSetRepo& r, const DocumentTypeRepo& repo, + const std::string& str) { + try { + r.parse(repo, str); + return false; + } catch (...) { + return true; + } +} + +} + +void FieldSetTest::testContains() +{ + TestDocMan testDocMan; + const DocumentTypeRepo& repo = testDocMan.getTypeRepo(); + const DocumentType& type = *repo.getDocumentType("testdoctype1"); + + const Field& headerField = type.getField("headerval"); + const Field& bodyField = type.getField("content"); + + NoFields none; + AllFields all; + DocIdOnly id; + HeaderFields h; + BodyFields b; + + CPPUNIT_ASSERT_EQUAL(false, headerField.contains( + type.getField("headerlongval"))); + CPPUNIT_ASSERT_EQUAL(true, headerField.contains(headerField)); + CPPUNIT_ASSERT_EQUAL(true, headerField.contains(id)); + CPPUNIT_ASSERT_EQUAL(false, headerField.contains(all)); + CPPUNIT_ASSERT_EQUAL(true, headerField.contains(none)); + CPPUNIT_ASSERT_EQUAL(false, none.contains(headerField)); + CPPUNIT_ASSERT_EQUAL(true, all.contains(headerField)); + CPPUNIT_ASSERT_EQUAL(true, all.contains(none)); + CPPUNIT_ASSERT_EQUAL(false, none.contains(all)); + CPPUNIT_ASSERT_EQUAL(true, all.contains(id)); + CPPUNIT_ASSERT_EQUAL(false, none.contains(id)); + CPPUNIT_ASSERT_EQUAL(true, id.contains(none)); + + CPPUNIT_ASSERT_EQUAL(true, h.contains(headerField)); + CPPUNIT_ASSERT_EQUAL(false, h.contains(bodyField)); + + CPPUNIT_ASSERT_EQUAL(false, b.contains(headerField)); + CPPUNIT_ASSERT_EQUAL(true, b.contains(bodyField)); + + FieldSetRepo r; + CPPUNIT_ASSERT_EQUAL(true, checkContains(r, repo, "[body]", + "testdoctype1:content")); + CPPUNIT_ASSERT_EQUAL(false, checkContains(r, repo, "[header]", + "testdoctype1:content")); + CPPUNIT_ASSERT_EQUAL(true, checkContains(r, repo, + "testdoctype1:content,headerval", + "testdoctype1:content")); + CPPUNIT_ASSERT_EQUAL(false, checkContains(r, repo, + "testdoctype1:content", + "testdoctype1:content,headerval")); + CPPUNIT_ASSERT_EQUAL(true, checkContains(r, repo, + "testdoctype1:headerval,content", + "testdoctype1:content,headerval")); + + CPPUNIT_ASSERT(checkError(r, repo, "nodoctype")); + CPPUNIT_ASSERT(checkError(r, repo, "unknowndoctype:foo")); + CPPUNIT_ASSERT(checkError(r, repo, "testdoctype1:unknownfield")); + CPPUNIT_ASSERT(checkError(r, repo, "[badid]")); +} + +std::string +FieldSetTest::stringifyFields(const Document& doc) const +{ + std::vector<std::string> output; + const StructFieldValue& fields(doc.getFields()); + for (StructFieldValue::const_iterator + it(fields.begin()), e(fields.end()); + it != e; ++it) + { + std::ostringstream ss; + const Field& f(it.field()); + ss << f.getName() << ": "; + FieldValue::UP val(fields.getValue(f)); + if (val.get()) { + ss << val->toString(); + } else { + ss << "(null)"; + } + output.push_back(ss.str()); + } + std::ostringstream ret; + std::sort(output.begin(), output.end()); + std::copy(output.begin(), output.end(), + std::ostream_iterator<std::string>(ret, "\n")); + return ret.str(); +} + +std::string +FieldSetTest::doCopyFields(const Document& src, + const DocumentTypeRepo& docRepo, + const std::string& fieldSetStr, + Document* dest) const +{ + Document destDoc(src.getType(), DocumentId("doc:test:fieldsdest")); + if (!dest) { + dest = &destDoc; + } + FieldSetRepo repo; + FieldSet::UP fset = repo.parse(docRepo, fieldSetStr); + FieldSet::copyFields(*dest, src, *fset); + return stringifyFields(*dest); +} + +std::string +FieldSetTest::doStripFields(const Document& doc, + const DocumentTypeRepo& docRepo, + const std::string& fieldSetStr) +{ + Document::UP copy(doc.clone()); + FieldSetRepo repo; + FieldSet::UP fset = repo.parse(docRepo, fieldSetStr); + FieldSet::stripFields(*copy, *fset); + return stringifyFields(*copy); +} + +Document::UP +FieldSetTest::createTestDocument(const TestDocMan& testDocMan) const +{ + Document::UP doc(testDocMan.createDocument("megafoo megabar", + "doc:test:fieldssrc", + "testdoctype1")); + doc->setValue(doc->getField("headerval"), IntFieldValue(5678)); + doc->setValue(doc->getField("hstringval"), + StringFieldValue("hello fantastic world")); + return doc; +} + +void +FieldSetTest::testCopyDocumentFields() +{ + TestDocMan testDocMan; + const DocumentTypeRepo& repo = testDocMan.getTypeRepo(); + Document::UP src(createTestDocument(testDocMan)); + + CPPUNIT_ASSERT_EQUAL(std::string("content: megafoo megabar\n"), + doCopyFields(*src, repo, "[body]")); + CPPUNIT_ASSERT_EQUAL(std::string(""), + doCopyFields(*src, repo, "[none]")); + CPPUNIT_ASSERT_EQUAL(std::string("headerval: 5678\n" + "hstringval: hello fantastic world\n"), + doCopyFields(*src, repo, "[header]")); + CPPUNIT_ASSERT_EQUAL(std::string("content: megafoo megabar\n" + "headerval: 5678\n" + "hstringval: hello fantastic world\n"), + doCopyFields(*src, repo, "[all]")); + CPPUNIT_ASSERT_EQUAL(std::string("content: megafoo megabar\n" + "hstringval: hello fantastic world\n"), + doCopyFields(*src, repo, "testdoctype1:hstringval,content")); + // Test that we overwrite already set fields in destination document + { + Document dest(src->getType(), DocumentId("doc:foo:bar")); + dest.setValue(dest.getField("content"), StringFieldValue("overwriteme")); + CPPUNIT_ASSERT_EQUAL(std::string("content: megafoo megabar\n"), + doCopyFields(*src, repo, "[body]", &dest)); + } +} + +std::string +FieldSetTest::doCopyDocument(const Document& src, + const DocumentTypeRepo& docRepo, + const std::string& fieldSetStr) +{ + FieldSetRepo repo; + FieldSet::UP fset = repo.parse(docRepo, fieldSetStr); + Document::UP doc(FieldSet::createDocumentSubsetCopy(src, *fset)); + return stringifyFields(*doc); +} + + +void +FieldSetTest::testDocumentSubsetCopy() +{ + TestDocMan testDocMan; + const DocumentTypeRepo& repo = testDocMan.getTypeRepo(); + Document::UP src(createTestDocument(testDocMan)); + + { + Document::UP doc(FieldSet::createDocumentSubsetCopy(*src, AllFields())); + // Test that document id and type are copied correctly. + CPPUNIT_ASSERT(doc.get()); + CPPUNIT_ASSERT_EQUAL(src->getId(), doc->getId()); + CPPUNIT_ASSERT_EQUAL(src->getType(), doc->getType()); + CPPUNIT_ASSERT_EQUAL(doCopyFields(*src, repo, "[all]"), + stringifyFields(*doc)); + } + + const char* fieldSets[] = { + "[all]", + "[none]", + "[header]", + "[body]", + "testdoctype1:hstringval,content" + }; + for (size_t i = 0; i < sizeof(fieldSets) / sizeof(fieldSets[0]); ++i) { + CPPUNIT_ASSERT_EQUAL(doCopyFields(*src, repo, fieldSets[i]), + doCopyDocument(*src, repo, fieldSets[i])); + } +} + +void +FieldSetTest::testSerialize() +{ + TestDocMan testDocMan; + const DocumentTypeRepo& docRepo = testDocMan.getTypeRepo(); + + const char* fieldSets[] = { + "[all]", + "[none]", + "[header]", + "[docid]", + "[body]", + "testdoctype1:content", + "testdoctype1:content,hstringval" + }; + + FieldSetRepo repo; + for (size_t i = 0; i < sizeof(fieldSets) / sizeof(fieldSets[0]); ++i) { + FieldSet::UP fs = repo.parse(docRepo, fieldSets[i]); + CPPUNIT_ASSERT_EQUAL(vespalib::string(fieldSets[i]), repo.serialize(*fs)); + } +} + +void +FieldSetTest::testStripFields() +{ + TestDocMan testDocMan; + const DocumentTypeRepo& repo = testDocMan.getTypeRepo(); + Document::UP src(createTestDocument(testDocMan)); + + CPPUNIT_ASSERT_EQUAL(std::string("content: megafoo megabar\n"), + doStripFields(*src, repo, "[body]")); + CPPUNIT_ASSERT_EQUAL(std::string(""), + doStripFields(*src, repo, "[none]")); + CPPUNIT_ASSERT_EQUAL(std::string(""), + doStripFields(*src, repo, "[id]")); + CPPUNIT_ASSERT_EQUAL(std::string("headerval: 5678\n" + "hstringval: hello fantastic world\n"), + doStripFields(*src, repo, "[header]")); + CPPUNIT_ASSERT_EQUAL(std::string("content: megafoo megabar\n" + "headerval: 5678\n" + "hstringval: hello fantastic world\n"), + doStripFields(*src, repo, "[all]")); + CPPUNIT_ASSERT_EQUAL(std::string("content: megafoo megabar\n" + "hstringval: hello fantastic world\n"), + doStripFields(*src, repo, "testdoctype1:hstringval,content")); +} + +} // document diff --git a/document/src/tests/fieldtestcase.h b/document/src/tests/fieldtestcase.h new file mode 100644 index 00000000000..0001b8fce1f --- /dev/null +++ b/document/src/tests/fieldtestcase.h @@ -0,0 +1,20 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/* $Id$*/ + +#pragma once + +#include <cppunit/extensions/HelperMacros.h> + +class Field_Test : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE( Field_Test); + CPPUNIT_TEST(testserialize); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + +protected: + void testserialize(); +}; + + diff --git a/document/src/tests/fieldvalue/.gitignore b/document/src/tests/fieldvalue/.gitignore new file mode 100644 index 00000000000..6b57b8c8d17 --- /dev/null +++ b/document/src/tests/fieldvalue/.gitignore @@ -0,0 +1,7 @@ +*.So +*_test +.depend +Makefile +document_document_test_app +document_fieldvalue_test_app +document_predicatefieldvalue_test_app diff --git a/document/src/tests/fieldvalue/CMakeLists.txt b/document/src/tests/fieldvalue/CMakeLists.txt new file mode 100644 index 00000000000..7b5fc637e4f --- /dev/null +++ b/document/src/tests/fieldvalue/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_document_test_app + SOURCES + document_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_document_test_app COMMAND document_document_test_app) +vespa_add_executable(document_fieldvalue_test_app + SOURCES + fieldvalue_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_fieldvalue_test_app COMMAND document_fieldvalue_test_app) +vespa_add_executable(document_predicatefieldvalue_test_app + SOURCES + predicatefieldvalue_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_predicatefieldvalue_test_app COMMAND document_predicatefieldvalue_test_app) diff --git a/document/src/tests/fieldvalue/document_test.cpp b/document/src/tests/fieldvalue/document_test.cpp new file mode 100644 index 00000000000..51216c6d77a --- /dev/null +++ b/document/src/tests/fieldvalue/document_test.cpp @@ -0,0 +1,31 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for document. + +#include <vespa/log/log.h> +LOG_SETUP("document_test"); +#include <vespa/fastos/fastos.h> + +#include <vespa/document/base/documentid.h> +#include <vespa/document/base/testdocrepo.h> +#include <vespa/document/fieldvalue/document.h> +#include <vespa/vespalib/testkit/testapp.h> + +using namespace document; + +namespace { + +TEST("require that document with id schema 'id' checks type") { + TestDocRepo repo; + const DataType *type = repo.getDocumentType("testdoctype1"); + ASSERT_TRUE(type); + + Document(*type, DocumentId("id:ns:testdoctype1::")); // Should not throw + + EXPECT_EXCEPTION(Document(*type, DocumentId("id:ns:type::")), + vespalib::IllegalArgumentException, + "testdoctype1 that don't match the id (type type)"); +} + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/document/src/tests/fieldvalue/fieldvalue_test.cpp b/document/src/tests/fieldvalue/fieldvalue_test.cpp new file mode 100644 index 00000000000..7fecb42417e --- /dev/null +++ b/document/src/tests/fieldvalue/fieldvalue_test.cpp @@ -0,0 +1,41 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for fieldvalue. + +#include <vespa/log/log.h> +LOG_SETUP("fieldvalue_test"); +#include <vespa/fastos/fastos.h> + +#include <vespa/document/fieldvalue/stringfieldvalue.h> +#include <vespa/document/fieldvalue/longfieldvalue.h> +#include <vespa/document/fieldvalue/intfieldvalue.h> + +#include <vespa/vespalib/testkit/testapp.h> + +using namespace document; + +namespace { + +TEST("require that StringFieldValue can be assigned primitives") { + StringFieldValue val; + val = "foo"; + EXPECT_EQUAL("foo", val.getValue()); + val = 1; + EXPECT_EQUAL("1", val.getValue()); + val = static_cast<int64_t>(2); + EXPECT_EQUAL("2", val.getValue()); + val = 3.0f; + EXPECT_EQUAL("3", val.getValue()); + val = 4.0; + EXPECT_EQUAL("4", val.getValue()); +} + +TEST("require that FieldValues does not change their storage size.") { + EXPECT_EQUAL(8u, sizeof(FieldValue)); + EXPECT_EQUAL(16u, sizeof(IntFieldValue)); + EXPECT_EQUAL(24u, sizeof(LongFieldValue)); + EXPECT_EQUAL(104u, sizeof(StringFieldValue)); +} + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/document/src/tests/fieldvalue/predicatefieldvalue_test.cpp b/document/src/tests/fieldvalue/predicatefieldvalue_test.cpp new file mode 100644 index 00000000000..94b0f406696 --- /dev/null +++ b/document/src/tests/fieldvalue/predicatefieldvalue_test.cpp @@ -0,0 +1,70 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for predicatefieldvalue. + +#include <vespa/log/log.h> +LOG_SETUP("predicatefieldvalue_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/datatype/datatype.h> +#include <vespa/document/fieldvalue/predicatefieldvalue.h> +#include <vespa/document/predicate/predicate_slime_builder.h> +#include <sstream> +#include <string> + +using std::ostringstream; +using std::string; +using vespalib::Slime; +using namespace document; + +namespace { + +void verifyEqual(const FieldValue & a, const FieldValue & b) { + ostringstream o1; + a.print(o1, false, ""); + ostringstream o2; + b.print(o2, false, ""); + ASSERT_EQUAL(o1.str(), o2.str()); +} + +TEST("require that PredicateFieldValue can be cloned, assigned, and operator=") { + PredicateSlimeBuilder builder; + builder.neg().feature("foo").value("bar").value("baz"); + PredicateFieldValue val(builder.build()); + + FieldValue::UP val2(val.clone()); + verifyEqual(val, *val2); + + PredicateFieldValue assigned; + assigned.assign(val); + verifyEqual(val, assigned); + + PredicateFieldValue operatorAssigned; + operatorAssigned = val; + verifyEqual(val, operatorAssigned); +} + +TEST("require that PredicateFieldValue can be created from datatype") { + FieldValue::UP val = DataType::PREDICATE->createFieldValue(); + ASSERT_TRUE(dynamic_cast<PredicateFieldValue *>(val.get())); +} + +TEST("require that PredicateFieldValue can be cloned") { + PredicateSlimeBuilder builder; + builder.neg().feature("foo").value("bar").value("baz"); + PredicateFieldValue val(builder.build()); + FieldValue::UP val2(val.clone()); + ostringstream o1; + val.print(o1, false, ""); + ostringstream o2; + val2->print(o2, false, ""); + ASSERT_EQUAL(o1.str(), o2.str()); +} + + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/document/src/tests/forcelinktest.cpp b/document/src/tests/forcelinktest.cpp new file mode 100644 index 00000000000..c62f327585f --- /dev/null +++ b/document/src/tests/forcelinktest.cpp @@ -0,0 +1,25 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <vespa/document/base/forcelink.h> +#include <cppunit/extensions/HelperMacros.h> + +namespace document { + +struct ForceLinkTest : public CppUnit::TestFixture { + void testUsage(); + + CPPUNIT_TEST_SUITE(ForceLinkTest); + CPPUNIT_TEST(testUsage); + CPPUNIT_TEST_SUITE_END(); + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ForceLinkTest); + +void ForceLinkTest::testUsage() +{ + ForceLink link; +} + +} // document diff --git a/document/src/tests/gid_filter_test.cpp b/document/src/tests/gid_filter_test.cpp new file mode 100644 index 00000000000..9caf55f6a0b --- /dev/null +++ b/document/src/tests/gid_filter_test.cpp @@ -0,0 +1,317 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Copyright 2016 Yahoo! Technologies Norway AS + +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <vespa/document/select/gid_filter.h> +#include <vespa/document/select/parser.h> +#include <vespa/document/base/documentid.h> +#include <vespa/document/base/testdocrepo.h> + +namespace document { +namespace select { + +class GidFilterTest : public CppUnit::TestFixture { + struct Fixture { + TestDocRepo _repo; + BucketIdFactory _id_factory; + std::unique_ptr<Node> _root; + + Fixture(vespalib::stringref selection) + : _repo(), + _id_factory(), + _root(Parser(_repo.getTypeRepo(), _id_factory).parse(selection)) + { + } + + Fixture(Fixture&&) = default; + Fixture& operator=(Fixture&&) = default; + + static Fixture for_selection(vespalib::stringref s) { + return Fixture(s); + } + }; + + static GlobalId id_to_gid(vespalib::stringref id_string) { + return DocumentId(id_string).getGlobalId(); + } + + bool might_match(vespalib::stringref selection, + vespalib::stringref id_string) const + { + Fixture f(selection); + auto filter = GidFilter::for_selection_root_node(*f._root); + return filter.gid_might_match_selection(id_to_gid(id_string)); + } + + CPPUNIT_TEST_SUITE(GidFilterTest); + CPPUNIT_TEST(same_user_for_selection_and_gid_returns_match); + CPPUNIT_TEST(differing_user_for_selection_and_gid_returns_mismatch); + CPPUNIT_TEST(user_location_constraint_is_order_invariant); + CPPUNIT_TEST(non_location_selection_always_matches); + CPPUNIT_TEST(location_selection_does_not_match_non_location_id); + CPPUNIT_TEST(simple_conjunctive_location_expressions_are_filtered); + CPPUNIT_TEST(complex_conjunctive_location_expressions_are_filtered); + CPPUNIT_TEST(simple_disjunctive_location_expressions_are_not_filtered); + CPPUNIT_TEST(complex_disjunctive_location_expressions_are_not_filtered); + CPPUNIT_TEST(non_location_id_comparisons_are_not_filtered); + CPPUNIT_TEST(unsupported_location_comparison_operands_not_filtered); + CPPUNIT_TEST(default_constructed_filter_always_matches); + CPPUNIT_TEST(most_significant_32_bits_are_ignored); + CPPUNIT_TEST(gid_filters_may_be_copy_constructed); + CPPUNIT_TEST(gid_filters_may_be_copy_assigned); + CPPUNIT_TEST(same_group_for_selection_and_gid_returns_match); + CPPUNIT_TEST(differing_group_for_selection_and_gid_returns_mismatch); + CPPUNIT_TEST(composite_user_comparison_sub_expressions_not_supported); + CPPUNIT_TEST(composite_group_comparison_sub_expressions_not_supported); + CPPUNIT_TEST_SUITE_END(); +public: + void same_user_for_selection_and_gid_returns_match(); + void differing_user_for_selection_and_gid_returns_mismatch(); + void user_location_constraint_is_order_invariant(); + void non_location_selection_always_matches(); + void location_selection_does_not_match_non_location_id(); + void simple_conjunctive_location_expressions_are_filtered(); + void complex_conjunctive_location_expressions_are_filtered(); + void simple_disjunctive_location_expressions_are_not_filtered(); + void complex_disjunctive_location_expressions_are_not_filtered(); + void non_location_id_comparisons_are_not_filtered(); + void unsupported_location_comparison_operands_not_filtered(); + void default_constructed_filter_always_matches(); + void most_significant_32_bits_are_ignored(); + void gid_filters_may_be_copy_constructed(); + void gid_filters_may_be_copy_assigned(); + void same_group_for_selection_and_gid_returns_match(); + void differing_group_for_selection_and_gid_returns_mismatch(); + void composite_user_comparison_sub_expressions_not_supported(); + void composite_group_comparison_sub_expressions_not_supported(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(GidFilterTest); + +void +GidFilterTest::same_user_for_selection_and_gid_returns_match() +{ + CPPUNIT_ASSERT(might_match("id.user == 12345", + "id::testdoctype1:n=12345:foo")); + // User locations are defined over [0, 2**63-1] + CPPUNIT_ASSERT(might_match("id.user == 0", + "id::testdoctype1:n=0:foo")); + CPPUNIT_ASSERT(might_match("id.user == 9223372036854775807" , + "id::testdoctype1:n=9223372036854775807:foo")); +} + +void +GidFilterTest::differing_user_for_selection_and_gid_returns_mismatch() +{ + CPPUNIT_ASSERT(!might_match("id.user == 1", "id::testdoctype1:n=2000:foo")); + // Similar, but non-identical, bit patterns + CPPUNIT_ASSERT(!might_match("id.user == 12345", + "id::testdoctype1:n=12346:foo")); + CPPUNIT_ASSERT(!might_match("id.user == 12345", + "id::testdoctype1:n=12344:foo")); +} + +void +GidFilterTest::user_location_constraint_is_order_invariant() +{ + CPPUNIT_ASSERT(might_match("12345 == id.user", + "id::testdoctype1:n=12345:foo")); + CPPUNIT_ASSERT(!might_match("12345 == id.user", + "id::testdoctype1:n=12346:foo")); +} + +void +GidFilterTest::non_location_selection_always_matches() +{ + CPPUNIT_ASSERT(might_match("testdoctype1.headerval == 67890", + "id::testdoctype1:n=12345:foo")); +} + +void +GidFilterTest::location_selection_does_not_match_non_location_id() +{ + // Test name is a half-truth; the MD5-derived ID _will_ give a false + // positive every 2**32 or so document ID when the stars and their bit + // patterns align :) + CPPUNIT_ASSERT(!might_match("id.user == 987654321", + "id::testdoctype1::foo")); + + CPPUNIT_ASSERT(!might_match("id.group == 'snusmumrikk'", + "id::testdoctype1::foo")); +} + +void +GidFilterTest::simple_conjunctive_location_expressions_are_filtered() +{ + // A conjunctive expression in this context is one where there exist a + // location predicate and the result of the entire expression can only + // be true iff the location predicate matches. + CPPUNIT_ASSERT(might_match("id.user == 12345 and true", + "id::testdoctype1:n=12345:bar")); + CPPUNIT_ASSERT(might_match("true and id.user == 12345", + "id::testdoctype1:n=12345:bar")); + + CPPUNIT_ASSERT(!might_match("id.user == 123456 and true", + "id::testdoctype1:n=12345:bar")); + CPPUNIT_ASSERT(!might_match("true and id.user == 123456", + "id::testdoctype1:n=12345:bar")); +} + +void +GidFilterTest::complex_conjunctive_location_expressions_are_filtered() +{ + CPPUNIT_ASSERT(might_match("(((testdoctype1.headerval < 5) and (1 != 2)) " + "and id.user == 12345)", + "id::testdoctype1:n=12345:bar")); + CPPUNIT_ASSERT(!might_match("(((1 != 2) and (id.user==12345)) and " + "(2 != 3)) and (testdoctype1.headerval < 5)", + "id::testdoctype1:n=23456:bar")); + // In this case the expression contains a disjunction but the outcome + // of evaluating it still strongly depends on the location predicate. + CPPUNIT_ASSERT(might_match("((id.user == 12345 and true) and " + "(true or false))", + "id::testdoctype1:n=12345:bar")); + CPPUNIT_ASSERT(!might_match("((id.user == 12345 and true) and " + "(true or false))", + "id::testdoctype1:n=23456:bar")); +} + +void +GidFilterTest::simple_disjunctive_location_expressions_are_not_filtered() +{ + // Documents mismatch location but match selection as a whole. + CPPUNIT_ASSERT(might_match("id.user == 12345 or true", + "id::testdoctype1:n=12345678:bar")); + CPPUNIT_ASSERT(might_match("true or id.user == 12345", + "id::testdoctype1:n=12345678:bar")); +} + +void +GidFilterTest::complex_disjunctive_location_expressions_are_not_filtered() +{ + CPPUNIT_ASSERT(might_match("((id.user == 12345) and true) or false", + "id::testdoctype1:n=12345678:bar")); + CPPUNIT_ASSERT(might_match("((id.user == 12345) or false) and true", + "id::testdoctype1:n=12345678:bar")); +} + +void +GidFilterTest::non_location_id_comparisons_are_not_filtered() +{ + // Note: these selections are syntactically valid but semantically + // invalid (comparing strings to integers), but are used to catch any + // logic holes where an id node is indiscriminately treated as something + // from which we should derive a GID-related integer. + CPPUNIT_ASSERT(might_match("id.namespace == 123456", + "id::testdoctype1:n=12345678:bar")); + CPPUNIT_ASSERT(might_match("id.type == 1234", + "id::testdoctype1:n=12345678:bar")); + CPPUNIT_ASSERT(might_match("id.scheme == 555", + "id::testdoctype1:n=12345678:bar")); + CPPUNIT_ASSERT(might_match("id.specific == 7654", + "id::testdoctype1:n=12345678:bar")); +} + +void +GidFilterTest::unsupported_location_comparison_operands_not_filtered() +{ + CPPUNIT_ASSERT(might_match("id.user == 'rick & morty'", + "id::testdoctype1:n=12345678:bar")); + CPPUNIT_ASSERT(might_match("id.group == 56789", + "id::testdoctype1:n=12345678:bar")); + CPPUNIT_ASSERT(might_match("id.user == testdoctype1.headervalue", + "id::testdoctype1:n=12345678:bar")); + CPPUNIT_ASSERT(might_match("id.group == testdoctype1.headervalue", + "id::testdoctype1:g=helloworld:bar")); +} + +void +GidFilterTest::default_constructed_filter_always_matches() +{ + GidFilter filter; + CPPUNIT_ASSERT(filter.gid_might_match_selection( + DocumentId("id::testdoctype1:n=12345678:bar").getGlobalId())); + CPPUNIT_ASSERT(filter.gid_might_match_selection( + DocumentId("id::testdoctype1::foo").getGlobalId())); +} + +void +GidFilterTest::most_significant_32_bits_are_ignored() +{ + // The fact that the 32 MSB are effectively ignored is an artifact of + // how the GID location extraction is historically performed and is not + // necessarily the optimum (in particular, an XOR combination of the upper + // and lower 32 bits would likely be much better), but it's what the + // behavior currently is and should thus be tested. + + // The following locations have the same 32 LSB: + CPPUNIT_ASSERT(might_match("id.user == 12345678901", + "id::testdoctype1:n=29525548085:bar")); +} + +void +GidFilterTest::gid_filters_may_be_copy_constructed() +{ + Fixture f("id.user == 1337"); + GidFilter filter = GidFilter::for_selection_root_node(*f._root); + + GidFilter copy_constructed(filter); + CPPUNIT_ASSERT(copy_constructed.gid_might_match_selection( + id_to_gid("id::testdoctype1:n=1337:zoid"))); + CPPUNIT_ASSERT(!copy_constructed.gid_might_match_selection( + id_to_gid("id::testdoctype1:n=555:zoid"))); + +} + +void +GidFilterTest::gid_filters_may_be_copy_assigned() +{ + Fixture f("id.user == 1337"); + GidFilter filter = GidFilter::for_selection_root_node(*f._root); + + GidFilter copy_assigned; + copy_assigned = filter; + + CPPUNIT_ASSERT(copy_assigned.gid_might_match_selection( + id_to_gid("id::testdoctype1:n=1337:zoid"))); + CPPUNIT_ASSERT(!copy_assigned.gid_might_match_selection( + id_to_gid("id::testdoctype1:n=555:zoid"))); +} + +void +GidFilterTest::same_group_for_selection_and_gid_returns_match() +{ + CPPUNIT_ASSERT(might_match("id.group == 'bjarne'", + "id::testdoctype1:g=bjarne:foo")); + CPPUNIT_ASSERT(might_match("id.group == 'andrei'", + "id::testdoctype1:g=andrei:bar")); +} + +void +GidFilterTest::differing_group_for_selection_and_gid_returns_mismatch() +{ + CPPUNIT_ASSERT(!might_match("id.group == 'cult of bjarne'", + "id::testdoctype1:g=stl:foo")); + CPPUNIT_ASSERT(!might_match("id.group == 'sutters mill'", + "id::testdoctype1:g=andrei:bar")); +} + +void +GidFilterTest::composite_user_comparison_sub_expressions_not_supported() +{ + // Technically this is a mismatch, but we currently only want to support + // the simple, obvious cases since this is not an expected use case. + CPPUNIT_ASSERT(might_match("id.user == (1 + 2)", + "id::testdoctype1:n=20:foo")); +} + +void +GidFilterTest::composite_group_comparison_sub_expressions_not_supported() +{ + CPPUNIT_ASSERT(might_match("id.group == 'foo'+'bar'", + "id::testdoctype1:g=sputnik_hits:foo")); +} + +} // select +} // document diff --git a/document/src/tests/globalidtest.cpp b/document/src/tests/globalidtest.cpp new file mode 100644 index 00000000000..d0d066e5510 --- /dev/null +++ b/document/src/tests/globalidtest.cpp @@ -0,0 +1,268 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <cppunit/extensions/HelperMacros.h> +#include <vespa/document/bucket/bucketidfactory.h> +#include <vespa/document/base/documentid.h> +#include <vespa/document/base/globalid.h> +#include <vespa/vespalib/util/random.h> +#include <vespa/vespalib/stllike/asciistream.h> + +namespace document { + +struct GlobalIdTest : public CppUnit::TestFixture { + void testNormalUsage(); + void testBucketIdConversion(); + void testGidRangeConversion(); + void testBucketOrderCmp(); + + void verifyGlobalIdRange(const std::vector<DocumentId>& ids, + uint32_t countBits); + + CPPUNIT_TEST_SUITE(GlobalIdTest); + CPPUNIT_TEST(testNormalUsage); + CPPUNIT_TEST(testBucketIdConversion); + CPPUNIT_TEST(testGidRangeConversion); + CPPUNIT_TEST(testBucketOrderCmp); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(GlobalIdTest); + +void +GlobalIdTest::testNormalUsage() +{ + const char* emptystring = "\0\0\0\0\0\0\0\0\0\0\0\0"; + const char* teststring = "1234567890ABCDEF"; + CPPUNIT_ASSERT(strlen(teststring) > GlobalId::LENGTH); + + { // Test empty global id + GlobalId id; + for (uint32_t i=0; i<GlobalId::LENGTH; ++i) { + CPPUNIT_ASSERT_EQUAL((unsigned char) '\0', id.get()[i]); + } + GlobalId id2(emptystring); + CPPUNIT_ASSERT_EQUAL(id, id2); + CPPUNIT_ASSERT(!(id != id2)); + CPPUNIT_ASSERT(!(id < id2) && !(id2 < id)); + CPPUNIT_ASSERT_EQUAL(std::string(emptystring, GlobalId::LENGTH), + std::string((const char*) id.get(), + GlobalId::LENGTH)); + } + { // Test non-empty global id + GlobalId empty; + GlobalId initialempty; + initialempty.set(teststring); + GlobalId id(teststring); + CPPUNIT_ASSERT_EQUAL(id, initialempty); + CPPUNIT_ASSERT(!(id != initialempty)); + CPPUNIT_ASSERT(!(id < initialempty) && !(initialempty < id)); + + CPPUNIT_ASSERT(id != empty); + CPPUNIT_ASSERT(!(id == empty)); + CPPUNIT_ASSERT(!(id < empty) && (empty < id)); + + CPPUNIT_ASSERT_EQUAL( + std::string(teststring, GlobalId::LENGTH), + std::string((const char*) id.get(), GlobalId::LENGTH)); + CPPUNIT_ASSERT_EQUAL(std::string(teststring, GlobalId::LENGTH), + std::string((const char*) initialempty.get(), + GlobalId::LENGTH)); + } + { // Test printing and parsing + GlobalId id1("LIN!#LNKASD#!MYL#&NK"); + CPPUNIT_ASSERT_EQUAL(vespalib::string("gid(0x4c494e21234c4e4b41534423)"), + id1.toString()); + GlobalId id2 = GlobalId::parse(id1.toString()); + CPPUNIT_ASSERT_EQUAL(id1, id2); + // Verify string representation too, to verify that operator== works + CPPUNIT_ASSERT_EQUAL(vespalib::string("gid(0x4c494e21234c4e4b41534423)"), + id2.toString()); + } +} + +namespace { + void verifyDocumentId(const std::string& s) { + document::DocumentId did(s); + BucketIdFactory factory; + BucketId bid = factory.getBucketId(did); + GlobalId gid = did.getGlobalId(); + BucketId generated = gid.convertToBucketId(); + //std::cerr << bid << ", " << generated << "\n"; + if (bid != generated) { + std::ostringstream ost; + ost << "Document id " << s << " with gid " << gid + << " belongs to bucket " << bid + << ", but globalid convert function generated bucketid " + << generated; + CPPUNIT_FAIL(ost.str()); + } + } +} + +void +GlobalIdTest::testBucketIdConversion() +{ + verifyDocumentId("userdoc:ns:1:abc"); + verifyDocumentId("userdoc:ns:1000:abc"); + verifyDocumentId("userdoc:hsgf:18446744073700000000:dfdfsdfg"); + verifyDocumentId("groupdoc:ns:somegroup:hmm"); + verifyDocumentId("doc::test"); + verifyDocumentId("doc:myns:http://foo.bar"); + verifyDocumentId("doc:jsrthsdf:a234aleingzldkifvasdfgadf"); +} + +void +GlobalIdTest::verifyGlobalIdRange(const std::vector<DocumentId>& ids, + uint32_t countBits) +{ + BucketIdFactory factory; + for (uint32_t i=0, n=ids.size(); i<n; ++i) { + // Create the bucket this document would be in with given + // countbits + BucketId bucket(factory.getBucketId(ids[i])); + bucket.setUsedBits(countBits); + bucket = bucket.stripUnused(); + // Get the min and max GIDs for this bucket + GlobalId first = GlobalId::calculateFirstInBucket(bucket); + GlobalId last = GlobalId::calculateLastInBucket(bucket); + // For each document in set, verify that they are within + // limits if they are contained in bucket. + for (uint32_t j=0; j<n; ++j) { + BucketId bid(factory.getBucketId(ids[j])); + GlobalId gid(ids[j].getGlobalId()); + uint64_t gidKey = gid.convertToBucketId().toKey(); + if (bucket.contains(bid)) { + if ((gidKey < last.convertToBucketId().toKey()) || (gidKey > last.convertToBucketId().toKey())) { + std::ostringstream msg; + msg << gid << " should be in the range " << first + << " - " << last; + msg << ", as bucket " << gid.convertToBucketId() + << " should be in the range " + << first.convertToBucketId() << " - " + << last.convertToBucketId() << "\n"; + msg << ", reverted " << std::hex + << gid.convertToBucketId().toKey() + << " should be in the range " + << first.convertToBucketId().toKey() << " - " + << last.convertToBucketId().toKey() << "\n"; + CPPUNIT_ASSERT_MESSAGE(msg.str(), + gid.convertToBucketId().toKey() >= + first.convertToBucketId().toKey()); + CPPUNIT_ASSERT_MESSAGE(msg.str(), + gid.convertToBucketId().toKey() <= + last.convertToBucketId().toKey()); + } + } else { + if ((gidKey >= first.convertToBucketId().toKey()) && (gidKey <= last.convertToBucketId().toKey())) { + std::ostringstream msg; + msg << gid << " should not be in the range " << first + << " - " << last; + CPPUNIT_ASSERT_MESSAGE(msg.str(), + (gid.convertToBucketId().toKey() < + first.convertToBucketId().toKey()) + || + (gid.convertToBucketId().toKey() > + last.convertToBucketId().toKey())); + } + } + } + } +} + +void +GlobalIdTest::testGidRangeConversion() +{ + // Generate a lot of random document ids used for test + std::vector<DocumentId> docIds; + vespalib::RandomGen randomizer(0xdeadbabe); + for (uint32_t j=0; j<100; ++j) { + vespalib::asciistream name_space; + vespalib::asciistream ost; + for (uint32_t i=0, n=randomizer.nextUint32(1, 5); i<n; ++i) { + name_space << (char) ('a' + randomizer.nextUint32(0, 25)); + } + uint32_t scheme = randomizer.nextUint32(0, 2); + switch (scheme) { + case 0: ost << "doc:" << name_space.str() << ":"; + break; + case 1: ost << "userdoc:" << name_space.str() << ":"; + ost << randomizer.nextUint32() << ":"; + break; + case 2: ost << "groupdoc:" << name_space.str() << ":"; + for (uint32_t i=0, n=randomizer.nextUint32(1, 10); i<n; ++i) + { + ost << (char) ('a' + randomizer.nextUint32(0, 25)); + } + ost << ":"; + break; + default: CPPUNIT_ASSERT(false); + } + ost << "http://"; + for (uint32_t i=0, n=randomizer.nextUint32(1, 20); i<n; ++i) { + ost << (char) ('a' + randomizer.nextUint32(0, 25)); + } + docIds.push_back(DocumentId(ost.str())); + } + //std::cerr << "\nDoing " << ((58 - 16) * docIds.size() * docIds.size()) + // << " tests for whether global id calculation is correct.\n"; + for (uint32_t i=1; i<=58; ++i) { + //std::cerr << "Verifying with " << i << " countbits\n"; + verifyGlobalIdRange(docIds, i); + } +} + +void +GlobalIdTest::testBucketOrderCmp() +{ + typedef GlobalId::BucketOrderCmp C; + CPPUNIT_ASSERT(C::compareRaw(0, 0) == 0); + CPPUNIT_ASSERT(C::compareRaw(0, 1) == -1); + CPPUNIT_ASSERT(C::compareRaw(1, 0) == 1); + CPPUNIT_ASSERT(C::compareRaw(255, 255) == 0); + CPPUNIT_ASSERT(C::compareRaw(0, 255) == -255); + CPPUNIT_ASSERT(C::compareRaw(255, 0) == 255); + CPPUNIT_ASSERT(C::compareRaw(254, 254) == 0); + CPPUNIT_ASSERT(C::compareRaw(254, 255) == -1); + CPPUNIT_ASSERT(C::compareRaw(255, 254) == 1); + { + // Test raw comparator object. + GlobalId foo = GlobalId::parse("gid(0x000001103330333077700000)"); + GlobalId bar = GlobalId::parse("gid(0x000000100030003000700000)"); + GlobalId baz = GlobalId::parse("gid(0x000000103330333000700000)"); + + GlobalId::BucketOrderCmp cmp; + CPPUNIT_ASSERT(!cmp(foo, foo)); + CPPUNIT_ASSERT(!cmp(bar, bar)); + CPPUNIT_ASSERT(!cmp(baz, baz)); + CPPUNIT_ASSERT(!cmp(foo, bar)); + CPPUNIT_ASSERT( cmp(bar, foo)); + CPPUNIT_ASSERT(!cmp(foo, baz)); + CPPUNIT_ASSERT( cmp(baz, foo)); + CPPUNIT_ASSERT(!cmp(baz, bar)); + CPPUNIT_ASSERT( cmp(bar, baz)); + } + { + // Test sorting by bucket. + GlobalId foo = GlobalId::parse("gid(0x000001103330333077700000)"); + GlobalId bar = GlobalId::parse("gid(0x000000100030003000700000)"); + GlobalId baz = GlobalId::parse("gid(0x000000103330333000700000)"); + + typedef std::map<GlobalId, uint32_t, GlobalId::BucketOrderCmp> GidMap; + GidMap gidMap; + gidMap[foo] = 666; + gidMap[bar] = 777; + gidMap[baz] = 888; + + GidMap::iterator it = gidMap.begin(); + CPPUNIT_ASSERT(it->first == bar); + CPPUNIT_ASSERT(it->second == 777); + ++it; + CPPUNIT_ASSERT(it->first == baz); + CPPUNIT_ASSERT(it->second == 888); + ++it; + CPPUNIT_ASSERT(it->first == foo); + CPPUNIT_ASSERT(it->second == 666); + } +} + +} // document diff --git a/document/src/tests/heapdebugger.h b/document/src/tests/heapdebugger.h new file mode 100644 index 00000000000..deb862fbec3 --- /dev/null +++ b/document/src/tests/heapdebugger.h @@ -0,0 +1,68 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * @file heapdebugger.h + * + * @author Ove Martin Malm + * @date Creation date: 2000-08-09 + * @version $Id$ + * @file mcheckhooks.h + * + * Copyright (c) : 1997-2000 Fast Search & Transfer ASA + * ALL RIGHTS RESERVED + */ + +#pragma once +#include <cstddef> + + +/** + * This function is used for counting memory usage. Must be called before any block is allocated + * (pt. linux only) + * Parameter controls operation + */ +extern void enableHeapUsageMonitor(int param = 0); + + +/** + * This function return the current memory used. + * @return Net number of bytes allocated on the heap + */ +extern size_t getHeapUsage(void); + +/** + * This enables heap debugging. Must be called before any block is allocated + * Parameter controls operation + * (pt. linux only) + */ +extern void enableHeapCorruptCheck(int param = 0); + +#define HEAPCHECKMODE_REMOVE -1 // Deinstall +#define HEAPCHECKMODE_NORMAL 0 // Normal +#define HEAPCHECKMODE_EXTENSIVE 1 // All allocated blocks checked on all ops.. +#define HEAPCHECKMODE_DISABLED 2 // No checking (but extra bytes allocated) + + + +/** + * Run a heap check now. Will lock the heap and run through a full check. Will crash if it fails... + */ +extern void checkHeapNow(void); + + +/** + * And this enables linux mcheck function with an approprate callback function. (absolutely linux only) + * see man mcheck + */ +extern void enableMCheck(void); + + + + + + + + + + + + diff --git a/document/src/tests/heapdebuggerlinux.cpp b/document/src/tests/heapdebuggerlinux.cpp new file mode 100644 index 00000000000..5afe583c5e1 --- /dev/null +++ b/document/src/tests/heapdebuggerlinux.cpp @@ -0,0 +1,708 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * + * @author Ove Martin Malm + * @date Creation date: 2000-21-08 + * @version $Id$ + * + * Copyright (c) : 1997-2000 Fast Search & Transfer ASA + * ALL RIGHTS RESERVED + */ + + +// These are necessary to make it compile.... +#define _MALLOC_INTERNAL +#define MALLOC_HOOKS +#include <vespa/fastos/fastos.h> +#include <stdio.h> +#define _LIBC +#include <malloc.h> +#include <mcheck.h> +#include <stdint.h> +#include <libintl.h> + +#include <iostream> +#include <pthread.h> +#include <signal.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> + +#include "heapdebugger.h" + +// Predeclaration +static void HeapCorrupt(const char * caller, const struct elemheader *hdr, enum mcheck_status err); + +// ------------------------------- +// Part #1 Memory usage + + +#ifndef INTERNAL_SIZE_T +#define INTERNAL_SIZE_T size_t +#endif + +#define SIZE_SZ (sizeof(INTERNAL_SIZE_T)) + +/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */ + +#define PREV_INUSE 0x1 + +/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */ + +#define IS_MMAPPED 0x2 + +/* Bits to mask off when extracting size */ + +#define SIZE_BITS (PREV_INUSE|IS_MMAPPED) + +struct malloc_chunk +{ + INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ + INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk* mchunkptr; + +/* conversion from malloc headers to user pointers, and back */ + +#define chunk2mem(p) ((Void_t*)((char*)(p) + 2*SIZE_SZ)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) + +/* Get size, ignoring use bits */ + +#define chunkSize(p) ((p)->size & ~(SIZE_BITS)) + + +static void (*old_free_hook_memusage)(void* buf, const void * caller ); +static void* (*old_malloc_hook_memusage)(size_t bytes, const void * caller); +static void* (*old_realloc_hook_memusage)(void *buf, size_t bytes, const void *caller); +static void* (*old_memalign_hook_memusage)(size_t alignment, size_t bytes,const void *caller); +static size_t MemoryUsage = 0; +static pthread_mutex_t memUsageMutex = PTHREAD_MUTEX_INITIALIZER; +static bool MemUsageHookInstalled = false; + + +static void me_use_free_hook(void *ptr, const void *caller) +{ + mchunkptr p; + + pthread_mutex_lock(&memUsageMutex); + + if (ptr) { + p = mem2chunk(ptr); + MemoryUsage -= chunkSize(p); + } + + __free_hook = old_free_hook_memusage; + + if (old_free_hook_memusage != NULL) { + (*old_free_hook_memusage) (ptr, caller); + } else { + free (ptr); + } + + __free_hook = me_use_free_hook; + + pthread_mutex_unlock(&memUsageMutex); +} + +static void* me_use_malloc_hook(size_t size, const void *caller) +{ + void *anew; + mchunkptr p; + + pthread_mutex_lock(&memUsageMutex); + + __malloc_hook = old_malloc_hook_memusage; + + if (old_malloc_hook_memusage != NULL) { + anew = (*old_malloc_hook_memusage) (size, caller); + } else { + anew = malloc (size); + } + + if (anew) { + p = mem2chunk(anew); + MemoryUsage += chunkSize(p); + } + + __malloc_hook = me_use_malloc_hook; + pthread_mutex_unlock(&memUsageMutex); + + return(anew); +} + +static void* me_use_realloc_hook(void *ptr, size_t size, const void* caller) +{ + void *anew; + mchunkptr p; + size_t oldsz = 0; + size_t newsz = 0; + + pthread_mutex_lock(&memUsageMutex); + + if (ptr) { + p = mem2chunk(ptr); + oldsz = chunkSize(p); + } + + __free_hook = old_free_hook_memusage; + __malloc_hook = old_malloc_hook_memusage; + __realloc_hook = old_realloc_hook_memusage; + + if (old_realloc_hook_memusage != NULL) { + anew = (*old_realloc_hook_memusage) (ptr, size, caller); + } else { + anew = realloc (ptr, size); + } + + if (anew) { + p = mem2chunk(anew); + newsz = chunkSize(p); + + MemoryUsage += (newsz - oldsz); + } + + __free_hook = me_use_free_hook; + __malloc_hook = me_use_malloc_hook; + __realloc_hook = me_use_realloc_hook; + + pthread_mutex_unlock(&memUsageMutex); + + return anew; +} + +static void* me_use_memalign_hook(size_t alignment, size_t bytes,const void *caller) +{ + + void *aligned; + + std::cout << "memalign" << std::endl; + __memalign_hook = old_memalign_hook_memusage; + + if (old_memalign_hook_memusage != NULL) { + aligned = (*old_memalign_hook_memusage) (alignment, bytes,caller); + } else { + aligned = memalign (alignment, bytes); + } + + __memalign_hook = me_use_memalign_hook; + + return aligned; +} + + +void enableHeapUsageMonitor(int param) +{ + (void)param; + if (!MemUsageHookInstalled) { + old_free_hook_memusage = __free_hook; + __free_hook = me_use_free_hook; + old_malloc_hook_memusage = __malloc_hook; + __malloc_hook = me_use_malloc_hook; + old_realloc_hook_memusage = __realloc_hook; + __realloc_hook = me_use_realloc_hook; + MemUsageHookInstalled = true; + std::cout << "Memory usage will be counted" << std::endl; + } +} + +size_t getHeapUsage(void) +{ + return(MemoryUsage); +} + + +// + Get status message.... + + + + +// ------------------- +// Part 2: Heap corrupt + +static void (*old_free_hook_memcorrupt)(void*, const void *); +static void* (*old_malloc_hook_memcorrupt)(size_t, const void *); +static void* (*old_realloc_hook_memcorrupt)(void*, size_t, const void *); +static void* (*old_memalign_hook_memcorrupt)(size_t alignment, size_t bytes,const void *caller); + +static pthread_mutex_t HeapCorruptCheckMutex = PTHREAD_MUTEX_INITIALIZER; + +static int HeapCorruptCheckMode = HEAPCHECKMODE_REMOVE; +static bool PrintUsage = false; + + +/* Arbitrary magical numbers. */ +#define MAGICWORD 0xfedabeeb +#define MAGICFREE 0xf1eef1ee +#define MAGICBYTE ((char) 0x01) // "End byte" +#define MALLOCFLOOD ((char) 0x02) // "Fresh (unused) memory" +#define FREEFLOOD ((char) 0x03) // "Dead" memory + +struct elemheader { + __malloc_size_t size; /* Exact size requested by user. */ + unsigned long int magic; /* Magic number to check header integrity. */ + struct elemheader *prev; + struct elemheader *next; +}; + +/* This is the beginning of the list of all memory blocks allocated. + It is only constructed if the pedantic testing is requested. */ + +static struct elemheader *root; + +static enum mcheck_status me_corrupt_mprobe (const struct elemheader * block) +{ + enum mcheck_status status; + switch (block->magic ^ ((uintptr_t) block->prev + (uintptr_t) block->next)) { + case MAGICFREE: + status = MCHECK_FREE; + break; + case MAGICWORD: + { + const char *elem = (const char *)block; + status = (elem[sizeof(struct elemheader)+block->size] != MAGICBYTE) ? MCHECK_TAIL : MCHECK_OK; + } + break; + + default: + status = MCHECK_HEAD; + break; + } + return status; +} + + + +static void unlink_blk (struct elemheader *ptr) +{ + if (ptr->next != NULL) { + ptr->next->prev = ptr->prev; + ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev + (uintptr_t) ptr->next->next); + } + + if (ptr->prev != NULL) { + ptr->prev->next = ptr->next; + ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev + (uintptr_t) ptr->prev->next); + } else { + root = ptr->next; + } +} + +static void link_blk (struct elemheader *ptr) +{ + ptr->prev = NULL; + ptr->next = root; + root = ptr; + ptr->magic = MAGICWORD ^ (uintptr_t) ptr->next; + + /* And the next block. */ + if (ptr->next != NULL) { + ptr->next->prev = ptr; + ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr + (uintptr_t) ptr->next->next); + } +} + +static void heapwalker(void) +{ + const struct elemheader *runp = root; + + while (runp) { + enum mcheck_status mstatus = me_corrupt_mprobe(runp); + if (mstatus != MCHECK_OK) { + HeapCorrupt("heapwalker",runp,mstatus); + } + runp = runp->next; + } +} + + +static void me_corrupt_free_hook (__ptr_t ptr, const __ptr_t caller) +{ + enum mcheck_status mstatus; + + pthread_mutex_lock(&HeapCorruptCheckMutex); + + if (HeapCorruptCheckMode == HEAPCHECKMODE_EXTENSIVE) { + heapwalker(); + } + + if (PrintUsage) { + if (ptr) { + struct elemheader *hdr = (struct elemheader *)ptr - 1; + std::cout << "pid :" << getpid() << " free: Attempting to delete area with size " << hdr->size << " bytes" << std::endl; + } else { + std::cout << "pid :" << getpid() << " free: Attempting to delete NULL pointer " << std::endl; + } + } + + + if (ptr) { + struct elemheader *hdr = (struct elemheader *)ptr - 1; + + mstatus = (HeapCorruptCheckMode == HEAPCHECKMODE_DISABLED) ? MCHECK_OK : me_corrupt_mprobe(hdr); + if (mstatus != MCHECK_OK) { + HeapCorrupt("free", hdr ,mstatus); + } + + if (mstatus == MCHECK_OK) { // ForGet element if not detected or reallocated.. + hdr->magic = MAGICFREE; + unlink_blk (hdr); + hdr->prev = hdr->next = NULL; + + char * userdata = (char *)hdr+sizeof(struct elemheader); + if (hdr->size > 0) { + memset (userdata, FREEFLOOD, hdr->size); + } + + ptr = (__ptr_t) hdr; + } else { + ptr = NULL; // ForGet element if not detected or reallocated.. + } + } + + + __free_hook = old_free_hook_memcorrupt; + + if (old_free_hook_memcorrupt != NULL) { + (*old_free_hook_memcorrupt) (ptr, caller); + } else { + free (ptr); + } + + __free_hook = me_corrupt_free_hook; + + pthread_mutex_unlock(&HeapCorruptCheckMutex); +} + +static __ptr_t me_corrupt_malloc_hook (__malloc_size_t size, const __ptr_t caller) +{ + struct elemheader *hdr; + enum mcheck_status mstatus; + + pthread_mutex_lock(&HeapCorruptCheckMutex); + + + if (HeapCorruptCheckMode == HEAPCHECKMODE_EXTENSIVE) { + heapwalker(); + } + + if (PrintUsage) { + std::cout << "pid :" << getpid() << " malloc: Attempting to allocate " << size << " bytes" << std::endl; + } + + + __malloc_hook = old_malloc_hook_memcorrupt; + if (old_malloc_hook_memcorrupt != NULL) { + hdr = (struct elemheader *) (*old_malloc_hook_memcorrupt) (sizeof (struct elemheader) + size + 1,caller); + } else { + hdr = (struct elemheader *) malloc (sizeof (struct elemheader) + size + 1); + } + + __malloc_hook = me_corrupt_malloc_hook; + + if (hdr) { + hdr->size = size; + link_blk (hdr); + + char * userdata = (char *)hdr+sizeof(struct elemheader); + userdata[size] = MAGICBYTE; + if (size > 0) { + memset (userdata, MALLOCFLOOD, size); + } + + // Self check! + mstatus = (HeapCorruptCheckMode == HEAPCHECKMODE_DISABLED) ? MCHECK_OK : me_corrupt_mprobe(hdr); + if (mstatus != MCHECK_OK) { + HeapCorrupt("malloc", hdr ,mstatus); + } + } + + pthread_mutex_unlock(&HeapCorruptCheckMutex); + + return hdr ? (__ptr_t) (hdr + 1) : NULL; +} + +static __ptr_t me_corrupt_realloc_hook (__ptr_t ptr, __malloc_size_t size, const __ptr_t caller) +{ + struct elemheader *hdr; + __malloc_size_t osize; + enum mcheck_status mstatus; + + pthread_mutex_lock(&HeapCorruptCheckMutex); + + if (HeapCorruptCheckMode == HEAPCHECKMODE_EXTENSIVE) { + heapwalker(); + } + + if (PrintUsage) { + std::cout << "pid :" << getpid() << " realloc: Attempting to allocate area with size " << size << " bytes."; + if (ptr) { + hdr = (struct elemheader *)ptr - 1; + std::cout << " Old buffer was " << hdr->size << " bytes" << std::endl; + } else { + std::cout << " No old buffer." << std::endl; + } + } + + if (ptr) { + hdr = ((struct elemheader *) ptr) - 1; + osize = hdr->size; + + mstatus = (HeapCorruptCheckMode == HEAPCHECKMODE_DISABLED) ? MCHECK_OK : me_corrupt_mprobe(hdr); + if (mstatus != MCHECK_OK) { + HeapCorrupt("realloc/1", hdr ,mstatus); + } + + unlink_blk (hdr); + if (size < osize) { + memset ((char *) ptr + size, FREEFLOOD, osize - size); + } + } else { + osize = 0; + hdr = NULL; + } + + __free_hook = old_free_hook_memcorrupt; + __malloc_hook = old_malloc_hook_memcorrupt; + __realloc_hook = old_realloc_hook_memcorrupt; + + if (old_realloc_hook_memcorrupt != NULL) { + hdr = (struct elemheader *) (*old_realloc_hook_memcorrupt) ((__ptr_t) hdr, sizeof (struct elemheader) + size + 1, caller); + } else { + hdr = (struct elemheader *) realloc ((__ptr_t) hdr,sizeof (struct elemheader) + size + 1); + } + + __free_hook = me_corrupt_free_hook; + __malloc_hook = me_corrupt_malloc_hook; + __realloc_hook = me_corrupt_realloc_hook; + + if (hdr) { + hdr->size = size; + link_blk (hdr); + + char * userdata = (char *)hdr+sizeof(struct elemheader); + userdata[size] = MAGICBYTE; + + if (size > osize) { + memset (&userdata[osize], MALLOCFLOOD, size-osize); + } + + mstatus = (HeapCorruptCheckMode == HEAPCHECKMODE_DISABLED) ? MCHECK_OK : me_corrupt_mprobe(hdr); + if (mstatus != MCHECK_OK) { + HeapCorrupt("realloc/2", hdr ,mstatus); + } + } + + pthread_mutex_unlock(&HeapCorruptCheckMutex); + + return hdr ? (__ptr_t) (hdr + 1) : NULL; +} + + +static void* me_corrupt_memalign_hook(size_t alignment, size_t bytes,const void *caller) +{ + void *aligned; + + std::cout << "memalign #2 " << std::endl; + __memalign_hook = old_memalign_hook_memcorrupt; + + if (old_memalign_hook_memcorrupt != NULL) { + aligned = (*old_memalign_hook_memcorrupt) (alignment, bytes,caller); + } else { + aligned = memalign (alignment, bytes); + } + + __memalign_hook = me_corrupt_memalign_hook; + + return aligned; +} + + + +void enableHeapCorruptCheck(int param) +{ + + pthread_mutex_lock(&HeapCorruptCheckMutex); + + + // This is install.. + if (HeapCorruptCheckMode == HEAPCHECKMODE_REMOVE && param != HEAPCHECKMODE_REMOVE) { + old_free_hook_memcorrupt = __free_hook; + __free_hook = me_corrupt_free_hook; + old_malloc_hook_memcorrupt = __malloc_hook; + __malloc_hook=me_corrupt_malloc_hook; + old_realloc_hook_memcorrupt = __realloc_hook; + __realloc_hook = me_corrupt_realloc_hook; + old_memalign_hook_memcorrupt = __memalign_hook; + __memalign_hook = me_corrupt_memalign_hook; + std::cout << "Heap corruption detector installed" << std::endl; + } + + // This is deinstall.. + if (HeapCorruptCheckMode != HEAPCHECKMODE_REMOVE && param == HEAPCHECKMODE_REMOVE) { + __free_hook = old_free_hook_memcorrupt; + __malloc_hook = old_malloc_hook_memcorrupt; + __realloc_hook = old_realloc_hook_memcorrupt; + __memalign_hook = old_memalign_hook_memcorrupt; + std::cout << "Heap corruption detector removed" << std::endl; + } + + HeapCorruptCheckMode = param; + pthread_mutex_unlock(&HeapCorruptCheckMutex); +} + + + + +void checkHeapNow(void) +{ + pthread_mutex_lock(&HeapCorruptCheckMutex); + heapwalker(); + pthread_mutex_unlock(&HeapCorruptCheckMutex); +} + + +//--------------------------------- +// Part 3: glibc's mcheck librrary + +static void mcheckabortfunc(enum mcheck_status err) +{ + HeapCorrupt("mcheckabortfunc",NULL,err); +} + + +void enableMCheck(void) +{ + mcheck(&mcheckabortfunc); +} + + +//--------------------------------- +// Part 4: error report & exit + +static void HeapDump(const char *info,const char *p,int cnt) +{ + char Legend[150]; + snprintf(Legend,sizeof(Legend),"Legend: Magic (tail) [%02x]. allocated, but unused: [%02x]. Reclaimed to free pool: [%02x]", + (unsigned char)MAGICBYTE, + (unsigned char)MALLOCFLOOD, + (unsigned char)FREEFLOOD); + + std::cout << Legend << std::endl; + std::cout << "Buffer dump: " << info << std::endl; + + for (int i = 0;i < cnt;i++) { + char c = p[i]; + if (isgraph(c) || c == ' ') { + std::cout << c; + } else { + char Tmp[10]; + snprintf(Tmp,sizeof(Tmp),"[%02x]",(unsigned char)c); + std::cout << Tmp; + } + } + std::cout << std::endl; +} + + +static void HeapCorrupt(const char * caller, const struct elemheader *hdr, enum mcheck_status err) +{ + + bool AbortNow = true; + +#define DUMPSIZE 100 + + if (!caller) { + caller = "?"; + } + switch (err) { + case MCHECK_DISABLED: + std::cout << caller + << ": Heap check was not called before the first allocation. No consistency checking can be done." + << std::endl; + break; + + case MCHECK_OK: + std::cout << caller + << ": No inconsistency detected." + << std::endl; + AbortNow = false; + break; + + case MCHECK_HEAD: + std::cout << caller + << ": The data immediately before the block was modified. This commonly happens when an array index or pointer is decremented too far." + << std::endl + << " This block is ignored (and may appear as memory leakage)." + << std::endl; + + if (hdr) { + HeapDump("Immediately before ",((const char *)hdr) - (DUMPSIZE/2) ,DUMPSIZE/2); + HeapDump("The element in question ",(const char *)hdr,DUMPSIZE/2); + } + AbortNow = false; + break; + + case MCHECK_TAIL: + std::cout << caller + << ": The data immediately after the block was modified. This commonly happens when an array index or pointer is incremented too far." + << std::endl; + + if (hdr) { + HeapDump("Tail of element in question: ", ((const char *)hdr) + sizeof(struct elemheader) + hdr->size + 1 - (DUMPSIZE/2),(DUMPSIZE/2)); + HeapDump("Immediately after element: ",((const char *)hdr) + sizeof(struct elemheader) + hdr->size + 1 + 1, (DUMPSIZE/2)); + } + + break; + + case MCHECK_FREE: + std::cout << caller + << ": The block was already freed." + << std::endl; + break; + + + default: + std::cout << caller + << ": Unexpected event " << err + << std::endl; + break; + } + + + if (hdr) { + std::cout << caller << ": The element size was " << hdr->size << " bytes." << std::endl; + if (err == MCHECK_HEAD) { + std::cout << caller << ": But size may be incorrect since other parts of the header is destroyed/undecodable." << std::endl; + } + } + + if (AbortNow) { + std::cout << caller << ": Application will crash now." << std::flush; + + // No core dump? Check ulimit -c + + kill(getpid(), SIGSEGV); + } + +} + +// Enable this one if you want the heapdebugger to be installed "before anything else" +// Uses same method as -lmcheck +#if 0 + +/* This one is called from malloc init.. */ + +static void MallocInitHook(void) +{ + enableHeapUsageMonitor(0); + enableHeapCorruptCheck(0); +} + +void (*__malloc_initialize_hook) (void) = MallocInitHook; + + +#endif diff --git a/document/src/tests/heapdebuggerother.cpp b/document/src/tests/heapdebuggerother.cpp new file mode 100644 index 00000000000..641951f1883 --- /dev/null +++ b/document/src/tests/heapdebuggerother.cpp @@ -0,0 +1,44 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * + * @author Ove Martin Malm + * @date Creation date: 2000-21-08 + * @version $Id$ + * + * Copyright (c) : 1997-2000 Fast Search & Transfer ASA + * ALL RIGHTS RESERVED + */ + +#include <vespa/fastos/fastos.h> +#include <stdlib.h> +#include "heapdebugger.h" + +// Not implemented +void enableHeapUsageMonitor(int param) +{ + (void)param; + +} + +// Not implemented +extern size_t getHeapUsage(void) +{ + return 0; +} + +// Not implemented +void enableHeapCorruptCheck(int param) +{ + (void)param; + +} + +// Not implemented +void enableMCheck(void) +{ +} + +// Not implemented +void checkHeapNow(void) +{ +} diff --git a/document/src/tests/orderingselectortest.cpp b/document/src/tests/orderingselectortest.cpp new file mode 100644 index 00000000000..b9cf7dc358a --- /dev/null +++ b/document/src/tests/orderingselectortest.cpp @@ -0,0 +1,98 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <vespa/document/select/orderingselector.h> +#include <vespa/document/bucket/bucketidfactory.h> +#include <vespa/document/base/testdocrepo.h> + +#include <vespa/vdstestlib/cppunit/macros.h> +#include <vespa/document/select/parser.h> +#include <memory> + +using document::select::Node; +using document::select::Parser; + +namespace document { + +struct OrderingSelectorTest : public CppUnit::TestFixture { + void testSimple(); + + CPPUNIT_TEST_SUITE(OrderingSelectorTest); + CPPUNIT_TEST(testSimple); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(OrderingSelectorTest); + +#define ASSERT_MATCH(expression, ordering, correct) \ +{ \ + BucketIdFactory idfactory; \ + TestDocRepo repo; \ + OrderingSelector selector; \ + Parser parser(repo.getTypeRepo(), idfactory); \ + std::unique_ptr<Node> node(parser.parse(expression)); \ + CPPUNIT_ASSERT(node.get() != 0); \ + OrderingSpecification::UP spec = selector.select(*node, ordering); \ + if (spec.get() == NULL && correct.get() == NULL) { \ + return;\ + }\ + if (spec.get() == NULL && correct.get() != NULL) { \ + CPPUNIT_ASSERT_MSG(std::string("Was NULL, expected ") + correct->toString(), false); \ + } \ + if (correct.get() == NULL && spec.get() != NULL) { \ + CPPUNIT_ASSERT_MSG(std::string("Expected NULL, was ") + spec->toString(), false); \ + } \ + CPPUNIT_ASSERT_EQUAL(*correct, *spec); \ +} + +void OrderingSelectorTest::testSimple() +{ + ASSERT_MATCH("id.order(10,10) < 100", OrderingSpecification::DESCENDING, + OrderingSpecification::UP( + new OrderingSpecification(OrderingSpecification::DESCENDING, (long)99, (short)10, (short)10))); + + ASSERT_MATCH("id.order(10,10) <= 100", OrderingSpecification::DESCENDING, + OrderingSpecification::UP( + new OrderingSpecification(OrderingSpecification::DESCENDING, (long)100, (short)10, (short)10))); + + ASSERT_MATCH("id.order(10,10) > 100", OrderingSpecification::DESCENDING, OrderingSpecification::UP()); + + ASSERT_MATCH("id.order(10,10) > 100", OrderingSpecification::ASCENDING, + OrderingSpecification::UP( + new OrderingSpecification(OrderingSpecification::ASCENDING, (long)101, (short)10, (short)10))); + + ASSERT_MATCH("id.user==1234 AND id.order(10,10) > 100", OrderingSpecification::ASCENDING, + OrderingSpecification::UP( + new OrderingSpecification(OrderingSpecification::ASCENDING, (long)101, (short)10, (short)10))); + + ASSERT_MATCH("id.order(10,10) >= 100", OrderingSpecification::ASCENDING, + OrderingSpecification::UP( + new OrderingSpecification(OrderingSpecification::ASCENDING, (long)100, (short)10, (short)10))); + + ASSERT_MATCH("id.order(10,10) == 100", OrderingSpecification::ASCENDING, + OrderingSpecification::UP( + new OrderingSpecification(OrderingSpecification::ASCENDING, (long)100, (short)10, (short)10))); + + ASSERT_MATCH("id.order(10,10) = 100", OrderingSpecification::DESCENDING, + OrderingSpecification::UP( + new OrderingSpecification(OrderingSpecification::DESCENDING, (long)100, (short)10, (short)10))); + + ASSERT_MATCH("id.order(10,10) > 30 AND id.order(10,10) < 100", OrderingSpecification::ASCENDING, + OrderingSpecification::UP( + new OrderingSpecification(OrderingSpecification::ASCENDING, (long)31, (short)10, (short)10))); + + ASSERT_MATCH("id.order(10,10) > 30 AND id.order(10,10) < 100", OrderingSpecification::DESCENDING, + OrderingSpecification::UP( + new OrderingSpecification(OrderingSpecification::DESCENDING, (long)99, (short)10, (short)10))); + + ASSERT_MATCH("id.order(10,10) > 30 OR id.order(10,10) > 70", OrderingSpecification::ASCENDING, + OrderingSpecification::UP( + new OrderingSpecification(OrderingSpecification::ASCENDING, (long)31, (short)10, (short)10))); + + ASSERT_MATCH("id.order(10,10) < 30 OR id.order(10,10) < 70", OrderingSpecification::DESCENDING, + OrderingSpecification::UP( + new OrderingSpecification(OrderingSpecification::DESCENDING, (long)69, (short)10, (short)10))); +} + + +} diff --git a/document/src/tests/positiontypetest.cpp b/document/src/tests/positiontypetest.cpp new file mode 100644 index 00000000000..33398e9fa00 --- /dev/null +++ b/document/src/tests/positiontypetest.cpp @@ -0,0 +1,53 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <vespa/document/datatype/positiondatatype.h> +#include <cppunit/extensions/HelperMacros.h> + +namespace document { + +class PositionTypeTest : public CppUnit::TestFixture { +public: + void requireThatNameIsCorrect(); + void requireThatExpectedFieldsAreThere(); + void requireThatZCurveFieldMatchesJava(); + + CPPUNIT_TEST_SUITE(PositionTypeTest); + CPPUNIT_TEST(requireThatNameIsCorrect); + CPPUNIT_TEST(requireThatExpectedFieldsAreThere); + CPPUNIT_TEST(requireThatZCurveFieldMatchesJava); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(PositionTypeTest); + +void +PositionTypeTest::requireThatNameIsCorrect() +{ + const StructDataType &type = PositionDataType::getInstance(); + CPPUNIT_ASSERT_EQUAL(vespalib::string("position"), type.getName()); +} + +void +PositionTypeTest::requireThatExpectedFieldsAreThere() +{ + const StructDataType &type = PositionDataType::getInstance(); + Field field = type.getField("x"); + CPPUNIT_ASSERT_EQUAL(*DataType::INT, field.getDataType()); + + field = type.getField("y"); + CPPUNIT_ASSERT_EQUAL(*DataType::INT, field.getDataType()); +} + +void +PositionTypeTest::requireThatZCurveFieldMatchesJava() +{ + CPPUNIT_ASSERT_EQUAL(vespalib::string("foo_zcurve"), + PositionDataType::getZCurveFieldName("foo")); + CPPUNIT_ASSERT( ! PositionDataType::isZCurveFieldName("foo")); + CPPUNIT_ASSERT( ! PositionDataType::isZCurveFieldName("_zcurve")); + CPPUNIT_ASSERT( PositionDataType::isZCurveFieldName("x_zcurve")); + CPPUNIT_ASSERT( ! PositionDataType::isZCurveFieldName("x_zcurvex")); + CPPUNIT_ASSERT_EQUAL(vespalib::stringref("x"), PositionDataType::cutZCurveFieldName("x_zcurve")); +} + +} // document 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(); } diff --git a/document/src/tests/primitivefieldvaluetest.cpp b/document/src/tests/primitivefieldvaluetest.cpp new file mode 100644 index 00000000000..90e51ec071d --- /dev/null +++ b/document/src/tests/primitivefieldvaluetest.cpp @@ -0,0 +1,422 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <vespa/document/fieldvalue/fieldvalues.h> +#include <vespa/document/serialization/vespadocumentdeserializer.h> +#include <vespa/vdstestlib/cppunit/macros.h> +#include <vespa/vespalib/objects/nbostream.h> + +using vespalib::nbostream; + +namespace document { + +struct PrimitiveFieldValueTest : public CppUnit::TestFixture { + void setUp() {} + void tearDown() {} + + void testLiterals(); + void testRaw(); + void testNumerics(); + void testFloatDoubleCasts(); + + CPPUNIT_TEST_SUITE(PrimitiveFieldValueTest); + CPPUNIT_TEST(testLiterals); + CPPUNIT_TEST(testRaw); + CPPUNIT_TEST(testNumerics); + CPPUNIT_TEST(testFloatDoubleCasts); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(PrimitiveFieldValueTest); + +namespace { +template <typename T> +void deserialize(const ByteBuffer &buffer, T &value) { + uint16_t version = Document::getNewestSerializationVersion(); + nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining()); + DocumentTypeRepo repo; + VespaDocumentDeserializer deserializer(repo, stream, version); + deserializer.read(value); +} + + /** + * Test common functionality, such as serialization, comparisons, and + * assignment. medium1 and medium2 should be equal, but not the same + * instance. + */ + template<typename Type> + void + testCommon(const Type& smallest, const Type& medium1, + const Type& medium2, const Type& largest) + { + try{ + // Less + CPPUNIT_ASSERT(!(smallest < smallest)); + CPPUNIT_ASSERT(smallest < medium1); + CPPUNIT_ASSERT(smallest < medium2); + CPPUNIT_ASSERT(smallest < largest); + CPPUNIT_ASSERT(!(medium1 < smallest)); + CPPUNIT_ASSERT(!(medium1 < medium1)); + CPPUNIT_ASSERT(!(medium1 < medium2)); + CPPUNIT_ASSERT(medium1 < largest); + CPPUNIT_ASSERT(!(medium2 < smallest)); + CPPUNIT_ASSERT(!(medium2 < medium1)); + CPPUNIT_ASSERT(!(medium2 < medium2)); + CPPUNIT_ASSERT(medium2 < largest); + CPPUNIT_ASSERT(!(largest < smallest)); + CPPUNIT_ASSERT(!(largest < medium1)); + CPPUNIT_ASSERT(!(largest < medium2)); + CPPUNIT_ASSERT(!(largest < largest)); + // Equal + CPPUNIT_ASSERT(smallest == smallest); + CPPUNIT_ASSERT(!(smallest == medium1)); + CPPUNIT_ASSERT(!(smallest == medium2)); + CPPUNIT_ASSERT(!(smallest == largest)); + CPPUNIT_ASSERT(!(medium1 == smallest)); + CPPUNIT_ASSERT(medium1 == medium1); + CPPUNIT_ASSERT(medium1 == medium2); + CPPUNIT_ASSERT(!(medium1 == largest)); + CPPUNIT_ASSERT(!(medium2 == smallest)); + CPPUNIT_ASSERT(medium2 == medium1); + CPPUNIT_ASSERT(medium2 == medium2); + CPPUNIT_ASSERT(!(medium2 == largest)); + CPPUNIT_ASSERT(!(largest == smallest)); + CPPUNIT_ASSERT(!(largest == medium1)); + CPPUNIT_ASSERT(!(largest == medium2)); + CPPUNIT_ASSERT(largest == largest); + // Greater + CPPUNIT_ASSERT(!(smallest > smallest)); + CPPUNIT_ASSERT(!(smallest > medium1)); + CPPUNIT_ASSERT(!(smallest > medium2)); + CPPUNIT_ASSERT(!(smallest > largest)); + CPPUNIT_ASSERT(medium1 > smallest); + CPPUNIT_ASSERT(!(medium1 > medium1)); + CPPUNIT_ASSERT(!(medium1 > medium2)); + CPPUNIT_ASSERT(!(medium1 > largest)); + CPPUNIT_ASSERT(medium2 > smallest); + CPPUNIT_ASSERT(!(medium2 > medium1)); + CPPUNIT_ASSERT(!(medium2 > medium2)); + CPPUNIT_ASSERT(!(medium2 > largest)); + CPPUNIT_ASSERT(largest > smallest); + CPPUNIT_ASSERT(largest > medium1); + CPPUNIT_ASSERT(largest > medium2); + CPPUNIT_ASSERT(!(largest > largest)); + // Currently >=, <= and != is deducted from the above, so not + // checking separately + + // Serialization + Type t; + std::unique_ptr<ByteBuffer> buf(smallest.serialize()); + buf->flip(); + deserialize(*buf, t); + CPPUNIT_ASSERT_EQUAL(smallest, t); + + buf = medium1.serialize(); + buf->flip(); + deserialize(*buf, t); + CPPUNIT_ASSERT_EQUAL(medium1, t); + CPPUNIT_ASSERT_EQUAL(medium2, t); + + buf = largest.serialize(); + buf->flip(); + deserialize(*buf, t); + CPPUNIT_ASSERT_EQUAL(largest, t); + + // Assignment + CPPUNIT_ASSERT_EQUAL(smallest, t = smallest); + CPPUNIT_ASSERT_EQUAL(medium1, t = medium1); + CPPUNIT_ASSERT_EQUAL(largest, t = largest); + + Type t1(smallest); + Type t2(medium1); + Type t3(medium2); + Type t4(largest); + CPPUNIT_ASSERT_EQUAL(smallest, t1); + CPPUNIT_ASSERT_EQUAL(medium1, t2); + CPPUNIT_ASSERT_EQUAL(medium2, t3); + CPPUNIT_ASSERT_EQUAL(largest, t4); + + t.assign(smallest); + CPPUNIT_ASSERT_EQUAL(smallest, t); + t.assign(medium2); + CPPUNIT_ASSERT_EQUAL(medium1, t); + t.assign(largest); + CPPUNIT_ASSERT_EQUAL(largest, t); + + // Catch errors and say what type there were trouble with. + } catch (std::exception& e) { + std::cerr << "\nFailed for type " << *smallest.getDataType() + << "\n"; + throw; + } + } + + template<typename Literal> + void + testLiteral() + { + testCommon(Literal(), + Literal("bar"), + Literal("bar"), + Literal("foo")); + Literal value("foo"); + // Textual output + CPPUNIT_ASSERT_EQUAL(std::string("foo"), value.toString(false, "")); + CPPUNIT_ASSERT_EQUAL(std::string("foo"), value.toString(true, " ")); + CPPUNIT_ASSERT_EQUAL(std::string("<value>foo</value>\n"), + value.toXml(" ")); + + // Conversion + CPPUNIT_ASSERT_EQUAL(typename Literal::string(value.getAsString()), value.getValue()); + + // Operator = + value = "anotherVal"; + CPPUNIT_ASSERT_EQUAL(typename Literal::string("anotherVal"), value.getValue()); + value = std::string("yetAnotherVal"); + CPPUNIT_ASSERT_EQUAL(typename Literal::string("yetAnotherVal"), value.getValue()); + + // Test that a just deserialized value can be serialized again + // (literals have lazy deserialization so behaves diff then + value = "foo"; + std::unique_ptr<ByteBuffer> buf(value.serialize()); + buf->flip(); + Literal value2("Other"); + deserialize(*buf, value2); + buf = value2.serialize(); + buf->flip(); + deserialize(*buf, value2); + CPPUNIT_ASSERT_EQUAL(value, value2); + + // Verify that get value ref gives us ref within original bytebuffer + // (operator== use above should not modify this) + buf = value.serialize(); + buf->flip(); + deserialize(*buf, value2); + + CPPUNIT_ASSERT_EQUAL(size_t(3), value2.getValueRef().size()); + // Zero termination + CPPUNIT_ASSERT(*(value2.getValueRef().c_str() + value2.getValueRef().size()) == '\0'); + } + +} + +void +PrimitiveFieldValueTest::testLiterals() +{ + testLiteral<StringFieldValue>(); +} + +void +PrimitiveFieldValueTest::testRaw() +{ + testCommon(RawFieldValue(), + RawFieldValue("bar\0bar", 7), + RawFieldValue("bar\0bar", 7), + RawFieldValue("bar\0other", 9)); + RawFieldValue value("\tfoo\0\r\n", 7); + // Textual output + CPPUNIT_ASSERT_EQUAL(std::string( + "0: 09 66 6f 6f 00 0d 0a .foo..."), + value.toString(false, "")); + CPPUNIT_ASSERT_EQUAL(std::string( + "0: 09 66 6f 6f 00 0d 0a .foo..."), + value.toString(true, " ")); + CPPUNIT_ASSERT_EQUAL(std::string( + "<value binaryencoding=\"base64\">CWZvbwANCg==</value>\n"), + value.toXml(" ")); + + value.setValue("grmpf", 4); + CPPUNIT_ASSERT(strncmp("grmpf", value.getValueRef().c_str(), + value.getValueRef().size()) == 0); +} + +#define ASSERT_FAILED_CONV(getter, totype, floating) \ +{ \ + totype toType; \ + FieldValue::UP copy(value.clone()); \ + try{ \ + getter; \ + std::ostringstream ost; \ + ost << "Conversion unexpectedly worked from max value of " \ + << *value.getDataType() << " to " << *toType.getDataType(); \ + CPPUNIT_FAIL(ost.str().c_str()); \ + } catch (std::exception& e) { \ + CPPUNIT_ASSERT_EQUAL( \ + std::string("bad numeric conversion: positive overflow"), \ + std::string(e.what())); \ + } \ + /* Verify that we can convert to smaller type if value is within \ + range. Only tests integer to integer. No floating point. */ \ + if (!floating) { \ + totype::Number maxV = std::numeric_limits<totype::Number>::max(); \ + value.setValue((Number) maxV); \ + getter; \ + } \ + value.assign(*copy); \ +} + +namespace { + + template<typename Numeric> + void + testNumeric(const std::string& maxVal, bool floatingPoint) + { + typedef typename Numeric::Number Number; + Number maxValue(std::numeric_limits<Number>::max()); + // Test common fieldvalue stuff + testCommon(Numeric(), + Numeric(Number(1)), + Numeric(Number(1)), + Numeric(Number(maxValue))); + Numeric value; + value.setValue(maxValue); + // Test textual output + CPPUNIT_ASSERT_EQUAL(maxVal, value.toString(false, "")); + CPPUNIT_ASSERT_EQUAL(maxVal, value.toString(true, " ")); + CPPUNIT_ASSERT_EQUAL("<value>" + maxVal + "</value>\n", + value.toXml(" ")); + // Test numeric conversions + // + // Currently, all safe conversion works. For instance, a byte can be + // converted to a long, a long can be converted to a byte, given + // that it has a value in the range -128 to 127. + // + // All integers will also convert automatically to floating point + // numbers. No checks is done as to how precise floating point + // representation can keep the value. + if (floatingPoint || sizeof(Number) > sizeof(unsigned char)) { + // No longer throws. This is guarded on the perimeter by java code. + // ASSERT_FAILED_CONV(value.getAsByte(), ByteFieldValue, floatingPoint); + } else { + CPPUNIT_ASSERT_EQUAL((char) maxValue, value.getAsByte()); + } + if (floatingPoint || sizeof(Number) > sizeof(int32_t)) { + // No longer throws. This is guarded on the perimeter by java code. + // ASSERT_FAILED_CONV(value.getAsInt(), IntFieldValue, floatingPoint); + } else { + CPPUNIT_ASSERT_EQUAL((int32_t) maxValue, value.getAsInt()); + } + if (floatingPoint || sizeof(Number) > sizeof(int64_t)) { + // No longer throws. This is guarded on the perimeter by java code. + // ASSERT_FAILED_CONV(value.getAsLong(), LongFieldValue, floatingPoint); + } else { + CPPUNIT_ASSERT_EQUAL((int64_t) maxValue, value.getAsLong()); + } + if (floatingPoint && sizeof(Number) > sizeof(float)) { + // No longer throws. This is guarded on the perimeter by java code. + // ASSERT_FAILED_CONV(value.getAsFloat(), FloatFieldValue, true); + } else { + CPPUNIT_ASSERT_EQUAL((float) maxValue, value.getAsFloat()); + } + CPPUNIT_ASSERT_EQUAL((double) maxValue, value.getAsDouble()); + // Test some simple conversions + Numeric a(0); + a = std::string("5"); + CPPUNIT_ASSERT_EQUAL(5, a.getAsInt()); + } + +} + +void +PrimitiveFieldValueTest::testFloatDoubleCasts() +{ + float inf(std::numeric_limits<float>::infinity()); + CPPUNIT_ASSERT_EQUAL(inf, static_cast<float>(static_cast<double>(inf))); +} + +void +PrimitiveFieldValueTest::testNumerics() +{ + testNumeric<ByteFieldValue>("127", false); + testNumeric<ShortFieldValue>("32767", false); + testNumeric<IntFieldValue>("2147483647", false); + testNumeric<LongFieldValue>("9223372036854775807", false); + testNumeric<FloatFieldValue>("3.40282e+38", true); + testNumeric<DoubleFieldValue>("1.79769e+308", true); + + // Test range + ByteFieldValue b1(-128); + ByteFieldValue b2(-1); + CPPUNIT_ASSERT_EQUAL(-128, (int) b1.getValue()); + CPPUNIT_ASSERT_EQUAL(-1, (int) b2.getValue()); + + ShortFieldValue s1(-32768); + ShortFieldValue s2(65535); + CPPUNIT_ASSERT_EQUAL((int16_t)-32768, s1.getValue()); + CPPUNIT_ASSERT_EQUAL((int16_t)65535, s2.getValue()); + CPPUNIT_ASSERT_EQUAL((int16_t)-1, s2.getValue()); + + IntFieldValue i1(-2147483647-1); + IntFieldValue i2(4294967295U); + + CPPUNIT_ASSERT_EQUAL((int) -2147483647-1, i1.getValue()); + CPPUNIT_ASSERT_EQUAL((int) -1, i2.getValue()); + + LongFieldValue l1(-9223372036854775807ll-1); + LongFieldValue l2(18446744073709551615ull); + + CPPUNIT_ASSERT_EQUAL((int64_t) -9223372036854775807ll-1, l1.getValue()); + CPPUNIT_ASSERT_EQUAL((int64_t) -1, l2.getValue()); + + b1 = "-128"; + b2 = "255"; + + CPPUNIT_ASSERT_EQUAL(-128, (int) b1.getValue()); + CPPUNIT_ASSERT_EQUAL(-1, (int) b2.getValue()); + i1 = "-2147483648"; + i2 = "4294967295"; + CPPUNIT_ASSERT_EQUAL((int) -2147483647-1, i1.getValue()); + CPPUNIT_ASSERT_EQUAL((int) -1, i2.getValue()); + + l1 = "-9223372036854775808"; + l2 = "18446744073709551615"; + + int64_t bnv = -1; + bnv <<= 63; + CPPUNIT_ASSERT_EQUAL(bnv, l1.getValue()); + CPPUNIT_ASSERT_EQUAL((int64_t) -9223372036854775807ll-1, l1.getValue()); + CPPUNIT_ASSERT_EQUAL((int64_t) -1, l2.getValue()); + + // Test some special cases for bytes + // (as unsigned char is not always handled as a number) + b1 = "0xff"; + CPPUNIT_ASSERT_EQUAL(-1, (int) b1.getValue()); + b1 = "53"; + CPPUNIT_ASSERT_EQUAL(53, (int) b1.getValue()); + CPPUNIT_ASSERT_EQUAL(vespalib::string("53"), b1.getAsString()); + + try{ + b1 = "-129"; + CPPUNIT_FAIL("Expected -129 to be invalid byte"); + } catch (std::exception& e) {} + try{ + b1 = "256"; + CPPUNIT_FAIL("Expected -129 to be invalid byte"); + } catch (std::exception& e) {} + try{ + s1 = "-32769"; + CPPUNIT_FAIL("Expected -32769 to be invalid int"); + } catch (std::exception& e) {} + try{ + s1 = "65536"; + CPPUNIT_FAIL("Expected 65536 to be invalid int"); + } catch (std::exception& e) {} + try{ + i1 = "-2147483649"; + CPPUNIT_FAIL("Expected -2147483649 to be invalid int"); + } catch (std::exception& e) {} + try{ + i1 = "4294967296"; + CPPUNIT_FAIL("Expected 4294967296 to be invalid int"); + } catch (std::exception& e) {} + try{ + l1 = "-9223372036854775809"; + CPPUNIT_FAIL("Expected -9223372036854775809 to be invalid long"); + } catch (std::exception& e) {} + try{ + l1 = "18446744073709551616"; + CPPUNIT_FAIL("Expected 18446744073709551616 to be invalid long"); + } catch (std::exception& e) {} +} + +} // document diff --git a/document/src/tests/repo/.gitignore b/document/src/tests/repo/.gitignore new file mode 100644 index 00000000000..bd4e77abb07 --- /dev/null +++ b/document/src/tests/repo/.gitignore @@ -0,0 +1,5 @@ +*.So +*_test +.depend +Makefile +document_documenttyperepo_test_app diff --git a/document/src/tests/repo/CMakeLists.txt b/document/src/tests/repo/CMakeLists.txt new file mode 100644 index 00000000000..efd74caf954 --- /dev/null +++ b/document/src/tests/repo/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(document_documenttyperepo_test_app + SOURCES + documenttyperepo_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_documenttyperepo_test_app COMMAND document_documenttyperepo_test_app) diff --git a/document/src/tests/repo/documenttyperepo_test.cpp b/document/src/tests/repo/documenttyperepo_test.cpp new file mode 100644 index 00000000000..003bd1698af --- /dev/null +++ b/document/src/tests/repo/documenttyperepo_test.cpp @@ -0,0 +1,519 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for documenttyperepo. + +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("documenttyperepo_test"); + +#include <vespa/config/print/asciiconfigwriter.h> +#include <vespa/document/config/config-documenttypes.h> +#include <vespa/document/datatype/annotationreferencedatatype.h> +#include <vespa/document/datatype/arraydatatype.h> +#include <vespa/document/datatype/documenttype.h> +#include <vespa/document/datatype/mapdatatype.h> +#include <vespa/document/datatype/weightedsetdatatype.h> +#include <vespa/document/repo/configbuilder.h> +#include <vespa/document/repo/documenttyperepo.h> +#include <stdlib.h> +#include <vespa/vespalib/objects/identifiable.h> +#include <vespa/vespalib/stllike/string.h> +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/exceptions.h> +#include <set> +#include <vespa/config/helper/configgetter.h> + +using config::AsciiConfigWriter; +using std::set; +using std::vector; +using vespalib::Identifiable; +using vespalib::IllegalArgumentException; +using vespalib::string; + +using namespace document::config_builder; +using namespace document; + +namespace { + +const string type_name = "test"; +const int32_t doc_type_id = 787121340; +const string header_name = type_name + ".header"; +const int32_t header_id = 30; +const string body_name = type_name + ".body"; +const int32_t body_id = 31; +const string type_name_2 = "test_2"; +const string header_name_2 = type_name_2 + ".header"; +const string body_name_2 = type_name_2 + ".body"; +const int32_t comp_level = 10; +const int32_t comp_minres = 80; +const size_t comp_minsize = 120; +const string field_name = "field_name"; +const string derived_name = "derived"; + +TEST("requireThatDocumentTypeCanBeLookedUp") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name).setId(header_id), + Struct(body_name).setId(body_id)); + DocumentTypeRepo repo(builder.config()); + + const DocumentType *type = repo.getDocumentType(type_name); + ASSERT_TRUE(type); + EXPECT_EQUAL(type_name, type->getName()); + EXPECT_EQUAL(doc_type_id, type->getId()); + EXPECT_EQUAL(header_name, type->getFieldsType().getName()); +/* + TODO(vekterli): Check fields struct ID after it has been determined which ID it should get + EXPECT_EQUAL(header_id, type->getHeader().getId()); + EXPECT_EQUAL(header_name, type->getHeader().getName()); + EXPECT_EQUAL(body_id, type->getBody().getId()); + EXPECT_EQUAL(body_name, type->getBody().getName()); +*/ +} + +TEST("requireThatDocumentTypeCanBeLookedUpWhenIdIsNotAHash") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id + 2, type_name, + Struct(header_name).setId(header_id), + Struct(body_name).setId(body_id)); + DocumentTypeRepo repo(builder.config()); + + const DocumentType *type = repo.getDocumentType(type_name); + ASSERT_TRUE(type); +} + +TEST("requireThatStructsCanConfigureCompression") { + DocumenttypesConfigBuilderHelper builder; + typedef DocumenttypesConfig::Documenttype::Datatype::Sstruct Sstruct; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).setCompression( + Sstruct::Compression::LZ4, + comp_level, comp_minres, comp_minsize)); + DocumentTypeRepo repo(builder.config()); + + const CompressionConfig &comp_config = + repo.getDocumentType(type_name)->getFieldsType() + .getCompressionConfig(); + EXPECT_EQUAL(CompressionConfig::LZ4, comp_config.type); + EXPECT_EQUAL(comp_level, comp_config.compressionLevel); + EXPECT_EQUAL(comp_minres, comp_config.threshold); + EXPECT_EQUAL(comp_minsize, comp_config.minSize); +} + +TEST("requireThatStructsCanHaveFields") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField(field_name, DataType::T_INT)); + DocumentTypeRepo repo(builder.config()); + + const StructDataType &s = repo.getDocumentType(type_name)->getFieldsType(); + ASSERT_EQUAL(1u, s.getFieldCount()); + const Field &field = s.getField(field_name); + EXPECT_EQUAL(DataType::T_INT, field.getDataType().getId()); +} + +template <typename T> +const T &getFieldDataType(const DocumentTypeRepo &repo) { + const DataType &d = repo.getDocumentType(type_name) + ->getFieldsType().getField(field_name).getDataType(); + const T *t = dynamic_cast<const T *>(&d); + ASSERT_TRUE(t); + return *t; +} + +TEST("requireThatArraysCanBeConfigured") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField(field_name, + Array(DataType::T_STRING))); + DocumentTypeRepo repo(builder.config()); + + const ArrayDataType &a = getFieldDataType<ArrayDataType>(repo); + EXPECT_EQUAL(DataType::T_STRING, a.getNestedType().getId()); +} + +TEST("requireThatWsetsCanBeConfigured") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField(field_name, + Wset(DataType::T_INT) + .removeIfZero().createIfNonExistent())); + DocumentTypeRepo repo(builder.config()); + + const WeightedSetDataType &w = getFieldDataType<WeightedSetDataType>(repo); + EXPECT_EQUAL(DataType::T_INT, w.getNestedType().getId()); + EXPECT_TRUE(w.createIfNonExistent()); + EXPECT_TRUE(w.removeIfZero()); +} + +TEST("requireThatMapsCanBeConfigured") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField(field_name, + Map(DataType::T_INT, DataType::T_STRING))); + DocumentTypeRepo repo(builder.config()); + + const MapDataType &m = getFieldDataType<MapDataType>(repo); + EXPECT_EQUAL(DataType::T_INT, m.getKeyType().getId()); + EXPECT_EQUAL(DataType::T_STRING, m.getValueType().getId()); +} + +TEST("requireThatAnnotationReferencesCanBeConfigured") { + int32_t annotation_type_id = 424; + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField(field_name, + AnnotationRef(annotation_type_id))) + .annotationType(annotation_type_id, "foo", -1); + DocumentTypeRepo repo(builder.config()); + + const AnnotationReferenceDataType &ar = + getFieldDataType<AnnotationReferenceDataType>(repo); + EXPECT_EQUAL(annotation_type_id, ar.getAnnotationType().getId()); +} + +TEST("requireThatFieldsCanNotBeHeaderAndBody") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name).addField(field_name, + DataType::T_STRING), + Struct(body_name).addField(field_name, + DataType::T_INT)); + EXPECT_EXCEPTION(DocumentTypeRepo repo(builder.config()), + IllegalArgumentException, + "Failed to add field 'field_name' to struct 'test.header': " + "Name in use by field with different id"); +} + +TEST("requireThatDocumentStructsAreCalledHeaderAndBody") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, Struct("foo"), Struct("bar")); + EXPECT_EXCEPTION(DocumentTypeRepo repo(builder.config()), + IllegalArgumentException, + "Previously defined as \"test.header\"."); +} + +TEST("requireThatDocumentsCanInheritFields") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField(field_name, DataType::T_INT)); + builder.document(doc_type_id + 1, derived_name, + Struct("derived.header"), + Struct("derived.body").addField("derived_field", + DataType::T_STRING)) + .inherit(doc_type_id); + DocumentTypeRepo repo(builder.config()); + + const StructDataType &s = + repo.getDocumentType(doc_type_id + 1)->getFieldsType(); + ASSERT_EQUAL(2u, s.getFieldCount()); + const Field &field = s.getField(field_name); + const DataType &type = field.getDataType(); + EXPECT_EQUAL(DataType::T_INT, type.getId()); +} + +TEST("requireThatDocumentsCanUseInheritedTypes") { + const int32_t id = 64; + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField("foo", + Array(DataType::T_INT).setId(id))); + builder.document(doc_type_id + 1, derived_name, + Struct("derived.header"), + Struct("derived.body").addField(field_name, id)) + .inherit(doc_type_id); + DocumentTypeRepo repo(builder.config()); + + const DataType &type = + repo.getDocumentType(doc_type_id + 1)->getFieldsType() + .getField(field_name).getDataType(); + EXPECT_EQUAL(id, type.getId()); + EXPECT_TRUE(dynamic_cast<const ArrayDataType *>(&type)); +} + +TEST("requireThatIllegalConfigsCausesExceptions") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), Struct(body_name)) + .inherit(doc_type_id + 1); + EXPECT_EXCEPTION(DocumentTypeRepo repo(builder.config()), + IllegalArgumentException, "Unable to find document"); + + builder = DocumenttypesConfigBuilderHelper(); + builder.document(doc_type_id, type_name, + Struct(header_name), Struct(body_name)); + builder.config().documenttype[0].datatype[0].type = + DocumenttypesConfig::Documenttype::Datatype::Type(-1); + EXPECT_EXCEPTION(DocumentTypeRepo repo(builder.config()), + IllegalArgumentException, "Unknown datatype type -1"); + + builder = DocumenttypesConfigBuilderHelper(); + const int id = 10000; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField(field_name, + Array(DataType::T_INT).setId(id))); + EXPECT_EQUAL(id, builder.config().documenttype[0].datatype[1].id); + builder.config().documenttype[0].datatype[1].array.element.id = id; + EXPECT_EXCEPTION(DocumentTypeRepo repo(builder.config()), + IllegalArgumentException, "Unknown datatype 10000"); + + builder = DocumenttypesConfigBuilderHelper(); + builder.document(doc_type_id, type_name, + Struct(header_name).setId(header_id), + Struct(body_name).addField("foo", + Struct("bar").setId(header_id))); + EXPECT_EXCEPTION(DocumentTypeRepo repo(builder.config()), + IllegalArgumentException, "Redefinition of data type"); + + builder = DocumenttypesConfigBuilderHelper(); + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField(field_name, + AnnotationRef(id))); + EXPECT_EXCEPTION(DocumentTypeRepo repo(builder.config()), + IllegalArgumentException, "Unknown AnnotationType"); + + builder = DocumenttypesConfigBuilderHelper(); + builder.document(doc_type_id, type_name, + Struct(header_name), Struct(body_name)) + .annotationType(id, type_name, DataType::T_STRING) + .annotationType(id, type_name, DataType::T_INT); + EXPECT_EXCEPTION(DocumentTypeRepo repo(builder.config()), + IllegalArgumentException, "Redefinition of annotation type"); + + builder = DocumenttypesConfigBuilderHelper(); + builder.document(doc_type_id, type_name, + Struct(header_name), Struct(body_name)) + .annotationType(id, type_name, DataType::T_STRING) + .annotationType(id, "foobar", DataType::T_STRING); + EXPECT_EXCEPTION(DocumentTypeRepo repo(builder.config()), + IllegalArgumentException, "Redefinition of annotation type"); +} + +TEST("requireThatDataTypesCanBeLookedUpById") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name).setId(header_id), Struct(body_name)); + builder.document(doc_type_id + 1, type_name_2, + Struct(header_name_2), Struct(body_name_2)); + DocumentTypeRepo repo(builder.config()); + + const DataType *type = + repo.getDataType(*repo.getDocumentType(doc_type_id), header_id); + ASSERT_TRUE(type); + EXPECT_EQUAL(header_name, type->getName()); + EXPECT_EQUAL(header_id, type->getId()); + + ASSERT_TRUE(!repo.getDataType(*repo.getDocumentType(doc_type_id), -1)); + ASSERT_TRUE(!repo.getDataType(*repo.getDocumentType(doc_type_id + 1), + header_id)); +} + +TEST("requireThatDataTypesCanBeLookedUpByName") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name).setId(header_id), + Struct(body_name)); + builder.document(doc_type_id + 1, type_name_2, + Struct(header_name_2), Struct(body_name_2)); + DocumentTypeRepo repo(builder.config()); + + const DataType *type = + repo.getDataType(*repo.getDocumentType(doc_type_id), header_name); + ASSERT_TRUE(type); + EXPECT_EQUAL(header_name, type->getName()); + EXPECT_EQUAL(header_id, type->getId()); + + EXPECT_TRUE(repo.getDataType(*repo.getDocumentType(doc_type_id), + type_name)); + EXPECT_TRUE(!repo.getDataType(*repo.getDocumentType(doc_type_id), + field_name)); + EXPECT_TRUE(!repo.getDataType(*repo.getDocumentType(doc_type_id + 1), + body_name)); +} + +TEST("requireThatInheritingDocCanRedefineIdenticalField") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name) + .addField(field_name, DataType::T_STRING) + .setId(body_id)); + builder.document(doc_type_id + 1, derived_name, + Struct("derived.header"), + Struct("derived.body") + .addField(field_name, DataType::T_STRING) + .setId(body_id)) + .inherit(doc_type_id); + DocumentTypeRepo repo(builder.config()); + + const StructDataType &s = repo.getDocumentType(doc_type_id + 1)->getFieldsType(); + ASSERT_EQUAL(1u, s.getFieldCount()); +} + +TEST("requireThatAnnotationTypesCanBeConfigured") { + const int32_t a_id = 654; + const string a_name = "annotation_name"; + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), Struct(body_name)) + .annotationType(a_id, a_name, DataType::T_STRING); + DocumentTypeRepo repo(builder.config()); + + const DocumentType *type = repo.getDocumentType(doc_type_id); + const AnnotationType *a_type = repo.getAnnotationType(*type, a_id); + ASSERT_TRUE(a_type); + EXPECT_EQUAL(a_name, a_type->getName()); + ASSERT_TRUE(a_type->getDataType()); + EXPECT_EQUAL(DataType::T_STRING, a_type->getDataType()->getId()); +} + +TEST("requireThatFieldIdV6IsDifferentFromV7") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField(field_name, DataType::T_INT)); + DocumentTypeRepo repo(builder.config()); + + const StructDataType &s = repo.getDocumentType(type_name)->getFieldsType(); + ASSERT_EQUAL(1u, s.getFieldCount()); + const Field &field = s.getField(field_name); + ASSERT_TRUE(field.getId(7) != field.getId(6)); +} + +TEST("requireThatDocumentsCanUseOtherDocumentTypes") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id + 1, type_name_2, + Struct(header_name_2), + Struct(body_name_2)); + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField(type_name_2, + doc_type_id + 1).setId(body_id)); + DocumentTypeRepo repo(builder.config()); + + const DataType &type = repo.getDocumentType(doc_type_id)->getFieldsType() + .getField(type_name_2).getDataType(); + EXPECT_EQUAL(doc_type_id + 1, type.getId()); + EXPECT_TRUE(dynamic_cast<const DocumentType *>(&type)); +} + +void storeId(set<int> *s, const DocumentType &type) { + s->insert(type.getId()); +} + +TEST("requireThatDocumentTypesCanBeIterated") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), Struct(body_name)); + builder.document(doc_type_id + 1, type_name_2, + Struct(header_name_2), Struct(body_name_2)); + DocumentTypeRepo repo(builder.config()); + + set<int> ids; + repo.forEachDocumentType(*makeClosure(storeId, &ids)); + + EXPECT_EQUAL(3u, ids.size()); + ASSERT_TRUE(ids.count(DataType::T_DOCUMENT)); + ASSERT_TRUE(ids.count(doc_type_id)); + ASSERT_TRUE(ids.count(doc_type_id + 1)); +} + +TEST("requireThatDocumentLookupChecksName") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name_2, + Struct(header_name_2), Struct(body_name_2)); + DocumentTypeRepo repo(builder.config()); + + // "type_name" will generate the document type id + // "doc_type_id". However, this config assigns that id to a + // different type. + const DocumentType *type = repo.getDocumentType(type_name); + ASSERT_TRUE(!type); +} + +TEST("requireThatBuildFromConfigWorks") { + DocumentTypeRepo repo(readDocumenttypesConfig("documenttypes.cfg")); + ASSERT_TRUE(repo.getDocumentType("document")); + ASSERT_TRUE(repo.getDocumentType("types")); + ASSERT_TRUE(repo.getDocumentType("types_search")); +} + +TEST("requireThatStructsCanBeRecursive") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name).setId(header_id).addField(field_name, + header_id), + Struct(body_name)); + DocumentTypeRepo repo(builder.config()); + + const StructDataType &s = repo.getDocumentType(type_name)->getFieldsType(); + ASSERT_EQUAL(1u, s.getFieldCount()); +} + +} // namespace + +TEST("requireThatMissingFileCausesException") { + EXPECT_EXCEPTION(readDocumenttypesConfig("illegal/missing_file"), + IllegalArgumentException, "Unable to open file"); +} + +TEST("requireThatFieldsCanHaveAnyDocumentType") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, + Struct(header_name), + Struct(body_name).addField(field_name, doc_type_id + 1)); + // Circular dependency + builder.document(doc_type_id + 1, type_name_2, + Struct(header_name_2), + Struct(body_name_2).addField(field_name, doc_type_id)); + DocumentTypeRepo repo(builder.config()); + const DocumentType *type1 = repo.getDocumentType(doc_type_id); + const DocumentType *type2 = repo.getDocumentType(doc_type_id + 1); + ASSERT_TRUE(type1); + EXPECT_EQUAL(type1, repo.getDataType(*type1, doc_type_id)); + EXPECT_EQUAL(type2, repo.getDataType(*type1, doc_type_id + 1)); + EXPECT_TRUE(type1->getFieldsType().hasField(field_name)); + ASSERT_TRUE(type2); + EXPECT_EQUAL(type1, repo.getDataType(*type2, doc_type_id)); + EXPECT_EQUAL(type2, repo.getDataType(*type2, doc_type_id + 1)); + EXPECT_TRUE(type2->getFieldsType().hasField(field_name)); +} + +TEST("requireThatBodyCanOccurBeforeHeaderInConfig") { + DocumenttypesConfigBuilderHelper builder; + // Add header and body in reverse order, then swap the ids. + builder.document(doc_type_id, type_name, + Struct(body_name).setId(body_id).addField("bodystuff", + DataType::T_STRING), + Struct(header_name).setId(header_id).addField( + "headerstuff", DataType::T_INT)); + std::swap(builder.config().documenttype[0].headerstruct, + builder.config().documenttype[0].bodystruct); + + DocumentTypeRepo repo(builder.config()); + const StructDataType &s = repo.getDocumentType(type_name)->getFieldsType(); + // Should have both fields in fields struct + EXPECT_TRUE(s.hasField("headerstuff")); + EXPECT_TRUE(s.hasField("bodystuff")); +} + +TEST("Require that Array can have nested DocumentType") { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, type_name, Struct(header_name), + Struct(body_name) + .addField(field_name, Array(doc_type_id))); + DocumentTypeRepo repo(builder.config()); + const DocumentType *type = repo.getDocumentType(doc_type_id); + ASSERT_TRUE(type); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/document/src/tests/repo/documenttypes.cfg b/document/src/tests/repo/documenttypes.cfg new file mode 100644 index 00000000000..4641663a94b --- /dev/null +++ b/document/src/tests/repo/documenttypes.cfg @@ -0,0 +1,421 @@ +documenttype[2] +documenttype[0].bodystruct 348447225 +documenttype[0].headerstruct 1328581348 +documenttype[0].id -853072901 +documenttype[0].name "types" +documenttype[0].version 0 +documenttype[0].datatype[27] +documenttype[0].datatype[00].id 49942803 +documenttype[0].datatype[00].type ARRAY +documenttype[0].datatype[00].array.element.id 16 +documenttype[0].datatype[01].id 339965458 +documenttype[0].datatype[01].type MAP +documenttype[0].datatype[01].map.key.id 2 +documenttype[0].datatype[01].map.value.id 2 +documenttype[0].datatype[02].id 69621385 +documenttype[0].datatype[02].type ARRAY +documenttype[0].datatype[02].array.element.id 339965458 +documenttype[0].datatype[03].id -2092985853 +documenttype[0].datatype[03].type STRUCT +documenttype[0].datatype[03].sstruct.name "mystruct" +documenttype[0].datatype[03].sstruct.version 0 +documenttype[0].datatype[03].sstruct.field[4] +documenttype[0].datatype[03].sstruct.field[bytearr].datatype 49942803 +documenttype[0].datatype[03].sstruct.field[bytearr].id 1079701754 +documenttype[0].datatype[03].sstruct.field[bytearr].id_v6 1198855694 +documenttype[0].datatype[03].sstruct.field[bytearr].name "bytearr" +documenttype[0].datatype[03].sstruct.field[mymap].datatype 339965458 +documenttype[0].datatype[03].sstruct.field[mymap].id 1954178122 +documenttype[0].datatype[03].sstruct.field[mymap].id_v6 707189723 +documenttype[0].datatype[03].sstruct.field[mymap].name "mymap" +documenttype[0].datatype[03].sstruct.field[structfield].datatype 2 +documenttype[0].datatype[03].sstruct.field[structfield].id 1726890940 +documenttype[0].datatype[03].sstruct.field[structfield].id_v6 418303145 +documenttype[0].datatype[03].sstruct.field[structfield].name "structfield" +documenttype[0].datatype[03].sstruct.field[title].datatype 2 +documenttype[0].datatype[03].sstruct.field[title].id 567626448 +documenttype[0].datatype[03].sstruct.field[title].id_v6 29129762 +documenttype[0].datatype[03].sstruct.field[title].name "title" +documenttype[0].datatype[04].id 759956026 +documenttype[0].datatype[04].type ARRAY +documenttype[0].datatype[04].array.element.id -2092985853 +documenttype[0].datatype[05].id 2125328771 +documenttype[0].datatype[05].type WSET +documenttype[0].datatype[05].wset.createifnonexistent false +documenttype[0].datatype[05].wset.removeifzero true +documenttype[0].datatype[05].wset.key.id 2 +documenttype[0].datatype[06].id -1486737430 +documenttype[0].datatype[06].type ARRAY +documenttype[0].datatype[06].array.element.id 2 +documenttype[0].datatype[07].id 1707615575 +documenttype[0].datatype[07].type ARRAY +documenttype[0].datatype[07].array.element.id -1486737430 +documenttype[0].datatype[08].id 171503364 +documenttype[0].datatype[08].type MAP +documenttype[0].datatype[08].map.key.id 1707615575 +documenttype[0].datatype[08].map.value.id 0 +documenttype[0].datatype[09].id 1100964733 +documenttype[0].datatype[09].type ARRAY +documenttype[0].datatype[09].array.element.id 171503364 +documenttype[0].datatype[10].id -1715531035 +documenttype[0].datatype[10].type MAP +documenttype[0].datatype[10].map.key.id 0 +documenttype[0].datatype[10].map.value.id 4 +documenttype[0].datatype[11].id -794985308 +documenttype[0].datatype[11].type ARRAY +documenttype[0].datatype[11].array.element.id 1707615575 +documenttype[0].datatype[12].id 109267174 +documenttype[0].datatype[12].type STRUCT +documenttype[0].datatype[12].sstruct.name "sct" +documenttype[0].datatype[12].sstruct.version 0 +documenttype[0].datatype[12].sstruct.field[2] +documenttype[0].datatype[12].sstruct.field[s1].datatype 2 +documenttype[0].datatype[12].sstruct.field[s1].id 2146820765 +documenttype[0].datatype[12].sstruct.field[s1].id_v6 142373281 +documenttype[0].datatype[12].sstruct.field[s1].name "s1" +documenttype[0].datatype[12].sstruct.field[s2].datatype 2 +documenttype[0].datatype[12].sstruct.field[s2].id 45366795 +documenttype[0].datatype[12].sstruct.field[s2].id_v6 31106270 +documenttype[0].datatype[12].sstruct.field[s2].name "s2" +documenttype[0].datatype[13].id -1244829667 +documenttype[0].datatype[13].type ARRAY +documenttype[0].datatype[13].array.element.id 109267174 +documenttype[0].datatype[14].id -1865479609 +documenttype[0].datatype[14].type MAP +documenttype[0].datatype[14].map.key.id 2 +documenttype[0].datatype[14].map.value.id 4 +documenttype[0].datatype[15].id 2138385264 +documenttype[0].datatype[15].type MAP +documenttype[0].datatype[15].map.key.id 0 +documenttype[0].datatype[15].map.value.id 5 +documenttype[0].datatype[16].id 1328286588 +documenttype[0].datatype[16].type WSET +documenttype[0].datatype[16].wset.createifnonexistent false +documenttype[0].datatype[16].wset.removeifzero false +documenttype[0].datatype[16].wset.key.id 2 +documenttype[0].datatype[17].id 294108848 +documenttype[0].datatype[17].type STRUCT +documenttype[0].datatype[17].sstruct.name "folder" +documenttype[0].datatype[17].sstruct.version 0 +documenttype[0].datatype[17].sstruct.field[4] +documenttype[0].datatype[17].sstruct.field[FlagsCounter].datatype -1865479609 +documenttype[0].datatype[17].sstruct.field[FlagsCounter].id 1741227606 +documenttype[0].datatype[17].sstruct.field[FlagsCounter].id_v6 1287497652 +documenttype[0].datatype[17].sstruct.field[FlagsCounter].name "FlagsCounter" +documenttype[0].datatype[17].sstruct.field[Name].datatype 2 +documenttype[0].datatype[17].sstruct.field[Name].id 2002760220 +documenttype[0].datatype[17].sstruct.field[Name].id_v6 62942997 +documenttype[0].datatype[17].sstruct.field[Name].name "Name" +documenttype[0].datatype[17].sstruct.field[Version].datatype 0 +documenttype[0].datatype[17].sstruct.field[Version].id 64430502 +documenttype[0].datatype[17].sstruct.field[Version].id_v6 634243672 +documenttype[0].datatype[17].sstruct.field[Version].name "Version" +documenttype[0].datatype[17].sstruct.field[anotherfolder].datatype 294108848 +documenttype[0].datatype[17].sstruct.field[anotherfolder].id 1582421848 +documenttype[0].datatype[17].sstruct.field[anotherfolder].id_v6 1898725199 +documenttype[0].datatype[17].sstruct.field[anotherfolder].name "anotherfolder" +documenttype[0].datatype[18].id 2065577986 +documenttype[0].datatype[18].type WSET +documenttype[0].datatype[18].wset.createifnonexistent true +documenttype[0].datatype[18].wset.removeifzero false +documenttype[0].datatype[18].wset.key.id 2 +documenttype[0].datatype[19].id 1901258752 +documenttype[0].datatype[19].type MAP +documenttype[0].datatype[19].map.key.id 0 +documenttype[0].datatype[19].map.value.id -2092985853 +documenttype[0].datatype[20].id 348447225 +documenttype[0].datatype[20].type STRUCT +documenttype[0].datatype[20].sstruct.name "types.body" +documenttype[0].datatype[20].sstruct.version 0 +documenttype[0].datatype[20].sstruct.field[1] +documenttype[0].datatype[20].sstruct.field[complexarray].datatype 1100964733 +documenttype[0].datatype[20].sstruct.field[complexarray].id 1028383787 +documenttype[0].datatype[20].sstruct.field[complexarray].id_v6 658530305 +documenttype[0].datatype[20].sstruct.field[complexarray].name "complexarray" +documenttype[0].datatype[21].id 2125154557 +documenttype[0].datatype[21].type MAP +documenttype[0].datatype[21].map.key.id 2 +documenttype[0].datatype[21].map.value.id 1 +documenttype[0].datatype[22].id -389833101 +documenttype[0].datatype[22].type MAP +documenttype[0].datatype[22].map.key.id 0 +documenttype[0].datatype[22].map.value.id 294108848 +documenttype[0].datatype[23].id -1245117006 +documenttype[0].datatype[23].type ARRAY +documenttype[0].datatype[23].array.element.id 0 +documenttype[0].datatype[24].id -1584287606 +documenttype[0].datatype[24].type MAP +documenttype[0].datatype[24].map.key.id 2 +documenttype[0].datatype[24].map.value.id 0 +documenttype[0].datatype[25].id 435886609 +documenttype[0].datatype[25].type MAP +documenttype[0].datatype[25].map.key.id 2 +documenttype[0].datatype[25].map.value.id -1245117006 +documenttype[0].datatype[26].id 1328581348 +documenttype[0].datatype[26].type STRUCT +documenttype[0].datatype[26].sstruct.name "types.header" +documenttype[0].datatype[26].sstruct.version 0 +documenttype[0].datatype[26].sstruct.field[25] +documenttype[0].datatype[26].sstruct.field[Folders].datatype -389833101 +documenttype[0].datatype[26].sstruct.field[Folders].id 34575524 +documenttype[0].datatype[26].sstruct.field[Folders].id_v6 280569744 +documenttype[0].datatype[26].sstruct.field[Folders].name "Folders" +documenttype[0].datatype[26].sstruct.field[abyte].datatype 16 +documenttype[0].datatype[26].sstruct.field[abyte].id 110138156 +documenttype[0].datatype[26].sstruct.field[abyte].id_v6 1369099343 +documenttype[0].datatype[26].sstruct.field[abyte].name "abyte" +documenttype[0].datatype[26].sstruct.field[album0].datatype 18 +documenttype[0].datatype[26].sstruct.field[album0].id 764312262 +documenttype[0].datatype[26].sstruct.field[album0].id_v6 1409364160 +documenttype[0].datatype[26].sstruct.field[album0].name "album0" +documenttype[0].datatype[26].sstruct.field[album1].datatype 18 +documenttype[0].datatype[26].sstruct.field[album1].id 1967160809 +documenttype[0].datatype[26].sstruct.field[album1].id_v6 1833811264 +documenttype[0].datatype[26].sstruct.field[album1].name "album1" +documenttype[0].datatype[26].sstruct.field[along].datatype 4 +documenttype[0].datatype[26].sstruct.field[along].id 1206464520 +documenttype[0].datatype[26].sstruct.field[along].id_v6 871280609 +documenttype[0].datatype[26].sstruct.field[along].name "along" +documenttype[0].datatype[26].sstruct.field[arrarr].datatype -794985308 +documenttype[0].datatype[26].sstruct.field[arrarr].id 1962567166 +documenttype[0].datatype[26].sstruct.field[arrarr].id_v6 885141301 +documenttype[0].datatype[26].sstruct.field[arrarr].name "arrarr" +documenttype[0].datatype[26].sstruct.field[arrayfield].datatype -1245117006 +documenttype[0].datatype[26].sstruct.field[arrayfield].id 965790107 +documenttype[0].datatype[26].sstruct.field[arrayfield].id_v6 1010955705 +documenttype[0].datatype[26].sstruct.field[arrayfield].name "arrayfield" +documenttype[0].datatype[26].sstruct.field[arraymapfield].datatype 435886609 +documenttype[0].datatype[26].sstruct.field[arraymapfield].id 1670805928 +documenttype[0].datatype[26].sstruct.field[arraymapfield].id_v6 1940354311 +documenttype[0].datatype[26].sstruct.field[arraymapfield].name "arraymapfield" +documenttype[0].datatype[26].sstruct.field[doublemapfield].datatype 2138385264 +documenttype[0].datatype[26].sstruct.field[doublemapfield].id 877047192 +documenttype[0].datatype[26].sstruct.field[doublemapfield].id_v6 957317090 +documenttype[0].datatype[26].sstruct.field[doublemapfield].name "doublemapfield" +documenttype[0].datatype[26].sstruct.field[floatmapfield].datatype 2125154557 +documenttype[0].datatype[26].sstruct.field[floatmapfield].id 1239120925 +documenttype[0].datatype[26].sstruct.field[floatmapfield].id_v6 1609437589 +documenttype[0].datatype[26].sstruct.field[floatmapfield].name "floatmapfield" +documenttype[0].datatype[26].sstruct.field[intmapfield].datatype -1584287606 +documenttype[0].datatype[26].sstruct.field[intmapfield].id 121004462 +documenttype[0].datatype[26].sstruct.field[intmapfield].id_v6 1642487905 +documenttype[0].datatype[26].sstruct.field[intmapfield].name "intmapfield" +documenttype[0].datatype[26].sstruct.field[foo].datatype 4 +documenttype[0].datatype[26].sstruct.field[foo].id 266111229 +documenttype[0].datatype[26].sstruct.field[foo].id_v6 383833303 +documenttype[0].datatype[26].sstruct.field[foo].name "foo" +documenttype[0].datatype[26].sstruct.field[longmapfield].datatype -1715531035 +documenttype[0].datatype[26].sstruct.field[longmapfield].id 477718745 +documenttype[0].datatype[26].sstruct.field[longmapfield].id_v6 920341968 +documenttype[0].datatype[26].sstruct.field[longmapfield].name "longmapfield" +documenttype[0].datatype[26].sstruct.field[maparr].datatype 69621385 +documenttype[0].datatype[26].sstruct.field[maparr].id 904375219 +documenttype[0].datatype[26].sstruct.field[maparr].id_v6 63700074 +documenttype[0].datatype[26].sstruct.field[maparr].name "maparr" +documenttype[0].datatype[26].sstruct.field[mystructarr].datatype 759956026 +documenttype[0].datatype[26].sstruct.field[mystructarr].id 595856991 +documenttype[0].datatype[26].sstruct.field[mystructarr].id_v6 764861972 +documenttype[0].datatype[26].sstruct.field[mystructarr].name "mystructarr" +documenttype[0].datatype[26].sstruct.field[mystructfield].datatype -2092985853 +documenttype[0].datatype[26].sstruct.field[mystructfield].id 1348513378 +documenttype[0].datatype[26].sstruct.field[mystructfield].id_v6 2033170300 +documenttype[0].datatype[26].sstruct.field[mystructfield].name "mystructfield" +documenttype[0].datatype[26].sstruct.field[mystructmap].datatype 1901258752 +documenttype[0].datatype[26].sstruct.field[mystructmap].id 1511423250 +documenttype[0].datatype[26].sstruct.field[mystructmap].id_v6 449602635 +documenttype[0].datatype[26].sstruct.field[mystructmap].name "mystructmap" +documenttype[0].datatype[26].sstruct.field[setfield].datatype 1328286588 +documenttype[0].datatype[26].sstruct.field[setfield].id 761581914 +documenttype[0].datatype[26].sstruct.field[setfield].id_v6 1762943268 +documenttype[0].datatype[26].sstruct.field[setfield].name "setfield" +documenttype[0].datatype[26].sstruct.field[setfield2].datatype 18 +documenttype[0].datatype[26].sstruct.field[setfield2].id 1066659198 +documenttype[0].datatype[26].sstruct.field[setfield2].id_v6 813038565 +documenttype[0].datatype[26].sstruct.field[setfield2].name "setfield2" +documenttype[0].datatype[26].sstruct.field[setfield3].datatype 2125328771 +documenttype[0].datatype[26].sstruct.field[setfield3].id 1180155772 +documenttype[0].datatype[26].sstruct.field[setfield3].id_v6 1697232199 +documenttype[0].datatype[26].sstruct.field[setfield3].name "setfield3" +documenttype[0].datatype[26].sstruct.field[setfield4].datatype 2065577986 +documenttype[0].datatype[26].sstruct.field[setfield4].id 1254131631 +documenttype[0].datatype[26].sstruct.field[setfield4].id_v6 119755202 +documenttype[0].datatype[26].sstruct.field[setfield4].name "setfield4" +documenttype[0].datatype[26].sstruct.field[stringmapfield].datatype 339965458 +documenttype[0].datatype[26].sstruct.field[stringmapfield].id 117465687 +documenttype[0].datatype[26].sstruct.field[stringmapfield].id_v6 1492788095 +documenttype[0].datatype[26].sstruct.field[stringmapfield].name "stringmapfield" +documenttype[0].datatype[26].sstruct.field[structarrayfield].datatype -1244829667 +documenttype[0].datatype[26].sstruct.field[structarrayfield].id 335048518 +documenttype[0].datatype[26].sstruct.field[structarrayfield].id_v6 607034174 +documenttype[0].datatype[26].sstruct.field[structarrayfield].name "structarrayfield" +documenttype[0].datatype[26].sstruct.field[structfield].datatype 109267174 +documenttype[0].datatype[26].sstruct.field[structfield].id 486207386 +documenttype[0].datatype[26].sstruct.field[structfield].id_v6 418303145 +documenttype[0].datatype[26].sstruct.field[structfield].name "structfield" +documenttype[0].datatype[26].sstruct.field[tagfield].datatype 18 +documenttype[0].datatype[26].sstruct.field[tagfield].id 1653562069 +documenttype[0].datatype[26].sstruct.field[tagfield].id_v6 938523246 +documenttype[0].datatype[26].sstruct.field[tagfield].name "tagfield" +documenttype[0].inherits[1] +documenttype[0].inherits[document.0].id 8 +documenttype[1].bodystruct -1408707420 +documenttype[1].headerstruct 625114831 +documenttype[1].id 666563184 +documenttype[1].name "types_search" +documenttype[1].version 0 +documenttype[1].datatype[12] +documenttype[1].datatype[00].id 1328286588 +documenttype[1].datatype[00].type WSET +documenttype[1].datatype[00].wset.createifnonexistent false +documenttype[1].datatype[00].wset.removeifzero false +documenttype[1].datatype[00].wset.key.id 2 +documenttype[1].datatype[01].id -1865479609 +documenttype[1].datatype[01].type MAP +documenttype[1].datatype[01].map.key.id 2 +documenttype[1].datatype[01].map.value.id 4 +documenttype[1].datatype[02].id 109267174 +documenttype[1].datatype[02].type STRUCT +documenttype[1].datatype[02].sstruct.name "sct" +documenttype[1].datatype[02].sstruct.version 0 +documenttype[1].datatype[02].sstruct.field[2] +documenttype[1].datatype[02].sstruct.field[s1].datatype 2 +documenttype[1].datatype[02].sstruct.field[s1].id 2146820765 +documenttype[1].datatype[02].sstruct.field[s1].id_v6 142373281 +documenttype[1].datatype[02].sstruct.field[s1].name "s1" +documenttype[1].datatype[02].sstruct.field[s2].datatype 2 +documenttype[1].datatype[02].sstruct.field[s2].id 45366795 +documenttype[1].datatype[02].sstruct.field[s2].id_v6 31106270 +documenttype[1].datatype[02].sstruct.field[s2].name "s2" +documenttype[1].datatype[03].id 294108848 +documenttype[1].datatype[03].type STRUCT +documenttype[1].datatype[03].sstruct.name "folder" +documenttype[1].datatype[03].sstruct.version 0 +documenttype[1].datatype[03].sstruct.field[4] +documenttype[1].datatype[03].sstruct.field[FlagsCounter].datatype -1865479609 +documenttype[1].datatype[03].sstruct.field[FlagsCounter].id 1741227606 +documenttype[1].datatype[03].sstruct.field[FlagsCounter].id_v6 1287497652 +documenttype[1].datatype[03].sstruct.field[FlagsCounter].name "FlagsCounter" +documenttype[1].datatype[03].sstruct.field[Name].datatype 2 +documenttype[1].datatype[03].sstruct.field[Name].id 2002760220 +documenttype[1].datatype[03].sstruct.field[Name].id_v6 62942997 +documenttype[1].datatype[03].sstruct.field[Name].name "Name" +documenttype[1].datatype[03].sstruct.field[Version].datatype 0 +documenttype[1].datatype[03].sstruct.field[Version].id 64430502 +documenttype[1].datatype[03].sstruct.field[Version].id_v6 634243672 +documenttype[1].datatype[03].sstruct.field[Version].name "Version" +documenttype[1].datatype[03].sstruct.field[anotherfolder].datatype 294108848 +documenttype[1].datatype[03].sstruct.field[anotherfolder].id 1582421848 +documenttype[1].datatype[03].sstruct.field[anotherfolder].id_v6 1898725199 +documenttype[1].datatype[03].sstruct.field[anotherfolder].name "anotherfolder" +documenttype[1].datatype[04].id 2125328771 +documenttype[1].datatype[04].type WSET +documenttype[1].datatype[04].wset.createifnonexistent false +documenttype[1].datatype[04].wset.removeifzero true +documenttype[1].datatype[04].wset.key.id 2 +documenttype[1].datatype[05].id -1245117006 +documenttype[1].datatype[05].type ARRAY +documenttype[1].datatype[05].array.element.id 0 +documenttype[1].datatype[06].id 2065577986 +documenttype[1].datatype[06].type WSET +documenttype[1].datatype[06].wset.createifnonexistent true +documenttype[1].datatype[06].wset.removeifzero false +documenttype[1].datatype[06].wset.key.id 2 +documenttype[1].datatype[07].id 339965458 +documenttype[1].datatype[07].type MAP +documenttype[1].datatype[07].map.key.id 2 +documenttype[1].datatype[07].map.value.id 2 +documenttype[1].datatype[08].id 625114831 +documenttype[1].datatype[08].type STRUCT +documenttype[1].datatype[08].sstruct.name "types_search.header" +documenttype[1].datatype[08].sstruct.version 0 +documenttype[1].datatype[08].sstruct.field[15] +documenttype[1].datatype[08].sstruct.field[abyte].datatype 16 +documenttype[1].datatype[08].sstruct.field[abyte].id 110138156 +documenttype[1].datatype[08].sstruct.field[abyte].id_v6 1369099343 +documenttype[1].datatype[08].sstruct.field[abyte].name "abyte" +documenttype[1].datatype[08].sstruct.field[album0].datatype 18 +documenttype[1].datatype[08].sstruct.field[album0].id 764312262 +documenttype[1].datatype[08].sstruct.field[album0].id_v6 1409364160 +documenttype[1].datatype[08].sstruct.field[album0].name "album0" +documenttype[1].datatype[08].sstruct.field[album1].datatype 18 +documenttype[1].datatype[08].sstruct.field[album1].id 1967160809 +documenttype[1].datatype[08].sstruct.field[album1].id_v6 1833811264 +documenttype[1].datatype[08].sstruct.field[album1].name "album1" +documenttype[1].datatype[08].sstruct.field[along].datatype 4 +documenttype[1].datatype[08].sstruct.field[along].id 1206464520 +documenttype[1].datatype[08].sstruct.field[along].id_v6 871280609 +documenttype[1].datatype[08].sstruct.field[along].name "along" +documenttype[1].datatype[08].sstruct.field[arrayfield].datatype -1245117006 +documenttype[1].datatype[08].sstruct.field[arrayfield].id 965790107 +documenttype[1].datatype[08].sstruct.field[arrayfield].id_v6 1010955705 +documenttype[1].datatype[08].sstruct.field[arrayfield].name "arrayfield" +documenttype[1].datatype[08].sstruct.field[foo].datatype 4 +documenttype[1].datatype[08].sstruct.field[foo].id 266111229 +documenttype[1].datatype[08].sstruct.field[foo].id_v6 383833303 +documenttype[1].datatype[08].sstruct.field[foo].name "foo" +documenttype[1].datatype[08].sstruct.field[other].datatype 4 +documenttype[1].datatype[08].sstruct.field[other].id 2443357 +documenttype[1].datatype[08].sstruct.field[other].id_v6 903806222 +documenttype[1].datatype[08].sstruct.field[other].name "other" +documenttype[1].datatype[08].sstruct.field[rankfeatures].datatype 2 +documenttype[1].datatype[08].sstruct.field[rankfeatures].id 1883197392 +documenttype[1].datatype[08].sstruct.field[rankfeatures].id_v6 699950698 +documenttype[1].datatype[08].sstruct.field[rankfeatures].name "rankfeatures" +documenttype[1].datatype[08].sstruct.field[setfield].datatype 1328286588 +documenttype[1].datatype[08].sstruct.field[setfield].id 761581914 +documenttype[1].datatype[08].sstruct.field[setfield].id_v6 1762943268 +documenttype[1].datatype[08].sstruct.field[setfield].name "setfield" +documenttype[1].datatype[08].sstruct.field[setfield2].datatype 18 +documenttype[1].datatype[08].sstruct.field[setfield2].id 1066659198 +documenttype[1].datatype[08].sstruct.field[setfield2].id_v6 813038565 +documenttype[1].datatype[08].sstruct.field[setfield2].name "setfield2" +documenttype[1].datatype[08].sstruct.field[setfield3].datatype 2125328771 +documenttype[1].datatype[08].sstruct.field[setfield3].id 1180155772 +documenttype[1].datatype[08].sstruct.field[setfield3].id_v6 1697232199 +documenttype[1].datatype[08].sstruct.field[setfield3].name "setfield3" +documenttype[1].datatype[08].sstruct.field[setfield4].datatype 2065577986 +documenttype[1].datatype[08].sstruct.field[setfield4].id 1254131631 +documenttype[1].datatype[08].sstruct.field[setfield4].id_v6 119755202 +documenttype[1].datatype[08].sstruct.field[setfield4].name "setfield4" +documenttype[1].datatype[08].sstruct.field[stringmapfield].datatype 339965458 +documenttype[1].datatype[08].sstruct.field[stringmapfield].id 117465687 +documenttype[1].datatype[08].sstruct.field[stringmapfield].id_v6 1492788095 +documenttype[1].datatype[08].sstruct.field[stringmapfield].name "stringmapfield" +documenttype[1].datatype[08].sstruct.field[summaryfeatures].datatype 2 +documenttype[1].datatype[08].sstruct.field[summaryfeatures].id 1840337115 +documenttype[1].datatype[08].sstruct.field[summaryfeatures].id_v6 1981648971 +documenttype[1].datatype[08].sstruct.field[summaryfeatures].name "summaryfeatures" +documenttype[1].datatype[08].sstruct.field[tagfield].datatype 18 +documenttype[1].datatype[08].sstruct.field[tagfield].id 1653562069 +documenttype[1].datatype[08].sstruct.field[tagfield].id_v6 938523246 +documenttype[1].datatype[08].sstruct.field[tagfield].name "tagfield" +documenttype[1].datatype[09].id 49942803 +documenttype[1].datatype[09].type ARRAY +documenttype[1].datatype[09].array.element.id 16 +documenttype[1].datatype[10].id -2092985853 +documenttype[1].datatype[10].type STRUCT +documenttype[1].datatype[10].sstruct.name "mystruct" +documenttype[1].datatype[10].sstruct.version 0 +documenttype[1].datatype[10].sstruct.field[4] +documenttype[1].datatype[10].sstruct.field[bytearr].datatype 49942803 +documenttype[1].datatype[10].sstruct.field[bytearr].id 1079701754 +documenttype[1].datatype[10].sstruct.field[bytearr].id_v6 1198855694 +documenttype[1].datatype[10].sstruct.field[bytearr].name "bytearr" +documenttype[1].datatype[10].sstruct.field[mymap].datatype 339965458 +documenttype[1].datatype[10].sstruct.field[mymap].id 1954178122 +documenttype[1].datatype[10].sstruct.field[mymap].id_v6 707189723 +documenttype[1].datatype[10].sstruct.field[mymap].name "mymap" +documenttype[1].datatype[10].sstruct.field[structfield].datatype 2 +documenttype[1].datatype[10].sstruct.field[structfield].id 1726890940 +documenttype[1].datatype[10].sstruct.field[structfield].id_v6 418303145 +documenttype[1].datatype[10].sstruct.field[structfield].name "structfield" +documenttype[1].datatype[10].sstruct.field[title].datatype 2 +documenttype[1].datatype[10].sstruct.field[title].id 567626448 +documenttype[1].datatype[10].sstruct.field[title].id_v6 29129762 +documenttype[1].datatype[10].sstruct.field[title].name "title" +documenttype[1].datatype[11].id -1408707420 +documenttype[1].datatype[11].type STRUCT +documenttype[1].datatype[11].sstruct.name "types_search.body" +documenttype[1].datatype[11].sstruct.version 0 +documenttype[1].inherits[1] +documenttype[1].inherits[document.0].id 8 diff --git a/document/src/tests/serialization/.gitignore b/document/src/tests/serialization/.gitignore new file mode 100644 index 00000000000..9f5bc440533 --- /dev/null +++ b/document/src/tests/serialization/.gitignore @@ -0,0 +1,6 @@ +*_test +.depend +Makefile +document_annotationserializer_test_app +document_compression_test_app +document_vespadocumentserializer_test_app diff --git a/document/src/tests/serialization/CMakeLists.txt b/document/src/tests/serialization/CMakeLists.txt new file mode 100644 index 00000000000..dcbf067e4a5 --- /dev/null +++ b/document/src/tests/serialization/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_vespadocumentserializer_test_app + SOURCES + vespadocumentserializer_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_vespadocumentserializer_test_app COMMAND document_vespadocumentserializer_test_app) +vespa_add_executable(document_annotationserializer_test_app + SOURCES + annotationserializer_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_annotationserializer_test_app COMMAND document_annotationserializer_test_app) +vespa_add_executable(document_compression_test_app + SOURCES + compression_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_compression_test_app COMMAND document_compression_test_app) diff --git a/document/src/tests/serialization/annotation.serialize.test.cfg b/document/src/tests/serialization/annotation.serialize.test.cfg new file mode 100644 index 00000000000..a8a79ddc6f2 --- /dev/null +++ b/document/src/tests/serialization/annotation.serialize.test.cfg @@ -0,0 +1,77 @@ +enablecompression false +datatype[5] +datatype[0].id 10001 +datatype[0].arraytype[0] +datatype[0].weightedsettype[0] +datatype[0].structtype[1] +datatype[0].structtype[0].name "empty" +datatype[0].structtype[0].version 0 +datatype[0].structtype[0].field[0] +datatype[0].structtype[0].inherits[0] +datatype[0].documenttype[0] +datatype[0].annotationreftype[0] +datatype[1].id 10012 +datatype[1].arraytype[0] +datatype[1].weightedsettype[0] +datatype[1].structtype[1] +datatype[1].structtype[0].name "myposition" +datatype[1].structtype[0].version 0 +datatype[1].structtype[0].field[2] +datatype[1].structtype[0].field[0].name "latitude" +datatype[1].structtype[0].field[0].datatype 4 +datatype[1].structtype[0].field[0].id[0] +datatype[1].structtype[0].field[1].name "longitude" +datatype[1].structtype[0].field[1].datatype 4 +datatype[1].structtype[0].field[1].id[0] +datatype[1].structtype[0].inherits[0] +datatype[1].documenttype[0] +datatype[1].annotationreftype[0] +datatype[2].id 10022 +datatype[2].arraytype[0] +datatype[2].weightedsettype[0] +datatype[2].structtype[0] +datatype[2].documenttype[0] +datatype[2].annotationreftype[1] +datatype[2].annotationreftype[0].annotation "text" +datatype[3].id 10032 +datatype[3].arraytype[1] +datatype[3].arraytype[0].datatype 10022 +datatype[3].weightedsettype[0] +datatype[3].structtype[0] +datatype[3].documenttype[0] +datatype[3].annotationreftype[0] +datatype[4].id 10002 +datatype[4].arraytype[0] +datatype[4].weightedsettype[0] +datatype[4].structtype[1] +datatype[4].structtype[0].name "annotation.city" +datatype[4].structtype[0].version 0 +datatype[4].structtype[0].field[2] +datatype[4].structtype[0].field[0].name "position" +datatype[4].structtype[0].field[0].datatype 10012 +datatype[4].structtype[0].field[0].id[0] +datatype[4].structtype[0].field[1].name "references" +datatype[4].structtype[0].field[1].datatype 10032 +datatype[4].structtype[0].field[1].id[0] +datatype[4].structtype[0].inherits[0] +datatype[4].documenttype[0] +datatype[4].annotationreftype[0] +annotationtype[6] +annotationtype[0].name "text" +annotationtype[0].id 20000 +annotationtype[0].datatype 10001 +annotationtype[1].name "begintag" +annotationtype[1].id 20001 +annotationtype[1].datatype 10001 +annotationtype[2].name "endtag" +annotationtype[2].id 20002 +annotationtype[2].datatype 10001 +annotationtype[3].name "body" +annotationtype[3].id 20003 +annotationtype[3].datatype 10001 +annotationtype[4].name "paragraph" +annotationtype[4].id 20004 +annotationtype[4].datatype 10001 +annotationtype[5].name "city" +annotationtype[5].id 20005 +annotationtype[5].datatype 10002 diff --git a/document/src/tests/serialization/annotation.serialize.test.repo.cfg b/document/src/tests/serialization/annotation.serialize.test.repo.cfg new file mode 100644 index 00000000000..d7fa447fa34 --- /dev/null +++ b/document/src/tests/serialization/annotation.serialize.test.repo.cfg @@ -0,0 +1,130 @@ +enablecompression false +documenttype[1] +documenttype[0].id 1000 +documenttype[0].name "my_document" +documenttype[0].version 0 +documenttype[0].headerstruct -284186494 +documenttype[0].bodystruct -2039820521 +documenttype[0].inherits[0] +documenttype[0].datatype[5] +documenttype[0].datatype[0].id 10001 +documenttype[0].datatype[0].type STRUCT +documenttype[0].datatype[0].array.element.id 0 +documenttype[0].datatype[0].map.key.id 0 +documenttype[0].datatype[0].map.value.id 0 +documenttype[0].datatype[0].wset.key.id 0 +documenttype[0].datatype[0].wset.createifnonexistent false +documenttype[0].datatype[0].wset.removeifzero false +documenttype[0].datatype[0].annotationref.annotation.id 0 +documenttype[0].datatype[0].sstruct.name "empty" +documenttype[0].datatype[0].sstruct.version 0 +documenttype[0].datatype[0].sstruct.compression.type NONE +documenttype[0].datatype[0].sstruct.compression.level 0 +documenttype[0].datatype[0].sstruct.compression.threshold 90 +documenttype[0].datatype[0].sstruct.compression.minsize 0 +documenttype[0].datatype[0].sstruct.field[0] +documenttype[0].datatype[1].id 10012 +documenttype[0].datatype[1].type STRUCT +documenttype[0].datatype[1].array.element.id 0 +documenttype[0].datatype[1].map.key.id 0 +documenttype[0].datatype[1].map.value.id 0 +documenttype[0].datatype[1].wset.key.id 0 +documenttype[0].datatype[1].wset.createifnonexistent false +documenttype[0].datatype[1].wset.removeifzero false +documenttype[0].datatype[1].annotationref.annotation.id 0 +documenttype[0].datatype[1].sstruct.name "myposition" +documenttype[0].datatype[1].sstruct.version 0 +documenttype[0].datatype[1].sstruct.compression.type NONE +documenttype[0].datatype[1].sstruct.compression.level 0 +documenttype[0].datatype[1].sstruct.compression.threshold 90 +documenttype[0].datatype[1].sstruct.compression.minsize 0 +documenttype[0].datatype[1].sstruct.field[2] +documenttype[0].datatype[1].sstruct.field[0].name "latitude" +documenttype[0].datatype[1].sstruct.field[0].id 945500241 +documenttype[0].datatype[1].sstruct.field[0].id_v6 1437005468 +documenttype[0].datatype[1].sstruct.field[0].datatype 4 +documenttype[0].datatype[1].sstruct.field[1].name "longitude" +documenttype[0].datatype[1].sstruct.field[1].id 1458007305 +documenttype[0].datatype[1].sstruct.field[1].id_v6 1517975127 +documenttype[0].datatype[1].sstruct.field[1].datatype 4 +documenttype[0].datatype[2].id 10022 +documenttype[0].datatype[2].type ANNOTATIONREF +documenttype[0].datatype[2].array.element.id 0 +documenttype[0].datatype[2].map.key.id 0 +documenttype[0].datatype[2].map.value.id 0 +documenttype[0].datatype[2].wset.key.id 0 +documenttype[0].datatype[2].wset.createifnonexistent false +documenttype[0].datatype[2].wset.removeifzero false +documenttype[0].datatype[2].annotationref.annotation.id 20000 +documenttype[0].datatype[2].sstruct.name "" +documenttype[0].datatype[2].sstruct.version 0 +documenttype[0].datatype[2].sstruct.compression.type NONE +documenttype[0].datatype[2].sstruct.compression.level 0 +documenttype[0].datatype[2].sstruct.compression.threshold 95 +documenttype[0].datatype[2].sstruct.compression.minsize 200 +documenttype[0].datatype[2].sstruct.field[0] +documenttype[0].datatype[3].id 10032 +documenttype[0].datatype[3].type ARRAY +documenttype[0].datatype[3].array.element.id 10022 +documenttype[0].datatype[3].map.key.id 0 +documenttype[0].datatype[3].map.value.id 0 +documenttype[0].datatype[3].wset.key.id 0 +documenttype[0].datatype[3].wset.createifnonexistent false +documenttype[0].datatype[3].wset.removeifzero false +documenttype[0].datatype[3].annotationref.annotation.id 0 +documenttype[0].datatype[3].sstruct.name "" +documenttype[0].datatype[3].sstruct.version 0 +documenttype[0].datatype[3].sstruct.compression.type NONE +documenttype[0].datatype[3].sstruct.compression.level 0 +documenttype[0].datatype[3].sstruct.compression.threshold 95 +documenttype[0].datatype[3].sstruct.compression.minsize 200 +documenttype[0].datatype[3].sstruct.field[0] +documenttype[0].datatype[4].id 10002 +documenttype[0].datatype[4].type STRUCT +documenttype[0].datatype[4].array.element.id 0 +documenttype[0].datatype[4].map.key.id 0 +documenttype[0].datatype[4].map.value.id 0 +documenttype[0].datatype[4].wset.key.id 0 +documenttype[0].datatype[4].wset.createifnonexistent false +documenttype[0].datatype[4].wset.removeifzero false +documenttype[0].datatype[4].annotationref.annotation.id 0 +documenttype[0].datatype[4].sstruct.name "annotation.city" +documenttype[0].datatype[4].sstruct.version 0 +documenttype[0].datatype[4].sstruct.compression.type NONE +documenttype[0].datatype[4].sstruct.compression.level 0 +documenttype[0].datatype[4].sstruct.compression.threshold 90 +documenttype[0].datatype[4].sstruct.compression.minsize 0 +documenttype[0].datatype[4].sstruct.field[2] +documenttype[0].datatype[4].sstruct.field[0].name "position" +documenttype[0].datatype[4].sstruct.field[0].id 818217702 +documenttype[0].datatype[4].sstruct.field[0].id_v6 632310942 +documenttype[0].datatype[4].sstruct.field[0].datatype 10012 +documenttype[0].datatype[4].sstruct.field[1].name "references" +documenttype[0].datatype[4].sstruct.field[1].id 1581548957 +documenttype[0].datatype[4].sstruct.field[1].id_v6 839497794 +documenttype[0].datatype[4].sstruct.field[1].datatype 10032 +documenttype[0].annotationtype[6] +documenttype[0].annotationtype[0].id 20000 +documenttype[0].annotationtype[0].name "text" +documenttype[0].annotationtype[0].datatype 10001 +documenttype[0].annotationtype[0].inherits[0] +documenttype[0].annotationtype[1].id 20001 +documenttype[0].annotationtype[1].name "begintag" +documenttype[0].annotationtype[1].datatype 10001 +documenttype[0].annotationtype[1].inherits[0] +documenttype[0].annotationtype[2].id 20002 +documenttype[0].annotationtype[2].name "endtag" +documenttype[0].annotationtype[2].datatype 10001 +documenttype[0].annotationtype[2].inherits[0] +documenttype[0].annotationtype[3].id 20003 +documenttype[0].annotationtype[3].name "body" +documenttype[0].annotationtype[3].datatype 10001 +documenttype[0].annotationtype[3].inherits[0] +documenttype[0].annotationtype[4].id 20004 +documenttype[0].annotationtype[4].name "paragraph" +documenttype[0].annotationtype[4].datatype 10001 +documenttype[0].annotationtype[4].inherits[0] +documenttype[0].annotationtype[5].id 20005 +documenttype[0].annotationtype[5].name "city" +documenttype[0].annotationtype[5].datatype 10002 +documenttype[0].annotationtype[5].inherits[0] diff --git a/document/src/tests/serialization/annotationserializer_test.cpp b/document/src/tests/serialization/annotationserializer_test.cpp new file mode 100644 index 00000000000..1cc3fc19373 --- /dev/null +++ b/document/src/tests/serialization/annotationserializer_test.cpp @@ -0,0 +1,274 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for annotation serialization. + +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("annotationserializer_test"); + +#include <stdlib.h> +#include <vespa/document/annotation/alternatespanlist.h> +#include <vespa/document/annotation/annotation.h> +#include <vespa/document/annotation/span.h> +#include <vespa/document/annotation/spanlist.h> +#include <vespa/document/annotation/spantree.h> +#include <vespa/document/config/config-documenttypes.h> +#include <vespa/document/fieldvalue/stringfieldvalue.h> +#include <vespa/document/repo/fixedtyperepo.h> +#include <vespa/document/serialization/annotationdeserializer.h> +#include <vespa/document/serialization/annotationserializer.h> +#include <vespa/document/serialization/vespadocumentdeserializer.h> +#include <vespa/document/serialization/vespadocumentserializer.h> +#include <vespa/vespalib/objects/nbostream.h> +#include <vespa/vespalib/testkit/testapp.h> +#include <fstream> +#include <sstream> +#include <vector> + +using document::DocumenttypesConfig; +using std::fstream; +using std::ostringstream; +using std::string; +using std::vector; +using vespalib::nbostream; +using namespace document; + +namespace { + +class Test : public vespalib::TestApp { + StringFieldValue::SpanTrees readSpanTree(const string &file_name, const FixedTypeRepo &repo); + + void requireThatSimpleSpanTreeIsDeserialized(); + void requireThatAdvancedSpanTreeIsDeserialized(); + void requireThatSpanTreeCanBeSerialized(); + void requireThatUnknownAnnotationIsSkipped(); + +public: + int Main(); +}; + +int +Test::Main() +{ + if (getenv("TEST_SUBSET") != 0) { return 0; } + TEST_INIT("annotationserializer_test"); + TEST_DO(requireThatSimpleSpanTreeIsDeserialized()); + TEST_DO(requireThatAdvancedSpanTreeIsDeserialized()); + TEST_DO(requireThatSpanTreeCanBeSerialized()); + TEST_DO(requireThatUnknownAnnotationIsSkipped()); + + TEST_DONE(); +} + +template <typename T, int N> int arraysize(const T (&)[N]) { return N; } + +StringFieldValue::SpanTrees +Test::readSpanTree(const string &file_name, const FixedTypeRepo &repo) { + FastOS_File file(file_name.c_str()); + ASSERT_TRUE(file.OpenReadOnlyExisting()); + char buffer[1024]; + ssize_t size = file.Read(buffer, arraysize(buffer)); + ASSERT_TRUE(size != -1); + + nbostream stream(buffer, size); + VespaDocumentDeserializer deserializer(repo, stream, 8); + StringFieldValue value; + deserializer.read(value); + + EXPECT_EQUAL(0u, stream.size()); + ASSERT_TRUE(value.hasSpanTrees()); + return value.getSpanTrees(); +} + +void Test::requireThatSimpleSpanTreeIsDeserialized() { + DocumentTypeRepo type_repo(readDocumenttypesConfig("annotation.serialize.test.repo.cfg")); + FixedTypeRepo repo(type_repo); + SpanTree::UP span_tree = std::move(readSpanTree("test_data_serialized_simple", repo).front()); + + EXPECT_EQUAL("html", span_tree->getName()); + const SimpleSpanList *root = dynamic_cast<const SimpleSpanList *>(&span_tree->getRoot()); + ASSERT_TRUE(root); + EXPECT_EQUAL(5u, root->size()); + SimpleSpanList::const_iterator it = root->begin(); + EXPECT_EQUAL(Span(0, 19), (*it++)); + EXPECT_EQUAL(Span(19, 5), (*it++)); + EXPECT_EQUAL(Span(24, 21), (*it++)); + EXPECT_EQUAL(Span(45, 23), (*it++)); + EXPECT_EQUAL(Span(68, 14), (*it++)); + EXPECT_TRUE(it == root->end()); +} + +struct AnnotationComparator { + vector<string> expect; + vector<string> actual; + template <typename ITR> + AnnotationComparator &addActual(ITR pos, ITR end) { + for (; pos != end; ++pos) { + ostringstream ost; + pos->print(ost, true, ""); + actual.push_back(ost.str()); + } + return *this; + } + AnnotationComparator &addExpected(const string &e) { + expect.push_back(e); + return *this; + } + void compare() { + std::sort(expect.begin(), expect.end()); + std::sort(actual.begin(), actual.end()); + EXPECT_EQUAL(expect.size(), actual.size()); + for (size_t i = 0; i < expect.size() && i < actual.size(); ++i) { + EXPECT_EQUAL(expect[i].size(), actual[i].size()); + EXPECT_EQUAL(expect[i], actual[i]); + } + } +}; + +void Test::requireThatAdvancedSpanTreeIsDeserialized() { + DocumentTypeRepo type_repo( + readDocumenttypesConfig("annotation.serialize.test.repo.cfg")); + FixedTypeRepo repo(type_repo, "my_document"); + SpanTree::UP span_tree = std::move(readSpanTree("test_data_serialized_advanced", repo).front()); + + EXPECT_EQUAL("html", span_tree->getName()); + const SpanList *root = dynamic_cast<const SpanList *>(&span_tree->getRoot()); + ASSERT_TRUE(root); + EXPECT_EQUAL(4u, root->size()); + SpanList::const_iterator it = root->begin(); + EXPECT_EQUAL(Span(0, 6), *(static_cast<Span *>(*it++))); + AlternateSpanList *alt_list = dynamic_cast<AlternateSpanList *>(*it++); + EXPECT_EQUAL(Span(27, 9), *(static_cast<Span *>(*it++))); + EXPECT_EQUAL(Span(36, 8), *(static_cast<Span *>(*it++))); + EXPECT_TRUE(it == root->end()); + + ASSERT_TRUE(alt_list); + EXPECT_EQUAL(2u, alt_list->getNumSubtrees()); + EXPECT_EQUAL(0.9, alt_list->getProbability(0)); + EXPECT_EQUAL(0.1, alt_list->getProbability(1)); + EXPECT_EQUAL(4u, alt_list->getSubtree(0).size()); + it = alt_list->getSubtree(0).begin(); + EXPECT_EQUAL(Span(6, 3), *(static_cast<Span *>(*it++))); + EXPECT_EQUAL(Span(9, 10), *(static_cast<Span *>(*it++))); + EXPECT_EQUAL(Span(19, 4), *(static_cast<Span *>(*it++))); + EXPECT_EQUAL(Span(23, 4), *(static_cast<Span *>(*it++))); + EXPECT_TRUE(it == alt_list->getSubtree(0).end()); + EXPECT_EQUAL(2u, alt_list->getSubtree(1).size()); + it = alt_list->getSubtree(1).begin(); + EXPECT_EQUAL(Span(6, 13), *(static_cast<Span *>(*it++))); + EXPECT_EQUAL(Span(19, 8), *(static_cast<Span *>(*it++))); + EXPECT_TRUE(it == alt_list->getSubtree(1).end()); + + EXPECT_EQUAL(12u, span_tree->numAnnotations()); + + AnnotationComparator comparator; + comparator.addActual(span_tree->begin(), span_tree->end()) + .addExpected("Annotation(AnnotationType(20001, begintag)\n" + " Span(6, 3))") + .addExpected("Annotation(AnnotationType(20000, text)\n" + " Span(9, 10))") + .addExpected("Annotation(AnnotationType(20000, text)\n" + " Span(19, 4))") + .addExpected("Annotation(AnnotationType(20002, endtag)\n" + " Span(23, 4))") + .addExpected("Annotation(AnnotationType(20000, text)\n" + " Span(6, 13))") + .addExpected("Annotation(AnnotationType(20003, body)\n" + " Span(19, 8))") + .addExpected("Annotation(AnnotationType(20004, paragraph)\n" + " AlternateSpanList(\n" + " Probability 0.9 : SpanList(\n" + " Span(6, 3)\n" + " Span(9, 10)\n" + " Span(19, 4)\n" + " Span(23, 4)),\n" + " Probability 0.1 : SpanList(\n" + " Span(6, 13)\n" + " Span(19, 8))))") + .addExpected("Annotation(AnnotationType(20001, begintag)\n" + " Span(0, 6))") + .addExpected("Annotation(AnnotationType(20000, text)\n" + " Span(27, 9))") + .addExpected("Annotation(AnnotationType(20002, endtag)\n" + " Span(36, 8))") + .addExpected("Annotation(AnnotationType(20003, body)\n" + " SpanList(\n" + " Span(0, 6)\n" + " AlternateSpanList(\n" + " Probability 0.9 : SpanList(\n" + " Span(6, 3)\n" + " Span(9, 10)\n" + " Span(19, 4)\n" + " Span(23, 4)),\n" + " Probability 0.1 : SpanList(\n" + " Span(6, 13)\n" + " Span(19, 8)))\n" + " Span(27, 9)\n" + " Span(36, 8)))") + .addExpected("Annotation(AnnotationType(20005, city)\n" + " Struct annotation.city(\n" + " position - Struct myposition(\n" + " latitude - 37,\n" + " longitude - -122\n" + " ),\n" + " references - Array(size: 2,\n" + " AnnotationReferenceFieldValue(n),\n" + " AnnotationReferenceFieldValue(n)\n" + " )\n" + " ))"); + TEST_DO(comparator.compare()); +} + +void Test::requireThatSpanTreeCanBeSerialized() { + DocumentTypeRepo type_repo( + readDocumenttypesConfig("annotation.serialize.test.repo.cfg")); + FixedTypeRepo repo(type_repo, "my_document"); + string file_name = "test_data_serialized_advanced"; + + FastOS_File file(file_name.c_str()); + ASSERT_TRUE(file.OpenReadOnlyExisting()); + char buffer[1024]; + ssize_t size = file.Read(buffer, arraysize(buffer)); + ASSERT_TRUE(size != -1); + + nbostream stream(buffer, size); + VespaDocumentDeserializer deserializer(repo, stream, 8); + StringFieldValue value; + deserializer.read(value); + + SpanTree::UP span_tree = std::move(value.getSpanTrees().front()); + EXPECT_EQUAL("html", span_tree->getName()); + EXPECT_EQUAL(0u, stream.size()); + + stream.clear(); + VespaDocumentSerializer serializer(stream); + serializer.write(value); + EXPECT_EQUAL(size, static_cast<ssize_t>(stream.size())); + int diff_count = 0; + for (size_t i = 0; i < stream.size(); ++i) { + if (buffer[i] != stream.peek()[i]) { + ++diff_count; + } + EXPECT_EQUAL((int) buffer[i], (int) stream.peek()[i]); + } + EXPECT_EQUAL(0, diff_count); +} + +void Test::requireThatUnknownAnnotationIsSkipped() { + AnnotationType type(42, "my type"); + Annotation annotation(type, FieldValue::UP(new StringFieldValue("foo"))); + nbostream stream; + AnnotationSerializer serializer(stream); + serializer.write(annotation); + + DocumentTypeRepo type_repo; // Doesn't know any annotation types. + FixedTypeRepo repo(type_repo); + AnnotationDeserializer deserializer(repo, stream, 8); + Annotation a; + deserializer.readAnnotation(a); + EXPECT_FALSE(a.valid()); + EXPECT_EQUAL(0u, stream.size()); +} + +} // namespace + +TEST_APPHOOK(Test); diff --git a/document/src/tests/serialization/compression_test.cpp b/document/src/tests/serialization/compression_test.cpp new file mode 100644 index 00000000000..01e19ae946b --- /dev/null +++ b/document/src/tests/serialization/compression_test.cpp @@ -0,0 +1,30 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for annotation serialization. + +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +#include <vespa/vespalib/testkit/test_kit.h> +#include <vespa/vespalib/stllike/string.h> +#include <vespa/document/util/compressor.h> + +LOG_SETUP("compression_test"); + +using namespace document; +using namespace vespalib; + +static vespalib::string _G_compressableText("AAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEE" + "AAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEE" + "AAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEE" + "AAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEE" + "AAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEE"); + +TEST("requireThatLZ4CompressFine") { + CompressionConfig cfg(CompressionConfig::Type::LZ4); + ConstBufferRef ref(_G_compressableText.c_str(), _G_compressableText.size()); + DataBuffer compressed; + EXPECT_EQUAL(CompressionConfig::Type::LZ4, compress(cfg, ref, compressed, false)); +} + +TEST_MAIN() { + TEST_RUN_ALL(); +} diff --git a/document/src/tests/serialization/test_data_serialized_advanced b/document/src/tests/serialization/test_data_serialized_advanced Binary files differnew file mode 100644 index 00000000000..d5d6ca6022a --- /dev/null +++ b/document/src/tests/serialization/test_data_serialized_advanced diff --git a/document/src/tests/serialization/test_data_serialized_simple b/document/src/tests/serialization/test_data_serialized_simple Binary files differnew file mode 100644 index 00000000000..ae65a2eefb4 --- /dev/null +++ b/document/src/tests/serialization/test_data_serialized_simple diff --git a/document/src/tests/serialization/vespadocumentserializer_test.cpp b/document/src/tests/serialization/vespadocumentserializer_test.cpp new file mode 100644 index 00000000000..0043548a906 --- /dev/null +++ b/document/src/tests/serialization/vespadocumentserializer_test.cpp @@ -0,0 +1,828 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for vespadocumentserializer. + +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("vespadocumentserializer_test"); + +#include <vespa/document/annotation/annotation.h> +#include <vespa/document/annotation/span.h> +#include <vespa/document/annotation/spantree.h> +#include <vespa/document/config/config-documenttypes.h> +#include <vespa/document/datatype/annotationreferencedatatype.h> +#include <vespa/document/datatype/arraydatatype.h> +#include <vespa/document/datatype/documenttype.h> +#include <vespa/document/fieldvalue/annotationreferencefieldvalue.h> +#include <vespa/document/fieldvalue/arrayfieldvalue.h> +#include <vespa/document/fieldvalue/bytefieldvalue.h> +#include <vespa/document/fieldvalue/document.h> +#include <vespa/document/fieldvalue/doublefieldvalue.h> +#include <vespa/document/fieldvalue/floatfieldvalue.h> +#include <vespa/document/fieldvalue/intfieldvalue.h> +#include <vespa/document/fieldvalue/longfieldvalue.h> +#include <vespa/document/fieldvalue/mapfieldvalue.h> +#include <vespa/document/fieldvalue/predicatefieldvalue.h> +#include <vespa/document/fieldvalue/rawfieldvalue.h> +#include <vespa/document/fieldvalue/shortfieldvalue.h> +#include <vespa/document/fieldvalue/stringfieldvalue.h> +#include <vespa/document/fieldvalue/structfieldvalue.h> +#include <vespa/document/fieldvalue/weightedsetfieldvalue.h> +#include <vespa/document/fieldvalue/tensorfieldvalue.h> +#include <vespa/document/predicate/predicate.h> +#include <vespa/document/predicate/predicate_slime_builder.h> +#include <vespa/document/repo/configbuilder.h> +#include <vespa/document/repo/fixedtyperepo.h> +#include <vespa/document/serialization/util.h> +#include <vespa/document/serialization/vespadocumentdeserializer.h> +#include <vespa/document/serialization/vespadocumentserializer.h> +#include <vespa/document/serialization/annotationserializer.h> +#include <vespa/vespalib/tensor/types.h> +#include <vespa/vespalib/tensor/tensor_builder.h> +#include <vespa/vespalib/tensor/tensor.h> +#include <vespa/vespalib/tensor/default_tensor.h> +#include <vespa/vespalib/tensor/tensor_factory.h> +#include <stdlib.h> +#include <vespa/vespalib/data/slime/cursor.h> +#include <vespa/vespalib/data/slime/slime.h> +#include <vespa/vespalib/io/fileutil.h> +#include <vespa/vespalib/objects/nbostream.h> +#include <vespa/vespalib/testkit/testapp.h> + +using document::DocumenttypesConfig; +using vespalib::File; +using vespalib::Slime; +using vespalib::nbostream; +using vespalib::slime::Cursor; +using vespalib::tensor::Tensor; +using vespalib::tensor::TensorBuilder; +using vespalib::tensor::TensorCells; +using vespalib::tensor::TensorDimensions; +using namespace document; +using std::string; +using std::vector; +using namespace document::config_builder; + +namespace { + +const int doc_type_id = 1234; +const string doc_name = "my document"; +const int body_id = 94; +const int inner_type_id = 95; +const int outer_type_id = 96; +const string type_name = "outer doc"; +const string inner_name = "inner doc"; +const int a_id = 12345; +const string a_name = "annotation"; +const int predicate_doc_type_id = 321; +const string predicate_field_name = "my_predicate"; + +DocumenttypesConfig getDocTypesConfig() { + DocumenttypesConfigBuilderHelper builder; + builder.document(doc_type_id, doc_name, + Struct("my document.header") + .addField("header field", DataType::T_INT), + Struct("my document.body") + .addField("body field", DataType::T_STRING)) + .annotationType(42, "foo_type", DataType::T_INT); + builder.document(inner_type_id, inner_name, + Struct(inner_name + ".header"), + Struct(inner_name + ".body") + .addField("str", DataType::T_STRING)) + .annotationType(a_id, a_name, DataType::T_STRING); + builder.document(outer_type_id, type_name, + Struct(type_name + ".header"), + Struct(type_name + ".body") + .addField(inner_name, inner_type_id).setId(body_id)); + builder.document(predicate_doc_type_id, "my_type", + Struct("my_type.header"), + Struct("my_type.body") + .addField(predicate_field_name, DataType::T_PREDICATE)); + return builder.config(); +} + +const DocumentTypeRepo doc_repo(getDocTypesConfig()); +const FixedTypeRepo repo(doc_repo, *doc_repo.getDocumentType(doc_type_id)); + +template <typename T> T newFieldValue(const T&) { return T(); } +template <> ArrayFieldValue newFieldValue(const ArrayFieldValue &value) +{ return ArrayFieldValue(*value.getDataType()); } +template <> MapFieldValue newFieldValue(const MapFieldValue &value) +{ return MapFieldValue(*value.getDataType()); } +template <> WeightedSetFieldValue newFieldValue( + const WeightedSetFieldValue &value) +{ return WeightedSetFieldValue(*value.getDataType()); } +template <> StructFieldValue newFieldValue(const StructFieldValue &value) +{ return StructFieldValue(*value.getDataType()); } +template <> AnnotationReferenceFieldValue newFieldValue( + const AnnotationReferenceFieldValue &value) +{ return AnnotationReferenceFieldValue(*value.getDataType()); } + +template<typename T> +void testDeserializeAndClone(const T& value, const nbostream &stream, bool checkEqual=true) { + T read_value = newFieldValue(value); + vespalib::MallocPtr buf(stream.size()); + memcpy(buf.str(), stream.peek(), stream.size()); + nbostream is(buf.c_str(), buf.size(), true); + VespaDocumentDeserializer deserializer(repo, is, 8); + deserializer.read(read_value); + + EXPECT_EQUAL(0u, is.size()); + if (checkEqual) { + EXPECT_EQUAL(value, read_value); + } + T clone(read_value); + buf.reset(); + if (checkEqual) { + EXPECT_EQUAL(value, clone); + } +} + +// Leaves the stream's read position at the start of the serialized object. +template<typename T> +void serializeAndDeserialize(const T& value, nbostream &stream, bool checkEqual=true) { + size_t start_size = stream.size(); + VespaDocumentSerializer serializer(stream); + serializer.write(value); + size_t serialized_size = stream.size() - start_size; + + testDeserializeAndClone(value, stream, checkEqual); + T read_value = newFieldValue(value); + + VespaDocumentDeserializer deserializer(repo, stream, 8); + deserializer.read(read_value); + + EXPECT_EQUAL(0u, stream.size()); + if (checkEqual) { + EXPECT_EQUAL(value, read_value); + } + stream.adjustReadPos(-serialized_size); +} + +template <typename T> struct ValueType { typedef typename T::Number Type; }; +template <> struct ValueType<IntFieldValue> { typedef uint32_t Type; }; +template <> struct ValueType<LongFieldValue> { typedef uint64_t Type; }; + +template<typename T> +void serializeAndDeserializeNumber(const T& value) { + const typename ValueType<T>::Type val = value.getValue(); + nbostream stream; + serializeAndDeserialize(value, stream); + + typename ValueType<T>::Type read_val; + stream >> read_val; + EXPECT_EQUAL(val, read_val); +} + +TEST("requireThatPrimitiveTypeFieldValueCanBeSerialized") { + TEST_DO(serializeAndDeserializeNumber(ByteFieldValue(42))); + TEST_DO(serializeAndDeserializeNumber(ShortFieldValue(0x1234))); + TEST_DO(serializeAndDeserializeNumber(IntFieldValue(0x12345678))); + TEST_DO(serializeAndDeserializeNumber(DoubleFieldValue(34567890.123456))); + TEST_DO(serializeAndDeserializeNumber(FloatFieldValue(3456.1234f))); + TEST_DO(serializeAndDeserializeNumber(LongFieldValue(0x12345678123456LL))); +} + +template <typename SizeType> +void checkLiteralFieldValue(nbostream &stream, const string &val) { + uint8_t read_coding; + SizeType size; + stream >> read_coding >> size; + EXPECT_EQUAL(0, read_coding); + size &= (SizeType(-1) >> 1); // Clear MSB. + vespalib::string read_val; + read_val.assign(stream.peek(), size); + stream.adjustReadPos(size); + EXPECT_EQUAL(val.size(), read_val.size()); + EXPECT_EQUAL(val, read_val); +} + +template <typename SizeType> +void checkStringFieldValue(const string &val) { + StringFieldValue value(val); + nbostream stream; + serializeAndDeserialize(value, stream); + + string val_with_null(val.c_str(), val.size() + 1); + checkLiteralFieldValue<SizeType>(stream, val_with_null); +} + +void setSpanTree(StringFieldValue & str, const SpanTree & tree) { + nbostream os; + AnnotationSerializer serializer(os); + serializer.write(tree); + str.setSpanTrees(vespalib::ConstBufferRef(os.peek(), os.size()), repo, VespaDocumentSerializer::getCurrentVersion(), false); +} + +void checkStringFieldValueWithAnnotation() { + StringFieldValue value("foo"); + Span::UP root(new Span(2, 3)); + SpanTree::UP tree(new SpanTree("test", std::move(root))); + AnnotationType annotation_type(42, "foo_type"); + tree->annotate(tree->getRoot(), annotation_type); + + nbostream os; + AnnotationSerializer serializer(os); + serializer.write(*tree); + + value.setSpanTrees(vespalib::ConstBufferRef(os.peek(), os.size()), repo, VespaDocumentSerializer::getCurrentVersion(), true); + nbostream stream; + serializeAndDeserialize(value, stream); +} + +TEST("requireThatStringFieldValueCanBeSerialized") { + TEST_DO(checkStringFieldValue<uint8_t>("foo bar baz")); + TEST_DO(checkStringFieldValue<uint32_t>(string(200, 'a'))); + TEST_DO(checkStringFieldValueWithAnnotation()); +} + +template <typename SizeType> +void checkRawFieldValue(const string &val) { + RawFieldValue value(val); + nbostream stream; + serializeAndDeserialize(value, stream); + + uint32_t size; + stream >> size; + string read_val(stream.peek(), size); + stream.adjustReadPos(size); + EXPECT_EQUAL(val, read_val); +} + +TEST("requireThatRawFieldValueCanBeSerialized") { + TEST_DO(checkRawFieldValue<uint8_t>("foo bar")); + TEST_DO(checkRawFieldValue<uint32_t>(string(200, 'b'))); +} + +TEST("requireThatPredicateFieldValueCanBeSerialized") { + PredicateSlimeBuilder builder; + builder.neg().feature("foo").value("bar").value("baz"); + PredicateFieldValue value(builder.build()); + nbostream stream; + serializeAndDeserialize(value, stream); +} + +template <typename SizeType> +SizeType readSize1_2_4(nbostream &stream) { + SizeType size; + stream >> size; + if (sizeof(SizeType) > 1) { + size &= (SizeType(-1) >> 2); // Clear MSBs + } + return size; +} + +template <typename SizeType> +void checkArrayFieldValue(SizeType value_count) { + ArrayDataType array_type(*DataType::INT); + ArrayFieldValue value(array_type); + for (uint32_t i = 0; i < value_count; ++i) { + value.add(static_cast<int32_t>(i)); + } + + nbostream stream; + serializeAndDeserialize(value, stream); + + SizeType size = readSize1_2_4<SizeType>(stream); + EXPECT_EQUAL(value_count, size); + uint32_t child; + for (uint32_t i = 0; i < value_count; ++i) { + stream >> child; + EXPECT_EQUAL(i, child); + } +} + +TEST("requireThatArrayFieldValueCanBeSerialized") { + TEST_DO(checkArrayFieldValue<uint8_t>(2)); + TEST_DO(checkArrayFieldValue<uint8_t>(0x7f)); + TEST_DO(checkArrayFieldValue<uint16_t>(0x80)); + TEST_DO(checkArrayFieldValue<uint16_t>(0x3fff)); + TEST_DO(checkArrayFieldValue<uint32_t>(0x4000)); +} + +TEST("requireThatOldVersionArrayFieldValueCanBeDeserialized") { + uint16_t old_version = 6; + + nbostream stream; + uint32_t type_id = 3; + uint32_t size = 2; + uint32_t element_size = 4; + uint32_t element1 = 21, element2 = 42; + stream << type_id << size + << element_size << element1 + << element_size << element2; + + ArrayDataType array_type(*DataType::INT); + ArrayFieldValue value(array_type); + VespaDocumentDeserializer deserializer(repo, stream, old_version); + deserializer.read(value); + ASSERT_TRUE(EXPECT_EQUAL(size, value.size())); + IntFieldValue *int_value = dynamic_cast<IntFieldValue *>(&value[0]); + ASSERT_TRUE(int_value); + EXPECT_EQUAL(element1, static_cast<uint32_t>(int_value->getValue())); + int_value = dynamic_cast<IntFieldValue *>(&value[1]); + ASSERT_TRUE(int_value); + EXPECT_EQUAL(element2, static_cast<uint32_t>(int_value->getValue())); +} + +template <typename SizeType> +void checkMapFieldValue(SizeType value_count, bool check_equal) { + MapDataType map_type(*DataType::LONG, *DataType::BYTE); + MapFieldValue value(map_type); + for (SizeType i = 0; i < value_count; ++i) { + value.push_back(LongFieldValue(i), ByteFieldValue(i)); + } + + nbostream stream; + serializeAndDeserialize(value, stream, check_equal); + + SizeType size = readSize1_2_4<SizeType>(stream); + EXPECT_EQUAL(value_count, size); + uint64_t key; + uint8_t val; + for (SizeType i = 0; i < value_count; ++i) { + stream >> key >> val; + EXPECT_EQUAL(i, key); + EXPECT_EQUAL(i % 256, val); + } +} + +TEST("requireThatMapFieldValueCanBeSerialized") { + TEST_DO(checkMapFieldValue<uint8_t>(2, true)); + TEST_DO(checkMapFieldValue<uint8_t>(0x7f, true)); + TEST_DO(checkMapFieldValue<uint16_t>(0x80, true)); + TEST_DO(checkMapFieldValue<uint16_t>(0x3fff, false)); + TEST_DO(checkMapFieldValue<uint32_t>(0x4000, false)); +} + +TEST("requireThatOldVersionMapFieldValueCanBeDeserialized") { + uint16_t old_version = 6; + + nbostream stream; + uint32_t type_id = 4; + uint32_t size = 2; + uint32_t element_size = 9; + uint64_t key1 = 21, key2 = 42; + uint8_t val1 = 1, val2 = 2; + stream << type_id << size + << element_size << key1 << val1 + << element_size << key2 << val2; + + MapDataType map_type(*DataType::LONG, *DataType::BYTE); + MapFieldValue value(map_type); + VespaDocumentDeserializer deserializer(repo, stream, old_version); + deserializer.read(value); + ASSERT_TRUE(EXPECT_EQUAL(size, value.size())); + + ASSERT_TRUE(value.contains(LongFieldValue(key1))); + ASSERT_TRUE(value.contains(LongFieldValue(key2))); + EXPECT_EQUAL(ByteFieldValue(val1), + *value.find(LongFieldValue(key1))->second); + EXPECT_EQUAL(ByteFieldValue(val2), + *value.find(LongFieldValue(key2))->second); +} + +TEST("requireThatWeightedSetFieldValueCanBeSerialized") { + WeightedSetDataType ws_type(*DataType::DOUBLE, false, false); + WeightedSetFieldValue value(ws_type); + value.add(DoubleFieldValue(3.14), 2); + value.add(DoubleFieldValue(2.71), 3); + + nbostream stream; + serializeAndDeserialize(value, stream); + + uint32_t type_id; + uint32_t size; + stream >> type_id >> size; + EXPECT_EQUAL(2u, size); + double val; + uint32_t weight; + stream >> size >> val >> weight; + EXPECT_EQUAL(12u, size); + EXPECT_EQUAL(3.14, val); + EXPECT_EQUAL(2u, weight); + stream >> size >> val >> weight; + EXPECT_EQUAL(12u, size); + EXPECT_EQUAL(2.71, val); + EXPECT_EQUAL(3u, weight); +} + +const Field field1("field1", *DataType::INT, false); +const Field field2("field2", *DataType::STRING, false); + +StructDataType getStructDataType() { + StructDataType struct_type("struct"); + struct_type.addField(field1); + struct_type.addField(field2); + return struct_type; +} + +StructFieldValue getStructFieldValue(const StructDataType& structType) { + StructFieldValue value(structType); + value.setValue(field1, IntFieldValue(42)); + value.setValue(field2, StringFieldValue("foooooooooooooooooooooobaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + return value; +} + +void checkStructSerialization(const StructFieldValue &value, + CompressionConfig::Type comp_type) { + nbostream stream; + serializeAndDeserialize(value, stream); + uint32_t data_size; + uint8_t compression_type; + uint16_t uncompressed_size; + uint8_t field_count; + int32_t element1_id, element2_id; + uint16_t element1_size, element2_size; + stream >> data_size >> compression_type; + if (CompressionConfig::isCompressed(comp_type)) { + stream >> uncompressed_size; + EXPECT_EQUAL(24u, data_size); + EXPECT_EQUAL(64u, uncompressed_size); + } else { + EXPECT_EQUAL(64u, data_size); + } + stream >> field_count + >> element1_id >> element1_size + >> element2_id >> element2_size; + EXPECT_EQUAL(comp_type, compression_type); + EXPECT_EQUAL(2u, field_count); + EXPECT_EQUAL(field1.getId(8), element1_id & 0x7fffffff); + EXPECT_EQUAL(4u, element1_size); + EXPECT_EQUAL(field2.getId(8), element2_id & 0x7fffffff); + EXPECT_EQUAL(60u, element2_size); +} + +TEST("requireThatUncompressedStructFieldValueCanBeSerialized") { + StructDataType structType(getStructDataType()); + StructFieldValue value = getStructFieldValue(structType); + checkStructSerialization(value, CompressionConfig::NONE); +} + +TEST("requireThatCompressedStructFieldValueCanBeSerialized") { + StructDataType structType(getStructDataType()); + StructFieldValue value = getStructFieldValue(structType); + const_cast<StructDataType &>(value.getStructType()).setCompressionConfig(CompressionConfig(CompressionConfig::LZ4, 0, 95)); + checkStructSerialization(value, CompressionConfig::LZ4); +} + +TEST("requireThatReserializationPreservesCompressionIfUnmodified") { + StructDataType structType(getStructDataType()); + StructFieldValue value = getStructFieldValue(structType); + const_cast<StructDataType &>(value.getStructType()). + setCompressionConfig(CompressionConfig(CompressionConfig::LZ4, 0, 95)); + + checkStructSerialization(value, CompressionConfig::LZ4); + + nbostream os; + VespaDocumentSerializer serializer(os); + serializer.write(value); + + StructDataType struct_type(getStructDataType()); + StructFieldValue value2(struct_type); + VespaDocumentDeserializer deserializer(repo, os, 8); + deserializer.read(value2); + checkStructSerialization(value, CompressionConfig::LZ4); + // No lazy serialization of structs anymore, only documents + checkStructSerialization(value2, CompressionConfig::NONE); + EXPECT_EQUAL(value, value2); +} + +template <typename T, int N> int arraysize(const T (&)[N]) { return N; } + +TEST("requireThatDocumentCanBeSerialized") { + const uint32_t serialization_version = 8; + const DocumentType &type = repo.getDocumentType(); + + DocumentId doc_id("doc::testdoc"); + Document value(type, doc_id); + + value.setValue(type.getField("header field"), IntFieldValue(42)); + value.setValue(type.getField("body field"), StringFieldValue("foobar")); + + nbostream stream; + serializeAndDeserialize(value, stream); + + uint16_t read_version; + uint32_t size; + stream >> read_version >> size; + EXPECT_EQUAL(serialization_version, read_version); + EXPECT_EQUAL(65u, size); + EXPECT_EQUAL(doc_id.getScheme().toString(), stream.peek()); + stream.adjustReadPos(doc_id.getScheme().toString().size() + 1); + uint8_t content_code; + stream >> content_code; + EXPECT_EQUAL(0x07, content_code); + EXPECT_EQUAL(type.getName(), stream.peek()); + stream.adjustReadPos(type.getName().size() + 1); + stream >> read_version; + EXPECT_EQUAL(0, read_version); +} + +TEST("requireThatOldVersionDocumentCanBeDeserialized") { + uint16_t old_version = 6; + uint16_t data_size = 432; + string doc_id = "doc::testdoc"; + uint8_t content_code = 0x01; + uint32_t crc = 42; + + nbostream stream; + stream << old_version << data_size; + stream.write(doc_id.c_str(), doc_id.size() + 1); + stream << content_code; + stream.write(doc_name.c_str(), doc_name.size() + 1); + stream << static_cast<uint16_t>(0) << crc; // version (unused) + + Document value; + VespaDocumentDeserializer deserializer(repo, stream, old_version); + deserializer.read(value); + + EXPECT_EQUAL(doc_id, value.getId().getScheme().toString()); + EXPECT_EQUAL(doc_name, value.getType().getName()); + EXPECT_TRUE(value.getFields().empty()); +} + +TEST("requireThatUnmodifiedDocumentRetainsUnknownFieldOnSerialization") { + + DocumenttypesConfigBuilderHelper builder1, builder2; + builder1.document(doc_type_id, doc_name, + Struct("my document.header") + .addField("field2", DataType::T_STRING), + Struct("my document.body")); + builder2.document(doc_type_id, doc_name, + Struct("my document.header") + .addField("field1", DataType::T_INT) + .addField("field2", DataType::T_STRING), + Struct("my document.body")); + + DocumentTypeRepo repo1Field(builder1.config()); + DocumentTypeRepo repo2Fields(builder2.config()); + + uint32_t serial_version = 8; + + DocumentId doc_id("doc::testdoc"); + Document value(*repo2Fields.getDocumentType(doc_type_id), doc_id); + + value.setValue("field1", IntFieldValue(42)); + value.setValue("field2", StringFieldValue("megafoo")); + + nbostream stream; + VespaDocumentSerializer serializer(stream); + serializer.write(value); + + Document read_value; + // Deserialize+serialize with type where field1 is not known. + VespaDocumentDeserializer deserializer1(repo1Field, stream, serial_version); + deserializer1.read(read_value); + EXPECT_EQUAL(0u, stream.size()); + + EXPECT_EQUAL(1u, read_value.getSetFieldCount()); + + stream.clear(); + serializer.write(read_value); + + Document read_value_2; + // Field should not have vanished. + VespaDocumentDeserializer deserializer2(repo2Fields, stream, serial_version); + deserializer2.read(read_value_2); + EXPECT_EQUAL(value, read_value_2); +} + +TEST("requireThatAnnotationReferenceFieldValueCanBeSerialized") { + AnnotationType annotation_type(0, "atype"); + AnnotationReferenceDataType type(annotation_type, 0); + int annotation_id = 420; + AnnotationReferenceFieldValue value(type, annotation_id); + nbostream stream; + serializeAndDeserialize(value, stream); + + int read_id = readSize1_2_4<uint16_t>(stream); + EXPECT_EQUAL(annotation_id, read_id); +} + +TEST("requireThatDocumentWithDocumentCanBeSerialized") { + const DocumentTypeRepo &my_repo = repo.getDocumentTypeRepo(); + const DocumentType *inner_type = my_repo.getDocumentType(inner_type_id); + ASSERT_TRUE(inner_type); + const AnnotationType *a_type = + my_repo.getAnnotationType(*inner_type, a_id); + StringFieldValue str("foo"); + SpanTree::UP tree(new SpanTree("name", Span::UP(new Span(0, 3)))); + tree->annotate(Annotation::UP(new Annotation(*a_type))); + + + setSpanTree(str, *tree); + const Field str_field("str", *DataType::STRING, false); + + Document inner(*inner_type, DocumentId("doc::in")); + inner.setValue(str_field, str); + const DocumentType *type = my_repo.getDocumentType(outer_type_id); + ASSERT_TRUE(type); + DocumentId doc_id("doc::testdoc"); + Document value(*type, doc_id); + const Field doc_field(inner_name, *inner_type, false); + value.setValue(doc_field, inner); + + nbostream stream; + serializeAndDeserialize(value, stream); +} + +TEST("requireThatReadDocumentTypeThrowsIfUnknownType") { + string my_type("my_unknown_type"); + nbostream stream; + stream.write(my_type.c_str(), my_type.size() + 1); + stream << static_cast<uint16_t>(0); // version (unused) + + DocumentType value; + VespaDocumentDeserializer deserializer(repo, stream, 8); + EXPECT_EXCEPTION(deserializer.read(value), DocumentTypeNotFoundException, + "Document type " + my_type + " not found"); +} + +template <typename FieldValueT> +void serializeToFile(FieldValueT &value, const string &file_name, + const DocumentType *type, const string &field_name) { + DocumentId doc_id("id:test:" + type->getName() + "::foo"); + Document doc(*type, doc_id); + doc.setValue(type->getField(field_name), value); + + nbostream stream; + VespaDocumentSerializer serializer(stream); + serializer.write(doc); + + File file(file_name); + file.open(File::CREATE); + file.write(stream.peek(), stream.size(), 0); +} + +void serializeToFile(PredicateFieldValue &value, const string &file_name) { + const DocumentType *type = doc_repo.getDocumentType(predicate_doc_type_id); + serializeToFile(value, file_name, type, predicate_field_name); +} + +template <typename FieldValueT> +void deserializeAndCheck(const string &file_name, FieldValueT &value, + const FixedTypeRepo &myrepo, + const string &field_name) { + File file(file_name); + file.open(File::READONLY); + vector<char> content(file.stat()._size); + size_t r = file.read(&content[0], content.size(), 0); + ASSERT_EQUAL(content.size(), r); + + nbostream stream(&content[0], content.size(), true); + Document doc; + VespaDocumentDeserializer deserializer(myrepo, stream, 8); + deserializer.read(doc); + + ASSERT_EQUAL(0, value.compare(*doc.getValue(field_name))); +} + +void deserializeAndCheck(const string &file_name, PredicateFieldValue &value) { + deserializeAndCheck(file_name, value, repo, predicate_field_name); +} + +void checkDeserialization(const string &name, std::unique_ptr<Slime> slime) { + const string data_dir = "../../test/resources/predicates/"; + + PredicateFieldValue value(std::move(slime)); + serializeToFile(value, data_dir + name + "__cpp"); + + deserializeAndCheck(data_dir + name + "__cpp", value); + deserializeAndCheck(data_dir + name + "__java", value); +} + +TEST("Require that predicate deserialization matches Java") { + PredicateSlimeBuilder builder; + + builder.feature("foo").range(6, 9); + checkDeserialization("foo_in_6_9", builder.build()); + + builder.feature("foo").greaterEqual(6); + checkDeserialization("foo_in_6_x", builder.build()); + + builder.feature("foo").lessEqual(9); + checkDeserialization("foo_in_x_9", builder.build()); + + builder.feature("foo").value("bar"); + checkDeserialization("foo_in_bar", builder.build()); + + builder.feature("foo").value("bar").value("baz"); + checkDeserialization("foo_in_bar_baz", builder.build()); + + builder.neg().feature("foo").value("bar"); + checkDeserialization("not_foo_in_bar", builder.build()); + + std::unique_ptr<Slime> slime(new Slime); + Cursor *cursor = &slime->setObject(); + cursor->setString(Predicate::KEY, "foo"); + cursor->setLong(Predicate::NODE_TYPE, Predicate::TYPE_FEATURE_SET); + checkDeserialization("foo_in_x", std::move(slime)); + + slime.reset(new Slime); + cursor = &slime->setObject(); + cursor->setString(Predicate::KEY, "foo"); + cursor->setLong(Predicate::NODE_TYPE, Predicate::TYPE_FEATURE_RANGE); + checkDeserialization("foo_in_x_x", std::move(slime)); + + std::unique_ptr<Slime> slime1 = builder.feature("foo").value("bar").build(); + std::unique_ptr<Slime> slime2 = builder.feature("baz").value("cox").build(); + builder.and_node(std::move(slime1), std::move(slime2)); + checkDeserialization("foo_in_bar_and_baz_in_cox", builder.build()); + + slime1 = builder.feature("foo").value("bar").build(); + slime2 = builder.feature("baz").value("cox").build(); + builder.or_node(std::move(slime1), std::move(slime2)); + checkDeserialization("foo_in_bar_or_baz_in_cox", builder.build()); + + builder.true_predicate(); + checkDeserialization("true", builder.build()); + + builder.false_predicate(); + checkDeserialization("false", builder.build()); +} + +namespace +{ + +Tensor::UP +createTensor(const TensorCells &cells, const TensorDimensions &dimensions) { + vespalib::tensor::DefaultTensor::builder builder; + return vespalib::tensor::TensorFactory::create(cells, dimensions, builder); +} + + +} + +TEST("Require that tensors can be serialized") +{ + TensorFieldValue noTensorValue; + TensorFieldValue emptyTensorValue; + TensorFieldValue twoCellsTwoDimsValue; + nbostream stream; + serializeAndDeserialize(noTensorValue, stream); + stream.clear(); + emptyTensorValue = createTensor({}, {}); + serializeAndDeserialize(emptyTensorValue, stream); + stream.clear(); + twoCellsTwoDimsValue = createTensor({ {{{"y", "3"}}, 3}, + {{{"x", "4"}, {"y", "5"}}, 7} }, + {"x", "y"}); + serializeAndDeserialize(twoCellsTwoDimsValue, stream); + EXPECT_NOT_EQUAL(noTensorValue, emptyTensorValue); + EXPECT_NOT_EQUAL(noTensorValue, twoCellsTwoDimsValue); + EXPECT_NOT_EQUAL(emptyTensorValue, twoCellsTwoDimsValue); +} + + +const int tensor_doc_type_id = 321; +const string tensor_field_name = "my_tensor"; + +DocumenttypesConfig getTensorDocTypesConfig() { + DocumenttypesConfigBuilderHelper builder; + builder.document(tensor_doc_type_id, "my_type", + Struct("my_type.header"), + Struct("my_type.body") + .addField(tensor_field_name, DataType::T_TENSOR)); + return builder.config(); +} + +const DocumentTypeRepo tensor_doc_repo(getTensorDocTypesConfig()); +const FixedTypeRepo tensor_repo(tensor_doc_repo, + *tensor_doc_repo.getDocumentType(doc_type_id)); + +void serializeToFile(TensorFieldValue &value, const string &file_name) { + const DocumentType *type = + tensor_doc_repo.getDocumentType(tensor_doc_type_id); + serializeToFile(value, file_name, type, tensor_field_name); +} + +void deserializeAndCheck(const string &file_name, TensorFieldValue &value) { + deserializeAndCheck(file_name, value, tensor_repo, tensor_field_name); +} + +void checkDeserialization(const string &name, std::unique_ptr<Tensor> tensor) { + const string data_dir = "../../test/resources/tensor/"; + + TensorFieldValue value; + if (tensor) { + value = tensor->clone(); + } + serializeToFile(value, data_dir + name + "__cpp"); + deserializeAndCheck(data_dir + name + "__cpp", value); + deserializeAndCheck(data_dir + name + "__java", value); +} + +TEST("Require that tensor deserialization matches Java") { + checkDeserialization("non_existing_tensor", std::unique_ptr<Tensor>()); + checkDeserialization("empty_tensor", createTensor({}, {})); + checkDeserialization("multi_cell_tensor", + createTensor({ {{{"dimX", "a"}, {"dimY", "bb"}}, 2.0 }, + {{{"dimX", "ccc"}, + {"dimY", "dddd"}}, 3.0}, + {{{"dimX", "e"}}, 5.0} }, + { "dimX", "dimY" })); +} + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/document/src/tests/setfieldvaluetest.h b/document/src/tests/setfieldvaluetest.h new file mode 100644 index 00000000000..a27aeedf11d --- /dev/null +++ b/document/src/tests/setfieldvaluetest.h @@ -0,0 +1,21 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/* $Id$*/ + +#pragma once + +#include <cppunit/extensions/HelperMacros.h> + +class SetFieldValue_Test : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE( SetFieldValue_Test); + CPPUNIT_TEST(testSet); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + + void tearDown(); +protected: + void testSet(); +}; + + diff --git a/document/src/tests/stringtokenizertest.cpp b/document/src/tests/stringtokenizertest.cpp new file mode 100644 index 00000000000..5ac41992621 --- /dev/null +++ b/document/src/tests/stringtokenizertest.cpp @@ -0,0 +1,109 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <cppunit/extensions/HelperMacros.h> +#include <iostream> +#include <set> +#include <sstream> +#include <vespa/vespalib/text/stringtokenizer.h> + +using vespalib::StringTokenizer; +using std::string; + +class StringTokenizerTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(StringTokenizerTest); + CPPUNIT_TEST(testSimpleUsage); + CPPUNIT_TEST_SUITE_END(); + +protected: + void testSimpleUsage(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(StringTokenizerTest); + +void StringTokenizerTest::testSimpleUsage() +{ + { + string s("This,is ,a,,list ,\tof,,sepa rated\n, \rtokens,"); + StringTokenizer tokenizer(s); + StringTokenizer::TokenList result; + result.push_back("This"); + result.push_back("is"); + result.push_back("a"); + result.push_back(""); + result.push_back("list"); + result.push_back("of"); + result.push_back(""); + result.push_back("sepa rated"); + result.push_back("tokens"); + result.push_back(""); + + CPPUNIT_ASSERT_EQUAL(result.size(), + static_cast<size_t>(tokenizer.size())); + for (unsigned int i=0; i<result.size(); i++) { + CPPUNIT_ASSERT_EQUAL(result[i], tokenizer[i]); + } + std::set<string> sorted(tokenizer.begin(), tokenizer.end()); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(8u), sorted.size()); + + tokenizer.removeEmptyTokens(); + CPPUNIT_ASSERT_EQUAL(7u, tokenizer.size()); + } + { + string s("\tAnother list with some \ntokens, and stuff."); + StringTokenizer tokenizer(s, " \t\n", ",."); + StringTokenizer::TokenList result; + result.push_back(""); + result.push_back("Another"); + result.push_back("list"); + result.push_back("with"); + result.push_back("some"); + result.push_back(""); + result.push_back("tokens"); + result.push_back("and"); + result.push_back("stuff"); + + CPPUNIT_ASSERT_EQUAL(result.size(), + static_cast<size_t>(tokenizer.size())); + for (unsigned int i=0; i<result.size(); i++) { + CPPUNIT_ASSERT_EQUAL(result[i], tokenizer[i]); + } + std::set<string> sorted(tokenizer.begin(), tokenizer.end()); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(8u), sorted.size()); + + tokenizer.removeEmptyTokens(); + CPPUNIT_ASSERT_EQUAL(7u, tokenizer.size()); + } + { + string s(" "); + StringTokenizer tokenizer(s); + CPPUNIT_ASSERT_EQUAL(0u, tokenizer.size()); + } + + { + string s(""); + StringTokenizer tokenizer(s); + CPPUNIT_ASSERT_EQUAL(0u, tokenizer.size()); + } + { + // Test that there aren't any problems with using signed chars. + string s("Here\x01\xff be\xff\xfe dragons\xff"); + StringTokenizer tokenizer(s, "\xff", "\x01 \xfe"); + StringTokenizer::TokenList result; + result.push_back("Here"); + result.push_back("be"); + result.push_back("dragons"); + result.push_back(""); + + CPPUNIT_ASSERT_EQUAL(result.size(), + static_cast<size_t>(tokenizer.size())); + for (unsigned int i=0; i<result.size(); i++) { + CPPUNIT_ASSERT_EQUAL(result[i], tokenizer[i]); + } + std::set<string> sorted(tokenizer.begin(), tokenizer.end()); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4u), sorted.size()); + + tokenizer.removeEmptyTokens(); + CPPUNIT_ASSERT_EQUAL(3u, tokenizer.size()); + } +} diff --git a/document/src/tests/struct_anno/.gitignore b/document/src/tests/struct_anno/.gitignore new file mode 100644 index 00000000000..7a5ecfc76aa --- /dev/null +++ b/document/src/tests/struct_anno/.gitignore @@ -0,0 +1,2 @@ +/struct_anno_test +document_struct_anno_test_app diff --git a/document/src/tests/struct_anno/CMakeLists.txt b/document/src/tests/struct_anno/CMakeLists.txt new file mode 100644 index 00000000000..64678282842 --- /dev/null +++ b/document/src/tests/struct_anno/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(document_struct_anno_test_app + SOURCES + struct_anno_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_struct_anno_test_app COMMAND document_struct_anno_test_app) diff --git a/document/src/tests/struct_anno/document.dat b/document/src/tests/struct_anno/document.dat Binary files differnew file mode 100644 index 00000000000..243b5f05a92 --- /dev/null +++ b/document/src/tests/struct_anno/document.dat diff --git a/document/src/tests/struct_anno/documentmanager.cfg b/document/src/tests/struct_anno/documentmanager.cfg new file mode 100644 index 00000000000..94020ca7b69 --- /dev/null +++ b/document/src/tests/struct_anno/documentmanager.cfg @@ -0,0 +1,160 @@ +enablecompression false +annotationtype[0] +datatype[8] +datatype[0].id -1049517126 +datatype[0].annotationreftype[0] +datatype[0].arraytype[0] +datatype[0].documenttype[1] +datatype[0].documenttype[0].bodystruct 1008689562 +datatype[0].documenttype[0].headerstruct 166307397 +datatype[0].documenttype[0].name "simple_def" +datatype[0].documenttype[0].version 0 +datatype[0].documenttype[0].inherits[1] +datatype[0].documenttype[0].inherits[0].name "document" +datatype[0].documenttype[0].inherits[0].version 0 +datatype[0].maptype[0] +datatype[0].structtype[0] +datatype[0].weightedsettype[0] +datatype[1].id -1267268530 +datatype[1].annotationreftype[0] +datatype[1].arraytype[0] +datatype[1].documenttype[0] +datatype[1].maptype[0] +datatype[1].structtype[1] +datatype[1].structtype[0].compresslevel 0 +datatype[1].structtype[0].compressminsize 800 +datatype[1].structtype[0].compressthreshold 95 +datatype[1].structtype[0].compresstype NONE +datatype[1].structtype[0].name "simple_def_search.header" +datatype[1].structtype[0].version 0 +datatype[1].structtype[0].field[3] +datatype[1].structtype[0].field[0].datatype 111553393 +datatype[1].structtype[0].field[0].name "my_url" +datatype[1].structtype[0].field[0].id[0] +datatype[1].structtype[0].field[1].datatype 2 +datatype[1].structtype[0].field[1].name "rankfeatures" +datatype[1].structtype[0].field[1].id[0] +datatype[1].structtype[0].field[2].datatype 2 +datatype[1].structtype[0].field[2].name "summaryfeatures" +datatype[1].structtype[0].field[2].id[0] +datatype[1].structtype[0].inherits[0] +datatype[1].weightedsettype[0] +datatype[2].id -495710767 +datatype[2].annotationreftype[0] +datatype[2].arraytype[0] +datatype[2].documenttype[1] +datatype[2].documenttype[0].bodystruct 1968090595 +datatype[2].documenttype[0].headerstruct -1267268530 +datatype[2].documenttype[0].name "simple_def_search" +datatype[2].documenttype[0].version 0 +datatype[2].documenttype[0].inherits[1] +datatype[2].documenttype[0].inherits[0].name "document" +datatype[2].documenttype[0].inherits[0].version 0 +datatype[2].maptype[0] +datatype[2].structtype[0] +datatype[2].weightedsettype[0] +datatype[3].id 1008689562 +datatype[3].annotationreftype[0] +datatype[3].arraytype[0] +datatype[3].documenttype[0] +datatype[3].maptype[0] +datatype[3].structtype[1] +datatype[3].structtype[0].compresslevel 0 +datatype[3].structtype[0].compressminsize 800 +datatype[3].structtype[0].compressthreshold 95 +datatype[3].structtype[0].compresstype NONE +datatype[3].structtype[0].name "simple_def.body" +datatype[3].structtype[0].version 0 +datatype[3].structtype[0].field[0] +datatype[3].structtype[0].inherits[0] +datatype[3].weightedsettype[0] +datatype[4].id 111553393 +datatype[4].annotationreftype[0] +datatype[4].arraytype[0] +datatype[4].documenttype[0] +datatype[4].maptype[0] +datatype[4].structtype[1] +datatype[4].structtype[0].compresslevel 0 +datatype[4].structtype[0].compressminsize 800 +datatype[4].structtype[0].compressthreshold 95 +datatype[4].structtype[0].compresstype NONE +datatype[4].structtype[0].name "url" +datatype[4].structtype[0].version 0 +datatype[4].structtype[0].field[7] +datatype[4].structtype[0].field[0].datatype 2 +datatype[4].structtype[0].field[0].name "all" +datatype[4].structtype[0].field[0].id[0] +datatype[4].structtype[0].field[1].datatype 2 +datatype[4].structtype[0].field[1].name "fragment" +datatype[4].structtype[0].field[1].id[0] +datatype[4].structtype[0].field[2].datatype 2 +datatype[4].structtype[0].field[2].name "host" +datatype[4].structtype[0].field[2].id[0] +datatype[4].structtype[0].field[3].datatype 2 +datatype[4].structtype[0].field[3].name "path" +datatype[4].structtype[0].field[3].id[0] +datatype[4].structtype[0].field[4].datatype 2 +datatype[4].structtype[0].field[4].name "port" +datatype[4].structtype[0].field[4].id[0] +datatype[4].structtype[0].field[5].datatype 2 +datatype[4].structtype[0].field[5].name "query" +datatype[4].structtype[0].field[5].id[0] +datatype[4].structtype[0].field[6].datatype 2 +datatype[4].structtype[0].field[6].name "scheme" +datatype[4].structtype[0].field[6].id[0] +datatype[4].structtype[0].inherits[0] +datatype[4].weightedsettype[0] +datatype[5].id 1381038251 +datatype[5].annotationreftype[0] +datatype[5].arraytype[0] +datatype[5].documenttype[0] +datatype[5].maptype[0] +datatype[5].structtype[1] +datatype[5].structtype[0].compresslevel 0 +datatype[5].structtype[0].compressminsize 800 +datatype[5].structtype[0].compressthreshold 95 +datatype[5].structtype[0].compresstype NONE +datatype[5].structtype[0].name "position" +datatype[5].structtype[0].version 0 +datatype[5].structtype[0].field[2] +datatype[5].structtype[0].field[0].datatype 0 +datatype[5].structtype[0].field[0].name "x" +datatype[5].structtype[0].field[0].id[0] +datatype[5].structtype[0].field[1].datatype 0 +datatype[5].structtype[0].field[1].name "y" +datatype[5].structtype[0].field[1].id[0] +datatype[5].structtype[0].inherits[0] +datatype[5].weightedsettype[0] +datatype[6].id 166307397 +datatype[6].annotationreftype[0] +datatype[6].arraytype[0] +datatype[6].documenttype[0] +datatype[6].maptype[0] +datatype[6].structtype[1] +datatype[6].structtype[0].compresslevel 0 +datatype[6].structtype[0].compressminsize 800 +datatype[6].structtype[0].compressthreshold 95 +datatype[6].structtype[0].compresstype NONE +datatype[6].structtype[0].name "simple_def.header" +datatype[6].structtype[0].version 0 +datatype[6].structtype[0].field[1] +datatype[6].structtype[0].field[0].datatype 111553393 +datatype[6].structtype[0].field[0].name "my_url" +datatype[6].structtype[0].field[0].id[0] +datatype[6].structtype[0].inherits[0] +datatype[6].weightedsettype[0] +datatype[7].id 1968090595 +datatype[7].annotationreftype[0] +datatype[7].arraytype[0] +datatype[7].documenttype[0] +datatype[7].maptype[0] +datatype[7].structtype[1] +datatype[7].structtype[0].compresslevel 0 +datatype[7].structtype[0].compressminsize 800 +datatype[7].structtype[0].compressthreshold 95 +datatype[7].structtype[0].compresstype NONE +datatype[7].structtype[0].name "simple_def_search.body" +datatype[7].structtype[0].version 0 +datatype[7].structtype[0].field[0] +datatype[7].structtype[0].inherits[0] +datatype[7].weightedsettype[0] diff --git a/document/src/tests/struct_anno/documenttypes.cfg b/document/src/tests/struct_anno/documenttypes.cfg new file mode 100644 index 00000000000..38ef6d54125 --- /dev/null +++ b/document/src/tests/struct_anno/documenttypes.cfg @@ -0,0 +1,98 @@ +enablecompression false +documenttype[2] +documenttype[0].id -495710767 +documenttype[0].name "simple_def_search" +documenttype[0].version 0 +documenttype[0].headerstruct -1267268530 +documenttype[0].bodystruct 1968090595 +documenttype[0].inherits[0] +documenttype[0].datatype[2] +documenttype[0].datatype[0].id -1267268530 +documenttype[0].datatype[0].type STRUCT +documenttype[0].datatype[0].array.element.id 0 +documenttype[0].datatype[0].map.key.id 0 +documenttype[0].datatype[0].map.value.id 0 +documenttype[0].datatype[0].wset.key.id 0 +documenttype[0].datatype[0].wset.createifnonexistent false +documenttype[0].datatype[0].wset.removeifzero false +documenttype[0].datatype[0].annotationref.annotation.id 0 +documenttype[0].datatype[0].sstruct.name "simple_def_search.header" +documenttype[0].datatype[0].sstruct.version 0 +documenttype[0].datatype[0].sstruct.compression.type NONE +documenttype[0].datatype[0].sstruct.compression.level 0 +documenttype[0].datatype[0].sstruct.compression.threshold 90 +documenttype[0].datatype[0].sstruct.compression.minsize 0 +documenttype[0].datatype[0].sstruct.field[3] +documenttype[0].datatype[0].sstruct.field[0].name "my_url" +documenttype[0].datatype[0].sstruct.field[0].id 1127377169 +documenttype[0].datatype[0].sstruct.field[0].id_v6 707904318 +documenttype[0].datatype[0].sstruct.field[0].datatype 111553393 +documenttype[0].datatype[0].sstruct.field[1].name "rankfeatures" +documenttype[0].datatype[0].sstruct.field[1].id 1883197392 +documenttype[0].datatype[0].sstruct.field[1].id_v6 699950698 +documenttype[0].datatype[0].sstruct.field[1].datatype 2 +documenttype[0].datatype[0].sstruct.field[2].name "summaryfeatures" +documenttype[0].datatype[0].sstruct.field[2].id 1840337115 +documenttype[0].datatype[0].sstruct.field[2].id_v6 1981648971 +documenttype[0].datatype[0].sstruct.field[2].datatype 2 +documenttype[0].datatype[1].id 1968090595 +documenttype[0].datatype[1].type STRUCT +documenttype[0].datatype[1].array.element.id 0 +documenttype[0].datatype[1].map.key.id 0 +documenttype[0].datatype[1].map.value.id 0 +documenttype[0].datatype[1].wset.key.id 0 +documenttype[0].datatype[1].wset.createifnonexistent false +documenttype[0].datatype[1].wset.removeifzero false +documenttype[0].datatype[1].annotationref.annotation.id 0 +documenttype[0].datatype[1].sstruct.name "simple_def_search.body" +documenttype[0].datatype[1].sstruct.version 0 +documenttype[0].datatype[1].sstruct.compression.type NONE +documenttype[0].datatype[1].sstruct.compression.level 0 +documenttype[0].datatype[1].sstruct.compression.threshold 90 +documenttype[0].datatype[1].sstruct.compression.minsize 0 +documenttype[0].datatype[1].sstruct.field[0] +documenttype[0].annotationtype[0] +documenttype[1].id -1049517126 +documenttype[1].name "simple_def" +documenttype[1].version 0 +documenttype[1].headerstruct 166307397 +documenttype[1].bodystruct 1008689562 +documenttype[1].inherits[0] +documenttype[1].datatype[2] +documenttype[1].datatype[0].id 166307397 +documenttype[1].datatype[0].type STRUCT +documenttype[1].datatype[0].array.element.id 0 +documenttype[1].datatype[0].map.key.id 0 +documenttype[1].datatype[0].map.value.id 0 +documenttype[1].datatype[0].wset.key.id 0 +documenttype[1].datatype[0].wset.createifnonexistent false +documenttype[1].datatype[0].wset.removeifzero false +documenttype[1].datatype[0].annotationref.annotation.id 0 +documenttype[1].datatype[0].sstruct.name "simple_def.header" +documenttype[1].datatype[0].sstruct.version 0 +documenttype[1].datatype[0].sstruct.compression.type NONE +documenttype[1].datatype[0].sstruct.compression.level 0 +documenttype[1].datatype[0].sstruct.compression.threshold 90 +documenttype[1].datatype[0].sstruct.compression.minsize 0 +documenttype[1].datatype[0].sstruct.field[1] +documenttype[1].datatype[0].sstruct.field[0].name "my_url" +documenttype[1].datatype[0].sstruct.field[0].id 1127377169 +documenttype[1].datatype[0].sstruct.field[0].id_v6 707904318 +documenttype[1].datatype[0].sstruct.field[0].datatype 111553393 +documenttype[1].datatype[1].id 1008689562 +documenttype[1].datatype[1].type STRUCT +documenttype[1].datatype[1].array.element.id 0 +documenttype[1].datatype[1].map.key.id 0 +documenttype[1].datatype[1].map.value.id 0 +documenttype[1].datatype[1].wset.key.id 0 +documenttype[1].datatype[1].wset.createifnonexistent false +documenttype[1].datatype[1].wset.removeifzero false +documenttype[1].datatype[1].annotationref.annotation.id 0 +documenttype[1].datatype[1].sstruct.name "simple_def.body" +documenttype[1].datatype[1].sstruct.version 0 +documenttype[1].datatype[1].sstruct.compression.type NONE +documenttype[1].datatype[1].sstruct.compression.level 0 +documenttype[1].datatype[1].sstruct.compression.threshold 90 +documenttype[1].datatype[1].sstruct.compression.minsize 0 +documenttype[1].datatype[1].sstruct.field[0] +documenttype[1].annotationtype[0] diff --git a/document/src/tests/struct_anno/struct_anno_test.cpp b/document/src/tests/struct_anno/struct_anno_test.cpp new file mode 100644 index 00000000000..85b3b8f2d0b --- /dev/null +++ b/document/src/tests/struct_anno/struct_anno_test.cpp @@ -0,0 +1,87 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("struct_anno_test"); + +#include <stdlib.h> +#include <vespa/document/annotation/alternatespanlist.h> +#include <vespa/document/annotation/annotation.h> +#include <vespa/document/annotation/span.h> +#include <vespa/document/annotation/spanlist.h> +#include <vespa/document/annotation/spantree.h> +#include <vespa/document/config/config-documenttypes.h> +#include <vespa/document/document.h> +#include <vespa/document/fieldvalue/stringfieldvalue.h> +#include <vespa/document/repo/fixedtyperepo.h> +#include <vespa/document/serialization/annotationdeserializer.h> +#include <vespa/document/serialization/annotationserializer.h> +#include <vespa/document/serialization/vespadocumentdeserializer.h> +#include <vespa/document/serialization/vespadocumentserializer.h> +#include <vespa/vespalib/objects/nbostream.h> +#include <vespa/vespalib/testkit/testapp.h> +#include <sstream> +#include <vector> + +using std::ostringstream; +using std::string; +using std::vector; +using vespalib::nbostream; +using namespace document; + +namespace { + +class Test : public vespalib::TestApp { + void requireThatStructFieldsCanContainAnnotations(); + +public: + int Main(); +}; + +int Test::Main() { + if (getenv("TEST_SUBSET") != 0) { return 0; } + TEST_INIT("struct_anno_test"); + TEST_DO(requireThatStructFieldsCanContainAnnotations()); + TEST_DONE(); +} + +template <typename T, int N> int arraysize(const T (&)[N]) { return N; } + +void Test::requireThatStructFieldsCanContainAnnotations() { + DocumentTypeRepo repo(readDocumenttypesConfig("documenttypes.cfg")); + + FastOS_File file("document.dat"); + ASSERT_TRUE(file.OpenReadOnlyExisting()); + char buffer[1024]; + ssize_t size = file.Read(buffer, arraysize(buffer)); + ASSERT_TRUE(size != -1); + + nbostream stream(buffer, size); + VespaDocumentDeserializer deserializer(repo, stream, 8); + Document doc; + deserializer.read(doc); + + FieldValue::UP urlRef = doc.getValue("my_url"); + ASSERT_TRUE(urlRef.get() != NULL); + const StructFieldValue *url = dynamic_cast<const StructFieldValue*>(urlRef.get()); + ASSERT_TRUE(url != NULL); + + FieldValue::UP strRef = url->getValue("scheme"); + const StringFieldValue *str = dynamic_cast<const StringFieldValue*>(strRef.get()); + ASSERT_TRUE(str != NULL); + + SpanTree::UP tree = std::move(str->getSpanTrees().front()); + + EXPECT_EQUAL("my_tree", tree->getName()); + const SimpleSpanList *root = dynamic_cast<const SimpleSpanList*>(&tree->getRoot()); + ASSERT_TRUE(root != NULL); + EXPECT_EQUAL(1u, root->size()); + SimpleSpanList::const_iterator it = root->begin(); + EXPECT_EQUAL(Span(0, 6), (*it++)); + EXPECT_TRUE(it == root->end()); + + EXPECT_EQUAL(1u, tree->numAnnotations()); +} + +} // namespace + +TEST_APPHOOK(Test); diff --git a/document/src/tests/structfieldvaluetest.cpp b/document/src/tests/structfieldvaluetest.cpp new file mode 100644 index 00000000000..c1bf53d5322 --- /dev/null +++ b/document/src/tests/structfieldvaluetest.cpp @@ -0,0 +1,185 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <vespa/document/fieldvalue/fieldvalues.h> +#include <vespa/document/repo/configbuilder.h> +#include <vespa/document/serialization/vespadocumentdeserializer.h> +#include <vespa/vdstestlib/cppunit/macros.h> +#include <vespa/vespalib/objects/nbostream.h> + +using vespalib::nbostream; +using document::config_builder::Struct; +using document::config_builder::Wset; +using document::config_builder::Array; +using document::config_builder::Map; + +namespace document { + +struct StructFieldValueTest : public CppUnit::TestFixture { + void setUp() {} + void tearDown() {} + + void testStruct(); + + CPPUNIT_TEST_SUITE(StructFieldValueTest); + CPPUNIT_TEST(testStruct); + CPPUNIT_TEST_SUITE_END(); + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(StructFieldValueTest); + +namespace { +template <typename T> +void deserialize(const ByteBuffer &buffer, T &value, const FixedTypeRepo &repo) +{ + uint16_t version = Document::getNewestSerializationVersion(); + nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining()); + VespaDocumentDeserializer deserializer(repo, stream, version); + deserializer.read(value); +} +} // namespace + +void StructFieldValueTest::testStruct() +{ + config_builder::DocumenttypesConfigBuilderHelper builder; + builder.document(42, "test", + Struct("test.header") + .addField("int", DataType::T_INT) + .addField("long", DataType::T_LONG) + .addField("content", DataType::T_STRING), + Struct("test.body")); + DocumentTypeRepo doc_repo(builder.config()); + const DocumentType *doc_type = doc_repo.getDocumentType(42); + CPPUNIT_ASSERT(doc_type); + FixedTypeRepo repo(doc_repo, *doc_type); + const DataType &type = *repo.getDataType("test.header"); + StructFieldValue value(type); + const Field &intF = value.getField("int"); + const Field &longF = value.getField("long"); + const Field &strF = value.getField("content"); + + // Initially empty + CPPUNIT_ASSERT_EQUAL(size_t(0), value.getSetFieldCount()); + CPPUNIT_ASSERT(!value.hasValue(intF)); + + value.setValue(intF, IntFieldValue(1)); + + // Not empty + CPPUNIT_ASSERT_EQUAL(size_t(1), value.getSetFieldCount()); + CPPUNIT_ASSERT(value.hasValue(intF)); + + // Adding some more + value.setValue(longF, LongFieldValue(2)); + + // Not empty + CPPUNIT_ASSERT_EQUAL(size_t(2), value.getSetFieldCount()); + CPPUNIT_ASSERT_EQUAL(1, value.getValue(intF)->getAsInt()); + CPPUNIT_ASSERT_EQUAL(2, value.getValue(longF)->getAsInt()); + + // Serialize & equality + std::unique_ptr<ByteBuffer> buffer(value.serialize()); + buffer->flip(); + + CPPUNIT_ASSERT_EQUAL(buffer->getLength(), buffer->getLimit()); + StructFieldValue value2(type); + CPPUNIT_ASSERT(value != value2); + + deserialize(*buffer, value2, repo); + + CPPUNIT_ASSERT(value2.hasValue(intF)); + CPPUNIT_ASSERT_EQUAL(value, value2); + + // Various ways of removing + { + // By value + buffer->setPos(0); + deserialize(*buffer, value2, repo); + value2.remove(intF); + CPPUNIT_ASSERT(!value2.hasValue(intF)); + CPPUNIT_ASSERT_EQUAL(size_t(1), value2.getSetFieldCount()); + + // Clearing all + buffer->setPos(0); + deserialize(*buffer, value2, repo); + value2.clear(); + CPPUNIT_ASSERT(!value2.hasValue(intF)); + CPPUNIT_ASSERT_EQUAL(size_t(0), value2.getSetFieldCount()); + } + + // Updating + value2 = value; + CPPUNIT_ASSERT_EQUAL(value, value2); + value2.setValue(strF, StringFieldValue("foo")); + CPPUNIT_ASSERT(value2.hasValue(strF)); + CPPUNIT_ASSERT_EQUAL(vespalib::string("foo"), + value2.getValue(strF)->getAsString()); + CPPUNIT_ASSERT(value != value2); + value2.assign(value); + CPPUNIT_ASSERT_EQUAL(value, value2); + StructFieldValue::UP valuePtr(value2.clone()); + + CPPUNIT_ASSERT(valuePtr.get()); + CPPUNIT_ASSERT_EQUAL(value, *valuePtr); + + // Iterating + const StructFieldValue& constVal(value); + for(StructFieldValue::const_iterator it = constVal.begin(); + it != constVal.end(); ++it) + { + constVal.getValue(it.field()); + } + + // Comparison + value2 = value; + CPPUNIT_ASSERT_EQUAL(0, value.compare(value2)); + value2.remove(intF); + CPPUNIT_ASSERT(value.compare(value2) < 0); + CPPUNIT_ASSERT(value2.compare(value) > 0); + value2 = value; + value2.setValue(intF, IntFieldValue(5)); + CPPUNIT_ASSERT(value.compare(value2) < 0); + CPPUNIT_ASSERT(value2.compare(value) > 0); + + // Output + CPPUNIT_ASSERT_EQUAL( + std::string("Struct test.header(\n" + " int - 1,\n" + " long - 2\n" + ")"), + value.toString(false)); + CPPUNIT_ASSERT_EQUAL( + std::string(" Struct test.header(\n" + ".. int - 1,\n" + ".. long - 2\n" + "..)"), + " " + value.toString(true, "..")); + CPPUNIT_ASSERT_EQUAL( + std::string("<value>\n" + " <int>1</int>\n" + " <long>2</long>\n" + "</value>"), + value.toXml(" ")); + + // Failure situations. + + // Refuse to accept non-struct types + try{ + StructFieldValue value6(*DataType::DOCUMENT); + CPPUNIT_FAIL("Didn't complain about non-struct type"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot generate a struct value with " + "non-struct type", e.what()); + } + + // Refuse to set wrong types + try{ + value2.setValue(intF, StringFieldValue("bar")); + CPPUNIT_FAIL("Failed to check type equality in setValue"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot assign value of type", e.what()); + } +} + +} // document + diff --git a/document/src/tests/tensor_fieldvalue/.gitignore b/document/src/tests/tensor_fieldvalue/.gitignore new file mode 100644 index 00000000000..74ede436f4c --- /dev/null +++ b/document/src/tests/tensor_fieldvalue/.gitignore @@ -0,0 +1 @@ +document_tensor_fieldvalue_test_app diff --git a/document/src/tests/tensor_fieldvalue/CMakeLists.txt b/document/src/tests/tensor_fieldvalue/CMakeLists.txt new file mode 100644 index 00000000000..d0fa1526bbc --- /dev/null +++ b/document/src/tests/tensor_fieldvalue/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(document_tensor_fieldvalue_test_app + SOURCES + tensor_fieldvalue_test.cpp + DEPENDS + document + AFTER + document_documentconfig +) +vespa_add_test(NAME document_tensor_fieldvalue_test_app COMMAND document_tensor_fieldvalue_test_app) diff --git a/document/src/tests/tensor_fieldvalue/tensor_fieldvalue_test.cpp b/document/src/tests/tensor_fieldvalue/tensor_fieldvalue_test.cpp new file mode 100644 index 00000000000..f0dd49c472c --- /dev/null +++ b/document/src/tests/tensor_fieldvalue/tensor_fieldvalue_test.cpp @@ -0,0 +1,72 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for tensor_fieldvalue. + +#include <vespa/log/log.h> +LOG_SETUP("fieldvalue_test"); +#include <vespa/fastos/fastos.h> + +#include <vespa/document/fieldvalue/tensorfieldvalue.h> +#include <vespa/vespalib/tensor/tensor.h> +#include <vespa/vespalib/tensor/types.h> +#include <vespa/vespalib/tensor/default_tensor.h> +#include <vespa/vespalib/tensor/tensor_factory.h> + +#include <vespa/vespalib/testkit/testapp.h> + +using namespace document; +using namespace vespalib::tensor; + +namespace +{ + +Tensor::UP +createTensor(const TensorCells &cells, const TensorDimensions &dimensions) { + vespalib::tensor::DefaultTensor::builder builder; + return vespalib::tensor::TensorFactory::create(cells, dimensions, builder); +} + +FieldValue::UP clone(FieldValue &fv) { + auto ret = FieldValue::UP(fv.clone()); + EXPECT_NOT_EQUAL(ret.get(), &fv); + EXPECT_EQUAL(*ret, fv); + EXPECT_EQUAL(fv, *ret); + return ret; +} + +} + +TEST("require that TensorFieldValue can be assigned tensors and cloned") { + TensorFieldValue noTensorValue; + TensorFieldValue emptyTensorValue; + TensorFieldValue twoCellsTwoDimsValue; + emptyTensorValue = createTensor({}, {}); + twoCellsTwoDimsValue = createTensor({ {{{"y", "3"}}, 3}, + {{{"x", "4"}, {"y", "5"}}, 7} }, + {"x", "y"}); + EXPECT_NOT_EQUAL(noTensorValue, emptyTensorValue); + EXPECT_NOT_EQUAL(noTensorValue, twoCellsTwoDimsValue); + EXPECT_NOT_EQUAL(emptyTensorValue, noTensorValue); + EXPECT_NOT_EQUAL(emptyTensorValue, twoCellsTwoDimsValue); + EXPECT_NOT_EQUAL(twoCellsTwoDimsValue, noTensorValue); + EXPECT_NOT_EQUAL(twoCellsTwoDimsValue, emptyTensorValue); + FieldValue::UP noneClone = clone(noTensorValue); + FieldValue::UP emptyClone = clone(emptyTensorValue); + FieldValue::UP twoClone = clone(twoCellsTwoDimsValue); + EXPECT_NOT_EQUAL(*noneClone, *emptyClone); + EXPECT_NOT_EQUAL(*noneClone, *twoClone); + EXPECT_NOT_EQUAL(*emptyClone, *noneClone); + EXPECT_NOT_EQUAL(*emptyClone, *twoClone); + EXPECT_NOT_EQUAL(*twoClone, *noneClone); + EXPECT_NOT_EQUAL(*twoClone, *emptyClone); + TensorFieldValue twoCellsTwoDimsValue2; + twoCellsTwoDimsValue2 = + createTensor({ {{{"y", "3"}}, 3}, + {{{"x", "4"}, {"y", "5"}}, 7} }, + {"x", "y"}); + EXPECT_NOT_EQUAL(*noneClone, twoCellsTwoDimsValue2); + EXPECT_NOT_EQUAL(*emptyClone, twoCellsTwoDimsValue2); + EXPECT_EQUAL(*twoClone, twoCellsTwoDimsValue2); +} + + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/document/src/tests/testbytebuffer.cpp b/document/src/tests/testbytebuffer.cpp new file mode 100644 index 00000000000..b4b8eb55e95 --- /dev/null +++ b/document/src/tests/testbytebuffer.cpp @@ -0,0 +1,493 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +/* $Id$*/ + +#include <vespa/fastos/fastos.h> +#include <vespa/document/util/stringutil.h> +#include <vespa/document/util/bytebuffer.h> +#include <vespa/document/fieldvalue/serializablearray.h> +#include "heapdebugger.h" +#include <iostream> +#include "testbytebuffer.h" +#include <vespa/vespalib/util/macro.h> + +using namespace document; + +CPPUNIT_TEST_SUITE_REGISTRATION( ByteBuffer_Test ); + + +void ByteBuffer_Test::setUp() +{ + enableHeapUsageMonitor(); +} + + +void ByteBuffer_Test::test_constructors() +{ + size_t MemUsedAtEntry=getHeapUsage(); + + + ByteBuffer* simple=new ByteBuffer(); + delete simple; + + ByteBuffer* less_simple=new ByteBuffer("hei",3); + CPPUNIT_ASSERT(strcmp(less_simple->getBufferAtPos(),"hei")==0); + delete less_simple; + + CPPUNIT_ASSERT(getHeapUsage()-MemUsedAtEntry == 0); +} + +void ByteBuffer_Test::test_assignment_operator() +{ + size_t MemUsedAtEntry=getHeapUsage(); + try { + ByteBuffer b1; + ByteBuffer b2 = b1; + + CPPUNIT_ASSERT_EQUAL(b1.getPos(),b2.getPos()); + CPPUNIT_ASSERT_EQUAL(b1.getLength(),b2.getLength()); + CPPUNIT_ASSERT_EQUAL(b1.getLimit(),b2.getLimit()); + CPPUNIT_ASSERT_EQUAL(b1.getRemaining(),b2.getRemaining()); + + } catch (std::exception &e) { + fprintf(stderr,"Unexpected exception at %s: \"%s\"\n", VESPA_STRLOC.c_str(),e.what()); + CPPUNIT_ASSERT(false); + } + + try { + ByteBuffer b1(100); + b1.putInt(1); + b1.putInt(2); + + ByteBuffer b2 = b1; + + CPPUNIT_ASSERT_EQUAL(b1.getPos(),b2.getPos()); + CPPUNIT_ASSERT_EQUAL(b1.getLength(),b2.getLength()); + CPPUNIT_ASSERT_EQUAL(b1.getLimit(),b2.getLimit()); + CPPUNIT_ASSERT_EQUAL(b1.getRemaining(),b2.getRemaining()); + + int test = 0; + b2.flip(); + b2.getInt(test); + CPPUNIT_ASSERT_EQUAL(1,test); + b2.getInt(test); + CPPUNIT_ASSERT_EQUAL(2,test); + + + CPPUNIT_ASSERT_EQUAL(b1.getPos(),b2.getPos()); + CPPUNIT_ASSERT_EQUAL(b1.getLength(),b2.getLength()); + CPPUNIT_ASSERT_EQUAL((size_t) 8,b2.getLimit()); + CPPUNIT_ASSERT_EQUAL((size_t) 0,b2.getRemaining()); + + // Test Selfassignment == no change + // + b2 = b2; + + + CPPUNIT_ASSERT_EQUAL(b1.getPos(),b2.getPos()); + CPPUNIT_ASSERT_EQUAL(b1.getLength(),b2.getLength()); + CPPUNIT_ASSERT_EQUAL((size_t) 8,b2.getLimit()); + CPPUNIT_ASSERT_EQUAL((size_t) 0,b2.getRemaining()); + + ByteBuffer b3; + // Empty + b2 = b3; + + CPPUNIT_ASSERT_EQUAL((size_t) 0,b2.getPos()); + CPPUNIT_ASSERT_EQUAL((size_t) 0,b2.getLength()); + CPPUNIT_ASSERT_EQUAL((size_t) 0,b2.getLimit()); + CPPUNIT_ASSERT_EQUAL((size_t) 0,b2.getRemaining()); + + } catch (std::exception &e) { + fprintf(stderr,"Unexpected exception at %s: \"%s\"\n", VESPA_STRLOC.c_str(),e.what()); + CPPUNIT_ASSERT(false); + } + + CPPUNIT_ASSERT(getHeapUsage()-MemUsedAtEntry == 0); + +} + +void ByteBuffer_Test::test_copy_constructor() +{ + size_t MemUsedAtEntry=getHeapUsage(); + try { + // Empty buffer first + ByteBuffer b1; + ByteBuffer b2(b1); + + CPPUNIT_ASSERT_EQUAL(b1.getPos(),b2.getPos()); + CPPUNIT_ASSERT_EQUAL(b1.getLength(),b2.getLength()); + CPPUNIT_ASSERT_EQUAL(b1.getLimit(),b2.getLimit()); + CPPUNIT_ASSERT_EQUAL(b1.getRemaining(),b2.getRemaining()); + + } catch (std::exception &e) { + fprintf(stderr,"Unexpected exception at %s: %s\n", VESPA_STRLOC.c_str(),e.what()); + CPPUNIT_ASSERT(false); + } + + try { + ByteBuffer b1(100); + b1.putInt(1); + b1.putInt(2); + ByteBuffer b2(b1); + + + CPPUNIT_ASSERT_EQUAL(b1.getPos(),b2.getPos()); + CPPUNIT_ASSERT_EQUAL(b1.getLength(),b2.getLength()); + CPPUNIT_ASSERT_EQUAL(b1.getLimit(),b2.getLimit()); + CPPUNIT_ASSERT_EQUAL(b1.getRemaining(),b2.getRemaining()); + + int test = 0; + b2.flip(); + b2.getInt(test); + CPPUNIT_ASSERT_EQUAL(1,test); + b2.getInt(test); + CPPUNIT_ASSERT_EQUAL(2,test); + + } catch (std::exception &e) { + fprintf(stderr,"Unexpected exception at %s: %s\n", VESPA_STRLOC.c_str(),e.what()); + CPPUNIT_ASSERT(false); + } + + CPPUNIT_ASSERT(getHeapUsage()-MemUsedAtEntry == 0); + +} + +void ByteBuffer_Test::test_slice() +{ + ByteBuffer* newBuf=ByteBuffer::copyBuffer("hei der",8); + + ByteBuffer* slice = new ByteBuffer; + slice->sliceFrom(*newBuf, 0,newBuf->getLength()); + delete newBuf; + newBuf = NULL; + + CPPUNIT_ASSERT(strcmp(slice->getBufferAtPos(),"hei der")==0); + + ByteBuffer* slice2 = new ByteBuffer; + slice2->sliceFrom(*slice, 4, slice->getLength()); + delete slice; + slice = NULL; + + CPPUNIT_ASSERT(strcmp(slice2->getBufferAtPos(),"der")==0); + CPPUNIT_ASSERT(strcmp(slice2->getBuffer(),"hei der")==0); + delete slice2; + slice2 = NULL; + + ByteBuffer* newBuf2=new ByteBuffer("hei der", 8); + ByteBuffer* slice3=new ByteBuffer; + ByteBuffer* slice4=new ByteBuffer; + + slice3->sliceFrom(*newBuf2, 4, newBuf2->getLength()); + slice4->sliceFrom(*newBuf2, 0, newBuf2->getLength()); + delete newBuf2; + newBuf2 = NULL; + + CPPUNIT_ASSERT(strcmp(slice3->getBufferAtPos(),"der")==0); + CPPUNIT_ASSERT(strcmp(slice4->getBuffer(),"hei der")==0); + + delete slice3; + slice3 = NULL; + + CPPUNIT_ASSERT(strcmp(slice4->getBuffer(),"hei der")==0); + + delete slice4; + slice4 = NULL; +} + +void ByteBuffer_Test::test_slice2() +{ + size_t MemUsedAtEntry=getHeapUsage(); + + ByteBuffer* newBuf=ByteBuffer::copyBuffer("hei der",8); + + ByteBuffer slice; + slice.sliceFrom(*newBuf, 0, newBuf->getLength()); + + delete newBuf; + newBuf = NULL; + + CPPUNIT_ASSERT(strcmp(slice.getBufferAtPos(),"hei der")==0); + + ByteBuffer slice2; + slice2.sliceFrom(slice, 4, slice.getLength()); + + CPPUNIT_ASSERT(strcmp(slice2.getBufferAtPos(),"der")==0); + CPPUNIT_ASSERT(strcmp(slice2.getBuffer(),"hei der")==0); + + ByteBuffer* newBuf2=new ByteBuffer("hei der", 8); + + slice.sliceFrom(*newBuf2, 4, newBuf2->getLength()); + slice2.sliceFrom(*newBuf2, 0, newBuf2->getLength()); + delete newBuf2; + newBuf2 = NULL; + + CPPUNIT_ASSERT(strcmp(slice.getBufferAtPos(),"der")==0); + CPPUNIT_ASSERT(strcmp(slice2.getBuffer(),"hei der")==0); + + CPPUNIT_ASSERT(getHeapUsage()-MemUsedAtEntry == 0); +} + + +void ByteBuffer_Test::test_putGetFlip() +{ + ByteBuffer* newBuf=new ByteBuffer(100); + + try { + newBuf->putInt(10); + int test; + newBuf->flip(); + + newBuf->getInt(test); + CPPUNIT_ASSERT(test==10); + + newBuf->clear(); + newBuf->putDouble(3.35); + newBuf->flip(); + CPPUNIT_ASSERT(newBuf->getRemaining()==sizeof(double)); + double test2; + newBuf->getDouble(test2); + CPPUNIT_ASSERT(test2==3.35); + + newBuf->clear(); + newBuf->putBytes("heisann",8); + newBuf->putInt(4); + CPPUNIT_ASSERT(newBuf->getPos()==12); + CPPUNIT_ASSERT(newBuf->getLength()==100); + newBuf->flip(); + CPPUNIT_ASSERT(newBuf->getRemaining()==12); + + char testStr[12]; + newBuf->getBytes(testStr, 8); + CPPUNIT_ASSERT(strcmp(testStr,"heisann")==0); + newBuf->getInt(test); + CPPUNIT_ASSERT(test==4); + } catch (std::exception &e) { + fprintf(stderr,"Unexpected exception at %s: %s\n", VESPA_STRLOC.c_str(),e.what()); + CPPUNIT_ASSERT(false); + } + delete newBuf; +} + + +void ByteBuffer_Test::test_NumberEncodings() +{ + ByteBuffer* buf=new ByteBuffer(1024); + + // Check 0 + buf->putInt1_2_4Bytes(124); + buf->putInt2_4_8Bytes(124); + buf->putInt1_4Bytes(124); + // Check 1 + buf->putInt1_2_4Bytes(127); + buf->putInt2_4_8Bytes(127); + buf->putInt1_4Bytes(127); + // Check 2 + buf->putInt1_2_4Bytes(128); + buf->putInt2_4_8Bytes(128); + buf->putInt1_4Bytes(128); + // Check 3 + buf->putInt1_2_4Bytes(255); + buf->putInt2_4_8Bytes(255); + buf->putInt1_4Bytes(255); + // Check 4 + buf->putInt1_2_4Bytes(256); + buf->putInt2_4_8Bytes(256); + buf->putInt1_4Bytes(256); + // Check 5 + buf->putInt1_2_4Bytes(0); + buf->putInt2_4_8Bytes(0); + buf->putInt1_4Bytes(0); + // Check 6 + buf->putInt1_2_4Bytes(1); + buf->putInt2_4_8Bytes(1); + buf->putInt1_4Bytes(1); + + // Check 7 + try { + buf->putInt1_2_4Bytes(0x7FFFFFFF); + CPPUNIT_ASSERT(false); + } catch (InputOutOfRangeException& e) { } + buf->putInt2_4_8Bytes(0x7FFFFFFFll); + buf->putInt1_4Bytes(0x7FFFFFFF); + + try { + buf->putInt2_4_8Bytes(0x7FFFFFFFFFFFFFFFll); + CPPUNIT_ASSERT(false); + } catch (InputOutOfRangeException& e) { } + + buf->putInt1_2_4Bytes(0x7FFF); + // Check 8 + buf->putInt2_4_8Bytes(0x7FFFll); + buf->putInt1_4Bytes(0x7FFF); + buf->putInt1_2_4Bytes(0x7F); + // Check 9 + buf->putInt2_4_8Bytes(0x7Fll); + buf->putInt1_4Bytes(0x7F); + + try { + buf->putInt1_2_4Bytes(-1); + CPPUNIT_ASSERT(false); + } catch (InputOutOfRangeException& e) { } + try { + buf->putInt2_4_8Bytes(-1); + CPPUNIT_ASSERT(false); + } catch (InputOutOfRangeException& e) { } + try { + buf->putInt1_4Bytes(-1); + CPPUNIT_ASSERT(false); + } catch (InputOutOfRangeException& e) { } + + try { + buf->putInt1_2_4Bytes(-0x7FFFFFFF); + CPPUNIT_ASSERT(false); + } catch (InputOutOfRangeException& e) { } + try { + buf->putInt2_4_8Bytes(-0x7FFFFFFF); + CPPUNIT_ASSERT(false); + } catch (InputOutOfRangeException& e) { } + try { + buf->putInt1_4Bytes(-0x7FFFFFFF); + CPPUNIT_ASSERT(false); + } catch (InputOutOfRangeException& e) { } + + try { + buf->putInt2_4_8Bytes(-0x7FFFFFFFFFFFFFFFll); + CPPUNIT_ASSERT(false); + } catch (InputOutOfRangeException& e) { } + + uint32_t endWritePos = buf->getPos(); + buf->setPos(0); + + int32_t tmp32; + int64_t tmp64; + + // Check 0 + buf->getInt1_2_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(124, tmp32); + buf->getInt2_4_8Bytes(tmp64); + CPPUNIT_ASSERT_EQUAL((int64_t)124, tmp64); + buf->getInt1_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(124, tmp32); + // Check 1 + buf->getInt1_2_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(127, tmp32); + buf->getInt2_4_8Bytes(tmp64); + CPPUNIT_ASSERT_EQUAL((int64_t)127, tmp64); + buf->getInt1_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(127, tmp32); + // Check 2 + buf->getInt1_2_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(128, tmp32); + buf->getInt2_4_8Bytes(tmp64); + CPPUNIT_ASSERT_EQUAL((int64_t)128, tmp64); + buf->getInt1_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(128, tmp32); + // Check 3 + buf->getInt1_2_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(255, tmp32); + buf->getInt2_4_8Bytes(tmp64); + CPPUNIT_ASSERT_EQUAL((int64_t)255, tmp64); + buf->getInt1_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(255, tmp32); + // Check 4 + buf->getInt1_2_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(256, tmp32); + buf->getInt2_4_8Bytes(tmp64); + CPPUNIT_ASSERT_EQUAL((int64_t)256, tmp64); + buf->getInt1_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(256, tmp32); + // Check 5 + buf->getInt1_2_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(0, tmp32); + buf->getInt2_4_8Bytes(tmp64); + CPPUNIT_ASSERT_EQUAL((int64_t)0, tmp64); + buf->getInt1_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(0, tmp32); + // Check 6 + buf->getInt1_2_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(1, tmp32); + buf->getInt2_4_8Bytes(tmp64); + CPPUNIT_ASSERT_EQUAL((int64_t)1, tmp64); + buf->getInt1_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(1, tmp32); + // Check 7 + buf->getInt2_4_8Bytes(tmp64); + CPPUNIT_ASSERT_EQUAL((int64_t)0x7FFFFFFF, tmp64); + buf->getInt1_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(0x7FFFFFFF, tmp32); + buf->getInt1_2_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(0x7FFF, tmp32); + // Check 8 + buf->getInt2_4_8Bytes(tmp64); + CPPUNIT_ASSERT_EQUAL((int64_t)0x7FFF, tmp64); + buf->getInt1_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(0x7FFF, tmp32); + buf->getInt1_2_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(0x7F, tmp32); + // Check 9 + buf->getInt2_4_8Bytes(tmp64); + CPPUNIT_ASSERT_EQUAL((int64_t)0x7F, tmp64); + buf->getInt1_4Bytes(tmp32); + CPPUNIT_ASSERT_EQUAL(0x7F, tmp32); + + uint32_t endReadPos = buf->getPos(); + CPPUNIT_ASSERT_EQUAL(endWritePos, endReadPos); + + delete buf; +} + +void ByteBuffer_Test::test_NumberLengths() +{ + ByteBuffer b; + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_4Bytes(0)); + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_4Bytes(1)); + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_4Bytes(4)); + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_4Bytes(31)); + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_4Bytes(126)); + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_4Bytes(127)); + CPPUNIT_ASSERT_EQUAL((size_t) 4, b.getSerializedSize1_4Bytes(128)); + CPPUNIT_ASSERT_EQUAL((size_t) 4, b.getSerializedSize1_4Bytes(129)); + CPPUNIT_ASSERT_EQUAL((size_t) 4, b.getSerializedSize1_4Bytes(255)); + CPPUNIT_ASSERT_EQUAL((size_t) 4, b.getSerializedSize1_4Bytes(256)); + CPPUNIT_ASSERT_EQUAL((size_t) 4, b.getSerializedSize1_4Bytes(0x7FFFFFFF)); + + CPPUNIT_ASSERT_EQUAL((size_t) 2, b.getSerializedSize2_4_8Bytes(0)); + CPPUNIT_ASSERT_EQUAL((size_t) 2, b.getSerializedSize2_4_8Bytes(1)); + CPPUNIT_ASSERT_EQUAL((size_t) 2, b.getSerializedSize2_4_8Bytes(4)); + CPPUNIT_ASSERT_EQUAL((size_t) 2, b.getSerializedSize2_4_8Bytes(31)); + CPPUNIT_ASSERT_EQUAL((size_t) 2, b.getSerializedSize2_4_8Bytes(126)); + CPPUNIT_ASSERT_EQUAL((size_t) 2, b.getSerializedSize2_4_8Bytes(127)); + CPPUNIT_ASSERT_EQUAL((size_t) 2, b.getSerializedSize2_4_8Bytes(128)); + CPPUNIT_ASSERT_EQUAL((size_t) 2, b.getSerializedSize2_4_8Bytes(32767)); + CPPUNIT_ASSERT_EQUAL((size_t) 4, b.getSerializedSize2_4_8Bytes(32768)); + CPPUNIT_ASSERT_EQUAL((size_t) 4, b.getSerializedSize2_4_8Bytes(32769)); + CPPUNIT_ASSERT_EQUAL((size_t) 4, b.getSerializedSize2_4_8Bytes(1030493)); + CPPUNIT_ASSERT_EQUAL((size_t) 4, b.getSerializedSize2_4_8Bytes(0x3FFFFFFF)); + CPPUNIT_ASSERT_EQUAL((size_t) 8, b.getSerializedSize2_4_8Bytes(0x40000000)); + CPPUNIT_ASSERT_EQUAL((size_t) 8, b.getSerializedSize2_4_8Bytes(0x40000001)); + + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_2_4Bytes(0)); + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_2_4Bytes(1)); + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_2_4Bytes(4)); + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_2_4Bytes(31)); + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_2_4Bytes(126)); + CPPUNIT_ASSERT_EQUAL((size_t) 1, b.getSerializedSize1_2_4Bytes(127)); + CPPUNIT_ASSERT_EQUAL((size_t) 2, b.getSerializedSize1_2_4Bytes(128)); + CPPUNIT_ASSERT_EQUAL((size_t) 2, b.getSerializedSize1_2_4Bytes(16383)); + CPPUNIT_ASSERT_EQUAL((size_t) 4, b.getSerializedSize1_2_4Bytes(16384)); + CPPUNIT_ASSERT_EQUAL((size_t) 4, b.getSerializedSize1_2_4Bytes(16385)); +} + +void ByteBuffer_Test::test_SerializableArray() +{ + SerializableArray array; + array.set(0,"http",4); + CPPUNIT_ASSERT_EQUAL(4ul, array.get(0).size()); + SerializableArray copy(array); + CPPUNIT_ASSERT_EQUAL(4ul, array.get(0).size()); + CPPUNIT_ASSERT_EQUAL(copy.get(0).size(), array.get(0).size()); + CPPUNIT_ASSERT(copy.get(0).c_str() != array.get(0).c_str()); + CPPUNIT_ASSERT_EQUAL(0, strcmp(copy.get(0).c_str(), array.get(0).c_str())); + CPPUNIT_ASSERT_EQUAL(16ul, sizeof(SerializableArray::Entry)); +} diff --git a/document/src/tests/testbytebuffer.h b/document/src/tests/testbytebuffer.h new file mode 100644 index 00000000000..58962d73449 --- /dev/null +++ b/document/src/tests/testbytebuffer.h @@ -0,0 +1,73 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/* $Id$*/ + +#pragma once + +#include <cppunit/extensions/HelperMacros.h> + +/** + CPPUnit test case for ByteBuffer class. +*/ +class ByteBuffer_Test : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE( ByteBuffer_Test); + CPPUNIT_TEST(test_constructors); + CPPUNIT_TEST(test_copy_constructor); + CPPUNIT_TEST(test_assignment_operator); + CPPUNIT_TEST(test_slice); + CPPUNIT_TEST(test_slice2); + CPPUNIT_TEST(test_putGetFlip); + CPPUNIT_TEST(test_NumberEncodings); + CPPUNIT_TEST(test_NumberLengths); + CPPUNIT_TEST(test_SerializableArray); + CPPUNIT_TEST_SUITE_END(); + +public: + /** + Initialization. + */ + void setUp(); + +protected: + /** + Test construction and deletion. + */ + void test_constructors(); + void test_SerializableArray(); + + /** + Test copy constructor + */ + void test_copy_constructor(); + /** + Test construction and deletion. + */ + void test_assignment_operator(); + + /** + Test the slice() method + */ + void test_slice(); + + /** + Test the slice2() method + */ + void test_slice2(); + + /** + Test put(), get() and flip() methods. + */ + void test_putGetFlip(); + + /** + Test writing integers with funny encodings. + */ + void test_NumberEncodings(); + + /** + Tests lengths of those encodings. + */ + void test_NumberLengths(); + +}; + + diff --git a/document/src/tests/testcase.h b/document/src/tests/testcase.h new file mode 100644 index 00000000000..68ac5066c2d --- /dev/null +++ b/document/src/tests/testcase.h @@ -0,0 +1,17 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/* $Id$*/ + +#pragma once + +#include <cppunit/extensions/HelperMacros.h> + +class Document_Test : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE( Document_Test); + CPPUNIT_TEST(testShit); + CPPUNIT_TEST_SUITE_END(); + +protected: + void testShit(); +}; + + diff --git a/document/src/tests/testdocmantest.cpp b/document/src/tests/testdocmantest.cpp new file mode 100644 index 00000000000..14140b4b508 --- /dev/null +++ b/document/src/tests/testdocmantest.cpp @@ -0,0 +1,62 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +/* $Id$*/ + +#include <vespa/fastos/fastos.h> +#include <iostream> +#include <set> +#include <sstream> +#include <vespa/document/base/testdocman.h> +#include <vespa/vdstestlib/cppunit/macros.h> + +namespace document { + +class TestDocManTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(TestDocManTest); + CPPUNIT_TEST(testSimpleUsage); + CPPUNIT_TEST_SUITE_END(); + +protected: + void testSimpleUsage(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestDocManTest); + +void TestDocManTest::testSimpleUsage() +{ + TestDocMan testdm; + Document::UP doc1(testdm.createRandomDocument()); + Document::UP doc2(testdm.createRandomDocument()); + Document::UP doc3(testdm.createRandomDocument(1)); + { + FieldValue::UP v(doc1->getValue(doc1->getField("content"))); + StringFieldValue& sval(dynamic_cast<StringFieldValue&>(*v)); + CPPUNIT_ASSERT_EQUAL(std::string("To be, or "), + std::string(sval.getValue().c_str())); + + FieldValue::UP v2(doc2->getValue(doc2->getField("content"))); + StringFieldValue& sval2(dynamic_cast<StringFieldValue&>(*v)); + CPPUNIT_ASSERT_EQUAL(std::string(sval.getValue().c_str()), + std::string(sval2.getValue().c_str())); + } + { + FieldValue::UP v(doc3->getValue(doc3->getField("content"))); + StringFieldValue& sval(dynamic_cast<StringFieldValue&>(*v)); + CPPUNIT_ASSERT_EQUAL( + std::string("To be, or not to be: that is the question:\n" + "Whether 'tis nobler in the mind to suffer\n" + "The slings and a"), + std::string(sval.getValue().c_str())); + } + CPPUNIT_ASSERT_EQUAL( + vespalib::string("id:mail:testdoctype1:n=51019:192.html"), + doc1->getId().toString()); + CPPUNIT_ASSERT_EQUAL( + vespalib::string("id:mail:testdoctype1:n=51019:192.html"), + doc2->getId().toString()); + CPPUNIT_ASSERT_EQUAL( + vespalib::string("id:mail:testdoctype1:n=10744:245.html"), + doc3->getId().toString()); +} + +} // document diff --git a/document/src/tests/testrunner.cpp b/document/src/tests/testrunner.cpp new file mode 100644 index 00000000000..aabdbb3f605 --- /dev/null +++ b/document/src/tests/testrunner.cpp @@ -0,0 +1,15 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +#include <vespa/vdstestlib/cppunit/cppunittestrunner.h> + +LOG_SETUP("documentcppunittests"); + +int +main(int argc, char **argv) +{ + vdstestlib::CppUnitTestRunner testRunner; + return testRunner.run(argc, argv); +} + diff --git a/document/src/tests/teststringutil.cpp b/document/src/tests/teststringutil.cpp new file mode 100644 index 00000000000..9d04372a19b --- /dev/null +++ b/document/src/tests/teststringutil.cpp @@ -0,0 +1,112 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * Test program for StringUtil methods + * + * @version $Id$ + */ + +#include <vespa/fastos/fastos.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <vespa/document/util/stringutil.h> +#include "heapdebugger.h" +#include "teststringutil.h" +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +CPPUNIT_TEST_SUITE_REGISTRATION( StringUtil_Test ); + +using namespace document; +using vespalib::string; + +void StringUtil_Test::setUp() +{ + enableHeapUsageMonitor(); +} + +void StringUtil_Test::test_escape() +{ + CPPUNIT_ASSERT_EQUAL(string("abz019ABZ"), StringUtil::escape("abz019ABZ")); + CPPUNIT_ASSERT_EQUAL(string("\\t"), StringUtil::escape("\t")); + CPPUNIT_ASSERT_EQUAL(string("\\n"), StringUtil::escape("\n")); + CPPUNIT_ASSERT_EQUAL(string("\\r"), StringUtil::escape("\r")); + CPPUNIT_ASSERT_EQUAL(string("\\\""), StringUtil::escape("\"")); + CPPUNIT_ASSERT_EQUAL(string("\\f"), StringUtil::escape("\f")); + CPPUNIT_ASSERT_EQUAL(string("\\\\"), StringUtil::escape("\\")); + CPPUNIT_ASSERT_EQUAL(string("\\x05"), StringUtil::escape("\x05")); + CPPUNIT_ASSERT_EQUAL(string("\\tA\\ncombined\\r\\x055test"), + StringUtil::escape("\tA\ncombined\r\x05""5test")); + CPPUNIT_ASSERT_EQUAL(string("A\\x20space\\x20separated\\x20string"), + StringUtil::escape("A space separated string", ' ')); +} + +void StringUtil_Test::test_unescape() +{ + CPPUNIT_ASSERT_EQUAL(string("abz019ABZ"), + StringUtil::unescape("abz019ABZ")); + CPPUNIT_ASSERT_EQUAL(string("\t"), StringUtil::unescape("\\t")); + CPPUNIT_ASSERT_EQUAL(string("\n"), StringUtil::unescape("\\n")); + CPPUNIT_ASSERT_EQUAL(string("\r"), StringUtil::unescape("\\r")); + CPPUNIT_ASSERT_EQUAL(string("\""), StringUtil::unescape("\\\"")); + CPPUNIT_ASSERT_EQUAL(string("\f"), StringUtil::unescape("\\f")); + CPPUNIT_ASSERT_EQUAL(string("\\"), StringUtil::unescape("\\\\")); + CPPUNIT_ASSERT_EQUAL(string("\x05"), StringUtil::unescape("\\x05")); + CPPUNIT_ASSERT_EQUAL(string("\tA\ncombined\r\x05""5test"), + StringUtil::unescape("\\tA\\ncombined\\r\\x055test")); + CPPUNIT_ASSERT_EQUAL(string("A space separated string"), + StringUtil::unescape("A\\x20space\\x20separated\\x20string")); +} + +void StringUtil_Test::test_printAsHex() +{ + std::vector<char> asciitable(256); + for (uint32_t i=0; i<256; ++i) asciitable[i] = i; + std::ostringstream ost; + ost << "\n "; + StringUtil::printAsHex(ost, &asciitable[0], asciitable.size(), + 16, true, " "); + std::string expected("\n" + " 0: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n" + " 16: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f\n" + " 32: 20 ! \" # $ % & ' ( ) * + , - . /\n" + " 48: 0 1 2 3 4 5 6 7 8 9 : ; < = > ?\n" + " 64: @ A B C D E F G H I J K L M N O\n" + " 80: P Q R S T U V W X Y Z [ \\ ] ^ _\n" + " 96: ` a b c d e f g h i j k l m n o\n" + " 112: p q r s t u v w x y z { | } ~ 7f\n" + " 128: 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f\n" + " 144: 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f\n" + " 160: a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af\n" + " 176: b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf\n" + " 192: c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf\n" + " 208: d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df\n" + " 224: e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef\n" + " 240: f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff"); + CPPUNIT_ASSERT_EQUAL(expected, ost.str()); + + ost.str(""); + ost << "\n"; + StringUtil::printAsHex(ost, &asciitable[0], asciitable.size(), + 15, false); + expected = "\n" + " 0: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e ...............\n" + " 15: 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d ...............\n" + " 30: 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c ...!\"#$%&'()*+,\n" + " 45: 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b -./0123456789:;\n" + " 60: 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a <=>?@ABCDEFGHIJ\n" + " 75: 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 KLMNOPQRSTUVWXY\n" + " 90: 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 Z[\\]^_`abcdefgh\n" + "105: 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 ijklmnopqrstuvw\n" + "120: 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 xyz{|}~........\n" + "135: 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 ...............\n" + "150: 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 ...............\n" + "165: a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 ...............\n" + "180: b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 ...............\n" + "195: c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 ...............\n" + "210: d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 ...............\n" + "225: e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef ...............\n" + "240: f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ...............\n" + "255: ff ."; + CPPUNIT_ASSERT_EQUAL(expected, ost.str()); +} diff --git a/document/src/tests/teststringutil.h b/document/src/tests/teststringutil.h new file mode 100644 index 00000000000..be25c9667d3 --- /dev/null +++ b/document/src/tests/teststringutil.h @@ -0,0 +1,26 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +/* $Id$*/ + +#pragma once + +#include <cppunit/extensions/HelperMacros.h> + +class StringUtil_Test : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE( StringUtil_Test); + CPPUNIT_TEST(test_escape); + CPPUNIT_TEST(test_unescape); + CPPUNIT_TEST(test_printAsHex); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + +protected: + void test_escape(); + void test_unescape(); + void test_printAsHex(); + +}; + + diff --git a/document/src/tests/testxml.cpp b/document/src/tests/testxml.cpp new file mode 100644 index 00000000000..7729c748b3f --- /dev/null +++ b/document/src/tests/testxml.cpp @@ -0,0 +1,153 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +/* $Id$*/ + +#include <vespa/fastos/fastos.h> +#include <cppunit/extensions/HelperMacros.h> +#include <vespa/document/config/config-documenttypes.h> +#include <vespa/document/repo/documenttyperepo.h> +#include <iostream> +#include <set> +#include <sstream> +#include <vespa/document/update/documentupdate.h> +#include <vespa/document/update/fieldupdate.h> +#include <vespa/document/update/addvalueupdate.h> +#include <vespa/document/update/assignvalueupdate.h> +#include <vespa/document/update/removevalueupdate.h> +#include <vespa/document/fieldvalue/fieldvalues.h> +#include <vespa/vespalib/text/stringtokenizer.h> + +using vespalib::StringTokenizer; + +namespace document { + +class TestXml : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(TestXml); + CPPUNIT_TEST(testSimpleUsage); + CPPUNIT_TEST(testDocumentUpdate); + CPPUNIT_TEST_SUITE_END(); + + void testSimpleUsage(); + void testDocumentUpdate(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestXml); + +namespace { + +Document::UP createTestDocument(const DocumentTypeRepo& repo) +{ + const DocumentType* type(repo.getDocumentType("testdoc")); + Document::UP + doc(new Document(*type, + DocumentId("doc:crawler/http://www.ntnu.no/"))); + doc->setRepo(repo); + std::string s("humlepungens buffer"); + ByteBuffer bb(s.c_str(), s.size()); + + doc->setValue(doc->getField("intattr"), IntFieldValue(50)); + doc->setValue(doc->getField("rawattr"), RawFieldValue("readable hei der", 7)); + doc->setValue(doc->getField("floatattr"), FloatFieldValue(3.56)); + doc->setValue(doc->getField("stringattr"), StringFieldValue("tjo hei")); + + doc->setValue(doc->getField("doubleattr"), DoubleFieldValue(17.78623142376453)); + doc->setValue(doc->getField("longattr"), LongFieldValue(346234765345239657LL)); + doc->setValue(doc->getField("byteattr"), ByteFieldValue('J')); + + ArrayFieldValue val(doc->getField("rawarrayattr").getDataType()); + RawFieldValue rawVal("readable hei", 3); + val.add(rawVal); + RawFieldValue rawVal2("readable hallo", 5); + val.add(rawVal2); + RawFieldValue rawVal3("readable hei der", 7); + val.add(rawVal3); + doc->setValue(doc->getField("rawarrayattr"), val); + + Document::UP doc2(new Document(*type, DocumentId( + "doc:crawler/http://www.ntnu.no/2"))); + doc2->setValue(doc2->getField("stringattr"), StringFieldValue("tjo hei paa du")); + doc->setValue(doc->getField("docfield"), *doc2); + + return doc; +} + +DocumentUpdate::UP +createTestDocumentUpdate(const DocumentTypeRepo& repo) +{ + const DocumentType* type(repo.getDocumentType("testdoc")); + DocumentId id("doc:crawler/http://www.ntnu.no/"); + + DocumentUpdate::UP up(new DocumentUpdate(*type, id)); + up->addUpdate(FieldUpdate(type->getField("intattr")) + .addUpdate(AssignValueUpdate(IntFieldValue(7)))); + up->addUpdate(FieldUpdate(type->getField("stringattr")) + .addUpdate(AssignValueUpdate( + StringFieldValue("New value")))); + up->addUpdate(FieldUpdate(type->getField("arrayattr")) + .addUpdate(AddValueUpdate(IntFieldValue(123))) + .addUpdate(AddValueUpdate(IntFieldValue(456)))); + up->addUpdate(FieldUpdate(type->getField("arrayattr")) + .addUpdate(RemoveValueUpdate(IntFieldValue(123))) + .addUpdate(RemoveValueUpdate(IntFieldValue(456))) + .addUpdate(RemoveValueUpdate(IntFieldValue(789)))); + return up; +} + +} // anonymous ns + +void TestXml::testSimpleUsage() +{ + DocumentTypeRepo repo(readDocumenttypesConfig("data/defaultdoctypes.cfg")); + Document::UP doc1(createTestDocument(repo)); + doc1->setValue(doc1->getField("stringattr"), StringFieldValue("tjohei���")); + + std::string expected = + "<document documenttype=\"testdoc\" documentid=\"doc:crawler/http://www.ntnu.no/\">\n" + " <doubleattr>17.7862</doubleattr>\n" + " <intattr>50</intattr>\n" + " <floatattr>3.56</floatattr>\n" + " <longattr>346234765345239657</longattr>\n" + " <byteattr>74</byteattr>\n" + " <rawarrayattr>\n" + " <item binaryencoding=\"base64\">cmVh</item>\n" + " <item binaryencoding=\"base64\">cmVhZGE=</item>\n" + " <item binaryencoding=\"base64\">cmVhZGFibA==</item>\n" + " </rawarrayattr>\n" + " <rawattr binaryencoding=\"base64\">cmVhZGFibA==</rawattr>\n" + " <stringattr>tjohei���</stringattr>\n" + " <docfield>\n" + " <document documenttype=\"testdoc\" documentid=\"doc:crawler/http://www.ntnu.no/2\">\n" + " <stringattr>tjo hei paa du</stringattr>\n" + " </document>\n" + " </docfield>\n" + " <content type=\"contenttype\" encoding=\"encoding\" language=\"language\">humlepungens buffer</content>\n" + "</document>"; +} + +void TestXml::testDocumentUpdate() +{ + DocumentTypeRepo repo(readDocumenttypesConfig("data/defaultdoctypes.cfg")); + DocumentUpdate::UP up1(createTestDocumentUpdate(repo)); + + std::string expected = + "<document type=\"testdoc\" id=\"doc:crawler/http://www.ntnu.no/\">\n" + " <alter field=\"intattr\">\n" + " <assign>7</assign>\n" + " </alter>\n" + " <alter field=\"stringattr\">\n" + " <assign>New value</assign>\n" + " </alter>\n" + " <alter field=\"arrayattr\">\n" + " <add weight=\"1\">123</add>\n" + " <add weight=\"1\">456</add>\n" + " </alter>\n" + " <alter field=\"arrayattr\">\n" + " <remove>123</remove>\n" + " <remove>456</remove>\n" + " <remove>789</remove>\n" + " </alter>\n" + "</document>"; + CPPUNIT_ASSERT_EQUAL(expected, up1->toXml(" ")); +} + +} // document diff --git a/document/src/tests/urltypetest.cpp b/document/src/tests/urltypetest.cpp new file mode 100644 index 00000000000..0aa447b9e40 --- /dev/null +++ b/document/src/tests/urltypetest.cpp @@ -0,0 +1,54 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <vespa/document/datatype/urldatatype.h> +#include <cppunit/extensions/HelperMacros.h> + +namespace document { + +class UrlTypeTest : public CppUnit::TestFixture { +public: + void requireThatNameIsCorrect(); + void requireThatExpectedFieldsAreThere(); + + CPPUNIT_TEST_SUITE(UrlTypeTest); + CPPUNIT_TEST(requireThatNameIsCorrect); + CPPUNIT_TEST(requireThatExpectedFieldsAreThere); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(UrlTypeTest); + +void +UrlTypeTest::requireThatNameIsCorrect() +{ + const StructDataType &type = UrlDataType::getInstance(); + CPPUNIT_ASSERT_EQUAL(vespalib::string("url"), type.getName()); +} + +void +UrlTypeTest::requireThatExpectedFieldsAreThere() +{ + const StructDataType &type = UrlDataType::getInstance(); + Field field = type.getField("all"); + CPPUNIT_ASSERT_EQUAL(*DataType::STRING, field.getDataType()); + + field = type.getField("scheme"); + CPPUNIT_ASSERT_EQUAL(*DataType::STRING, field.getDataType()); + + field = type.getField("host"); + CPPUNIT_ASSERT_EQUAL(*DataType::STRING, field.getDataType()); + + field = type.getField("port"); + CPPUNIT_ASSERT_EQUAL(*DataType::STRING, field.getDataType()); + + field = type.getField("path"); + CPPUNIT_ASSERT_EQUAL(*DataType::STRING, field.getDataType()); + + field = type.getField("query"); + CPPUNIT_ASSERT_EQUAL(*DataType::STRING, field.getDataType()); + + field = type.getField("fragment"); + CPPUNIT_ASSERT_EQUAL(*DataType::STRING, field.getDataType()); +} + +} // document diff --git a/document/src/tests/vespaxml/fieldpathupdates.xml b/document/src/tests/vespaxml/fieldpathupdates.xml new file mode 100755 index 00000000000..c775509f070 --- /dev/null +++ b/document/src/tests/vespaxml/fieldpathupdates.xml @@ -0,0 +1,54 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <update documenttype="news" documentid="doc:test:http://www.ntnu.no/"> + <assign fieldpath="url">assignUrl</assign> + <assign fieldpath="title">assignTitle</assign> + <assign fieldpath="last_downloaded">1</assign> + <assign fieldpath="value_long">2</assign> + <assign fieldpath="value_content">assignContent</assign> + <assign fieldpath="stringarr"> + <item>assignString1</item> + <item>assignString2</item> + </assign> + <assign fieldpath="intarr"> + <item>3</item> + <item>4</item> + </assign> + <assign fieldpath="longarr"> + <item>5</item> + <item>6</item> + </assign> + <assign fieldpath="bytearr"> + <item>7</item> + <item>8</item> + </assign> + <assign fieldpath="floatarr"> + <item>9</item> + <item>10</item> + </assign> + <assign fieldpath="weightedsetint"> + <item weight="11">11</item> + <item weight="12">12</item> + </assign> + <assign fieldpath="weightedsetstring"> + <item weight="13">assign13</item> + <item weight="14">assign14</item> + </assign> + + <add fieldpath="stringarr"> + <item>addString1</item> + <item>addString2</item> + </add> + <add fieldpath="longarr"> + <item>5</item> + </add> + + <assign fieldpath="weightedsetint{13}">13</assign> + <assign fieldpath="weightedsetint{14}">14</assign> + <assign fieldpath="weightedsetstring{add13}">1</assign> + <assign fieldpath="weightedsetstring{assign13}">130</assign> + + <remove fieldpath="weightedsetstring{assign14}"/> + <remove fieldpath="bytearr"/> + </update> +</vespafeed> diff --git a/document/src/tests/vespaxml/test1.expected.xml b/document/src/tests/vespaxml/test1.expected.xml new file mode 100644 index 00000000000..a369cfff4a6 --- /dev/null +++ b/document/src/tests/vespaxml/test1.expected.xml @@ -0,0 +1,5 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<document documenttype="news" documentid="doc:crawler:http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>100</last_downloaded> +</document> diff --git a/document/src/tests/vespaxml/test1.xml b/document/src/tests/vespaxml/test1.xml new file mode 100644 index 00000000000..1c155182b14 --- /dev/null +++ b/document/src/tests/vespaxml/test1.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd idprefix="doc:crawler:"> + <document documenttype="news" documentid="http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>100</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test10.xml b/document/src/tests/vespaxml/test10.xml new file mode 100644 index 00000000000..b310f14da1c --- /dev/null +++ b/document/src/tests/vespaxml/test10.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler/http://www.ntnu.no/"> + <title>Test<Title</title> + <last_downloaded>hundred</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test11.xml b/document/src/tests/vespaxml/test11.xml new file mode 100644 index 00000000000..d8cdb32697e --- /dev/null +++ b/document/src/tests/vespaxml/test11.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vesparemove> + <document type="news" id="doc:crawler/http://www.ntnu���.no/"> + </document> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test12.xml b/document/src/tests/vespaxml/test12.xml new file mode 100644 index 00000000000..1f2f3375ade --- /dev/null +++ b/document/src/tests/vespaxml/test12.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler/http://www.ntnu.no/"> + <title></title> + <last_downloaded></last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test13.xml b/document/src/tests/vespaxml/test13.xml new file mode 100644 index 00000000000..8f12ea42c93 --- /dev/null +++ b/document/src/tests/vespaxml/test13.xml @@ -0,0 +1,7 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <document type="news" id="doc:crawler:http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>100</last_downloaded> + </document> +</vespa> diff --git a/document/src/tests/vespaxml/test14.xml b/document/src/tests/vespaxml/test14.xml new file mode 100644 index 00000000000..7e0eaa270ae --- /dev/null +++ b/document/src/tests/vespaxml/test14.xml @@ -0,0 +1,8 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler/http://www.ntnu.no/"> + <last_downloaded>123</last_downloaded> + </doc> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test15.xml b/document/src/tests/vespaxml/test15.xml new file mode 100644 index 00000000000..f4b25cd0aea --- /dev/null +++ b/document/src/tests/vespaxml/test15.xml @@ -0,0 +1,8 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler/http://www.ntnu.no/"> + <title>TestTitle</tit> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test16.xml b/document/src/tests/vespaxml/test16.xml new file mode 100644 index 00000000000..727e1dc834b --- /dev/null +++ b/document/src/tests/vespaxml/test16.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <documentid type="news" id="doc:crawler:http://www.ntnu.no/"> + </documentid> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test17.xml b/document/src/tests/vespaxml/test17.xml new file mode 100644 index 00000000000..c0046253c79 --- /dev/null +++ b/document/src/tests/vespaxml/test17.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> + <vespaadd> + <document type="news" id="doc:crawler/http://www.ntnu.no/1"> + </document> + </vespaadd> + <vespaadd> + <document type="news" id="doc:crawler/http://www.ntnu.no/2"> + </document> + </vespaadd> diff --git a/document/src/tests/vespaxml/test18.xml b/document/src/tests/vespaxml/test18.xml new file mode 100644 index 00000000000..c8798f8fc43 --- /dev/null +++ b/document/src/tests/vespaxml/test18.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="article" id="doc:crawler/http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>100</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test2.expected.xml b/document/src/tests/vespaxml/test2.expected.xml new file mode 100644 index 00000000000..a369cfff4a6 --- /dev/null +++ b/document/src/tests/vespaxml/test2.expected.xml @@ -0,0 +1,5 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<document documenttype="news" documentid="doc:crawler:http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>100</last_downloaded> +</document> diff --git a/document/src/tests/vespaxml/test2.xml b/document/src/tests/vespaxml/test2.xml new file mode 100644 index 00000000000..9dae17e830f --- /dev/null +++ b/document/src/tests/vespaxml/test2.xml @@ -0,0 +1,7 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <document documenttype="news" documentid="doc:crawler:http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>100</last_downloaded> + </document> +</vespafeed> diff --git a/document/src/tests/vespaxml/test20.xml b/document/src/tests/vespaxml/test20.xml new file mode 100644 index 00000000000..867c4829c36 --- /dev/null +++ b/document/src/tests/vespaxml/test20.xml @@ -0,0 +1,8 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <endoffeed> + <name>default</name> + <generations>10</generations> + <increment>11</increment> + </endoffeed> +</vespafeed> diff --git a/document/src/tests/vespaxml/test21.xml b/document/src/tests/vespaxml/test21.xml new file mode 100644 index 00000000000..daad994b84b --- /dev/null +++ b/document/src/tests/vespaxml/test21.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler/http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>21474836480</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test22.xml b/document/src/tests/vespaxml/test22.xml new file mode 100644 index 00000000000..ec20024693d --- /dev/null +++ b/document/src/tests/vespaxml/test22.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler:http://www.ntnu.no/"> + <title>TestTitle</title> + <value_long>9223372036854775807</value_long> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test23.xml b/document/src/tests/vespaxml/test23.xml new file mode 100644 index 00000000000..68afd6ec565 --- /dev/null +++ b/document/src/tests/vespaxml/test23.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler:http://www.ntnu.no/"> + <title>TestTitle</title> + <value_long>-9223372036854775807</value_long> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test24.xml b/document/src/tests/vespaxml/test24.xml new file mode 100644 index 00000000000..b386bb2c092 --- /dev/null +++ b/document/src/tests/vespaxml/test24.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler:http://www.ntnu.no/"> + <title>TestTitle</title> + <value_long>18446744073709551615</value_long> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test25.xml b/document/src/tests/vespaxml/test25.xml new file mode 100644 index 00000000000..72a19e8da60 --- /dev/null +++ b/document/src/tests/vespaxml/test25.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler/http://www.ntnu.no/"> + <title>TestTitle</title> + <value_long>18446744073709551616</value_long> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test26.xml b/document/src/tests/vespaxml/test26.xml new file mode 100644 index 00000000000..698e615c274 --- /dev/null +++ b/document/src/tests/vespaxml/test26.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler:http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>0x123</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test27.xml b/document/src/tests/vespaxml/test27.xml new file mode 100644 index 00000000000..94c5f3c1d32 --- /dev/null +++ b/document/src/tests/vespaxml/test27.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler/http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>0x123</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test28.xml b/document/src/tests/vespaxml/test28.xml new file mode 100644 index 00000000000..f1995d295bb --- /dev/null +++ b/document/src/tests/vespaxml/test28.xml @@ -0,0 +1,10 @@ +<!-- Test content --> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler:http://www.ntnu.no/"> + <title>TestTitleContent</title> + <value_content contenttype="text/html" encoding="utf-8" language="en"><![CDATA[<html><body><h1>This is the title</h1></body></html>]]></value_content> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test29.xml b/document/src/tests/vespaxml/test29.xml new file mode 100644 index 00000000000..1f33b9f7555 --- /dev/null +++ b/document/src/tests/vespaxml/test29.xml @@ -0,0 +1,18 @@ +<!-- Test field attribute --> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news"> + <field name="uri">doc:crawler:http://www.ntnu.no/</field> + <field name="title">TestTitleContent</field> + <field name="value_content" contenttype="text/html" encoding="UTF-8" language="NO">This is content</field> + <last_downloaded>345</last_downloaded> + </document> + <document type="news"> + <field name="uri">doc:crawler:http://www.ntnu.no/2</field> + <field name="title">TestTitleContent2</field> + <field name="value_content">This is content2</field> + <last_downloaded>345</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test3.xml b/document/src/tests/vespaxml/test3.xml new file mode 100644 index 00000000000..81d87679211 --- /dev/null +++ b/document/src/tests/vespaxml/test3.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vesparemove idprefix="doc:crawler:"> + <documentid type="news"> + <uri>http://www.ntnu.no/</uri> + <last_downloaded></last_downloaded> + </documentid> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test30.xml b/document/src/tests/vespaxml/test30.xml new file mode 100644 index 00000000000..6fbb6fae8ba --- /dev/null +++ b/document/src/tests/vespaxml/test30.xml @@ -0,0 +1,15 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed xmlns:xsd="http://www.w3.org/2001/XMLSchema-datatypes" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <vespaadd> + <document type="news" id="doc:crawler:http://www.ntnu.no/"> + <title xsi:type="xsd:hexBinary">E5AEB6E59BADE3808220044BE680AC</title> + <last_downloaded>100</last_downloaded> + </document> + <document type="news" id="doc:crawler:http://www.ntnu.org/"> + <title xsi:type="xsd:base64Binary">5a625Zut44CCIARL5oCs</title> + <last_downloaded>100</last_downloaded> + </document> + </vespaadd> +</vespafeed> + diff --git a/document/src/tests/vespaxml/test32.xml b/document/src/tests/vespaxml/test32.xml new file mode 100644 index 00000000000..b2f7976c124 --- /dev/null +++ b/document/src/tests/vespaxml/test32.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vesparemove> + <documentid id="doc:crawler:http://www.ntnu���.no/"/> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test33.xml b/document/src/tests/vespaxml/test33.xml new file mode 100644 index 00000000000..264be7f3e02 --- /dev/null +++ b/document/src/tests/vespaxml/test33.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vesparemove> + <documentid> + <uri>http://www.ntnu.no/</uri> + </documentid> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test34.xml b/document/src/tests/vespaxml/test34.xml new file mode 100644 index 00000000000..a3c80415899 --- /dev/null +++ b/document/src/tests/vespaxml/test34.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vesparemove> + <documentid id="http://www.ntnu.no/"/> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test35.xml b/document/src/tests/vespaxml/test35.xml new file mode 100644 index 00000000000..e02a755cc99 --- /dev/null +++ b/document/src/tests/vespaxml/test35.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler:http://www.ntnu.no/"> + <title binaryencoding="base64">VmVzcGEgcnVsZXM=</title> + <last_downloaded>100</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test36.xml b/document/src/tests/vespaxml/test36.xml new file mode 100644 index 00000000000..3eed0c4cb2a --- /dev/null +++ b/document/src/tests/vespaxml/test36.xml @@ -0,0 +1,43 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd idprefix="doc:crawler:"> + <document type="news" id="http://www.ntnu.no/1"> + <title>TestTitle1</title> + <last_downloaded>100</last_downloaded> + <stringarr> + <item>one</item> + <item>two</item> + <item>three</item> + <item>four</item> + <item>five</item> + </stringarr> + <intarr> + <item>1</item> + <item>2</item> + <item>3</item> + <item>4</item> + <item>5</item> + </intarr> + </document> + <document type="news" id="http://www.ntnu.no/2"> + <title>TestTitle2</title> + <intarr> + <item>o1</item> + </intarr> + </document> + <document type="news" id="http://www.ntnu.no/3"> + <title>TestTitle3</title> + <intarr> + <tem>1</tem> + </intarr> + </document> + <document type="news" id="http://www.ntnu.no/4"> + <title>TestTitle4</title> + <intarr></intarr> + </document> + <document type="news" id="http://www.ntnu.no/5"> + <title>TestTitle5</title> + <intarr>1</intarr> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test37.xml b/document/src/tests/vespaxml/test37.xml new file mode 100644 index 00000000000..1c2f6e01006 --- /dev/null +++ b/document/src/tests/vespaxml/test37.xml @@ -0,0 +1,23 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd idprefix="doc:crawler:"> + <document type="news" id="http://www.ntnu.no/1"> + <title>TestTitle1</title> + <last_downloaded>100</last_downloaded> + <weightedsetstring> + <item>one</item> + <item>two</item> + <item>three</item> + <item>four</item> + <item>five</item> + </weightedsetstring> + <weightedsetint> + <item>1</item> + <item>2</item> + <item>3</item> + <item>4</item> + <item>5</item> + </weightedsetint> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test4.xml b/document/src/tests/vespaxml/test4.xml new file mode 100644 index 00000000000..7c0c3682cd3 --- /dev/null +++ b/document/src/tests/vespaxml/test4.xml @@ -0,0 +1,23 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + + <vespaadd idprefix="doc:crawler:"> + <document type="news" id="http://www.uio.no/"> + <title>TestTitle</title> + <last_downloaded>100</last_downloaded> + </document> + + <document type="news" id="http://www.dagbladet.no/"> + <title>Title2</title> + <last_downloaded>100</last_downloaded> + </document> + </vespaadd> + + <vespaadd> + <document type="news" id="doc:crawler2:http://www.vg.no/"> + <title>Title2</title> + <last_downloaded>100</last_downloaded> + </document> + </vespaadd> + +</vespafeed> diff --git a/document/src/tests/vespaxml/test40.xml b/document/src/tests/vespaxml/test40.xml new file mode 100644 index 00000000000..ecfb745415a --- /dev/null +++ b/document/src/tests/vespaxml/test40.xml @@ -0,0 +1,6 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <update documenttype="news" documentid="doc:crawler:http://www.ntnu.no/"> + <assign field="title">TestTitle</assign> + </update> +</vespafeed> diff --git a/document/src/tests/vespaxml/test41.xml b/document/src/tests/vespaxml/test41.xml new file mode 100644 index 00000000000..052f4076025 --- /dev/null +++ b/document/src/tests/vespaxml/test41.xml @@ -0,0 +1,22 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <update documenttype="news" documentid="doc:crawler:http://www.ntnu.no/"> + <assign field="stringarr"> + <item>First</item> + <item>Second</item> + </assign> + <add field="stringarr"> + <item>Third</item> + <item>Fourth</item> + </add> + <remove field="stringarr"> + <item>Fifth</item> + <item>Sixth</item> + </remove> + <assign field="intarr"> + <item>100</item> + <item>200</item> + </assign> + <assign field="intarr"></assign> + </update> +</vespafeed> diff --git a/document/src/tests/vespaxml/test42.xml b/document/src/tests/vespaxml/test42.xml new file mode 100644 index 00000000000..da3e94410df --- /dev/null +++ b/document/src/tests/vespaxml/test42.xml @@ -0,0 +1,22 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <update documenttype="news" documentid="doc:crawler:http://www.ntnu.no/"> + <assign field="weightedsetstring"> + <item weight="1">First</item> + <item weight="2">Second</item> + </assign> + <add field="weightedsetstring"> + <item weight="3">Third</item> + <item weight="4">Fourth</item> + </add> + <remove field="weightedsetstring"> + <item>Fifth</item> + <item>Sixth</item> + </remove> + <assign field="weightedsetint"> + <item weight="1">100</item> + <item weight="2">200</item> + </assign> + <assign field="weightedsetint"></assign> + </update> +</vespafeed> diff --git a/document/src/tests/vespaxml/test43.xml b/document/src/tests/vespaxml/test43.xml new file mode 100644 index 00000000000..16944dde9fa --- /dev/null +++ b/document/src/tests/vespaxml/test43.xml @@ -0,0 +1,32 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <document documenttype="news" documentid="doc:crawler:http://www.ntnu.no/"> + <value_long>1008</value_long> + <weightedsetstring> + <item weight="1">First</item> + <item weight="2">Second</item> + </weightedsetstring> + </document> + <update documenttype="news" documentid="doc:crawler:http://www.ntnu.no/"> + <increment field="value_long" by="1" /> + <decrement field="value_long" by="2" /> + <divide field="value_long" by="3" /> + <multiply field="value_long" by="4" /> + </update> + <update documenttype="news" documentid="doc:crawler:http://www.ntnu.no/"> + <alter field="value_long"> + <increment by="5" /> + <decrement by="6" /> + </alter> + <alter field="value_long"> + <divide by="7" /> + <multiply by="8" /> + </alter> + </update> + <update documenttype="news" documentid="doc:crawler:http://www.ntnu.no/"> + <increment field="weightedsetstring" by="9"> + <key>First</key> + <key>Second</key> + </increment> + </update> +</vespafeed> diff --git a/document/src/tests/vespaxml/test45.xml b/document/src/tests/vespaxml/test45.xml new file mode 100644 index 00000000000..eb13e59a0da --- /dev/null +++ b/document/src/tests/vespaxml/test45.xml @@ -0,0 +1,6 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <update documenttype="news" documentid="doc:crawler:http://www.ntnu.no/"> + <remove field="floatarr" /> + </update> +</vespafeed> diff --git a/document/src/tests/vespaxml/test46.xml b/document/src/tests/vespaxml/test46.xml new file mode 100644 index 00000000000..75d69f3a8b5 --- /dev/null +++ b/document/src/tests/vespaxml/test46.xml @@ -0,0 +1,10 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <document documenttype="news" documentid="doc:crawler:http://www.blargh.example.com/" lastmodified="98798787"> + <value_long>1008</value_long> + <weightedsetstring> + <item weight="1">First</item> + <item weight="2">Second</item> + </weightedsetstring> + </document> +</vespafeed> diff --git a/document/src/tests/vespaxml/test47.xml b/document/src/tests/vespaxml/test47.xml new file mode 100644 index 00000000000..5944158c7c6 --- /dev/null +++ b/document/src/tests/vespaxml/test47.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <!-- VERY BAD: --> + <vespaadd> + <documentid type="news"> + <uri>doc:this:is:very:bad</uri> + </documentid> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test48.xml b/document/src/tests/vespaxml/test48.xml new file mode 100644 index 00000000000..a9bdd94d098 --- /dev/null +++ b/document/src/tests/vespaxml/test48.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <!-- VERY BAD: --> + <documentid type="news" id="doc:this:is:even:worse" /> +</vespafeed> diff --git a/document/src/tests/vespaxml/test49.xml b/document/src/tests/vespaxml/test49.xml new file mode 100644 index 00000000000..dc8a67c64bb --- /dev/null +++ b/document/src/tests/vespaxml/test49.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <!-- VERY BAD: --> + <documentid type="news"> + <uri>doc:this:is:very:very:ugly</uri> + </documentid> +</vespafeed> diff --git a/document/src/tests/vespaxml/test5.expected.xml b/document/src/tests/vespaxml/test5.expected.xml new file mode 100644 index 00000000000..164799749c8 --- /dev/null +++ b/document/src/tests/vespaxml/test5.expected.xml @@ -0,0 +1,3 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<remove documentid="doc:crawler:http://www.ntnu.no/"/> + diff --git a/document/src/tests/vespaxml/test5.xml b/document/src/tests/vespaxml/test5.xml new file mode 100644 index 00000000000..a6a6f8f0f9f --- /dev/null +++ b/document/src/tests/vespaxml/test5.xml @@ -0,0 +1,6 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vesparemove> + <documentid documentid="doc:crawler:http://www.ntnu.no/"/> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test50.xml b/document/src/tests/vespaxml/test50.xml new file mode 100644 index 00000000000..0909bcf2795 --- /dev/null +++ b/document/src/tests/vespaxml/test50.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <!-- OK: --> + <vesparemove> + <documentid type="news" id="doc:this:an:ok:removal"/> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test51.xml b/document/src/tests/vespaxml/test51.xml new file mode 100644 index 00000000000..698a305d33c --- /dev/null +++ b/document/src/tests/vespaxml/test51.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <!-- OK: --> + <remove documentid="doc:this:is:also:an:ok:removal"/> +</vespafeed> diff --git a/document/src/tests/vespaxml/test52.xml b/document/src/tests/vespaxml/test52.xml new file mode 100644 index 00000000000..d569f5ada1b --- /dev/null +++ b/document/src/tests/vespaxml/test52.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <!-- OK: --> + <vespaadd> + <document documentid="doc:blah:blah:blah" documenttype="news"> + <value_long>2345</value_long> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test53.xml b/document/src/tests/vespaxml/test53.xml new file mode 100644 index 00000000000..28c2161c956 --- /dev/null +++ b/document/src/tests/vespaxml/test53.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <!-- HALF BAD, BUT NOT ALLOWED: --> + <vespaadd> + <document documentid="doc:blah:blah:blah" documenttype="news"> + <value_long>2345</value_long> + </document> + <documentid type="news" id="doc:half:bad:add"/> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test54.xml b/document/src/tests/vespaxml/test54.xml new file mode 100644 index 00000000000..90f41b6d32d --- /dev/null +++ b/document/src/tests/vespaxml/test54.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <!-- VERY BAD: --> + <vesparemove> + <document documentid="doc:bluh:bluh:bluh" documenttype="news"> + <value_long>45</value_long> + </document> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test55.xml b/document/src/tests/vespaxml/test55.xml new file mode 100644 index 00000000000..8fcd4a31d1f --- /dev/null +++ b/document/src/tests/vespaxml/test55.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <!-- HALF BAD, BUT NOT ALLOWED: --> + <vesparemove> + <documentid type="news" id="doc:this:remove:is:half:bad"/> + <document documentid="doc:bluh:bluh:bluh" documenttype="news"> + <value_long>45</value_long> + </document> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test56.xml b/document/src/tests/vespaxml/test56.xml new file mode 100644 index 00000000000..489ff494fc0 --- /dev/null +++ b/document/src/tests/vespaxml/test56.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <!-- VERY BAD: --> + <vesparemove> + <document documentid="doc:bluh:bluh:bluh" documenttype="news" /> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test57.xml b/document/src/tests/vespaxml/test57.xml new file mode 100644 index 00000000000..6653b0260af --- /dev/null +++ b/document/src/tests/vespaxml/test57.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <!-- OK: --> + <document documentid="doc:blih:blih:blih" documenttype="news"> + <value_long>235</value_long> + </document> +</vespafeed> diff --git a/document/src/tests/vespaxml/test58.xml b/document/src/tests/vespaxml/test58.xml new file mode 100644 index 00000000000..2577f3a0e26 --- /dev/null +++ b/document/src/tests/vespaxml/test58.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + + <document type="news" id="doc:music:http://music.yahoo.com"> + <url>http://music.yahoo.com</url> + <stringarr> + <item>yahoo<item> + <item>hello</item> + </stringarr> + </document> + + + +</vespafeed> diff --git a/document/src/tests/vespaxml/test59.xml b/document/src/tests/vespaxml/test59.xml new file mode 100644 index 00000000000..970f5232e7b --- /dev/null +++ b/document/src/tests/vespaxml/test59.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <update documentid="doc:blih:blih:blih" documenttype="news"> + <increment field="title" by="3549" /> + </update> +</vespafeed> diff --git a/document/src/tests/vespaxml/test6.xml b/document/src/tests/vespaxml/test6.xml new file mode 100644 index 00000000000..c67352bbb22 --- /dev/null +++ b/document/src/tests/vespaxml/test6.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vesparemove> + <documentid type="news" id="doc:crawler:http://www.ntnu���.no/"/> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test7.xml b/document/src/tests/vespaxml/test7.xml new file mode 100644 index 00000000000..2c346a5b5cf --- /dev/null +++ b/document/src/tests/vespaxml/test7.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed idprefix="doc:crawler:"> + <vespaadd> + <document type="news" id="http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>100.5</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test8.xml b/document/src/tests/vespaxml/test8.xml new file mode 100644 index 00000000000..cb9617fefd8 --- /dev/null +++ b/document/src/tests/vespaxml/test8.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" id="doc:crawler:http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>hundred</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test9.xml b/document/src/tests/vespaxml/test9.xml new file mode 100644 index 00000000000..8b86da414b8 --- /dev/null +++ b/document/src/tests/vespaxml/test9.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vesparemove> + <documentid id="doc:crawler/http://www.ntnu���.no/"/> + </vesparemove> +</vespafeed> diff --git a/document/src/tests/vespaxml/test_arraystruct.xml b/document/src/tests/vespaxml/test_arraystruct.xml new file mode 100644 index 00000000000..5eeae4aab2d --- /dev/null +++ b/document/src/tests/vespaxml/test_arraystruct.xml @@ -0,0 +1,19 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <document documenttype="news" documentid="doc:test:struct"> + <mystructarr> + <item> + <intval>36</intval> + <stringval>test</stringval> + </item> + <item> + <intval>39</intval> + <stringval>test2</stringval> + </item> + <item> + <intval>100</intval> + <stringval>cooool</stringval> + </item> + </mystructarr> + </document> +</vespafeed> diff --git a/document/src/tests/vespaxml/test_doc5.xml b/document/src/tests/vespaxml/test_doc5.xml new file mode 100644 index 00000000000..fa6d9c4726e --- /dev/null +++ b/document/src/tests/vespaxml/test_doc5.xml @@ -0,0 +1,8 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <endoffeed> + <name>default</name> + <generation>10</generation> + <increment>20</increment> + </endoffeed> +</vespafeed> diff --git a/document/src/tests/vespaxml/test_doc6.xml b/document/src/tests/vespaxml/test_doc6.xml new file mode 100644 index 00000000000..2f32701d88e --- /dev/null +++ b/document/src/tests/vespaxml/test_doc6.xml @@ -0,0 +1,6 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <endoffeed> + <name>default</name> + </endoffeed> +</vespafeed> diff --git a/document/src/tests/vespaxml/test_doc8.xml b/document/src/tests/vespaxml/test_doc8.xml new file mode 100644 index 00000000000..d636e7baa96 --- /dev/null +++ b/document/src/tests/vespaxml/test_doc8.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd> + <document type="news" version="13" id="http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>100</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test_externalentity.xml b/document/src/tests/vespaxml/test_externalentity.xml new file mode 100644 index 00000000000..1d87319b106 --- /dev/null +++ b/document/src/tests/vespaxml/test_externalentity.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<!DOCTYPE vespafeed [<!ENTITY xxe SYSTEM "xxe.txt">]> +<vespafeed> + <vespaadd idprefix="doc:crawler:"> + <document documenttype="news" documentid="http://www.ntnu.no/"> + <title>&xxe;</title> + <last_downloaded>100</last_downloaded> + </document> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/test_idprefix.xml b/document/src/tests/vespaxml/test_idprefix.xml new file mode 100644 index 00000000000..e3a177d6606 --- /dev/null +++ b/document/src/tests/vespaxml/test_idprefix.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + + <document type="news" id="http://music.yahoo.com/bobdylan/BestOf"> + <title>Best of Bob Dylan</title> + </document> + + <document type="news" id="http://music.yahoo.com/metallica/BestOf"> + <title>Best of Metallica</title> + </document> + +</vespafeed> diff --git a/document/src/tests/vespaxml/test_struct.xml b/document/src/tests/vespaxml/test_struct.xml new file mode 100644 index 00000000000..c18c9263b4a --- /dev/null +++ b/document/src/tests/vespaxml/test_struct.xml @@ -0,0 +1,9 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <document documenttype="news" documentid="doc:test:struct"> + <mystruct> + <intval>36</intval> + <stringval>test</stringval> + </mystruct> + </document> +</vespafeed> diff --git a/document/src/tests/vespaxml/test_update1.xml b/document/src/tests/vespaxml/test_update1.xml new file mode 100644 index 00000000000..27c7d6214d3 --- /dev/null +++ b/document/src/tests/vespaxml/test_update1.xml @@ -0,0 +1,12 @@ +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<vespafeed> + <vespaadd idprefix="doc:crawler:"> + <document type="news" id="http://www.ntnu.no/"> + <title>TestTitle</title> + <last_downloaded>100</last_downloaded> + </document> + <update documenttype="news" documentid="http://www.ntnu.no/"> + <divide field="last_downloaded" by="0" /> + </update> + </vespaadd> +</vespafeed> diff --git a/document/src/tests/vespaxml/vespaxmldoctype.cfg b/document/src/tests/vespaxml/vespaxmldoctype.cfg new file mode 100644 index 00000000000..3a4aa280e0d --- /dev/null +++ b/document/src/tests/vespaxml/vespaxmldoctype.cfg @@ -0,0 +1,133 @@ +enablecompression false +datatype[12] +datatype[0].id 1002 +datatype[0].arraytype[1] +datatype[0].arraytype[0].datatype 2 +datatype[0].weightedsettype[0] +datatype[0].structtype[0] +datatype[0].documenttype[0] +datatype[1].id 1000 +datatype[1].arraytype[1] +datatype[1].arraytype[0].datatype 0 +datatype[1].weightedsettype[0] +datatype[1].structtype[0] +datatype[1].documenttype[0] +datatype[2].id 1004 +datatype[2].arraytype[1] +datatype[2].arraytype[0].datatype 4 +datatype[2].weightedsettype[0] +datatype[2].structtype[0] +datatype[2].documenttype[0] +datatype[3].id 1016 +datatype[3].arraytype[1] +datatype[3].arraytype[0].datatype 16 +datatype[3].weightedsettype[0] +datatype[3].structtype[0] +datatype[3].documenttype[0] +datatype[4].id 1001 +datatype[4].arraytype[1] +datatype[4].arraytype[0].datatype 1 +datatype[4].weightedsettype[0] +datatype[4].structtype[0] +datatype[4].documenttype[0] +datatype[5].id 2001 +datatype[5].arraytype[0] +datatype[5].weightedsettype[1] +datatype[5].weightedsettype[0].datatype 0 +datatype[5].weightedsettype[0].createifnonexistant false +datatype[5].weightedsettype[0].removeifzero false +datatype[5].structtype[0] +datatype[5].documenttype[0] +datatype[6].id 2002 +datatype[6].arraytype[0] +datatype[6].weightedsettype[1] +datatype[6].weightedsettype[0].datatype 2 +datatype[6].weightedsettype[0].createifnonexistant false +datatype[6].weightedsettype[0].removeifzero false +datatype[6].structtype[0] +datatype[6].documenttype[0] +datatype[7].id 3000 +datatype[7].arraytype[0] +datatype[7].weightedsettype[0] +datatype[7].structtype[1] +datatype[7].structtype[0].name news.mystruct +datatype[7].structtype[0].version 0 +datatype[7].structtype[0].field[2] +datatype[7].structtype[0].field[0].name intval +datatype[7].structtype[0].field[0].id[0] +datatype[7].structtype[0].field[0].datatype 0 +datatype[7].structtype[0].field[1].name stringval +datatype[7].structtype[0].field[1].id[0] +datatype[7].structtype[0].field[1].datatype 2 +datatype[7].documenttype[0] +datatype[8].id 103000 +datatype[8].arraytype[1] +datatype[8].arraytype[0].datatype 3000 +datatype[8].weightedsettype[0] +datatype[8].structtype[0] +datatype[8].documenttype[0] +datatype[9].id 5000 +datatype[9].arraytype[0] +datatype[9].weightedsettype[0] +datatype[9].structtype[1] +datatype[9].structtype[0].name news.header +datatype[9].structtype[0].version 0 +datatype[9].structtype[0].field[13] +datatype[9].structtype[0].field[0].name url +datatype[9].structtype[0].field[0].id[0] +datatype[9].structtype[0].field[0].datatype 10 +datatype[9].structtype[0].field[1].name title +datatype[9].structtype[0].field[1].id[0] +datatype[9].structtype[0].field[1].datatype 2 +datatype[9].structtype[0].field[2].name last_downloaded +datatype[9].structtype[0].field[2].id[0] +datatype[9].structtype[0].field[2].datatype 0 +datatype[9].structtype[0].field[3].name value_long +datatype[9].structtype[0].field[3].id[0] +datatype[9].structtype[0].field[3].datatype 4 +datatype[9].structtype[0].field[4].name stringarr +datatype[9].structtype[0].field[4].id[0] +datatype[9].structtype[0].field[4].datatype 1002 +datatype[9].structtype[0].field[5].name intarr +datatype[9].structtype[0].field[5].id[0] +datatype[9].structtype[0].field[5].datatype 1000 +datatype[9].structtype[0].field[6].name longarr +datatype[9].structtype[0].field[6].id[0] +datatype[9].structtype[0].field[6].datatype 1004 +datatype[9].structtype[0].field[7].name bytearr +datatype[9].structtype[0].field[7].id[0] +datatype[9].structtype[0].field[7].datatype 1016 +datatype[9].structtype[0].field[8].name floatarr +datatype[9].structtype[0].field[8].id[0] +datatype[9].structtype[0].field[8].datatype 1001 +datatype[9].structtype[0].field[9].name weightedsetint +datatype[9].structtype[0].field[9].id[0] +datatype[9].structtype[0].field[9].datatype 2001 +datatype[9].structtype[0].field[10].name weightedsetstring +datatype[9].structtype[0].field[10].id[0] +datatype[9].structtype[0].field[10].datatype 2002 +datatype[9].structtype[0].field[11].name mystruct +datatype[9].structtype[0].field[11].id[0] +datatype[9].structtype[0].field[11].datatype 3000 +datatype[9].structtype[0].field[12].name mystructarr +datatype[9].structtype[0].field[12].id[0] +datatype[9].structtype[0].field[12].datatype 103000 +datatype[9].documenttype[0] +datatype[10].id 5001 +datatype[10].arraytype[0] +datatype[10].weightedsettype[0] +datatype[10].structtype[1] +datatype[10].structtype[0].name news.body +datatype[10].structtype[0].version 0 +datatype[10].structtype[0].field[0] +datatype[10].documenttype[0] +datatype[11].id 5002 +datatype[11].arraytype[0] +datatype[11].weightedsettype[0] +datatype[11].structtype[0] +datatype[11].documenttype[1] +datatype[11].documenttype[0].name news +datatype[11].documenttype[0].version 0 +datatype[11].documenttype[0].inherits[0] +datatype[11].documenttype[0].headerstruct 5000 +datatype[11].documenttype[0].bodystruct 5001 diff --git a/document/src/tests/vespaxml/xxe.txt b/document/src/tests/vespaxml/xxe.txt new file mode 100644 index 00000000000..c8c1b8233cc --- /dev/null +++ b/document/src/tests/vespaxml/xxe.txt @@ -0,0 +1 @@ +scary xxe exploit
\ No newline at end of file diff --git a/document/src/tests/weightedsetfieldvaluetest.cpp b/document/src/tests/weightedsetfieldvaluetest.cpp new file mode 100644 index 00000000000..bc1bdca6028 --- /dev/null +++ b/document/src/tests/weightedsetfieldvaluetest.cpp @@ -0,0 +1,324 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include <vespa/document/fieldvalue/fieldvalues.h> +#include <vespa/document/serialization/vespadocumentdeserializer.h> +#include <vespa/vdstestlib/cppunit/macros.h> +#include <vespa/vespalib/objects/nbostream.h> + +using vespalib::nbostream; + +namespace document { + +struct WeightedSetFieldValueTest : public CppUnit::TestFixture { + void setUp() {} + void tearDown() {} + + void testWeightedSet(); + void testAddIgnoreZeroWeight(); + + CPPUNIT_TEST_SUITE(WeightedSetFieldValueTest); + CPPUNIT_TEST(testWeightedSet); + CPPUNIT_TEST(testAddIgnoreZeroWeight); + CPPUNIT_TEST_SUITE_END(); + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(WeightedSetFieldValueTest); + +namespace { +template <typename T> +void deserialize(const ByteBuffer &buffer, T &value) { + uint16_t version = Document::getNewestSerializationVersion(); + nbostream stream(buffer.getBufferAtPos(), buffer.getRemaining()); + DocumentTypeRepo repo; + VespaDocumentDeserializer deserializer(repo, stream, version); + deserializer.read(value); +} + +template<typename Type1, typename Type2> +void verifyFailedAssignment(Type1& lval, const Type2& rval) +{ + try{ + lval = rval; + CPPUNIT_FAIL("Failed to check type equality in operator="); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot assign value of type", e.what()); + } + try{ + lval.assign(rval); + CPPUNIT_FAIL("Failed to check type equality in assign()"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot assign value of type", e.what()); + } +} + +template<typename Type> +void verifyFailedUpdate(Type& lval, const FieldValue& rval) +{ + try{ + lval.add(rval); + CPPUNIT_FAIL("Failed to check type equality in add()"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("These types are not compatible", e.what()); + } + try{ + lval.contains(rval); + CPPUNIT_FAIL("Failed to check type equality in contains()"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("These types are not compatible", e.what()); + } + try{ + lval.remove(rval); + CPPUNIT_FAIL("Failed to check type equality in remove()"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("These types are not compatible", e.what()); + } +} +} // namespace + +void WeightedSetFieldValueTest::testWeightedSet() +{ + WeightedSetDataType type(*DataType::INT, false, false); + WeightedSetFieldValue value(type); + + // Initially empty + CPPUNIT_ASSERT_EQUAL(size_t(0), value.size()); + CPPUNIT_ASSERT(value.isEmpty()); + CPPUNIT_ASSERT(!value.contains(IntFieldValue(1))); + + CPPUNIT_ASSERT(value.add(IntFieldValue(1))); + + // Not empty + CPPUNIT_ASSERT_EQUAL(size_t(1), value.size()); + CPPUNIT_ASSERT(!value.isEmpty()); + CPPUNIT_ASSERT(value.contains(IntFieldValue(1))); + + // Adding some more + CPPUNIT_ASSERT(value.add(IntFieldValue(2), 5)); + CPPUNIT_ASSERT(value.add(IntFieldValue(3), 6)); + + // Not empty + CPPUNIT_ASSERT_EQUAL(size_t(3), value.size()); + CPPUNIT_ASSERT(!value.isEmpty()); + CPPUNIT_ASSERT_EQUAL(1, value.get(IntFieldValue(1))); + CPPUNIT_ASSERT_EQUAL(5, value.get(IntFieldValue(2))); + CPPUNIT_ASSERT_EQUAL(6, value.get(IntFieldValue(3))); + + // Serialize & equality + std::unique_ptr<ByteBuffer> buffer(value.serialize()); + buffer->flip(); + WeightedSetFieldValue value2(type); + CPPUNIT_ASSERT(value != value2); + deserialize(*buffer, value2); + CPPUNIT_ASSERT_EQUAL(value, value2); + + // Various ways of removing + { + // By value + buffer->setPos(0); + deserialize(*buffer, value2); + CPPUNIT_ASSERT(value2.remove(IntFieldValue(1))); + CPPUNIT_ASSERT(!value2.contains(IntFieldValue(1))); + CPPUNIT_ASSERT_EQUAL(size_t(2), value2.size()); + + // Clearing all + buffer->setPos(0); + deserialize(*buffer, value2); + value2.clear(); + CPPUNIT_ASSERT(!value2.contains(IntFieldValue(1))); + CPPUNIT_ASSERT_EQUAL(size_t(0), value2.size()); + CPPUNIT_ASSERT(value2.isEmpty()); + } + + // Updating + value2 = value; + CPPUNIT_ASSERT_EQUAL(value, value2); + CPPUNIT_ASSERT(!value2.add(IntFieldValue(2), 10)); // false = overwritten + CPPUNIT_ASSERT(value2.add(IntFieldValue(17), 9)); // true = added new + CPPUNIT_ASSERT_EQUAL(10, value2.get(IntFieldValue(2))); + CPPUNIT_ASSERT(value != value2); + value2.assign(value); + CPPUNIT_ASSERT_EQUAL(value, value2); + WeightedSetFieldValue::UP valuePtr(value2.clone()); + CPPUNIT_ASSERT_EQUAL(value, *valuePtr); + + // Iterating + const WeightedSetFieldValue& constVal(value); + for(WeightedSetFieldValue::const_iterator it = constVal.begin(); + it != constVal.end(); ++it) + { + const FieldValue& fval1(*it->first); + (void) fval1; + CPPUNIT_ASSERT_EQUAL((uint32_t) IntFieldValue::classId, + it->first->getClass().id()); + const IntFieldValue& val = dynamic_cast<const IntFieldValue&>(*it->second); + (void) val; + } + value2 = value; + for(WeightedSetFieldValue::iterator it = value2.begin(); + it != value2.end(); ++it) + { + IntFieldValue& val = dynamic_cast<IntFieldValue&>(*it->second); + val.setValue(7); + } + CPPUNIT_ASSERT(value != value2); + CPPUNIT_ASSERT_EQUAL(7, value2.get(IntFieldValue(2))); + + // Comparison + value2 = value; + CPPUNIT_ASSERT_EQUAL(0, value.compare(value2)); + value2.remove(IntFieldValue(1)); + CPPUNIT_ASSERT(value.compare(value2) > 0); + CPPUNIT_ASSERT(value2.compare(value) < 0); + value2 = value; + value2.add(IntFieldValue(7)); + CPPUNIT_ASSERT(value.compare(value2) < 0); + CPPUNIT_ASSERT(value2.compare(value) > 0); + + // Output + CPPUNIT_ASSERT_EQUAL( + std::string( + "WeightedSet<Int>(\n" + " 1 - weight 1,\n" + " 2 - weight 5,\n" + " 3 - weight 6\n" + ")"), + value.toString(false)); + CPPUNIT_ASSERT_EQUAL( + std::string( + " WeightedSet<Int>(\n" + ".. 1 - weight 1,\n" + ".. 2 - weight 5,\n" + ".. 3 - weight 6\n" + "..)"), + " " + value.toString(true, "..")); + CPPUNIT_ASSERT_EQUAL( + std::string( + "<value>\n" + " <item weight=\"1\">1</item>\n" + " <item weight=\"5\">2</item>\n" + " <item weight=\"6\">3</item>\n" + "</value>"), + value.toXml(" ")); + + // Failure situations. + + // Refuse to accept non-weightedset types + try{ + ArrayDataType arrayType(*DataType::STRING); + WeightedSetFieldValue value6(arrayType); + CPPUNIT_FAIL("Didn't complain about non-weightedset type"); + } catch (std::exception& e) { + CPPUNIT_ASSERT_CONTAIN("Cannot generate a weighted set value with " + "non-weighted set type", e.what()); + } + + // Verify that datatypes are verified + // Created almost equal types to try to get it to fail + WeightedSetDataType type1(*DataType::INT, false, false); + WeightedSetDataType type2(*DataType::LONG, false, false); + WeightedSetDataType type3(type1, false, false); + WeightedSetDataType type4(type2, false, false); + WeightedSetDataType type5(type2, false, true); + WeightedSetDataType type6(type2, true, false); + + // Type differs in nested of nested type (verify recursivity) + { + WeightedSetFieldValue value3(type3); + WeightedSetFieldValue value4(type4); + verifyFailedAssignment(value3, value4); + } + // Type arguments differ + { + WeightedSetFieldValue value4(type4); + WeightedSetFieldValue value5(type5); + WeightedSetFieldValue value6(type6); + verifyFailedAssignment(value4, value5); + verifyFailedAssignment(value4, value6); + verifyFailedAssignment(value5, value4); + verifyFailedAssignment(value5, value6); + verifyFailedAssignment(value6, value4); + verifyFailedAssignment(value6, value5); + } + // Updates are checked too + { + WeightedSetFieldValue value3(type3); + WeightedSetFieldValue subValue(type2); + subValue.add(LongFieldValue(4)); + verifyFailedUpdate(value3, subValue); + } + + // Compare see difference even of close types. + { + WeightedSetFieldValue subValue2(type2); + subValue2.add(LongFieldValue(3)); + WeightedSetFieldValue value3(type3); + WeightedSetFieldValue value4(type4); + value4.add(subValue2); + CPPUNIT_ASSERT(value3.compare(value4) != 0); + } + + // Test createIfNonExisting and removeIfZero + { + WeightedSetDataType mytype1(*DataType::STRING, false, false); + WeightedSetDataType mytype2(*DataType::STRING, true, true); + CPPUNIT_ASSERT_EQUAL(*DataType::TAG, static_cast<DataType &>(mytype2)); + + WeightedSetFieldValue val1(mytype1); + val1.add("foo", 4); + try{ + val1.increment("bar", 2); + CPPUNIT_FAIL("Expected exception incrementing with " + "createIfNonExistent set false"); + } catch (std::exception& e) {} + try{ + val1.decrement("bar", 2); + CPPUNIT_FAIL("Expected exception incrementing with " + "createIfNonExistent set false"); + } catch (std::exception& e) {} + val1.increment("foo", 6); + CPPUNIT_ASSERT_EQUAL(10, val1.get("foo")); + val1.decrement("foo", 3); + CPPUNIT_ASSERT_EQUAL(7, val1.get("foo")); + val1.decrement("foo", 7); + CPPUNIT_ASSERT(val1.contains("foo")); + + WeightedSetFieldValue val2(mytype2); + val2.add("foo", 4); + val2.increment("bar", 2); + CPPUNIT_ASSERT_EQUAL(2, val2.get("bar")); + val2.decrement("bar", 4); + CPPUNIT_ASSERT_EQUAL(-2, val2.get("bar")); + val2.increment("bar", 2); + CPPUNIT_ASSERT(!val2.contains("bar")); + + val2.decrement("foo", 4); + CPPUNIT_ASSERT(!val2.contains("foo")); + + val2.decrement("foo", 4); + CPPUNIT_ASSERT_EQUAL(-4, val2.get("foo")); + + val2.add("foo", 0); + CPPUNIT_ASSERT(!val2.contains("foo")); + } +} + +void +WeightedSetFieldValueTest::testAddIgnoreZeroWeight() +{ + // Data type with auto-create and remove-if-zero set. + WeightedSetDataType wsetType(*DataType::STRING, true, true); + WeightedSetFieldValue ws(wsetType); + + ws.addIgnoreZeroWeight(StringFieldValue("yarn"), 0); + CPPUNIT_ASSERT(ws.contains("yarn")); + CPPUNIT_ASSERT_EQUAL(0, ws.get("yarn")); + + ws.addIgnoreZeroWeight(StringFieldValue("flarn"), 1); + CPPUNIT_ASSERT(ws.contains("flarn")); + CPPUNIT_ASSERT_EQUAL(1, ws.get("flarn")); +} + +} // document + diff --git a/document/src/tests/weightedsetfieldvaluetest.h b/document/src/tests/weightedsetfieldvaluetest.h new file mode 100644 index 00000000000..8cf52d9ea0e --- /dev/null +++ b/document/src/tests/weightedsetfieldvaluetest.h @@ -0,0 +1,6 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/* $Id$*/ + +#pragma once + + |