瀏覽代碼

Well-known types implementation

- Add well-known types library
- Add well-known types protos and support
- Implement simple tests for well-known types
Alexey Edelev 5 年之前
父節點
當前提交
c83250303c

+ 3 - 1
CMakeLists.txt

@@ -69,7 +69,9 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
 add_subdirectory("src/protobuf")
 add_subdirectory("src/grpc")
 add_subdirectory("src/generator")
-
+if(NOT WIN32)#TODO: There are linking issues with windows build of well-known types...
+	add_subdirectory("src/wellknowntypes")
+endif()
 configure_package_config_file(
     "${CMAKE_CURRENT_SOURCE_DIR}/ProjectConfig.cmake.in" "${QTPROTOBUF_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
     INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake")

+ 4 - 0
cmake/QtProtobufGen.cmake

@@ -103,5 +103,9 @@ function(generate_qtprotobuf)
     target_include_directories(${QtProtobuf_GENERATED} PUBLIC ${OUT_DIR} PRIVATE ${Qt5Core_INCLUDE_DIRS}
         $<TARGET_PROPERTY:${QTPROTOBUF_COMMON_NAMESPACE}::QtProtobuf,INTERFACE_INCLUDE_DIRECTORIES>
         $<TARGET_PROPERTY:${QTPROTOBUF_COMMON_NAMESPACE}::QtGrpc,INTERFACE_INCLUDE_DIRECTORIES> ${OUT_DIR})
+	if(NOT WIN32)
+		target_include_directories(${QtProtobuf_GENERATED} PRIVATE
+			$<TARGET_PROPERTY:${QTPROTOBUF_COMMON_NAMESPACE}::QtProtobufWellKnownTypes,INTERFACE_INCLUDE_DIRECTORIES>)
+	endif()
 endfunction()
 

+ 4 - 4
src/protobuf/QtProtobufConfig.cmake.in

@@ -5,8 +5,8 @@ if(NOT TARGET @TARGET@ AND NOT @TARGET@_BINARY_DIR)
 endif()
 
 @PACKAGE_INIT@
-set(PROTO_PARSER ${CMAKE_CURRENT_LIST_DIR}/parsemessages.go)
-set(GENERATOR_TARGET @GENERATOR_TARGET@)
-set(QTPROTOBUF_COMMON_NAMESPACE @QTPROTOBUF_COMMON_NAMESPACE@)
-set(QTPROTOBUF_EXECUTABLE_INSTALL @QTPROTOBUF_EXECUTABLE_INSTALL@)
+set(PROTO_PARSER "${CMAKE_CURRENT_LIST_DIR}/parsemessages.go")
+set(GENERATOR_TARGET "@GENERATOR_TARGET@")
+set(QTPROTOBUF_COMMON_NAMESPACE "@QTPROTOBUF_COMMON_NAMESPACE@")
+set(QTPROTOBUF_EXECUTABLE_INSTALL "@QTPROTOBUF_EXECUTABLE_INSTALL@")
 include("${CMAKE_CURRENT_LIST_DIR}/QtProtobufGen.cmake")

+ 119 - 0
src/wellknowntypes/CMakeLists.txt

@@ -0,0 +1,119 @@
+set(TARGET QtProtobufWellKnownTypes)
+set(TARGET_STATIC ${TARGET}Static)
+set(TARGET_EXPORT ${TARGET}Targets)
+set(TARGET_CONFIG ${TARGET}Config)
+
+set(TARGET_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}/QtProtobuf/google/protobuf)
+set(TARGET_LIB_DIR ${CMAKE_INSTALL_LIBDIR})
+set(TARGET_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
+set(TARGET_BIN_DIR ${CMAKE_INSTALL_BINDIR})
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+find_package(Qt5 COMPONENTS Core Qml REQUIRED)
+find_package(Protobuf QUIET)
+
+set(PROTO_PARSER ${QTPROTOBUF_BINARY_DIR}/parsemessages.go)
+
+include(${QTPROTOBUF_CMAKE_DIR}/Coverage.cmake)
+include(${QTPROTOBUF_CMAKE_DIR}/GenerateQtHeaders.cmake)
+include(${QTPROTOBUF_CMAKE_DIR}/QtProtobufGen.cmake)
+
+file(GLOB SOURCES
+    timestamp.qpb.cpp)
+
+file(GLOB HEADERS
+    qtprotobufwellknowntypes_global.qbp.h)
+
+file(GLOB PUBLIC_HEADERS
+    qtprotobufwellknowntypes_global.qbp.h)
+
+function(add_wellknowntype VARIABLE TYPENAME)
+    list(APPEND LOOKUP_DIRS ${QTPROTOBUF_SOURCE_DIR}/3rdparty/grpc/third_party/protobuf/src)
+    list(APPEND LOOKUP_DIRS ${Protobuf_INCLUDE_DIRS})
+    foreach(INCLUDE_DIR ${LOOKUP_DIRS})
+        file(GLOB PROTO_FILE ${INCLUDE_DIR}/google/protobuf/${TYPENAME}.proto)
+        if (NOT "${PROTO_FILE}" STREQUAL "")
+            message(STATUS "Add well-known type ${PROTO_FILE}")
+            generate_qtprotobuf(TARGET ${TYPENAME}
+                OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated/google/protobuf
+                PROTO_FILES ${PROTO_FILE}
+                PROTO_INCLUDES -I${QTPROTOBUF_SOURCE_DIR}/3rdparty/grpc/third_party/protobuf/src
+                QML TRUE)
+            break()
+        endif()
+    endforeach()
+    target_include_directories(${QtProtobuf_GENERATED} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/generated)
+    if(WIN32)
+		get_target_property(EXISTING_FLAGS ${TARGET} LINK_FLAGS)
+		set_target_properties(${QtProtobuf_GENERATED} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
+		set_target_properties(${TARGET} PROPERTIES LINK_DIRECTORIES "$<TARGET_FILE_DIR:${QtProtobuf_GENERATED}>")
+		if("${EXISTING_FLAGS}" STREQUAL "EXISTING_FLAGS-NOTFOUND")
+			set_target_properties(${TARGET} PROPERTIES LINK_FLAGS "/WHOLEARCHIVE:${QtProtobuf_GENERATED}")
+		else()
+			set_target_properties(${TARGET} PROPERTIES LINK_FLAGS "${EXISTING_FLAGS} /WHOLEARCHIVE:${QtProtobuf_GENERATED}")
+		endif()
+    else()
+        set(${VARIABLE} ${${VARIABLE}} -Wl,--whole-archive ${QtProtobuf_GENERATED} -Wl,--no-whole-archive PARENT_SCOPE)
+    endif()
+endfunction()
+
+add_library(${TARGET} SHARED ${SOURCES})
+
+add_wellknowntype(GENERATED_TARGETS any)
+add_wellknowntype(GENERATED_TARGETS api)
+add_wellknowntype(GENERATED_TARGETS duration)
+add_wellknowntype(GENERATED_TARGETS empty)
+add_wellknowntype(GENERATED_TARGETS field_mask)
+add_wellknowntype(GENERATED_TARGETS source_context)
+add_wellknowntype(GENERATED_TARGETS struct)
+add_wellknowntype(GENERATED_TARGETS timestamp)
+add_wellknowntype(GENERATED_TARGETS type)
+add_wellknowntype(GENERATED_TARGETS wrappers)
+
+protobuf_generate_qt_headers(PUBLIC_HEADERS ${PUBLIC_HEADERS} COMPONENT ${TARGET})
+
+target_compile_definitions(${TARGET} PRIVATE QT_BUILD_PROTOBUF_WELLKNOWNTYPES_LIB PUBLIC QTPROTOBUF_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
+    QTPROTOBUF_VERSION_MINOR=${PROJECT_VERSION_MINOR})
+
+add_library(${QTPROTOBUF_COMMON_NAMESPACE}::${TARGET} ALIAS ${TARGET})
+set_target_properties(${TARGET} PROPERTIES VERSION ${PROJECT_VERSION} PUBLIC_HEADER "${PUBLIC_HEADERS};${GENERATED_PUBLIC_HEADERS}" OUTPUT_NAME ${TARGET}
+    PROTO_INCLUDES -I${QTPROTOBUF_SOURCE_DIR}/3rdparty/grpc/third_party/protobuf/src)
+set_target_properties(${TARGET} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
+target_include_directories(${TARGET} PUBLIC
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/generated>
+    $<BUILD_INTERFACE:${QTPROTOBUF_BINARY_DIR}/include/${TARGET}>
+    $<INSTALL_INTERFACE:${TARGET_INCLUDE_DIR}>
+)
+
+target_link_libraries(${TARGET} PUBLIC Qt5::Core Qt5::Qml ${QTPROTOBUF_COMMON_NAMESPACE}::QtProtobuf PRIVATE ${GENERATED_TARGETS})
+target_compile_features(${TARGET} PUBLIC cxx_std_14
+                                         cxx_auto_type
+                                         cxx_decltype
+                                         cxx_final
+                                         cxx_override
+                                         cxx_nullptr
+                                         cxx_lambdas
+                                         cxx_func_identifier)
+
+install(TARGETS ${TARGET}
+    EXPORT ${TARGET_EXPORT}
+    ARCHIVE DESTINATION ${TARGET_LIB_DIR}
+    PUBLIC_HEADER DESTINATION ${TARGET_INCLUDE_DIR}
+    LIBRARY DESTINATION ${TARGET_LIB_DIR}
+    RUNTIME DESTINATION ${TARGET_BIN_DIR})
+
+install(EXPORT ${TARGET_EXPORT} NAMESPACE ${QTPROTOBUF_COMMON_NAMESPACE}:: FILE ${TARGET_EXPORT}.cmake DESTINATION ${TARGET_CMAKE_DIR})
+
+include(CMakePackageConfigHelpers)
+configure_package_config_file(
+    "${TARGET_CONFIG}.cmake.in" "${QTPROTOBUF_BINARY_DIR}/${TARGET_CONFIG}.cmake"
+    INSTALL_DESTINATION "${TARGET_CMAKE_DIR}")
+
+install(FILES "${QTPROTOBUF_BINARY_DIR}/${TARGET_CONFIG}.cmake" DESTINATION "${TARGET_CMAKE_DIR}")
+
+export(TARGETS ${TARGET} NAMESPACE ${QTPROTOBUF_COMMON_NAMESPACE}:: FILE ${TARGET_EXPORT}.cmake)
+
+add_coverage_target(TARGET ${TARGET})

+ 9 - 0
src/wellknowntypes/QtProtobufWellKnownTypesConfig.cmake.in

@@ -0,0 +1,9 @@
+include(CMakeFindDependencyMacro)
+
+find_dependency(QtProtobufProject COMPONENTS QtProtobuf REQUIRED CONFIG)
+
+if(NOT TARGET @TARGET@ AND NOT @TARGET@_BINARY_DIR)
+    include("${CMAKE_CURRENT_LIST_DIR}/@TARGET_EXPORT@.cmake")
+endif()
+
+@PACKAGE_INIT@

+ 75 - 0
src/wellknowntypes/qtprotobufwellknowntypes_global.qbp.h

@@ -0,0 +1,75 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 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.
+ */
+
+#pragma once
+
+#include "google/protobuf/any.qpb.h"
+#include "google/protobuf/api.qpb.h"
+#include "google/protobuf/duration.qpb.h"
+#include "google/protobuf/empty.qpb.h"
+#include "google/protobuf/field_mask.qpb.h"
+#include "google/protobuf/source_context.qpb.h"
+#include "google/protobuf/struct.qpb.h"
+#include "google/protobuf/timestamp.qpb.h"
+#include "google/protobuf/type.qpb.h"
+#include "google/protobuf/wrappers.qpb.h"
+
+namespace google {
+
+namespace protobuf {
+
+inline void qRegisterProtobufTypes() {
+    NullValueGadget::registerTypes();
+    SyntaxGadget::registerTypes();
+    qRegisterProtobufType<Any>();
+    qRegisterProtobufType<Api>();
+    qRegisterProtobufType<Method>();
+    qRegisterProtobufType<Mixin>();
+    qRegisterProtobufType<Duration>();
+    qRegisterProtobufType<Empty>();
+    qRegisterProtobufType<FieldMask>();
+    qRegisterProtobufType<SourceContext>();
+    qRegisterProtobufType<Struct>();
+    qRegisterProtobufType<Value>();
+    qRegisterProtobufType<ListValue>();
+    qRegisterProtobufType<Timestamp>();
+    qRegisterProtobufType<Type>();
+    qRegisterProtobufType<Field>();
+    qRegisterProtobufType<Enum>();
+    qRegisterProtobufType<EnumValue>();
+    qRegisterProtobufType<Option>();
+    qRegisterProtobufType<DoubleValue>();
+    qRegisterProtobufType<FloatValue>();
+    qRegisterProtobufType<Int64Value>();
+    qRegisterProtobufType<UInt64Value>();
+    qRegisterProtobufType<Int32Value>();
+    qRegisterProtobufType<UInt32Value>();
+    qRegisterProtobufType<StringValue>();
+    qRegisterProtobufType<BytesValue>();
+}
+
+}
+
+}

+ 3 - 0
tests/CMakeLists.txt

@@ -2,3 +2,6 @@ add_subdirectory("test_protobuf")
 add_subdirectory("test_grpc")
 add_subdirectory("test_qml")
 add_subdirectory("test_protobuf_multifile")
+if(NOT WIN32)#TODO: There are linking issues with windows build of well-known types...
+	add_subdirectory("test_wellknowntypes")
+endif()

+ 17 - 0
tests/test_wellknowntypes/CMakeLists.txt

@@ -0,0 +1,17 @@
+set(TARGET wellknowntypes_test)
+
+include(${QTPROTOBUF_CMAKE_DIR}/QtProtobufCommon.cmake)
+find_package(QtProtobufProject CONFIG COMPONENTS QtProtobuf QtGrpc QtProtobufWellKnownTypes REQUIRED)
+
+file(GLOB SOURCES
+    simpletest.cpp)
+
+add_test_target(TARGET ${TARGET}
+    SOURCES ${SOURCES}
+    PROTO_INCLUDES $<TARGET_PROPERTY:${QTPROTOBUF_COMMON_NAMESPACE}::QtProtobufWellKnownTypes,PROTO_INCLUDES>
+    QML TRUE)
+add_target_windeployqt(TARGET ${TARGET}
+    QML_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+
+target_link_libraries(${TARGET} ${QTPROTOBUF_COMMON_NAMESPACE}::QtProtobufWellKnownTypes)
+add_test(NAME ${TARGET} COMMAND ${TARGET})

+ 8 - 0
tests/test_wellknowntypes/proto/wellknowntypes.proto

@@ -0,0 +1,8 @@
+syntax = "proto3";
+
+package qtprotobufnamespace.wellknowntypes.tests;
+import "google/protobuf/empty.proto";
+
+message EmptyMessage {
+    google.protobuf.Empty testField = 1;
+}

+ 259 - 0
tests/test_wellknowntypes/simpletest.cpp

@@ -0,0 +1,259 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 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 <QVariantList>
+#include <QMetaProperty>
+#include <QSignalSpy>
+
+#include <gtest/gtest.h>
+#include <qtprotobufwellknowntypes_global.qbp.h>
+
+using namespace google::protobuf;
+
+namespace QtProtobuf {
+namespace tests {
+
+class WellknowntypesTest : public ::testing::Test
+{
+public:
+    // see simpletest.proto for property names and their field indices
+    WellknowntypesTest() {}
+
+    template<typename MessageType, typename PropertyType>
+    static void assertMessagePropertyRegistered(int fieldIndex, const char *propertyTypeName, const char *propertyName, bool skipMetatypeCheck = false)
+    {
+        // TODO: there should be(?) a mapping avaialble: PropertyType -> propertyTypeName
+
+        const int propertyNumber = MessageType::propertyOrdering.at(fieldIndex);
+        ASSERT_STREQ(MessageType::staticMetaObject.property(propertyNumber).typeName(), propertyTypeName);
+        if(!skipMetatypeCheck) {
+            ASSERT_EQ(MessageType::staticMetaObject.property(propertyNumber).userType(), qMetaTypeId<PropertyType>());
+        }
+        ASSERT_STREQ(MessageType::staticMetaObject.property(propertyNumber).name(), propertyName);
+    }
+
+    static void SetUpTestCase() {
+        QtProtobuf::qRegisterProtobufTypes();
+        google::protobuf::qRegisterProtobufTypes();
+    }
+};
+
+TEST_F(WellknowntypesTest, AnyTest)
+{
+    ASSERT_GT(qMetaTypeId<Any>(), 0);
+    assertMessagePropertyRegistered<Any, QString>(1, "QString", "type_url");
+    assertMessagePropertyRegistered<Any, QByteArray>(2, "QByteArray", "value");
+}
+
+TEST_F(WellknowntypesTest, ApiTest)
+{
+    ASSERT_GT(qMetaTypeId<Api>(), 0);
+
+    assertMessagePropertyRegistered<Api, QString>(1, "QString", "name");
+    assertMessagePropertyRegistered<Api, MethodRepeated>(2, "MethodRepeated", "methods");
+    assertMessagePropertyRegistered<Api, OptionRepeated>(3, "OptionRepeated", "options");
+    assertMessagePropertyRegistered<Api, QString>(4, "QString", "version");
+    assertMessagePropertyRegistered<Api, SourceContext>(5, "SourceContext*", "source_context", true);
+    assertMessagePropertyRegistered<Api, MixinRepeated>(6, "MixinRepeated", "mixins");
+    assertMessagePropertyRegistered<Api, SyntaxGadget::Syntax>(7, "google::protobuf::SyntaxGadget::Syntax", "syntax");
+}
+
+TEST_F(WellknowntypesTest, MethodTest)
+{
+    ASSERT_GT(qMetaTypeId<Method>(), 0);
+    assertMessagePropertyRegistered<Method, QString>(1, "QString", "name");
+    assertMessagePropertyRegistered<Method, QString>(2, "QString", "request_type_url");
+    assertMessagePropertyRegistered<Method, bool>(3, "bool", "request_streaming");
+    assertMessagePropertyRegistered<Method, QString>(4, "QString", "response_type_url");
+    assertMessagePropertyRegistered<Method, bool>(5, "bool", "response_streaming");
+    assertMessagePropertyRegistered<Method, OptionRepeated>(6, "OptionRepeated", "options");
+    assertMessagePropertyRegistered<Method, SyntaxGadget::Syntax>(7, "google::protobuf::SyntaxGadget::Syntax", "syntax");
+}
+
+TEST_F(WellknowntypesTest, MixinTest)
+{
+    ASSERT_GT(qMetaTypeId<Mixin>(), 0);
+    assertMessagePropertyRegistered<Mixin, QString>(1, "QString", "name");
+    assertMessagePropertyRegistered<Mixin, QString>(2, "QString", "root");
+}
+
+TEST_F(WellknowntypesTest, DurationTest)
+{
+    ASSERT_GT(qMetaTypeId<Duration>(), 0);
+    assertMessagePropertyRegistered<Duration, QtProtobuf::int64>(1, "QtProtobuf::int64", "seconds");
+    assertMessagePropertyRegistered<Duration, QtProtobuf::int32>(2, "QtProtobuf::int32", "nanos");
+}
+
+TEST_F(WellknowntypesTest, EmptyTest)
+{
+    ASSERT_GT(qMetaTypeId<Empty>(), 0);
+}
+
+TEST_F(WellknowntypesTest, FieldMaskTest)
+{
+    ASSERT_GT(qMetaTypeId<FieldMask>(), 0);
+    assertMessagePropertyRegistered<FieldMask, QStringList>(1, "QStringList", "paths");
+}
+
+TEST_F(WellknowntypesTest, SourceContextTest)
+{
+    ASSERT_GT(qMetaTypeId<SourceContext>(), 0);
+    assertMessagePropertyRegistered<SourceContext, QString>(1, "QString", "file_name");
+}
+
+TEST_F(WellknowntypesTest, StructTest)
+{
+    ASSERT_GT(qMetaTypeId<Struct>(), 0);
+    assertMessagePropertyRegistered<Struct, Struct::FieldsEntry>(1, "Struct::FieldsEntry", "fields");
+}
+
+TEST_F(WellknowntypesTest, ValueTest)
+{
+    ASSERT_GT(qMetaTypeId<Value>(), 0);
+    assertMessagePropertyRegistered<Value, NullValueGadget::NullValue>(1, "google::protobuf::NullValueGadget::NullValue", "null_value");
+    assertMessagePropertyRegistered<Value, double>(2, "double", "number_value");
+    assertMessagePropertyRegistered<Value, QString>(3, "QString", "string_value");
+    assertMessagePropertyRegistered<Value, bool>(4, "bool", "bool_value");
+    assertMessagePropertyRegistered<Value, Struct *>(5, "Struct*", "struct_value");
+    assertMessagePropertyRegistered<Value, ListValue *>(6, "ListValue*", "list_value");
+}
+
+TEST_F(WellknowntypesTest, ListValueTest)
+{
+    ASSERT_GT(qMetaTypeId<ListValue>(), 0);
+    assertMessagePropertyRegistered<ListValue, ValueRepeated>(1, "ValueRepeated", "values");
+}
+
+TEST_F(WellknowntypesTest, TimestampTest)
+{
+    ASSERT_GT(qMetaTypeId<Timestamp>(), 0);
+    assertMessagePropertyRegistered<Timestamp, QtProtobuf::int64>(1, "QtProtobuf::int64", "seconds");
+    assertMessagePropertyRegistered<Timestamp, QtProtobuf::int32>(2, "QtProtobuf::int32", "nanos");
+}
+
+TEST_F(WellknowntypesTest, TypeTest)
+{
+    ASSERT_GT(qMetaTypeId<Type>(), 0);
+    Q_PROPERTY(QStringList oneofs READ oneofs WRITE setOneofs NOTIFY oneofsChanged)
+
+    assertMessagePropertyRegistered<Type, QString>(1, "QString", "name");
+    assertMessagePropertyRegistered<Type, FieldRepeated>(2, "FieldRepeated", "fields");
+    assertMessagePropertyRegistered<Type, QStringList>(3, "QStringList", "oneofs");
+    assertMessagePropertyRegistered<Type, OptionRepeated>(4, "OptionRepeated", "options");
+    assertMessagePropertyRegistered<Type, SourceContext *>(5, "SourceContext*", "source_context", true);
+    assertMessagePropertyRegistered<Type, SyntaxGadget::Syntax>(6, "google::protobuf::SyntaxGadget::Syntax", "syntax");
+}
+
+TEST_F(WellknowntypesTest, FieldTest)
+{
+    ASSERT_GT(qMetaTypeId<Field>(), 0);
+
+    assertMessagePropertyRegistered<Field, Field::Kind>(1, "Kind", "kind");
+    assertMessagePropertyRegistered<Field, Field::Cardinality>(2, "Cardinality", "cardinality");
+    assertMessagePropertyRegistered<Field, QtProtobuf::int32>(3, "QtProtobuf::int32", "number");
+    assertMessagePropertyRegistered<Field, QString>(4, "QString", "name");
+    assertMessagePropertyRegistered<Field, QString>(6, "QString", "type_url");
+    assertMessagePropertyRegistered<Field, QtProtobuf::int32>(7, "QtProtobuf::int32", "oneof_index");
+    assertMessagePropertyRegistered<Field, bool>(8, "bool", "packed");
+    assertMessagePropertyRegistered<Field, OptionRepeated>(9, "OptionRepeated", "options");
+    assertMessagePropertyRegistered<Field, QString>(10, "QString", "json_name");
+    assertMessagePropertyRegistered<Field, QString>(11, "QString", "default_value");
+}
+
+TEST_F(WellknowntypesTest, EnumTest)
+{
+    ASSERT_GT(qMetaTypeId<Enum>(), 0);
+    assertMessagePropertyRegistered<Enum, QString>(1, "QString", "name");
+    assertMessagePropertyRegistered<Enum, EnumValueRepeated>(2, "EnumValueRepeated", "enumvalue");
+    assertMessagePropertyRegistered<Enum, OptionRepeated>(3, "OptionRepeated", "options");
+//    assertMessagePropertyRegistered<Enum, SourceContext *>(4, "SourceContext*", "source_context");
+    assertMessagePropertyRegistered<Enum, SyntaxGadget::Syntax>(5, "google::protobuf::SyntaxGadget::Syntax", "syntax");
+}
+
+TEST_F(WellknowntypesTest, EnumValueTest)
+{
+    ASSERT_GT(qMetaTypeId<EnumValue>(), 0);
+    assertMessagePropertyRegistered<EnumValue, QString>(1, "QString", "name");
+    assertMessagePropertyRegistered<EnumValue, QtProtobuf::int32>(2, "QtProtobuf::int32", "number");
+    assertMessagePropertyRegistered<EnumValue, OptionRepeated>(3, "OptionRepeated", "options");
+}
+
+TEST_F(WellknowntypesTest, OptionTest)
+{
+    ASSERT_GT(qMetaTypeId<Option>(), 0);
+    assertMessagePropertyRegistered<Option, QString>(1, "QString", "name");
+    assertMessagePropertyRegistered<Option, Any *>(2, "Any*", "value", true);
+}
+
+TEST_F(WellknowntypesTest, DoubleValueTest)
+{
+    ASSERT_GT(qMetaTypeId<DoubleValue>(), 0);
+    assertMessagePropertyRegistered<DoubleValue, double>(1, "double", "value");
+}
+
+TEST_F(WellknowntypesTest, FloatValueTest)
+{
+    ASSERT_GT(qMetaTypeId<FloatValue>(), 0);
+    assertMessagePropertyRegistered<FloatValue, float>(1, "float", "value");
+}
+
+TEST_F(WellknowntypesTest, Int64ValueTest)
+{
+    ASSERT_GT(qMetaTypeId<Int64Value>(), 0);
+    assertMessagePropertyRegistered<Int64Value, QtProtobuf::int64>(1, "QtProtobuf::int64", "value");
+}
+
+TEST_F(WellknowntypesTest, UInt64ValueTest)
+{
+    ASSERT_GT(qMetaTypeId<UInt64Value>(), 0);
+    assertMessagePropertyRegistered<UInt64Value, QtProtobuf::uint64>(1, "QtProtobuf::uint64", "value");
+}
+
+TEST_F(WellknowntypesTest, Int32ValueTest)
+{
+    ASSERT_GT(qMetaTypeId<Int32Value>(), 0);
+    assertMessagePropertyRegistered<Int32Value, QtProtobuf::int32>(1, "QtProtobuf::int32", "value");
+}
+
+TEST_F(WellknowntypesTest, UInt32ValueTest)
+{
+    ASSERT_GT(qMetaTypeId<UInt32Value>(), 0);
+    assertMessagePropertyRegistered<UInt32Value, QtProtobuf::uint32>(1, "QtProtobuf::uint32", "value");
+}
+
+TEST_F(WellknowntypesTest, StringValueTest)
+{
+    ASSERT_GT(qMetaTypeId<StringValue>(), 0);
+    assertMessagePropertyRegistered<StringValue, QString>(1, "QString", "value");
+}
+
+TEST_F(WellknowntypesTest, BytesValueTest)
+{
+    ASSERT_GT(qMetaTypeId<BytesValue>(), 0);
+    assertMessagePropertyRegistered<BytesValue, QByteArray>(1, "QByteArray", "value");
+}
+
+}
+}