Browse Source

Implement basic nested types support

- Add support of multi-level nested types generation. Level limited
  by generator stack size.
- Refactor protobuf class printers to support nested types
- Fix issue with local enums generation, introduced in previous
  refactoring phase
- Add basic nested types tests
- Fix few minor coding-style issues
- Enable failure output for travis

Fixes: #122
Alexey Edelev 4 years ago
parent
commit
40f17e1f74

+ 1 - 1
.travis.yml

@@ -22,4 +22,4 @@ script:
   - set PATH="%PATH%;C:\Qt\5.13.2\msvc2017\bin;C:\Go\bin;C:\ProgramData\chocolatey\lib\yasm\tools"
   - cmake -DCMAKE_PREFIX_PATH="C:\Qt\5.13.2\msvc2017;C:\Go\bin;C:\ProgramData\chocolatey\lib\yasm\tools" ..
   - cmake --build .
-  - ctest -C DEBUG -E qtgrpc_*
+  - ctest -C DEBUG -E qtgrpc_* --output-on-failure

+ 1 - 1
examples/simplechat/simplechatengine.cpp

@@ -100,7 +100,7 @@ qtprotobuf::examples::ChatMessage::ContentType SimpleChatEngine::clipBoardConten
         if (mime != nullptr) {
             if (mime->hasImage() || mime->hasUrls()) {
                 return qtprotobuf::examples::ChatMessage::ContentType::Image;
-            } else if(mime->hasText()) {
+            } else if (mime->hasText()) {
                 return qtprotobuf::examples::ChatMessage::ContentType::Text;
             }
         }

+ 0 - 4
src/generator/descriptorprinterbase.h

@@ -53,10 +53,6 @@ public:
     {}
     virtual ~DescriptorPrinterBase() = default;
 public:
