File indexing completed on 2024-05-12 17:15:20
0001 /* 0002 Copyright (C) 2013 Andreas Hartmetz <ahartmetz@gmail.com> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LGPL. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 Boston, MA 02110-1301, USA. 0018 0019 Alternatively, this file is available under the Mozilla Public License 0020 Version 1.1. You may obtain a copy of the License at 0021 http://www.mozilla.org/MPL/ 0022 */ 0023 0024 #include "introspection.h" 0025 0026 #include "../testutil.h" 0027 0028 #include <tinyxml2.h> 0029 0030 #include <fstream> 0031 #include <string> 0032 #include <iostream> 0033 0034 namespace tx2 = tinyxml2; 0035 0036 // Extract the doctype declaration to insert into the re-synthesized XML. It could just be 0037 // omitted from the reference results, but I'd rather test with completely "real" data. 0038 0039 static void addDoctypeDecl(tx2::XMLDocument *doc) 0040 { 0041 tx2::XMLDocument d; 0042 d.LoadFile(TEST_DATADIR "/introspect1.xml"); 0043 tx2::XMLNode *doctypeDecl = d.FirstChild()->ShallowClone(doc); 0044 doc->InsertFirstChild(doctypeDecl); 0045 } 0046 0047 static std::string readFile(const char *filename) 0048 { 0049 // Dear STL, you must be kidding... 0050 std::ifstream ifs(filename); 0051 return std::string((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()); 0052 } 0053 0054 /////////////////////////////////////////////////////////////////////////// 0055 // Functions to turn introspection data structure back into XML 0056 0057 // the first argument can't be a XMLElement because an XMLDocument isn't an XMLElement 0058 static void xmlizeNode(tx2::XMLNode *el, const IntrospectionNode *node); 0059 static void xmlizeInterface(tx2::XMLElement *el, const Interface *iface); 0060 static void xmlizeMethod(tx2::XMLElement *el, const Method *method); 0061 static void xmlizeArgument(tx2::XMLElement *el, const Argument *arg, bool isSignal); 0062 static void xmlizeProperty(tx2::XMLElement *el, const Property *property); 0063 0064 static tx2::XMLElement *addElement(tx2::XMLNode *xmlNode, const char *type, const std::string &name) 0065 { 0066 tx2::XMLElement *el = xmlNode->GetDocument()->NewElement(type); 0067 el->SetAttribute("name", name.c_str()); 0068 xmlNode->InsertEndChild(el); 0069 return el; 0070 } 0071 0072 static void xmlizeNode(tx2::XMLNode *el, const IntrospectionNode *node) 0073 { 0074 tx2::XMLElement *nodeEl = addElement(el, "node", node->name); 0075 0076 std::map<std::string, Interface>::const_iterator iIt = node->interfaces.begin(); 0077 for (; iIt != node->interfaces.end(); ++iIt) { 0078 xmlizeInterface(nodeEl, &iIt->second); 0079 } 0080 0081 std::map<std::string, IntrospectionNode *>::const_iterator nIt = node->children.begin(); 0082 for (; nIt != node->children.end(); ++nIt) { 0083 xmlizeNode(nodeEl, nIt->second); 0084 } 0085 0086 } 0087 0088 static void xmlizeInterface(tx2::XMLElement *el, const Interface *iface) 0089 { 0090 tx2::XMLElement *ifaceEl = addElement(el, "interface", iface->name); 0091 0092 std::map<std::string, Method>::const_iterator mIt = iface->methods.begin(); 0093 for (; mIt != iface->methods.end(); ++mIt) { 0094 xmlizeMethod(ifaceEl, &mIt->second); 0095 } 0096 0097 std::map<std::string, Property>::const_iterator pIt = iface->properties.begin(); 0098 for (; pIt != iface->properties.end(); ++pIt) { 0099 xmlizeProperty(ifaceEl, &pIt->second); 0100 } 0101 } 0102 0103 static void xmlizeMethod(tx2::XMLElement *el, const Method *method) 0104 { 0105 bool isSignal = method->type == Message::SignalMessage; 0106 tx2::XMLElement *methodEl = addElement(el, isSignal ? "signal" : "method", method->name); 0107 0108 for (const Argument &arg : method->arguments) { 0109 xmlizeArgument(methodEl, &arg, isSignal); 0110 } 0111 } 0112 0113 static void xmlizeArgument(tx2::XMLElement *el, const Argument *arg, bool /* TODO! isSignal */) 0114 { 0115 tx2::XMLElement *argEl = addElement(el, "arg", arg->name); 0116 if (!arg->name.empty()) { 0117 argEl->SetAttribute("name", arg->name.c_str()); 0118 } 0119 argEl->SetAttribute("type", arg->type.c_str()); 0120 argEl->SetAttribute("direction", arg->isDirectionOut ? "out" : "in"); 0121 } 0122 0123 static void xmlizeProperty(tx2::XMLElement *el, const Property *property) 0124 { 0125 tx2::XMLElement *propEl = addElement(el, "property", property->name); 0126 propEl->SetAttribute("type", property->type.c_str()); 0127 propEl->SetAttribute("type", property->type.c_str()); 0128 0129 const char *access = property->access == Property::Read ? "read" : 0130 property->access == Property::Write ? "write" : "readwrite"; 0131 propEl->SetAttribute("access", access); 0132 } 0133 0134 0135 0136 /////////////////////////////////////////////////////////////////////////// 0137 0138 static void testBasicRoundtrip() 0139 { 0140 const char *filename = TEST_DATADIR "/introspect1.xml"; 0141 IntrospectionTree tree; 0142 TEST(tree.mergeXml(readFile(filename).c_str(), "")); 0143 0144 tx2::XMLDocument doc; 0145 addDoctypeDecl(&doc); 0146 xmlizeNode(&doc, tree.m_rootNode); 0147 0148 { 0149 tx2::XMLDocument normalized; 0150 normalized.LoadFile(TEST_DATADIR "/introspect1.xml"); 0151 tx2::XMLPrinter printer; 0152 normalized.Print(&printer); 0153 std::cout << printer.CStr() << '\n'; 0154 } 0155 0156 tx2::XMLPrinter printer; 0157 doc.Print(&printer); 0158 std::cout << printer.CStr() << '\n'; 0159 } 0160 0161 int main(int, char *[]) 0162 { 0163 testBasicRoundtrip(); 0164 std::cout << "Passed!\n"; 0165 }