diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-02-15 00:27:11 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-02-15 00:27:11 +0100 |
commit | 253492406f04657fe71e6c0c6603496241280478 (patch) | |
tree | 5a9c1b785a5559025ff7d26bf9ed880ce98ff0ce /test | |
parent | 551b7be64f207845cb05b8ec593f9bf2d7f0c940 (diff) | |
parent | b708dd4cce828c1089a18fefcc22804f7cdad908 (diff) |
Merge branch 'master' into astoecke_parser_stack_new
Conflicts:
application/CMakeLists.txt
application/src/core/parser/stack/DocumentHandler.hpp
application/src/core/parser/stack/DomainHandler.hpp
application/src/core/parser/stack/ImportIncludeHandler.hpp
Diffstat (limited to 'test')
-rw-r--r-- | test/core/common/VariantReaderTest.cpp | 122 | ||||
-rw-r--r-- | test/core/model/DocumentTest.cpp | 42 | ||||
-rw-r--r-- | test/core/model/DomainTest.cpp | 236 | ||||
-rw-r--r-- | test/core/model/TestAdvanced.hpp | 24 | ||||
-rw-r--r-- | test/core/model/TestDomain.hpp | 18 | ||||
-rw-r--r-- | test/formats/osxml/OsxmlParserTest.cpp | 100 | ||||
-rw-r--r-- | test/plugins/html/DemoOutputTest.cpp | 23 |
7 files changed, 447 insertions, 118 deletions
diff --git a/test/core/common/VariantReaderTest.cpp b/test/core/common/VariantReaderTest.cpp index f6a699b..a23af09 100644 --- a/test/core/common/VariantReaderTest.cpp +++ b/test/core/common/VariantReaderTest.cpp @@ -205,7 +205,7 @@ TEST(VariantReader, parseUnescapedString) // Simple case with whitespace { - CharReader reader(" hello world ; "); + CharReader reader(" hello world ; aha"); auto res = VariantReader::parseUnescapedString(reader, logger, {';'}); ASSERT_TRUE(res.first); ASSERT_EQ("hello world", res.second); @@ -228,6 +228,54 @@ TEST(VariantReader, parseUnescapedString) } } +TEST(VariantReader, parseBool) +{ + // Valid bools + { + CharReader reader(" true "); + auto res = VariantReader::parseBool(reader, logger); + ASSERT_TRUE(res.first); + ASSERT_TRUE(res.second); + } + { + CharReader reader(" false "); + auto res = VariantReader::parseBool(reader, logger); + ASSERT_TRUE(res.first); + ASSERT_FALSE(res.second); + } + { + CharReader reader(" true bla"); + auto res = VariantReader::parseBool(reader, logger); + ASSERT_TRUE(res.first); + ASSERT_TRUE(res.second); + reader.consumeWhitespace(); + char c; + ASSERT_TRUE(reader.read(c)); + ASSERT_EQ('b', c); + ASSERT_TRUE(reader.read(c)); + ASSERT_EQ('l', c); + ASSERT_TRUE(reader.read(c)); + ASSERT_EQ('a', c); + ASSERT_FALSE(reader.read(c)); + } + // invalid bools. + { + CharReader reader(" bla "); + auto res = VariantReader::parseBool(reader, logger); + ASSERT_FALSE(res.first); + } + { + CharReader reader(" TRUE "); + auto res = VariantReader::parseBool(reader, logger); + ASSERT_FALSE(res.first); + } + { + CharReader reader(" tr ue "); + auto res = VariantReader::parseBool(reader, logger); + ASSERT_FALSE(res.first); + } +} + static const std::unordered_set<char> noDelim; TEST(VariantReader, parseInteger) @@ -574,6 +622,13 @@ TEST(VariantReader, parseObject) auto res = VariantReader::parseObject(reader, logger); ASSERT_FALSE(res.first); } + + // Mutliple commas + { + CharReader reader("[r=50,,t=70, b=70,g=60]"); + auto res = VariantReader::parseObject(reader, logger); + ASSERT_FALSE(res.first); + } } TEST(VariantReader, parseCardinality) @@ -1134,5 +1189,70 @@ TEST(VariantReader, parseGenericComplex) ASSERT_TRUE(reader.peek(c)); ASSERT_EQ(';', c); } + +TEST(VariantReader, parseTyped) +{ + { + auto res = VariantReader::parseTyped(VariantType::BOOL, "true", logger); + ASSERT_TRUE(res.first); + ASSERT_EQ(VariantType::BOOL, res.second.getType()); + ASSERT_TRUE(res.second.asBool()); + } + { + auto res = + VariantReader::parseTyped(VariantType::INT, " 1254", logger); + ASSERT_TRUE(res.first); + ASSERT_EQ(VariantType::INT, res.second.getType()); + ASSERT_EQ(1254, res.second.asInt()); + } + { + auto res = + VariantReader::parseTyped(VariantType::DOUBLE, " 3.14", logger); + ASSERT_TRUE(res.first); + ASSERT_EQ(VariantType::DOUBLE, res.second.getType()); + ASSERT_EQ(3.14, res.second.asDouble()); + } + { + auto res = VariantReader::parseTyped(VariantType::STRING, + "\'my string\'", logger); + ASSERT_TRUE(res.first); + ASSERT_EQ(VariantType::STRING, res.second.getType()); + ASSERT_EQ("my string", res.second.asString()); + } + { + auto res = + VariantReader::parseTyped(VariantType::STRING, "my string", logger); + ASSERT_FALSE(res.first); + } + { + auto res = + VariantReader::parseTyped(VariantType::ARRAY, "[1, 4, 5]", logger); + ASSERT_TRUE(res.first); + ASSERT_EQ(VariantType::ARRAY, res.second.getType()); + Variant::arrayType actual = res.second.asArray(); + Variant::arrayType expected{{1}, {4}, {5}}; + ASSERT_EQ(expected, actual); + } + { + auto res = VariantReader::parseTyped( + VariantType::MAP, "[a=\"str\", b=true, i=4]", logger); + ASSERT_TRUE(res.first); + ASSERT_EQ(VariantType::MAP, res.second.getType()); + Variant::mapType actual = res.second.asMap(); + Variant::mapType expected{{"a", {"str"}}, {"b", {true}}, {"i", {4}}}; + ASSERT_EQ(expected, actual); + } + { + auto res = VariantReader::parseTyped(VariantType::CARDINALITY, + "{1-2, >18}", logger); + ASSERT_TRUE(res.first); + ASSERT_EQ(VariantType::CARDINALITY, res.second.getType()); + Variant::cardinalityType actual = res.second.asCardinality(); + Variant::cardinalityType expected; + expected.merge({1, 2}); + expected.merge(Variant::rangeType::typeRangeFrom(19)); + ASSERT_EQ(expected, actual); + } +} } diff --git a/test/core/model/DocumentTest.cpp b/test/core/model/DocumentTest.cpp index 3164b7e..0c6eea6 100644 --- a/test/core/model/DocumentTest.cpp +++ b/test/core/model/DocumentTest.cpp @@ -67,11 +67,9 @@ TEST(Document, construct) ASSERT_EQ("text", text->getDescriptor()->getName()); ASSERT_TRUE(text->getDescriptor()->hasField()); ASSERT_EQ(1U, text->getField().size()); - ASSERT_TRUE( - text->getField()[0]->isa(typeOf<DocumentPrimitive>())); - Variant content = text->getField()[0] - .cast<DocumentPrimitive>() - ->getContent(); + ASSERT_TRUE(text->getField()[0]->isa(typeOf<DocumentPrimitive>())); + Variant content = + text->getField()[0].cast<DocumentPrimitive>()->getContent(); ASSERT_EQ("Some introductory text", content.asString()); } } @@ -101,11 +99,10 @@ TEST(Document, construct) ASSERT_EQ("text", text->getDescriptor()->getName()); ASSERT_TRUE(text->getDescriptor()->hasField()); ASSERT_EQ(1U, text->getField().size()); - ASSERT_TRUE(text->getField()[0]->isa( - typeOf<DocumentPrimitive>())); - Variant content = text->getField()[0] - .cast<DocumentPrimitive>() - ->getContent(); + ASSERT_TRUE( + text->getField()[0]->isa(typeOf<DocumentPrimitive>())); + Variant content = + text->getField()[0].cast<DocumentPrimitive>()->getContent(); ASSERT_EQ("Some actual text", content.asString()); } } @@ -149,7 +146,8 @@ TEST(Document, validate) } // now let's extend the rootClass with a default field. - Rooted<FieldDescriptor> rootField{new FieldDescriptor(mgr, rootClass)}; + Rooted<FieldDescriptor> rootField = + rootClass->createFieldDescriptor(logger); // and add a child class for it. Rooted<StructuredClass> childClass{ new StructuredClass(mgr, "child", domain, single)}; @@ -195,7 +193,8 @@ TEST(Document, validate) * Make it even more complicated: child gets a field for further child * instances now. */ - Rooted<FieldDescriptor> childField{new FieldDescriptor(mgr, childClass)}; + Rooted<FieldDescriptor> childField = + childClass->createFieldDescriptor(logger); childField->addChild(childClass); { /* @@ -211,10 +210,13 @@ TEST(Document, validate) ASSERT_FALSE(doc->validate(logger)); } /* - * Override the default field in childSubClass. + * Override the default field in childSubClass with an optional field. */ - Rooted<FieldDescriptor> childSubField{ - new FieldDescriptor(mgr, childSubClass)}; + Rooted<FieldDescriptor> childSubField = + childSubClass->createFieldDescriptor( + logger, FieldDescriptor::FieldType::TREE, "dummy", true); + // add a child pro forma to make it valid. + childSubField->addChild(childSubClass); { /* * Now a document with one instance of the Child subclass should be @@ -229,8 +231,10 @@ TEST(Document, validate) ASSERT_TRUE(doc->validate(logger)); } // add a primitive field to the subclass with integer content. - Rooted<FieldDescriptor> primitive_field{new FieldDescriptor( - mgr, childSubClass, sys->getIntType(), "int", false)}; + Rooted<FieldDescriptor> primitive_field = + childSubClass->createPrimitiveFieldDescriptor( + sys->getIntType(), logger, FieldDescriptor::FieldType::SUBTREE, + "int", false); { /* * Now a document with one instance of the Child subclass should be @@ -265,12 +269,12 @@ TEST(Document, validate) doc->referenceDomain(domain); Rooted<StructuredEntity> root = buildRootStructuredEntity(doc, logger, {"root"}); - Rooted<Anchor> start{new Anchor(mgr, "start", root)}; + Rooted<Anchor> start{new Anchor(mgr, root)}; Rooted<StructuredEntity> child = buildStructuredEntity(doc, logger, root, {"childSub"}); Rooted<DocumentPrimitive> primitive{ new DocumentPrimitive(mgr, child, {2}, "int")}; - Rooted<Anchor> end{new Anchor(mgr, "end", root)}; + Rooted<Anchor> end{new Anchor(mgr, root)}; ASSERT_EQ(ValidationState::UNKNOWN, doc->getValidationState()); ASSERT_TRUE(doc->validate(logger)); // then add an AnnotationEntity without Anchors. diff --git a/test/core/model/DomainTest.cpp b/test/core/model/DomainTest.cpp index 32ef7f0..8fcbdf2 100644 --- a/test/core/model/DomainTest.cpp +++ b/test/core/model/DomainTest.cpp @@ -91,7 +91,7 @@ Rooted<StructuredClass> getClass(const std::string name, Handle<Domain> dom) TEST(Descriptor, pathTo) { // Start with some easy examples from the book domain. - Logger logger; + TerminalLogger logger{std::cout}; Manager mgr{1}; Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)}; // Get the domain. @@ -101,14 +101,14 @@ TEST(Descriptor, pathTo) Rooted<StructuredClass> book = getClass("book", domain); Rooted<StructuredClass> section = getClass("section", domain); // get the path in between. - std::vector<Rooted<Node>> path = book->pathTo(section); + NodeVector<Node> path = book->pathTo(section, logger); ASSERT_EQ(1U, path.size()); ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); // get the text node. Rooted<StructuredClass> text = getClass("text", domain); // get the path between book and text via paragraph. - path = book->pathTo(text); + path = book->pathTo(text, logger); ASSERT_EQ(3U, path.size()); ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); ASSERT_TRUE(path[1]->isa(&RttiTypes::StructuredClass)); @@ -118,9 +118,21 @@ TEST(Descriptor, pathTo) // get the subsection node. Rooted<StructuredClass> subsection = getClass("subsection", domain); // try to get the path between book and subsection. - path = book->pathTo(subsection); + path = book->pathTo(subsection, logger); // this should be impossible. ASSERT_EQ(0U, path.size()); + + // try to construct the path between section and the text field. + auto res = section->pathTo(text->getFieldDescriptor(), logger); + ASSERT_TRUE(res.second); + path = res.first; + ASSERT_EQ(4U, path.size()); + ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); + ASSERT_TRUE(path[1]->isa(&RttiTypes::StructuredClass)); + ASSERT_EQ("paragraph", path[1]->getName()); + ASSERT_TRUE(path[2]->isa(&RttiTypes::FieldDescriptor)); + ASSERT_TRUE(path[3]->isa(&RttiTypes::StructuredClass)); + ASSERT_EQ("text", path[3]->getName()); } TEST(Descriptor, pathToAdvanced) @@ -134,20 +146,19 @@ TEST(Descriptor, pathToAdvanced) * * To achieve that we have the following structure: * 1.) The start class inherits from A. - * 2.) A has the target as child in the default field, but the default - * field is overridden in the start class. - * 3.) A has B as child in another field. - * 4.) B is transparent and has no children (but C as subclass) - * 5.) C is a subclass of B, transparent and has + * 2.) A has B as child in the default field. + * 3.) B is transparent and has no children (but C as subclass) + * 4.) C is a subclass of B, transparent and has * the target as child (shortest path). - * 6.) start has D as child in the default field. - * 7.) D is transparent has E as child in the default field. - * 8.) E is transparent and has target as child in the default field + * 5.) A has D as child in the default field. + * 6.) D is transparent has E as child in the default field. + * 7.) E is transparent and has target as child in the default field * (longer path) * - * So the path start_field , E , E_field should be returned. + * So the path A_second_field, C, C_field should be returned. */ Manager mgr{1}; + TerminalLogger logger{std::cout}; Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)}; // Construct the domain Rooted<Domain> domain{new Domain(mgr, sys, "nasty")}; @@ -162,8 +173,8 @@ TEST(Descriptor, pathToAdvanced) Rooted<StructuredClass> B{new StructuredClass( mgr, "B", domain, Cardinality::any(), {nullptr}, true, false)}; - Rooted<StructuredClass> C{ - new StructuredClass(mgr, "C", domain, Cardinality::any(), B, true, false)}; + Rooted<StructuredClass> C{new StructuredClass( + mgr, "C", domain, Cardinality::any(), B, true, false)}; Rooted<StructuredClass> D{new StructuredClass( mgr, "D", domain, Cardinality::any(), {nullptr}, true, false)}; @@ -174,46 +185,157 @@ TEST(Descriptor, pathToAdvanced) Rooted<StructuredClass> target{ new StructuredClass(mgr, "target", domain, Cardinality::any())}; - // We create two fields for A - Rooted<FieldDescriptor> A_field{new FieldDescriptor(mgr, A)}; - - A_field->addChild(target); - Rooted<FieldDescriptor> A_field2{new FieldDescriptor( - mgr, A, FieldDescriptor::FieldType::SUBTREE, "second")}; + // We create a field for A + Rooted<FieldDescriptor> A_field = A->createFieldDescriptor(logger); + A_field->addChild(B); + A_field->addChild(D); - A_field2->addChild(B); // We create no field for B // One for C - Rooted<FieldDescriptor> C_field{new FieldDescriptor(mgr, C)}; - + Rooted<FieldDescriptor> C_field = C->createFieldDescriptor(logger); C_field->addChild(target); - // one for start - Rooted<FieldDescriptor> start_field{new FieldDescriptor(mgr, start)}; - start_field->addChild(D); // One for D - Rooted<FieldDescriptor> D_field{new FieldDescriptor(mgr, D)}; - + Rooted<FieldDescriptor> D_field = D->createFieldDescriptor(logger); D_field->addChild(E); - // One for E - Rooted<FieldDescriptor> E_field{new FieldDescriptor(mgr, E)}; + // One for E + Rooted<FieldDescriptor> E_field = E->createFieldDescriptor(logger); E_field->addChild(target); + ASSERT_TRUE(domain->validate(logger)); + #ifdef MANAGER_GRAPHVIZ_EXPORT // dump the manager state mgr.exportGraphviz("nastyDomain.dot"); #endif // and now we should be able to find the shortest path as suggested - std::vector<Rooted<Node>> path = start->pathTo(target); + NodeVector<Node> path = start->pathTo(target, logger); ASSERT_EQ(3U, path.size()); ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); - ASSERT_EQ("second", path[0]->getName()); + ASSERT_EQ("", path[0]->getName()); ASSERT_TRUE(path[1]->isa(&RttiTypes::StructuredClass)); - ASSERT_EQ("B", path[1]->getName()); + ASSERT_EQ("C", path[1]->getName()); ASSERT_TRUE(path[2]->isa(&RttiTypes::FieldDescriptor)); - ASSERT_EQ("$default", path[2]->getName()); + ASSERT_EQ("", path[2]->getName()); +} + +TEST(Descriptor, getDefaultFields) +{ + // construct a domain with lots of default fields to test. + // start with a single structure class. + Manager mgr{1}; + TerminalLogger logger{std::cout}; + Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)}; + // Construct the domain + Rooted<Domain> domain{new Domain(mgr, sys, "nasty")}; + + Rooted<StructuredClass> A{new StructuredClass( + mgr, "A", domain, Cardinality::any(), nullptr, false, true)}; + + // in this trivial case no field should be found. + ASSERT_TRUE(A->getDefaultFields().empty()); + + // create a field. + Rooted<FieldDescriptor> A_prim_field = + A->createPrimitiveFieldDescriptor(sys->getStringType(), logger); + // now we should find that. + auto fields = A->getDefaultFields(); + ASSERT_EQ(1, fields.size()); + ASSERT_EQ(A_prim_field, fields[0]); + + // remove that field from A and add it to another class. + + Rooted<StructuredClass> B{new StructuredClass( + mgr, "B", domain, Cardinality::any(), nullptr, false, true)}; + + B->moveFieldDescriptor(A_prim_field, logger); + + // new we shouldn't find the field anymore. + ASSERT_TRUE(A->getDefaultFields().empty()); + + // but we should find it again if we set B as superclass of A. + A->setSuperclass(B, logger); + fields = A->getDefaultFields(); + ASSERT_EQ(1, fields.size()); + ASSERT_EQ(A_prim_field, fields[0]); + + // and we should not be able to find it if we override the field. + Rooted<FieldDescriptor> A_field = A->createFieldDescriptor(logger); + ASSERT_TRUE(A->getDefaultFields().empty()); + + // add a transparent child class. + + Rooted<StructuredClass> C{new StructuredClass( + mgr, "C", domain, Cardinality::any(), nullptr, true, false)}; + A_field->addChild(C); + + // add a primitive field for it. + Rooted<FieldDescriptor> C_field = + C->createPrimitiveFieldDescriptor(sys->getStringType(), logger); + + // now we should find that. + fields = A->getDefaultFields(); + ASSERT_EQ(1, fields.size()); + ASSERT_EQ(C_field, fields[0]); + + // add another transparent child class to A with a daughter class that has + // in turn a subclass with a primitive field. + Rooted<StructuredClass> D{new StructuredClass( + mgr, "D", domain, Cardinality::any(), nullptr, true, false)}; + A_field->addChild(D); + Rooted<FieldDescriptor> D_field = D->createFieldDescriptor(logger); + Rooted<StructuredClass> E{new StructuredClass( + mgr, "E", domain, Cardinality::any(), nullptr, true, false)}; + D_field->addChild(E); + Rooted<StructuredClass> F{new StructuredClass( + mgr, "E", domain, Cardinality::any(), E, true, false)}; + Rooted<FieldDescriptor> F_field = + F->createPrimitiveFieldDescriptor(sys->getStringType(), logger); + + // now we should find both primitive fields, but the C field first. + fields = A->getDefaultFields(); + ASSERT_EQ(2, fields.size()); + ASSERT_EQ(C_field, fields[0]); + ASSERT_EQ(F_field, fields[1]); +} + +TEST(Descriptor, getPermittedChildren) +{ + // analyze the book domain. + TerminalLogger logger{std::cout}; + Manager mgr{1}; + Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)}; + // Get the domain. + Rooted<Domain> domain = constructBookDomain(mgr, sys, logger); + // get the relevant classes. + Rooted<StructuredClass> book = getClass("book", domain); + Rooted<StructuredClass> section = getClass("section", domain); + Rooted<StructuredClass> paragraph = getClass("paragraph", domain); + Rooted<StructuredClass> text = getClass("text", domain); + /* + * as permitted children we expect section, paragraph and text in exactly + * that order. section should be before paragraph because of declaration + * order and text should be last because it needs a transparent paragraph + * in between. + */ + NodeVector<StructuredClass> children = book->getPermittedChildren(); + ASSERT_EQ(3, children.size()); + ASSERT_EQ(section, children[0]); + ASSERT_EQ(paragraph, children[1]); + ASSERT_EQ(text, children[2]); + + // Now we add a subclass to text. + Rooted<StructuredClass> sub{new StructuredClass( + mgr, "Subclass", domain, Cardinality::any(), text, true, false)}; + // And that should be in the result list as well now. + children = book->getPermittedChildren(); + ASSERT_EQ(4, children.size()); + ASSERT_EQ(section, children[0]); + ASSERT_EQ(paragraph, children[1]); + ASSERT_EQ(text, children[2]); + ASSERT_EQ(sub, children[3]); } TEST(StructuredClass, isSubclassOf) @@ -313,8 +435,8 @@ TEST(Domain, validate) ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_TRUE(domain->validate(logger)); // Let's add a primitive field (without a primitive type at first) - Rooted<FieldDescriptor> base_field{ - new FieldDescriptor(mgr, base, nullptr)}; + Rooted<FieldDescriptor> base_field = + base->createPrimitiveFieldDescriptor(nullptr, logger); // this should not be valid. ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_FALSE(domain->validate(logger)); @@ -322,13 +444,6 @@ TEST(Domain, validate) base_field->setPrimitiveType(sys->getStringType()); ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_TRUE(domain->validate(logger)); - // not anymore, however, if we tamper with the FieldType. - base_field->setFieldType(FieldDescriptor::FieldType::TREE); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_FALSE(domain->validate(logger)); - base_field->setFieldType(FieldDescriptor::FieldType::PRIMITIVE); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); // add a subclass for our base class. Rooted<StructuredClass> sub{new StructuredClass(mgr, "sub", domain)}; // this should be valid in itself. @@ -338,7 +453,7 @@ TEST(Domain, validate) sub->setSuperclass(base, logger); ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_TRUE(domain->validate(logger)); - // and still we we remove the subclass from the base class. + // and still if we remove the subclass from the base class. base->removeSubclass(sub, logger); ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_TRUE(domain->validate(logger)); @@ -349,19 +464,11 @@ TEST(Domain, validate) ASSERT_TRUE(domain->validate(logger)); ASSERT_EQ(base, sub->getSuperclass()); // add a non-primitive field to the child class. - Rooted<FieldDescriptor> sub_field{new FieldDescriptor(mgr, sub)}; - // this should be valid - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - // .. until we set a primitive type. - sub_field->setPrimitiveType(sys->getStringType()); + Rooted<FieldDescriptor> sub_field = sub->createFieldDescriptor(logger); + // this should not be valid because we allow no children. ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_FALSE(domain->validate(logger)); - // and valid again if we unset it. - sub_field->setPrimitiveType(nullptr); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - // we should also be able to add a child and have it still be valid. + // we should also be able to add a child and make it valid. sub_field->addChild(base); ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_TRUE(domain->validate(logger)); @@ -373,6 +480,23 @@ TEST(Domain, validate) sub_field->removeChild(base); ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_TRUE(domain->validate(logger)); + // if we set a primitive type it should be invalid + sub_field->setPrimitiveType(sys->getStringType()); + ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); + ASSERT_FALSE(domain->validate(logger)); + // and valid again if we unset it. + sub_field->setPrimitiveType(nullptr); + ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); + ASSERT_TRUE(domain->validate(logger)); + // It should be invalid if we set another TREE field. + Rooted<FieldDescriptor> sub_field2 = sub->createFieldDescriptor( + logger, FieldDescriptor::FieldType::TREE, "test", false); + ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); + ASSERT_FALSE(domain->validate(logger)); + // but valid again if we remove it + sub->removeFieldDescriptor(sub_field2); + ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); + ASSERT_TRUE(domain->validate(logger)); } } } diff --git a/test/core/model/TestAdvanced.hpp b/test/core/model/TestAdvanced.hpp index 915d973..27f33cc 100644 --- a/test/core/model/TestAdvanced.hpp +++ b/test/core/model/TestAdvanced.hpp @@ -55,18 +55,18 @@ static Rooted<Domain> constructHeadingDomain(Manager &mgr, Cardinality card; card.merge({0, 1}); // set up heading StructuredClass. - Rooted<StructuredClass> heading{new StructuredClass( - mgr, "heading", domain, card, {nullptr}, true)}; - // as field want to copy the field of paragraph. + Rooted<StructuredClass> heading{ + new StructuredClass(mgr, "heading", domain, card, {nullptr}, true)}; + // as field want to reference the field of paragraph. Rooted<StructuredClass> p = resolveDescriptor(bookDomain, "paragraph"); - heading->copyFieldDescriptor(p->getFieldDescriptors()[0]); + heading->addFieldDescriptor(p->getFieldDescriptor(), logger); // create a new field for headings in each section type. std::vector<std::string> secclasses{"book", "section", "subsection", "paragraph"}; for (auto &s : secclasses) { Rooted<StructuredClass> desc = resolveDescriptor(bookDomain, s); - Rooted<FieldDescriptor> heading_field{new FieldDescriptor( - mgr, desc, FieldDescriptor::FieldType::SUBTREE, "heading")}; + Rooted<FieldDescriptor> heading_field = desc->createFieldDescriptor( + logger, FieldDescriptor::FieldType::SUBTREE, "heading", true); heading_field->addChild(heading); } return domain; @@ -88,8 +88,8 @@ static Rooted<Domain> constructListDomain(Manager &mgr, Rooted<StructuredClass> item{new StructuredClass( mgr, "item", domain, Cardinality::any(), {nullptr}, false)}; - // as field we want to copy the field of paragraph. - item->copyFieldDescriptor(p->getFieldDescriptors()[0]); + // as field we want to reference the field of paragraph. + item->addFieldDescriptor(p->getFieldDescriptor(), logger); // set up list StructuredClasses. std::vector<std::string> listTypes{"ol", "ul"}; for (auto &listType : listTypes) { @@ -150,19 +150,17 @@ static bool addHeading(Logger &logger, Handle<Document> doc, return true; } -static int annoIdx = 1; - // Only works for non-overlapping annotations! static bool addAnnotation(Logger &logger, Handle<Document> doc, Handle<StructuredEntity> parent, const std::string &text, const std::string &annoClass) { - Manager& mgr = parent->getManager(); - Rooted<Anchor> start{new Anchor(mgr, std::to_string(annoIdx++), parent)}; + Manager &mgr = parent->getManager(); + Rooted<Anchor> start{new Anchor(mgr, parent)}; if (!addText(logger, doc, parent, text)) { return false; } - Rooted<Anchor> end{new Anchor(mgr, std::to_string(annoIdx++), parent)}; + Rooted<Anchor> end{new Anchor(mgr, parent)}; Rooted<AnnotationEntity> anno = buildAnnotationEntity(doc, logger, {annoClass}, start, end); if (anno.isNull()) { diff --git a/test/core/model/TestDomain.hpp b/test/core/model/TestDomain.hpp index 5ac510c..c107a0d 100644 --- a/test/core/model/TestDomain.hpp +++ b/test/core/model/TestDomain.hpp @@ -42,7 +42,7 @@ static Rooted<Domain> constructBookDomain(Manager &mgr, mgr, "book", domain, single, {nullptr}, false, true)}; // The structure field of it. - Rooted<FieldDescriptor> book_field{new FieldDescriptor(mgr, book)}; + Rooted<FieldDescriptor> book_field = book->createFieldDescriptor(logger); // From there on the "section". Rooted<StructuredClass> section{ @@ -50,7 +50,8 @@ static Rooted<Domain> constructBookDomain(Manager &mgr, book_field->addChild(section); // And the field of it. - Rooted<FieldDescriptor> section_field{new FieldDescriptor(mgr, section)}; + Rooted<FieldDescriptor> section_field = + section->createFieldDescriptor(logger); // We also add the "paragraph", which is transparent. Rooted<StructuredClass> paragraph{new StructuredClass( @@ -59,8 +60,8 @@ static Rooted<Domain> constructBookDomain(Manager &mgr, book_field->addChild(paragraph); // And the field of it. - Rooted<FieldDescriptor> paragraph_field{ - new FieldDescriptor(mgr, paragraph)}; + Rooted<FieldDescriptor> paragraph_field = + paragraph->createFieldDescriptor(logger); // We append "subsection" to section. Rooted<StructuredClass> subsection{ @@ -68,8 +69,8 @@ static Rooted<Domain> constructBookDomain(Manager &mgr, section_field->addChild(subsection); // And the field of it. - Rooted<FieldDescriptor> subsection_field{ - new FieldDescriptor(mgr, subsection)}; + Rooted<FieldDescriptor> subsection_field = + subsection->createFieldDescriptor(logger); // and we add the paragraph to subsections fields subsection_field->addChild(paragraph); @@ -80,9 +81,8 @@ static Rooted<Domain> constructBookDomain(Manager &mgr, paragraph_field->addChild(text); // ... and has a primitive field. - Rooted<FieldDescriptor> text_field{new FieldDescriptor( - mgr, text, domain->getTypesystems()[0]->getTypes()[0], - DEFAULT_FIELD_NAME, false)}; + Rooted<FieldDescriptor> text_field = + text->createPrimitiveFieldDescriptor(sys->getStringType(), logger); return domain; } diff --git a/test/formats/osxml/OsxmlParserTest.cpp b/test/formats/osxml/OsxmlParserTest.cpp index c0fb50d..269a3f6 100644 --- a/test/formats/osxml/OsxmlParserTest.cpp +++ b/test/formats/osxml/OsxmlParserTest.cpp @@ -22,6 +22,7 @@ #include <core/common/CharReader.hpp> #include <core/common/SourceContextReader.hpp> +#include <core/model/Document.hpp> #include <core/model/Domain.hpp> #include <core/model/Node.hpp> #include <core/model/Project.hpp> @@ -168,6 +169,7 @@ static void checkFieldDescriptor( ASSERT_EQ(parent, field->getParent()); ASSERT_EQ(type, field->getFieldType()); ASSERT_EQ(primitiveType, field->getPrimitiveType()); + ASSERT_EQ(primitiveType != nullptr, field->isPrimitive()); ASSERT_EQ(optional, field->isOptional()); // check the children. ASSERT_EQ(children.size(), field->getChildren().size()); @@ -179,7 +181,7 @@ static void checkFieldDescriptor( static void checkFieldDescriptor( Handle<Descriptor> desc, Handle<Descriptor> parent, NodeVector<StructuredClass> children, - const std::string &name = DEFAULT_FIELD_NAME, + const std::string &name = "", FieldDescriptor::FieldType type = FieldDescriptor::FieldType::TREE, Handle<Type> primitiveType = nullptr, bool optional = false) { @@ -191,7 +193,7 @@ static void checkFieldDescriptor( static void checkFieldDescriptor( Handle<Descriptor> desc, NodeVector<StructuredClass> children, - const std::string &name = DEFAULT_FIELD_NAME, + const std::string &name = "", FieldDescriptor::FieldType type = FieldDescriptor::FieldType::TREE, Handle<Type> primitiveType = nullptr, bool optional = false) { @@ -242,7 +244,7 @@ TEST(XmlParser, domainParsing) checkFieldDescriptor(subsection, {paragraph}); checkFieldDescriptor(paragraph, {text}); checkFieldDescriptor( - text, {}, DEFAULT_FIELD_NAME, FieldDescriptor::FieldType::PRIMITIVE, + text, {}, "", FieldDescriptor::FieldType::TREE, env.project->getSystemTypesystem()->getStringType(), false); // check parent handling using the headings domain. @@ -290,7 +292,7 @@ TEST(XmlParser, domainParsing) std::vector<Rooted<Descriptor>> descs{comment_anno, comment, reply}; for (auto &d : descs) { checkFieldDescriptor(d, {paragraph}, "content", - FieldDescriptor::FieldType::SUBTREE, nullptr, + FieldDescriptor::FieldType::TREE, nullptr, false); checkFieldDescriptor(d, {reply}, "replies", FieldDescriptor::FieldType::SUBTREE, nullptr, @@ -303,12 +305,98 @@ TEST(XmlParser, domainParsing) checkFieldDescriptor(heading, paragraph, {text, comment}); } +static void checkStructuredEntity( + Handle<Node> s, Handle<Node> expectedParent, Handle<StructuredClass> strct, + const Variant::mapType &expectedAttributes = Variant::mapType{}, + const std::string &expectedName = "") +{ + ASSERT_FALSE(s == nullptr); + ASSERT_TRUE(s->isa(&RttiTypes::StructuredEntity)); + Rooted<StructuredEntity> entity = s.cast<StructuredEntity>(); + ASSERT_EQ(expectedParent, entity->getParent()); + ASSERT_EQ(strct, entity->getDescriptor()); + ASSERT_EQ(expectedAttributes, entity->getAttributes()); + ASSERT_EQ(expectedName, entity->getName()); +} + +static void checkStructuredEntity( + Handle<Node> s, Handle<Node> expectedParent, Handle<Document> doc, + const std::string &className, + const Variant::mapType &expectedAttributes = Variant::mapType{}, + const std::string &expectedName = "") +{ + auto res = doc->resolve(&RttiTypes::StructuredClass, className); + if (res.size() != 1) { + throw OusiaException("resolution error!"); + } + Handle<StructuredClass> sc = res[0].node.cast<StructuredClass>(); + checkStructuredEntity(s, expectedParent, sc, expectedAttributes, + expectedName); +} + +static void checkText(Handle<Node> p, Handle<Node> expectedParent, + Handle<Document> doc, Variant expected) +{ + checkStructuredEntity(p, expectedParent, doc, "paragraph"); + Rooted<StructuredEntity> par = p.cast<StructuredEntity>(); + ASSERT_EQ(1, par->getField().size()); + checkStructuredEntity(par->getField()[0], par, doc, "text"); + Rooted<StructuredEntity> text = par->getField()[0].cast<StructuredEntity>(); + ASSERT_EQ(1, text->getField().size()); + + Handle<StructureNode> d = text->getField()[0]; + ASSERT_FALSE(d == nullptr); + ASSERT_TRUE(d->isa(&RttiTypes::DocumentPrimitive)); + Rooted<DocumentPrimitive> prim = d.cast<DocumentPrimitive>(); + ASSERT_EQ(text, prim->getParent()); + ASSERT_EQ(expected, prim->getContent()); +} + TEST(XmlParser, documentParsing) { XmlStandaloneEnvironment env(logger); - Rooted<Node> book_domain_node = + Rooted<Node> book_document_node = env.parse("simple_book.oxd", "", "", RttiSet{&RttiTypes::Document}); - //TODO: Check result + ASSERT_FALSE(book_document_node == nullptr); + ASSERT_TRUE(book_document_node->isa(&RttiTypes::Document)); + Rooted<Document> doc = book_document_node.cast<Document>(); + ASSERT_TRUE(doc->validate(logger)); + checkStructuredEntity(doc->getRoot(), doc, doc, "book"); + { + Rooted<StructuredEntity> book = doc->getRoot(); + ASSERT_EQ(2, book->getField().size()); + checkText(book->getField()[0], book, doc, + "This might be some introductory text or a dedication."); + checkStructuredEntity(book->getField()[1], book, doc, "chapter", + Variant::mapType{}, "myFirstChapter"); + { + Rooted<StructuredEntity> chapter = + book->getField()[1].cast<StructuredEntity>(); + ASSERT_EQ(3, chapter->getField().size()); + checkText(chapter->getField()[0], chapter, doc, + "Here we might have an introduction to the chapter."); + checkStructuredEntity(chapter->getField()[1], chapter, doc, + "section", Variant::mapType{}, + "myFirstSection"); + { + Rooted<StructuredEntity> section = + chapter->getField()[1].cast<StructuredEntity>(); + ASSERT_EQ(1, section->getField().size()); + checkText(section->getField()[0], section, doc, + "Here we might find the actual section content."); + } + checkStructuredEntity(chapter->getField()[2], chapter, doc, + "section", Variant::mapType{}, + "mySndSection"); + { + Rooted<StructuredEntity> section = + chapter->getField()[2].cast<StructuredEntity>(); + ASSERT_EQ(1, section->getField().size()); + checkText(section->getField()[0], section, doc, + "Here we might find the actual section content."); + } + } + } } } diff --git a/test/plugins/html/DemoOutputTest.cpp b/test/plugins/html/DemoOutputTest.cpp index 5006655..2f56e40 100644 --- a/test/plugins/html/DemoOutputTest.cpp +++ b/test/plugins/html/DemoOutputTest.cpp @@ -41,14 +41,11 @@ TEST(DemoHTMLTransformer, writeHTML) Manager mgr{1}; Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)}; // Get the domains. - Rooted<Domain> bookDom = - constructBookDomain(mgr, sys, logger); + Rooted<Domain> bookDom = constructBookDomain(mgr, sys, logger); Rooted<Domain> headingDom = constructHeadingDomain(mgr, sys, bookDom, logger); - Rooted<Domain> listDom = - constructListDomain(mgr, sys, bookDom, logger); - Rooted<Domain> emDom = - constructEmphasisDomain(mgr, sys, logger); + Rooted<Domain> listDom = constructListDomain(mgr, sys, bookDom, logger); + Rooted<Domain> emDom = constructEmphasisDomain(mgr, sys, logger); // Construct the document. Rooted<Document> doc = constructAdvancedDocument( mgr, logger, bookDom, headingDom, listDom, emDom); @@ -79,10 +76,8 @@ TEST(DemoHTMLTransformer, AnnotationProcessing) Manager mgr{1}; Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)}; // Get the domains. - Rooted<Domain> bookDom = - constructBookDomain(mgr, sys, logger); - Rooted<Domain> emDom = - constructEmphasisDomain(mgr, sys, logger); + Rooted<Domain> bookDom = constructBookDomain(mgr, sys, logger); + Rooted<Domain> emDom = constructEmphasisDomain(mgr, sys, logger); // Construct a document only containing overlapping annotations. // it has the form: <em>bla<strong>blub</em>bla</strong> Rooted<Document> doc{new Document(mgr, "annotations.oxd")}; @@ -93,13 +88,13 @@ TEST(DemoHTMLTransformer, AnnotationProcessing) Rooted<StructuredEntity> p = buildStructuredEntity(doc, logger, book, {"paragraph"}); ASSERT_TRUE(p != nullptr); - Rooted<Anchor> em_start{new Anchor(mgr, "1", p)}; + Rooted<Anchor> em_start{new Anchor(mgr, p)}; ASSERT_TRUE(addText(logger, doc, p, "bla")); - Rooted<Anchor> strong_start{new Anchor(mgr, "2", p)}; + Rooted<Anchor> strong_start{new Anchor(mgr, p)}; ASSERT_TRUE(addText(logger, doc, p, "blub")); - Rooted<Anchor> em_end{new Anchor(mgr, "3", p)}; + Rooted<Anchor> em_end{new Anchor(mgr, p)}; ASSERT_TRUE(addText(logger, doc, p, "bla")); - Rooted<Anchor> strong_end{new Anchor(mgr, "4", p)}; + Rooted<Anchor> strong_end{new Anchor(mgr, p)}; buildAnnotationEntity(doc, logger, {"emphasized"}, em_start, em_end); buildAnnotationEntity(doc, logger, {"strong"}, strong_start, strong_end); |