diff options
Diffstat (limited to 'document/src/tests/repo/documenttyperepo_test.cpp')
-rw-r--r-- | document/src/tests/repo/documenttyperepo_test.cpp | 519 |
1 files changed, 519 insertions, 0 deletions
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(); } |