/* Ousía Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include namespace ousia { namespace RttiTypes { extern const Rtti Document; extern const Rtti Domain; extern const Rtti Typesystem; } struct XmlStandaloneEnvironment : public StandaloneEnvironment { XmlParser xmlParser; FileLocator fileLocator; XmlStandaloneEnvironment(ConcreteLogger &logger) : StandaloneEnvironment(logger) { fileLocator.addDefaultSearchPaths(); fileLocator.addUnittestSearchPath("xmlparser"); registry.registerDefaultExtensions(); registry.registerParser({"text/vnd.ousia.oxm", "text/vnd.ousia.oxd"}, {&RttiTypes::Node}, &xmlParser); registry.registerResourceLocator(&fileLocator); } }; static TerminalLogger logger(std::cerr, true); TEST(XmlParser, mismatchedTag) { XmlStandaloneEnvironment env(logger); env.parse("mismatchedTag.oxm", "", "", RttiSet{&RttiTypes::Document}); ASSERT_TRUE(logger.hasError()); } TEST(XmlParser, generic) { XmlStandaloneEnvironment env(logger); env.parse("generic.oxm", "", "", RttiSet{&RttiTypes::Node}); #ifdef MANAGER_GRAPHVIZ_EXPORT env.manager.exportGraphviz("xmlDocument.dot"); #endif } static void checkStructuredClass( Handle n, const std::string &name, Handle domain, Variant cardinality = AnyCardinality, Handle attributesDescriptor = nullptr, Handle superclass = nullptr, bool transparent = false, bool root = false) { ASSERT_FALSE(n == nullptr); Handle sc = n.cast(); ASSERT_FALSE(sc == nullptr); ASSERT_EQ(name, sc->getName()); ASSERT_EQ(domain, sc->getParent()); ASSERT_EQ(cardinality, sc->getCardinality()); ASSERT_EQ(transparent, sc->isTransparent()); ASSERT_EQ(root, sc->hasRootPermission()); ASSERT_EQ(attributesDescriptor, sc->getAttributesDescriptor()); } static Rooted checkStructuredClass( const std::string &resolve, const std::string &name, Handle domain, Variant cardinality = AnyCardinality, Handle attributesDescriptor = nullptr, Handle superclass = nullptr, bool transparent = false, bool root = false) { auto res = domain->resolve(RttiTypes::StructuredClass, resolve); if (res.size() != 1) { throw OusiaException("resolution error!"); } Handle sc = res[0].node.cast(); checkStructuredClass(sc, name, domain, cardinality, attributesDescriptor, superclass, transparent, root); return sc; } static void checkAnnotationClass( Handle n, const std::string &name, Handle domain, Handle attributesDescriptor = nullptr) { ASSERT_FALSE(n == nullptr); Handle ac = n.cast(); ASSERT_FALSE(ac == nullptr); ASSERT_EQ(name, ac->getName()); ASSERT_EQ(domain, ac->getParent()); ASSERT_EQ(attributesDescriptor, ac->getAttributesDescriptor()); } static Rooted checkAnnotationClass( const std::string &resolve, const std::string &name, Handle domain, Handle attributesDescriptor = nullptr) { auto res = domain->resolve(RttiTypes::AnnotationClass, resolve); if (res.size() != 1) { throw OusiaException("resolution error!"); } Handle ac = res[0].node.cast(); checkAnnotationClass(ac, name, domain, attributesDescriptor); return ac; } static void checkFieldDescriptor( Handle n, const std::string &name, Handle parent, NodeVector children, FieldDescriptor::FieldType type = FieldDescriptor::FieldType::TREE, Handle primitiveType = nullptr, bool optional = false) { ASSERT_FALSE(n == nullptr); Handle field = n.cast(); ASSERT_FALSE(field.isNull()); ASSERT_EQ(name, field->getName()); ASSERT_EQ(parent, field->getParent()); ASSERT_EQ(type, field->getFieldType()); ASSERT_EQ(primitiveType, field->getPrimitiveType()); ASSERT_EQ(optional, field->isOptional()); // check the children. ASSERT_EQ(children.size(), field->getChildren().size()); for (unsigned int c = 0; c < children.size(); c++) { ASSERT_EQ(children[c], field->getChildren()[c]); } } static void checkFieldDescriptor( Handle desc, NodeVector children, const std::string &name = DEFAULT_FIELD_NAME, FieldDescriptor::FieldType type = FieldDescriptor::FieldType::TREE, Handle primitiveType = nullptr, bool optional = false) { auto res = desc->resolve(RttiTypes::FieldDescriptor, name); ASSERT_EQ(1, res.size()); checkFieldDescriptor(res[0].node, name, desc, children, type, primitiveType, optional); } TEST(XmlParser, domainParsing) { XmlStandaloneEnvironment env(logger); Rooted book_domain_node = env.parse("book_domain.oxm", "", "", RttiSet{&RttiTypes::Domain}); ASSERT_FALSE(book_domain_node == nullptr); ASSERT_FALSE(logger.hasError()); // check the domain node. Rooted book_domain = book_domain_node.cast(); ASSERT_EQ("book", book_domain->getName()); // get the book struct node. Cardinality single; single.merge({1}); Rooted book = checkStructuredClass( "book", "book", book_domain, single, nullptr, nullptr, false, true); // get the chapter struct node. Rooted chapter = checkStructuredClass("chapter", "chapter", book_domain); Rooted section = checkStructuredClass("section", "section", book_domain); Rooted subsection = checkStructuredClass("subsection", "subsection", book_domain); Rooted paragraph = checkStructuredClass("paragraph", "paragraph", book_domain, AnyCardinality, nullptr, nullptr, true, false); Rooted text = checkStructuredClass("text", "text", book_domain, AnyCardinality, nullptr, nullptr, true, false); // check the FieldDescriptors. checkFieldDescriptor(book, {chapter, paragraph}); checkFieldDescriptor(chapter, {section, paragraph}); checkFieldDescriptor(section, {subsection, paragraph}); checkFieldDescriptor(subsection, {paragraph}); checkFieldDescriptor(paragraph, {text}); checkFieldDescriptor( text, {}, "content", FieldDescriptor::FieldType::PRIMITIVE, env.project->getSystemTypesystem()->getStringType(), false); // check parent handling using the headings domain. Rooted headings_domain_node = env.parse("headings_domain.oxm", "", "", RttiSet{&RttiTypes::Domain}); ASSERT_FALSE(headings_domain_node == nullptr); ASSERT_FALSE(logger.hasError()); Rooted headings_domain = headings_domain_node.cast(); // now there should be a heading struct. Rooted heading = checkStructuredClass("heading", "heading", headings_domain, single, nullptr, nullptr, true, false); // which should allow text content checkFieldDescriptor(heading, {text}); // and each struct in the book domain (except for text) should have a // heading field now. checkFieldDescriptor(book, {heading}, "heading", FieldDescriptor::FieldType::SUBTREE, nullptr, true); checkFieldDescriptor(chapter, {heading}, "heading", FieldDescriptor::FieldType::SUBTREE, nullptr, true); checkFieldDescriptor(section, {heading}, "heading", FieldDescriptor::FieldType::SUBTREE, nullptr, true); checkFieldDescriptor(subsection, {heading}, "heading", FieldDescriptor::FieldType::SUBTREE, nullptr, true); checkFieldDescriptor(paragraph, {heading}, "heading", FieldDescriptor::FieldType::SUBTREE, nullptr, true); // check annotation handling using the comments domain. Rooted comments_domain_node = env.parse("comments_domain.oxm", "", "", RttiSet{&RttiTypes::Domain}); ASSERT_FALSE(comments_domain_node == nullptr); ASSERT_FALSE(logger.hasError()); Rooted comments_domain = comments_domain_node.cast(); // now we should be able to find a comment annotation. Rooted comment_anno = checkAnnotationClass("comment", "comment", comments_domain); // as well as a comment struct Rooted comment = checkStructuredClass("comment", "comment", comments_domain); // and a reply struct Rooted reply = checkStructuredClass("reply", "reply", comments_domain); // check the fields for each of them. { std::vector> descs{comment_anno, comment, reply}; for (auto &d : descs) { checkFieldDescriptor(d, {paragraph}, "content", FieldDescriptor::FieldType::SUBTREE, nullptr, false); checkFieldDescriptor(d, {reply}, "replies", FieldDescriptor::FieldType::SUBTREE, nullptr, false); } } // paragraph should have comment as child now as well. checkFieldDescriptor(paragraph, {text, comment}); // as should heading, because it references the paragraph default field. // TODO: This does not work as of now, because in fact fields get copied, // not referenced. Should we reference fields, though? //checkFieldDescriptor(heading, {text, comment}); } }