-    void printClassDeclaration() {
-        mPrinter->Print({{"classname", mName}}, Templates::ProtoClassDefinitionTemplate);
-    }
-
     void encloseClass() {
         mPrinter->Print(Templates::SemicolonBlockEnclosureTemplate);
         mPrinter->Print("\n");

+ 31 - 34
src/generator/generatorbase.cpp

@@ -44,28 +44,6 @@ GeneratorBase::GeneratorBase(Mode mode) : m_mode(mode)
 {
 
 }
-void GeneratorBase::iterateNonNestedFileds(const ::google::protobuf::FileDescriptor *file, std::function<void(const ::google::protobuf::Descriptor *)> callback) const
-{
-    for (int i = 0; i < file->message_type_count(); i++) {
-        const Descriptor *message = file->message_type(i);
-
-        //Detect nested fields and filter maps fields
-        int mapsFieldsCount = 0;
-        for (int j = 0; j < message->nested_type_count(); j++) {
-            for (int k = 0; k < message->field_count(); k++) {
-                if (message->field(k)->is_map() && message->field(k)->message_type() == message->nested_type(j)) {
-                    ++mapsFieldsCount;
-                }
-            }
-        }
-
-        if (message->nested_type_count() > 0 && message->nested_type_count() > mapsFieldsCount) {
-            std::cerr << file->name() << ":" << (message->index() + 1) << ": " << " Error: Meta object features not supported for nested classes in " << message->full_name() << std::endl;
-            continue;
-        }
-        callback(message);
-    }
-}
 
 bool GeneratorBase::GenerateAll(const std::vector<const FileDescriptor *> &files, const string &parameter, GeneratorContext *generatorContext, string *error) const
 {
@@ -103,7 +81,7 @@ void GeneratorBase::printInclude(const std::shared_ptr<::google::protobuf::io::P
 {
     assert(field != nullptr);
     std::string newInclude;
-    const char *includeTemplate;
+    const char *includeTemplate = "";
     switch (field->type()) {
     case FieldDescriptor::TYPE_MESSAGE:
         if (field->is_map()) {
@@ -114,19 +92,38 @@ void GeneratorBase::printInclude(const std::shared_ptr<::google::protobuf::io::P
             printInclude(printer, message, field->message_type()->field(1), existingIncludes);
             includeTemplate = Templates::ExternalIncludeTemplate;
         } else {
-            std::string outFileBasename = "";
-            std::string fieldPackage = field->message_type()->file()->package();
-            if (fieldPackage != message->file()->package()) {
-                std::vector<std::string> packages = utils::split(fieldPackage, '.');
-                for (auto package : packages) {
-                    outFileBasename += package + "/";
+            if (!common::isNested(field->message_type())) {
+                std::string outFileBasename = "";
+                std::string fieldPackage = field->message_type()->file()->package();
+                if (fieldPackage != message->file()->package()) {
+                    std::vector<std::string> packages = utils::split(fieldPackage, '.');
+                    for (auto package : packages) {
+                        outFileBasename += package + "/";
+                    }
                 }
-            }
 
-            std::string typeName = field->message_type()->name();
-            utils::tolower(typeName);
-            newInclude = outFileBasename + typeName;
-            includeTemplate = Templates::InternalIncludeTemplate;
+                std::string typeName = field->message_type()->name();
+                utils::tolower(typeName);
+                newInclude = outFileBasename + typeName;
+                includeTemplate = Templates::InternalIncludeTemplate;
+            } else if (!common::isNestedOf(field->message_type(), message)) {
+                auto containingMessage = common::findHighestMessage(field->message_type());
+                if (containingMessage != message) {
+                    std::string outFileBasename = "";
+                    std::string fieldPackage = containingMessage->file()->package();
+                    if (fieldPackage != message->file()->package()) {
+                        std::vector<std::string> packages = utils::split(fieldPackage, '.');
+                        for (auto package : packages) {
+                            outFileBasename += package + "/";
+                        }
+                    }
+
+                    std::string typeName = containingMessage->name();
+                    utils::tolower(typeName);
+                    newInclude = outFileBasename + typeName;
+                    includeTemplate = Templates::InternalIncludeTemplate;
+                }
+            }
         }
         break;
     case FieldDescriptor::TYPE_BYTES:

+ 1 - 1
src/generator/generatorbase.h

@@ -146,8 +146,8 @@ public:
     static void printQtProtobufUsingNamespace(const std::shared_ptr<::google::protobuf::io::Printer> printer);
     static void printNamespaces(const std::shared_ptr<::google::protobuf::io::Printer> printer, const std::vector<std::string> namespaces);
 protected:
-    void iterateNonNestedFileds(const ::google::protobuf::FileDescriptor *file, std::function<void(const ::google::protobuf::Descriptor *)> callback) const;
     static std::string generateBaseName(const ::google::protobuf::FileDescriptor *file, std::string name);
+
 private:
     Mode m_mode;
 };

+ 53 - 1
src/generator/generatorcommon.cpp

@@ -84,6 +84,7 @@ TypeMap common::produceMessageTypeMap(const ::Descriptor *type, const Descriptor
         {"full_list_type", fullListName},
         {"scope_list_type", scopeListName},
         {"namespaces", namespaces},
+        {"scope_namespaces", scopeNamespaces},
         {"qml_package", qmlPackage},
         {"property_type", fullName},
         {"property_list_type", fullListName},
@@ -100,7 +101,7 @@ TypeMap common::produceEnumTypeMap(const EnumDescriptor *type, const Descriptor
     std::string name = utils::upperCaseName(type->name());
     std::string qmlPackage = getNamespacesString(namespaceList, ".");//qml package should consist only from proto spackage
     std::string enumGadget = scope != nullptr ? utils::upperCaseName(scope->name()) : "";//Not used
-    if(visibility == GLOBAL_ENUM) {
+    if (visibility == GLOBAL_ENUM) {
         enumGadget = name + Templates::EnumClassSuffix;
         namespaceList.push_back(enumGadget);//Global enums are stored in helper Gadget
     }
@@ -131,6 +132,7 @@ TypeMap common::produceEnumTypeMap(const EnumDescriptor *type, const Descriptor
         {"full_list_type", fullListName},
         {"scope_list_type", scopeListName},
         {"namespaces", namespaces},
+        {"scope_namespaces", scopeNamespaces},
         {"qml_package", qmlPackage},
         {"property_type", propertyType},
         {"property_list_type", fullListName},
@@ -210,6 +212,7 @@ TypeMap common::produceSimpleTypeMap(FieldDescriptor::Type type)
         {"full_list_type", fullListName},
         {"scope_list_type", scopeListName},
         {"namespaces", namespaces},
+        {"scope_namespaces", namespaces},
         {"qml_package", qmlPackage},
         {"property_type", fullName},
         {"qml_alias_type", qmlAliasType},
@@ -359,3 +362,52 @@ MethodMap common::produceMethodMap(const MethodDescriptor *method, const std::st
                   {"return_name", "ret"}
                  };
 }
+
+void common::iterateMessages(const ::google::protobuf::FileDescriptor *file, std::function<void(const ::google::protobuf::Descriptor *)> callback)
+{
+    for (int i = 0; i < file->message_type_count(); i++) {
+        callback(file->message_type(i));
+    }
+}
+
+void common::iterateNestedMessages(const ::google::protobuf::Descriptor *message, std::function<void(const ::google::protobuf::Descriptor *)> callback)
+{
+    for (int i = 0; i < message->nested_type_count(); i++) {
+        auto nestedMessage = message->nested_type(i);
+        for (int j = 0; j < message->field_count(); j++) {
+            auto field = message->field(j);
+            if (!field->is_map() && field->message_type() == nestedMessage) { //Probably there is more correct way to detect map in nested messages.                                                                              //TODO: Have idea to make maps nested classes instead of typedefs.
+                callback(nestedMessage);
+            }
+        }
+    }
+}
+
+bool common::hasNestedMessages(const ::google::protobuf::Descriptor *message)
+{
+    for (int i = 0; i < message->nested_type_count(); i++) {
+        auto nestedMessage = message->nested_type(i);
+        for (int j = 0; j < message->field_count(); j++) {
+            auto field = message->field(j);
+            if (!field->is_map() && field->message_type() == nestedMessage) { //Probably there is more correct way to detect map in nested messages.                                                                              //TODO: Have idea to make maps nested classes instead of typedefs.
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+bool common::isNested(const ::google::protobuf::Descriptor *message)
+{
+    return message->containing_type() != nullptr;
+}
+
+const ::google::protobuf::Descriptor *common::findHighestMessage(const ::google::protobuf::Descriptor *message)
+{
+    const ::google::protobuf::Descriptor *highestMessage = message;
+    while (highestMessage->containing_type() != nullptr) {
+        highestMessage = highestMessage->containing_type();
+    }
+    return highestMessage;
+}

+ 11 - 0
src/generator/generatorcommon.h

@@ -83,6 +83,17 @@ struct common {
     }
 
     static MethodMap produceMethodMap(const ::google::protobuf::MethodDescriptor *method, const std::string &scope); //TODO: scope should be ServiceDescriptor
+
+    static void iterateMessages(const ::google::protobuf::FileDescriptor *file, std::function<void(const ::google::protobuf::Descriptor *)> callback);
+    static void iterateNestedMessages(const ::google::protobuf::Descriptor *message, std::function<void(const ::google::protobuf::Descriptor *)> callback);
+
+    static bool hasNestedMessages(const ::google::protobuf::Descriptor *message);
+
+    static bool isNested(const ::google::protobuf::Descriptor *message);
+    static bool isNestedOf(const ::google::protobuf::Descriptor *message, const ::google::protobuf::Descriptor *containing) {
+        return containing == message->containing_type();
+    }
+    static const ::google::protobuf::Descriptor *findHighestMessage(const ::google::protobuf::Descriptor *message);
 };
 
 }

+ 69 - 3
src/generator/messagedeclarationprinter.cpp

@@ -43,6 +43,52 @@ MessageDeclarationPrinter::MessageDeclarationPrinter(const Descriptor *message,
     mTypeMap = common::produceMessageTypeMap(message, nullptr);
 }
 
+void MessageDeclarationPrinter::printClassForwardDeclarationPrivate()
+{
+    if (common::hasNestedMessages(mDescriptor)) {
+        mPrinter->Print({{"namespace", mName + Templates::QtProtobufNestedNamespace}}, Templates::NamespaceTemplate);
+        common::iterateNestedMessages(mDescriptor, [this](const ::google::protobuf::Descriptor *nestedMessage) {
+            MessageDeclarationPrinter nesterPrinter(nestedMessage, mPrinter);
+            nesterPrinter.printClassForwardDeclarationPrivate();
+        });
+        mPrinter->Print(Templates::SimpleBlockEnclosureTemplate);
+    }
+
+    mPrinter->Print({{"classname", mName}}, Templates::ProtoClassForwardDeclarationTemplate);
+    mPrinter->Print({{"classname", mName}}, Templates::ComplexListTypeUsingTemplate);
+}
+
+void MessageDeclarationPrinter::printClassForwardDeclaration()
+{
+    printNamespaces();
+    printClassForwardDeclarationPrivate();
+    encloseNamespaces();
+}
+
+void MessageDeclarationPrinter::printClassDeclaration()
+{
+    printNamespaces();
+    printClassDeclarationPrivate();
+    encloseNamespaces();
+    printMetaTypesDeclaration();//Meta types declaration should be outside of namespaces block
+}
+
+void MessageDeclarationPrinter::printClassDeclarationPrivate()
+{
+    mPrinter->Print({{"namespace", mName + Templates::QtProtobufNestedNamespace}}, Templates::NamespaceTemplate);
+    common::iterateNestedMessages(mDescriptor, [this](const ::google::protobuf::Descriptor *nestedMessage) {
+        MessageDeclarationPrinter nesterPrinter(nestedMessage, mPrinter);
+        nesterPrinter.printClassDeclarationPrivate();
+    });
+    mPrinter->Print(Templates::SimpleBlockEnclosureTemplate);
+
+    printComments(mDescriptor);
+    printClassDeclarationBegin();
+    printClassBody();
+    encloseClass();
+    printListType();
+}
+
 void MessageDeclarationPrinter::printCopyFunctionality()
 {
     assert(mDescriptor != nullptr);
@@ -88,7 +134,7 @@ void MessageDeclarationPrinter::printConstructor(int fieldCount)
         FieldDescriptor::Type fieldType = field->type();
         if (field->is_repeated() && !field->is_map()) {
             parameterTemplate = Templates::ConstructorRepeatedParameterTemplate;
-        } else if(fieldType == FieldDescriptor::TYPE_BYTES
+        } else if (fieldType == FieldDescriptor::TYPE_BYTES
                   || fieldType == FieldDescriptor::TYPE_STRING
                   || fieldType == FieldDescriptor::TYPE_MESSAGE
                   || field->is_map()) {
@@ -101,7 +147,7 @@ void MessageDeclarationPrinter::printConstructor(int fieldCount)
 }
 
 void MessageDeclarationPrinter::printMaps()
-{
+{    
     Indent();
     for (int i = 0; i < mDescriptor->field_count(); i++) {
         const FieldDescriptor *field = mDescriptor->field(i);
@@ -114,6 +160,20 @@ void MessageDeclarationPrinter::printMaps()
     Outdent();
 }
 
+void MessageDeclarationPrinter::printNested()
+{
+    Indent();
+    common::iterateNestedMessages(mDescriptor, [&](const ::google::protobuf::Descriptor *nestedMessage) {
+        mPrinter->Print(common::produceMessageTypeMap(nestedMessage, mDescriptor), Templates::NestedMessageUsingTemplate);
+    });
+    Outdent();
+}
+
+void MessageDeclarationPrinter::printClassDeclarationBegin()
+{
+    mPrinter->Print({{"classname", mName}}, Templates::ProtoClassDeclarationBeginTemplate);
+}
+
 void MessageDeclarationPrinter::printMetaTypesDeclaration()
 {
     mPrinter->Print(mTypeMap,
@@ -140,6 +200,11 @@ void MessageDeclarationPrinter::printMetaTypesDeclaration()
             mPrinter->Print(propertyMap, Templates::DeclareMetaTypeMapTemplate);
         }
     });
+
+    common::iterateNestedMessages(mDescriptor, [this](const Descriptor *nestedMessage) {
+        MessageDeclarationPrinter nesterPrinter(nestedMessage, mPrinter);
+        nesterPrinter.printMetaTypesDeclaration();
+    });
 }
 
 void MessageDeclarationPrinter::printProperties()
@@ -285,6 +350,7 @@ void MessageDeclarationPrinter::printClassBody()
     printQEnums();
 
     printPublicBlock();
+    printNested();
     printMaps();
 
     Indent();
@@ -325,7 +391,7 @@ void MessageDeclarationPrinter::printClassMembers()
     common::iterateMessageFields(mDescriptor, [&](const FieldDescriptor *field, const PropertyMap &propertyMap) {
         if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() && !field->is_repeated()) {
             mPrinter->Print(propertyMap, Templates::ComplexMemberTemplate);
-        } else if(field->is_repeated() && !field->is_map()){
+        } else if (field->is_repeated() && !field->is_map()) {
              mPrinter->Print(propertyMap, Templates::ListMemberTemplate);
         } else {
             mPrinter->Print(propertyMap, Templates::MemberTemplate);

+ 8 - 11
src/generator/messagedeclarationprinter.h

@@ -42,16 +42,8 @@ public:
     MessageDeclarationPrinter(const ::google::protobuf::Descriptor *message, const std::shared_ptr<::google::protobuf::io::Printer> &printer);
     virtual ~MessageDeclarationPrinter() = default;
 
-    void run() {
-        printNamespaces();
-        printComments(mDescriptor);
-        printClassDeclaration();
-        printClassBody();
-        encloseClass();
-        printListType();
-        encloseNamespaces();
-        printMetaTypesDeclaration();
-    }
+    void printClassDeclaration();
+    void printClassForwardDeclaration();
 
 private:
     void printCopyFunctionality();
@@ -69,10 +61,15 @@ private:
     void printDestructor();
     void printListType();
     void printMaps();
-
+    void printNested();
     void printMetaTypesDeclaration();
+    void printClassDeclarationBegin();
 
     void printQEnums();
+
+    //Recursive functionality
+    void printClassDeclarationPrivate();
+    void printClassForwardDeclarationPrivate();
 };
 
 }

+ 29 - 1
src/generator/messagedefinitionprinter.cpp

@@ -37,6 +37,34 @@ MessageDefinitionPrinter::MessageDefinitionPrinter(const Descriptor *message, co
     mTypeMap = common::produceMessageTypeMap(message, nullptr);
 }
 
+void MessageDefinitionPrinter::printClassDefinitionPrivate()
+{
+    if (common::hasNestedMessages(mDescriptor)) {
+        mPrinter->Print({{"namespace", mName + Templates::QtProtobufNestedNamespace}}, Templates::NamespaceTemplate);
+        common::iterateNestedMessages(mDescriptor, [this](const Descriptor *nestedMessage) {
+            MessageDefinitionPrinter nestedPrinter(nestedMessage, mPrinter);
+            nestedPrinter.printClassDefinitionPrivate();
+        });
+        mPrinter->Print(Templates::SimpleBlockEnclosureTemplate);
+    }
+
+    printDestructor();
+    printFieldsOrdering();
+    printRegisterBody();
+    printConstructors();
+    printCopyFunctionality();
+    printMoveSemantic();
+    printComparisonOperators();
+    printGetters();
+}
+
+void MessageDefinitionPrinter::printClassDefinition()
+{
+    printNamespaces();
+    printClassDefinitionPrivate();
+    encloseNamespaces();
+}
+
 void MessageDefinitionPrinter::printRegisterBody()
 {
     mPrinter->Print(mTypeMap,
@@ -96,7 +124,7 @@ void MessageDefinitionPrinter::printConstructor(int fieldCount)
         FieldDescriptor::Type fieldType = field->type();
         if (field->is_repeated() && !field->is_map()) {
             parameterTemplate = Templates::ConstructorRepeatedParameterTemplate;
-        } else if(fieldType == FieldDescriptor::TYPE_BYTES
+        } else if (fieldType == FieldDescriptor::TYPE_BYTES
                   || fieldType == FieldDescriptor::TYPE_STRING
                   || fieldType == FieldDescriptor::TYPE_MESSAGE
                   || field->is_map()) {

+ 3 - 12
src/generator/messagedefinitionprinter.h

@@ -41,18 +41,7 @@ class MessageDefinitionPrinter : public DescriptorPrinterBase<google::protobuf::
 public:
     MessageDefinitionPrinter(const google::protobuf::Descriptor *message, const std::shared_ptr<::google::protobuf::io::Printer> &printer);
 
-    void run() {
-        printNamespaces();
-        printDestructor();
-        printFieldsOrdering();
-        printRegisterBody();
-        printConstructors();
-        printCopyFunctionality();
-        printMoveSemantic();
-        printComparisonOperators();
-        printGetters();
-        encloseNamespaces();
-    }
+    void printClassDefinition();
 
 private:
     void printRegisterBody();
@@ -65,6 +54,8 @@ private:
     void printComparisonOperators();
     void printGetters();
     void printDestructor();
+
+    void printClassDefinitionPrivate();
 };
 
 }}

+ 14 - 28
src/generator/multifilegenerator.cpp

@@ -62,24 +62,7 @@ bool MultiFileGenerator::Generate(const FileDescriptor *file,
         return false;
     }
 
-    for (int i = 0; i < file->message_type_count(); i++) {
-        const Descriptor *message = file->message_type(i);
-
-        //Detect nested fields and filter maps fields
-        int mapsFieldsCount = 0;
-        for (int j = 0; j < message->nested_type_count(); j++) {
-            for (int k = 0; k < message->field_count(); k++) {
-                if (message->field(k)->is_map() && message->field(k)->message_type() == message->nested_type(j)) {
-                    ++mapsFieldsCount;
-                }
-            }
-        }
-
-        if (message->nested_type_count() > 0 && message->nested_type_count() > mapsFieldsCount) {
-            std::cerr << file->name() << ":" << (message->index() + 1) << ": " << " Error: Meta object features not supported for nested classes in " << message->full_name() << std::endl;
-            continue;
-        }
-
+    common::iterateMessages(file, [&] (const ::google::protobuf::Descriptor *message) {
         std::string baseFilename(message->name());
         utils::tolower(baseFilename);
         baseFilename = generateBaseName(file, baseFilename);
@@ -105,23 +88,26 @@ bool MultiFileGenerator::Generate(const FileDescriptor *file,
             printInclude(headerPrinter, message, message->field(i), existingIncludes);
         }
 
+        //Print dependency classes forward declaration
         for (int i = 0; i < message->field_count(); i++) {
             auto field = message->field(i);
             if (field->type() == FieldDescriptor::TYPE_MESSAGE
                     && !field->is_map() && !field->is_repeated()) {
                 auto dependency = field->message_type();
-                auto namespaces = common::getNamespaces(dependency);
-                printNamespaces(headerPrinter, namespaces);
-                headerPrinter->Print({{"classname", utils::upperCaseName(dependency->name())}}, Templates::ProtoClassDeclarationTemplate);
-                headerPrinter->Print("\n");
-                for (size_t i = 0; i < namespaces.size(); ++i) {
-                    headerPrinter->Print(Templates::SimpleBlockEnclosureTemplate);
+                if (!common::isNested(dependency)) {//TODO: need to check class relations and apply namespaces accordingly.
+                    auto namespaces = common::getNamespaces(dependency);
+                    printNamespaces(headerPrinter, namespaces);
+                    headerPrinter->Print({{"classname", utils::upperCaseName(dependency->name())}}, Templates::ProtoClassForwardDeclarationTemplate);
+                    headerPrinter->Print("\n");
+                    for (size_t i = 0; i < namespaces.size(); ++i) {
+                        headerPrinter->Print(Templates::SimpleBlockEnclosureTemplate);
+                    }
+                    headerPrinter->Print("\n");
                 }
-                headerPrinter->Print("\n");
             }
         }
 
-        messageDecl.run();
+        messageDecl.printClassDeclaration();
 
         printDisclaimer(sourcePrinter);
         std::string includeFileName = message->name();
@@ -132,8 +118,8 @@ bool MultiFileGenerator::Generate(const FileDescriptor *file,
         }
 
         MessageDefinitionPrinter messageDef(message, sourcePrinter);
-        messageDef.run();
-    }
+        messageDef.printClassDefinition();
+    });
 
     for (int i = 0; i < file->service_count(); i++) {
         const ServiceDescriptor *service = file->service(i);

+ 6 - 23
src/generator/singlefilegenerator.cpp

@@ -125,35 +125,18 @@ bool SingleFileGenerator::GenerateMessages(const ::google::protobuf::FileDescrip
         enumSourceDef.run();
     }
 
-    std::vector<std::string> namespaces = utils::split(file->package(), '.');
-    std::string namespacesColonDelimited;
-
-    assert(namespaces.size() > 0);
-    for (size_t i = 0; i < namespaces.size(); i++) {
-        if (i > 0) {
-            namespacesColonDelimited = namespacesColonDelimited.append("::");
-        }
-        namespacesColonDelimited = namespacesColonDelimited.append(namespaces[i]);
-        headerPrinter->Print({{"namespace", namespaces[i]}}, Templates::NamespaceTemplate);
-    }
-
-    iterateNonNestedFileds(file, [&headerPrinter](const ::google::protobuf::Descriptor *message){
-        std::string qualifiedClassName = utils::upperCaseName(message->name());
-        headerPrinter->Print({{"classname", qualifiedClassName}}, Templates::ProtoClassDeclarationTemplate);
-        headerPrinter->Print({{"classname", qualifiedClassName}}, Templates::ComplexListTypeUsingTemplate);
+    common::iterateMessages(file, [&headerPrinter](const ::google::protobuf::Descriptor *message) {
+        MessageDeclarationPrinter messageDecl(message, headerPrinter);
+        messageDecl.printClassForwardDeclaration();
     });
 
-    for (size_t i = 0; i < namespaces.size(); i++) {
-        headerPrinter->Print(Templates::SimpleBlockEnclosureTemplate);
-    }
-
-    iterateNonNestedFileds(file, [&headerPrinter, &sourcePrinter](const ::google::protobuf::Descriptor *message){
+    common::iterateMessages(file, [&headerPrinter, &sourcePrinter](const ::google::protobuf::Descriptor *message) {
         MessageDeclarationPrinter messageDecl(message, headerPrinter);
+        messageDecl.printClassDeclaration();
 
-        messageDecl.run();
 
         MessageDefinitionPrinter messageDef(message, sourcePrinter);
-        messageDef.run();
+        messageDef.printClassDefinition();
     });
 
     return true;

+ 7 - 3
src/generator/templates.cpp

@@ -58,6 +58,7 @@ const char *Templates::UsingQtProtobufNamespaceTemplate = "\nusing namespace QtP
 const char *Templates::ManualRegistrationDeclaration = "static void registerTypes();\n";
 const char *Templates::ManualRegistrationComplexTypeDefinition = "void $type$::registerTypes()\n{\n"
                                                                  "    qRegisterMetaType<$type$>(\"$full_type$\");\n"
+                                                                 "    qRegisterMetaType<$type$*>(\"$full_type$*\");\n" //Somehow for aliastypes qRegisterMetaType logic doesn't work for pointer type registration
                                                                  "    qRegisterMetaType<$list_type$>(\"$full_list_type$\");\n"
                                                                  "";
 const char *Templates::ManualRegistrationGlobalEnumDefinition = "void $enum_gadget$::registerTypes()\n{\n"
@@ -66,6 +67,8 @@ const char *Templates::ComplexGlobalEnumFieldRegistrationTemplate = "qRegisterMe
 const char *Templates::ComplexListTypeUsingTemplate = "using $classname$Repeated = QList<QSharedPointer<$classname$>>;\n";
 const char *Templates::MapTypeUsingTemplate = "using $type$ = QMap<$key_type$, $value_type$>;\n";
 const char *Templates::MessageMapTypeUsingTemplate = "using $type$ = QMap<$key_type$, QSharedPointer<$value_type$>>;\n";
+const char *Templates::NestedMessageUsingTemplate = "using $type$ = $scope_namespaces$_QtProtobufNested::$type$;\n"
+                                                    "using $list_type$ = $scope_namespaces$_QtProtobufNested::$list_type$;\n";
 
 const char *Templates::EnumTypeRepeatedTemplate = "using $list_type$ = QList<$type$>;\n";
 
@@ -74,8 +77,8 @@ const char *Templates::UsingNamespaceTemplate = "using namespace $namespace$;\n"
 const char *Templates::ClassDeclarationTemplate = "\nclass $classname$ : public QObject\n"
                                                          "{\n"
                                                          "    Q_OBJECT\n";
-const char *Templates::ProtoClassDeclarationTemplate = "class $classname$;\n";
-const char *Templates::ProtoClassDefinitionTemplate = "\nclass $classname$ : public QObject\n"
+const char *Templates::ProtoClassForwardDeclarationTemplate = "class $classname$;\n";
+const char *Templates::ProtoClassDeclarationBeginTemplate = "\nclass $classname$ : public QObject\n"
                                                       "{\n"
                                                       "    Q_OBJECT\n"
                                                       "    Q_PROTOBUF_OBJECT\n"
@@ -263,7 +266,7 @@ const char *Templates::DeclareMetaTypeMapTemplate = "#ifndef Q_PROTOBUF_MAP_$key
 
 const char *Templates::RegisterLocalEnumTemplate = "qRegisterProtobufEnumType<$scope_type$>();\n"
                                                    "qRegisterMetaType<$scope_type$>(\"$type$\");\n"
-                                                   "qRegisterMetaType<$scope_list_type$>(\"$full_type$\");\n"
+                                                   "qRegisterMetaType<$scope_type$>(\"$full_type$\");\n"
                                                    "qRegisterMetaType<$scope_list_type$>(\"$full_list_type$\");\n";
 const char *Templates::RegisterMapTemplate = "qRegisterMetaType<$scope_type$>(\"$full_type$\");\n"
                                              "qRegisterMetaType<$scope_type$>(\"$full_list_type$\");\n"
@@ -353,3 +356,4 @@ const char *Templates::GrpcFileSuffix = "_grpc";
 const char *Templates::EnumClassSuffix = "Gadget";
 
 const char *Templates::QtProtobufNamespace = "QtProtobuf";
+const char *Templates::QtProtobufNestedNamespace = "_QtProtobufNested";

+ 4 - 2
src/generator/templates.h

@@ -57,12 +57,13 @@ public:
     static const char *ComplexListTypeUsingTemplate;
     static const char *MapTypeUsingTemplate;
     static const char *MessageMapTypeUsingTemplate;
+    static const char *NestedMessageUsingTemplate;
     static const char *EnumTypeRepeatedTemplate;
     static const char *NamespaceTemplate;
     static const char *UsingNamespaceTemplate;
     static const char *ClassDeclarationTemplate;
-    static const char *ProtoClassDeclarationTemplate;
-    static const char *ProtoClassDefinitionTemplate;
+    static const char *ProtoClassForwardDeclarationTemplate;
+    static const char *ProtoClassDeclarationBeginTemplate;
     static const char *ConstructorHeaderTemplate;
     static const char *ClassDefinitionTemplate;
     static const char *QObjectMacro;
@@ -203,6 +204,7 @@ public:
     static const std::unordered_map<::google::protobuf::FieldDescriptor::Type, std::string> TypeReflection;
 
     static const char *QtProtobufNamespace;
+    static const char *QtProtobufNestedNamespace;
 };
 
 } //namespace generator

+ 1 - 1
src/protobuf/qprotobufjsonserializer.cpp

@@ -316,7 +316,7 @@ public:
             QtProtobuf::QProtobufSelfcheckIterator it(data);
             QtProtobuf::QProtobufSelfcheckIterator last = it;
             last += it.size();
-            while(it != last) {
+            while (it != last) {
                 ok = true;
                 handler.deserializer(qPtr, it, newValue);
                 qDebug() << "newValue" << newValue;

+ 7 - 7
tests/test_grpc/clienttest.cpp

@@ -258,17 +258,17 @@ TEST_F(ClientTest, StringEchoStreamAbortByTimerTest)
 
     int i = 0;
     QtProtobuf::QGrpcSubscription *subscription = testClient.subscribeTestMethodServerStreamUpdates(request);
-    QTimer::singleShot(3500, subscription, [subscription](){
+    QTimer::singleShot(3500, subscription, [subscription]() {
         subscription->cancel();
     });
 
     bool isFinished = false;
-    QObject::connect(subscription, &QtProtobuf::QGrpcSubscription::finished, [&isFinished](){
+    QObject::connect(subscription, &QtProtobuf::QGrpcSubscription::finished, [&isFinished]() {
         isFinished = true;
     });
 
     bool isError = false;
-    QObject::connect(subscription, &QtProtobuf::QGrpcSubscription::error, [&isError](){
+    QObject::connect(subscription, &QtProtobuf::QGrpcSubscription::error, [&isError]() {
         isError = true;
     });
 
@@ -568,12 +568,12 @@ TEST_F(ClientTest, MultipleSubscriptionsCancelTest)
     ASSERT_EQ(subscription, subscriptionNext);
 
     bool isFinished = false;
-    QObject::connect(subscription, &QtProtobuf::QGrpcSubscription::finished, [&isFinished](){
+    QObject::connect(subscription, &QtProtobuf::QGrpcSubscription::finished, [&isFinished]() {
         isFinished = true;
     });
 
     bool isFinishedNext = false;
-    QObject::connect(subscriptionNext, &QtProtobuf::QGrpcSubscription::finished, [&isFinishedNext](){
+    QObject::connect(subscriptionNext, &QtProtobuf::QGrpcSubscription::finished, [&isFinishedNext]() {
         isFinishedNext = true;
     });
 
@@ -590,12 +590,12 @@ TEST_F(ClientTest, MultipleSubscriptionsCancelTest)
     ASSERT_EQ(subscription, subscriptionNext);
 
     isFinished = false;
-    QObject::connect(subscription, &QtProtobuf::QGrpcSubscription::finished, [&isFinished](){
+    QObject::connect(subscription, &QtProtobuf::QGrpcSubscription::finished, [&isFinished]() {
         isFinished = true;
     });
 
     isFinishedNext = false;
-    QObject::connect(subscriptionNext, &QtProtobuf::QGrpcSubscription::finished, [&isFinishedNext](){
+    QObject::connect(subscriptionNext, &QtProtobuf::QGrpcSubscription::finished, [&isFinishedNext]() {
         isFinishedNext = true;
     });
 

+ 2 - 1
tests/test_protobuf/CMakeLists.txt

@@ -10,7 +10,8 @@ file(GLOB SOURCES
     converterstest.cpp
     jsonserializationtest.cpp
     jsondeserializationtest.cpp
-    duplicatedmetatypestest.cpp)
+    duplicatedmetatypestest.cpp
+    nestedtest.cpp)
 
 add_test_target(TARGET ${TARGET}
     SOURCES ${SOURCES}

+ 28 - 0
tests/test_protobuf/nestedtest.cpp

@@ -0,0 +1,28 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of QtProtobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "nestedmessages.qpb.h"
+
+#include "./nestedtest.cpp.inc"

+ 175 - 0
tests/test_protobuf/nestedtest.cpp.inc

@@ -0,0 +1,175 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of QtProtobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <gtest/gtest.h>
+#include <qprotobufserializer.h>
+#include "../testscommon.h"
+
+using namespace qtprotobufnamespace::tests::nested;
+
+namespace QtProtobuf {
+namespace tests {
+
+class NestedTest : public ::testing::Test
+{
+public:
+    NestedTest() = default;
+    void SetUp() override;
+    static void SetUpTestCase();
+protected:
+    std::unique_ptr<QProtobufSerializer> serializer;
+};
+
+
+void NestedTest::SetUpTestCase()
+{
+    //Register all types
+    QtProtobuf::qRegisterProtobufTypes();
+}
+
+void NestedTest::SetUp()
+{
+    serializer.reset(new QProtobufSerializer);
+}
+
+TEST_F(NestedTest, NestedMessageTest)
+{
+    assertMessagePropertyRegistered<NestedFieldMessage::NestedMessage, QtProtobuf::sint32>(1, "QtProtobuf::sint32", "testFieldInt");
+}
+
+TEST_F(NestedTest, SimpleTest)
+{
+    const char *propertyName = "nested";
+
+    assertMessagePropertyRegistered<NestedFieldMessage, NestedFieldMessage::NestedMessage*>(2, "qtprotobufnamespace::tests::nested::NestedFieldMessage::NestedMessage*", "nested");
+
+    NestedFieldMessage test{10, {15}};
+    EXPECT_EQ(test.nested().testFieldInt(), 15);
+
+    ASSERT_TRUE(test.setProperty(propertyName, QVariant::fromValue<NestedFieldMessage::NestedMessage*>(new NestedFieldMessage::NestedMessage{55})));
+    ASSERT_TRUE(*(test.property(propertyName).value<NestedFieldMessage::NestedMessage*>()) == NestedFieldMessage::NestedMessage{55});
+    ASSERT_TRUE(test.nested() == NestedFieldMessage::NestedMessage{55});
+
+    assertMessagePropertyRegistered<NestedFieldMessage2::NestedMessageLevel1, NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*>(1, "qtprotobufnamespace::tests::nested::NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*", "nested");
+
+    NestedFieldMessage2::NestedMessageLevel1 level1{{20}};
+    EXPECT_EQ(level1.nested().testFieldInt(), 20);
+    ASSERT_TRUE(level1.setProperty(propertyName, QVariant::fromValue<NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*>(new NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2{55})));
+    ASSERT_TRUE(*(level1.property(propertyName).value<NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*>()) == NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2{55});
+    ASSERT_TRUE(level1.nested() == NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2{55});
+
+    assertMessagePropertyRegistered<NestedFieldMessage2, NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*>(3, "qtprotobufnamespace::tests::nested::NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*", "nested2");
+
+    NestedFieldMessage2 test2{level1, {20}};
+    EXPECT_EQ(test2.nested1().nested().testFieldInt(), 55);
+    EXPECT_EQ(test2.nested2().testFieldInt(), 20);
+
+    ASSERT_TRUE(test2.setProperty("nested1", QVariant::fromValue<NestedFieldMessage2::NestedMessageLevel1*>(new NestedFieldMessage2::NestedMessageLevel1{{65}})));
+    ASSERT_TRUE(*(test2.property("nested1").value<NestedFieldMessage2::NestedMessageLevel1*>()) == NestedFieldMessage2::NestedMessageLevel1{{65}});
+    ASSERT_TRUE(test2.nested1() == NestedFieldMessage2::NestedMessageLevel1{{65}});
+
+    ASSERT_TRUE(test2.setProperty("nested2", QVariant::fromValue<NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*>(new NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2{75})));
+    ASSERT_TRUE(*(test2.property("nested2").value<NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*>()) == NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2{75});
+    ASSERT_TRUE(test2.nested2() == NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2{75});
+}
+
+TEST_F(NestedTest, SerializationTest)
+{
+    NestedFieldMessage::NestedMessage nested{15};
+    QByteArray result = nested.serialize(serializer.get());
+    EXPECT_EQ(result.size(), 2);
+    EXPECT_STREQ(result.toHex().toStdString().c_str(), "081e");
+
+    NestedFieldMessage test{10, nested};
+    result = test.serialize(serializer.get());
+    EXPECT_TRUE(result == QByteArray::fromHex("1202081e0814")
+                || result == QByteArray::fromHex("08141202081e"));
+
+    NestedFieldMessage2::NestedMessageLevel1 level1{{10}};
+    result = level1.serialize(serializer.get());
+    EXPECT_STREQ(result.toHex().toStdString().c_str(), "0a020814");
+
+    NestedFieldMessage2 test2{level1, {15}};
+    result = test2.serialize(serializer.get());
+    EXPECT_TRUE(result == QByteArray::fromHex("1a02081e12040a020814")
+                || result == QByteArray::fromHex("12040a0208141a02081e"));
+
+    NeighborNested neigbor{{15},{20}};
+    result = neigbor.serialize(serializer.get());
+    EXPECT_TRUE(result == QByteArray::fromHex("120208280a02081e")
+                || result == QByteArray::fromHex("0a02081e12020828"));
+}
+
+TEST_F(NestedTest, DeserializationTest)
+{
+    NestedFieldMessage::NestedMessage nested;
+
+    nested.deserialize(serializer.get(), QByteArray::fromHex("081e"));
+    EXPECT_EQ(nested.testFieldInt(), 15);
+
+    NestedFieldMessage test;
+    test.deserialize(serializer.get(), QByteArray::fromHex("1202081e0814"));
+    EXPECT_EQ(test.nested().testFieldInt(), 15);
+
+    NestedFieldMessage2::NestedMessageLevel1 level1;
+    level1.deserialize(serializer.get(), QByteArray::fromHex("0a020814"));
+    EXPECT_EQ(level1.nested().testFieldInt(), 10);
+
+    NestedFieldMessage2 test2;
+    test2.deserialize(serializer.get(), QByteArray::fromHex("1a02081e12040a020814"));
+    EXPECT_EQ(test2.nested1().nested().testFieldInt(), 10);
+    EXPECT_EQ(test2.nested2().testFieldInt(), 15);
+
+    NeighborNested neigbor;
+    neigbor.deserialize(serializer.get(), QByteArray::fromHex("120208280a02081e"));
+    EXPECT_EQ(neigbor.neighborNested().testFieldInt(), 15);
+    EXPECT_EQ(neigbor.neighborNested2().testFieldInt(), 20);
+}
+
+
+TEST_F(NestedTest, NeighborTest)
+{
+    assertMessagePropertyRegistered<NeighborNested, NestedFieldMessage::NestedMessage*>(1, "qtprotobufnamespace::tests::nested::NestedFieldMessage::NestedMessage*", "neighborNested");
+    assertMessagePropertyRegistered<NeighborNested, NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*>(2, "qtprotobufnamespace::tests::nested::NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*", "neighborNested2");
+
+    NeighborNested test{{15},{20}};
+    EXPECT_EQ(test.neighborNested().testFieldInt(), 15);
+    EXPECT_EQ(test.neighborNested2().testFieldInt(), 20);
+
+    const char *propertyName = "neighborNested";
+
+    ASSERT_TRUE(test.setProperty(propertyName, QVariant::fromValue<NestedFieldMessage::NestedMessage*>(new NestedFieldMessage::NestedMessage{55})));
+    ASSERT_TRUE(*(test.property(propertyName).value<NestedFieldMessage::NestedMessage*>()) == NestedFieldMessage::NestedMessage{55});
+    ASSERT_TRUE(test.neighborNested() == NestedFieldMessage::NestedMessage{55});
+
+    propertyName = "neighborNested2";
+
+    ASSERT_TRUE(test.setProperty(propertyName, QVariant::fromValue<NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*>(new NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2{75})));
+    ASSERT_TRUE(*(test.property(propertyName).value<NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2*>()) == NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2{75});
+    ASSERT_TRUE(test.neighborNested2() == NestedFieldMessage2::NestedMessageLevel1::NestedMessageLevel2{75});
+}
+
+}
+}

+ 18 - 2
tests/test_protobuf/proto/nestedmessages.proto

@@ -2,10 +2,26 @@ syntax = "proto3";
 
 package qtprotobufnamespace.tests.nested;
 
-message NestedSimpleIntMessage {
+message NestedFieldMessage {
     sint32 testFieldInt = 1;
-    message SimpleIntMessage {
+    message NestedMessage {
         sint32 testFieldInt = 1;
     }
+    NestedMessage nested = 2;   
 }
 
+message NestedFieldMessage2 {
+    message NestedMessageLevel1 {
+        message NestedMessageLevel2 {
+            sint32 testFieldInt = 1;
+        }
+        NestedMessageLevel2 nested = 1;
+    }
+    NestedMessageLevel1 nested1 = 2;   
+    NestedMessageLevel1.NestedMessageLevel2 nested2 = 3;   
+}
+
+message NeighborNested {
+    NestedFieldMessage.NestedMessage neighborNested = 1;
+    NestedFieldMessage2.NestedMessageLevel1.NestedMessageLevel2 neighborNested2 = 2;
+}

+ 8 - 1
tests/test_protobuf/simpletest.cpp.inc

@@ -326,7 +326,14 @@ TEST_F(SimpleTest, SimpleFileEnumsTest)
 
 TEST_F(SimpleTest, ComplexMessageTest)
 {
-    ComplexMessage msg;
+    const char *propertyName = "testComplexField";
+    assertMessagePropertyRegistered<qtprotobufnamespace::tests::ComplexMessage, qtprotobufnamespace::tests::SimpleStringMessage*>(
+                2, "qtprotobufnamespace::tests::SimpleStringMessage*", propertyName);
+
+    ComplexMessage test;
+    ASSERT_TRUE(test.setProperty(propertyName, QVariant::fromValue<qtprotobufnamespace::tests::SimpleStringMessage*>(new qtprotobufnamespace::tests::SimpleStringMessage{"test qwerty"})));
+    ASSERT_TRUE(*(test.property(propertyName).value<qtprotobufnamespace::tests::SimpleStringMessage*>()) == qtprotobufnamespace::tests::SimpleStringMessage{"test qwerty"});
+    ASSERT_TRUE(test.testComplexField() == qtprotobufnamespace::tests::SimpleStringMessage{"test qwerty"});
 }
 
 TEST_F(SimpleTest, SimpleBytesMessageTest)

+ 2 - 2
tests/test_protobuf_multifile/CMakeLists.txt

@@ -3,13 +3,13 @@ set(TARGET qtprotobuf_test_multifile)
 include(${QT_PROTOBUF_CMAKE_DIR}/QtProtobufTest.cmake)
 
 file(GLOB SOURCES
-    simpletest.cpp)
+    simpletest.cpp
+    nestedtest.cpp)
 
 file(GLOB PROTO_FILES ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR}/../test_protobuf/proto/*.proto)
 
 add_test_target(TARGET ${TARGET}
     PROTO_FILES ${PROTO_FILES}
-    EXCLUDE_HEADERS qtprotobufnamespace/tests/nested/nestedsimpleintmessage.h
     SOURCES ${SOURCES}
     QML
     MULTI)

+ 30 - 0
tests/test_protobuf_multifile/nestedtest.cpp

@@ -0,0 +1,30 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of QtProtobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "qtprotobufnamespace/tests/nested/neighbornested.h"
+#include "qtprotobufnamespace/tests/nested/nestedfieldmessage.h"
+#include "qtprotobufnamespace/tests/nested/nestedfieldmessage2.h"
+
+#include "../test_protobuf/nestedtest.cpp.inc"

+ 1 - 1
tests/testscommon.h

@@ -39,7 +39,7 @@ static void assertMessagePropertyRegistered(int fieldIndex, const char *property
 
     const int propertyNumber = MessageType::propertyOrdering.at(fieldIndex);
     ASSERT_STREQ(MessageType::staticMetaObject.property(propertyNumber).typeName(), propertyTypeName);
-    if(!skipMetatypeCheck) {
+    if (!skipMetatypeCheck) {
         ASSERT_EQ(MessageType::staticMetaObject.property(propertyNumber).userType(), qMetaTypeId<PropertyType>());
     }
     ASSERT_STREQ(MessageType::staticMetaObject.property(propertyNumber).name(), propertyName);