This is an automated email from the ASF dual-hosted git repository.

pengzheng pushed a commit to branch support/2.4
in repository https://gitbox.apache.org/repos/asf/celix.git

commit afe9db02ae0332958200ad7a5b9f8f166506d9d2
Author: xuzhenbao <[email protected]>
AuthorDate: Thu Nov 23 11:39:37 2023 +0800

    Add unit test for dfi,and fix some bugs
    
    (cherry picked from commit 476210103c492033678b73e8f55a4a764e8d156c)
---
 libs/dfi/gtest/descriptors/example4.descriptor     |   2 +
 libs/dfi/gtest/descriptors/example6.descriptor     |   9 +
 .../invalidHeader.descriptor}                      |   5 +-
 libs/dfi/gtest/src/dyn_interface_tests.cpp         |   6 +
 libs/dfi/gtest/src/dyn_type_tests.cpp              |  56 ++++
 libs/dfi/gtest/src/json_rpc_tests.cpp              | 370 ++++++++++++++++++++-
 libs/dfi/gtest/src/json_serializer_tests.cpp       | 185 +++++++++++
 libs/dfi/src/dyn_avpr_type.c                       |   4 +-
 libs/dfi/src/dyn_interface.c                       |   8 +-
 libs/dfi/src/dyn_message.c                         |   4 +-
 libs/dfi/src/dyn_type.c                            |  18 +-
 libs/dfi/src/json_rpc.c                            |  11 +-
 libs/dfi/src/json_serializer.c                     |  13 +-
 13 files changed, 669 insertions(+), 22 deletions(-)

diff --git a/libs/dfi/gtest/descriptors/example4.descriptor 
b/libs/dfi/gtest/descriptors/example4.descriptor
index 4c959c4d..77a7a7f5 100644
--- a/libs/dfi/gtest/descriptors/example4.descriptor
+++ b/libs/dfi/gtest/descriptors/example4.descriptor
@@ -6,3 +6,5 @@ version=1.0.0
 :types
 :methods
 getName(V)t=getName(#am=handle;P#am=out;*t)N
+setName=setName(#am=handle;Pt)N
+setConstName=setConstName(#am=handle;P#const=true;t)N
diff --git a/libs/dfi/gtest/descriptors/example6.descriptor 
b/libs/dfi/gtest/descriptors/example6.descriptor
new file mode 100644
index 00000000..4ce4e935
--- /dev/null
+++ b/libs/dfi/gtest/descriptors/example6.descriptor
@@ -0,0 +1,9 @@
+:header
+type=interface
+name=example6
+version=1.0.0
+:annotations
+:types
+CptData={Dt[D#v1=1;#v2=2;E d t s e}
+:methods
+compatibility=cpt(#am=handle;PLCptData;#am=out;*LCptData;)N
diff --git a/libs/dfi/gtest/descriptors/example4.descriptor 
b/libs/dfi/gtest/descriptors/invalids/invalidHeader.descriptor
similarity index 50%
copy from libs/dfi/gtest/descriptors/example4.descriptor
copy to libs/dfi/gtest/descriptors/invalids/invalidHeader.descriptor
index 4c959c4d..5afd8dd3 100644
--- a/libs/dfi/gtest/descriptors/example4.descriptor
+++ b/libs/dfi/gtest/descriptors/invalids/invalidHeader.descriptor
@@ -1,8 +1,9 @@
 :header
 type=interface
-name=example4
+name=
 version=1.0.0
 :annotations
 :types
+item={DD a b}
 :methods
-getName(V)t=getName(#am=handle;P#am=out;*t)N
+example1=items(#am=handle;P#am=out;**[Litem;)N
\ No newline at end of file
diff --git a/libs/dfi/gtest/src/dyn_interface_tests.cpp 
b/libs/dfi/gtest/src/dyn_interface_tests.cpp
index f254ef4f..2ddaa147 100644
--- a/libs/dfi/gtest/src/dyn_interface_tests.cpp
+++ b/libs/dfi/gtest/src/dyn_interface_tests.cpp
@@ -128,6 +128,12 @@ extern "C" {
         ASSERT_EQ(1, status); //Test fails because of a space at the end of 
the name
         fclose(desc); desc=NULL;
 
+        /* Invalid header */
+        desc = fopen("descriptors/invalids/invalidHeader.descriptor", "r");
+        assert(desc != NULL);
+        status = dynInterface_parse(desc, &dynIntf);
+        ASSERT_EQ(1, status); //Test fails because of missing name value
+        fclose(desc); desc=NULL;
 
         /* Header without Version */
         desc = fopen("descriptors/invalids/noVersion.descriptor", "r");
diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp 
b/libs/dfi/gtest/src/dyn_type_tests.cpp
index 2427f1ae..dda8efd1 100644
--- a/libs/dfi/gtest/src/dyn_type_tests.cpp
+++ b/libs/dfi/gtest/src/dyn_type_tests.cpp
@@ -24,6 +24,7 @@ extern "C" {
     
     #include "dyn_common.h"
     #include "dyn_type.h"
+    #include "celix_err.h"
 
        static void stdLog(void*, int level, const char *file, int line, const 
char *msg, ...) {
            va_list ap;
@@ -323,4 +324,59 @@ TEST_F(DynTypeTests, NrOfEntriesTest) {
     ASSERT_EQ(0, rc);
     ASSERT_EQ(4, dynType_complex_nrOfEntries(type));
     dynType_destroy(type);
+}
+
+TEST_F(DynTypeTests, ComplexHasDuplicateName) {
+    dyn_type *type = NULL;
+    auto rc = dynType_parseWithStr(R"({II a a})", nullptr, nullptr, &type);
+    ASSERT_EQ(3, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+}
+
+TEST_F(DynTypeTests, ComplexHasEmptyName) {
+    dyn_type *type = NULL;
+    auto rc = dynType_parseWithStr(R"({II a })", nullptr, nullptr, &type);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+}
+
+TEST_F(DynTypeTests, SchemaEndsWithoutNullTerminator) {
+    dyn_type *type = NULL;
+    //ends with '-'
+    auto rc = dynType_parseWithStr(R"({II a b}-)", nullptr, nullptr, &type);
+    ASSERT_EQ(3, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+}
+
+TEST_F(DynTypeTests, MetaInfoMissingValue) {
+    dyn_type *type = NULL;
+    auto rc = dynType_parseWithStr(R"(#testMetaInfo=)", nullptr, nullptr, 
&type);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+}
+
+TEST_F(DynTypeTests, ParseNestedTypeFailed) {
+    dyn_type *type = NULL;
+    //missing '='
+    auto rc = dynType_parseWithStr(R"(Ttype)", nullptr, nullptr, &type);
+    ASSERT_EQ(3, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+
+    //missing value
+    rc = dynType_parseWithStr(R"(Ttype=)", nullptr, nullptr, &type);
+    ASSERT_EQ(3, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+
+    //missing ';'
+    rc = dynType_parseWithStr(R"(Ttype={DD a b})", nullptr, nullptr, &type);
+    ASSERT_EQ(3, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+}
+
+TEST_F(DynTypeTests, ParseReferenceFailed) {
+    dyn_type *type = NULL;
+    //missing ';'
+    auto rc = dynType_parseWithStr(R"(Ttype={DD a b};ltype)", nullptr, 
nullptr, &type);
+    ASSERT_EQ(3, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
 }
\ No newline at end of file
diff --git a/libs/dfi/gtest/src/json_rpc_tests.cpp 
b/libs/dfi/gtest/src/json_rpc_tests.cpp
index 2a67e494..32e45663 100644
--- a/libs/dfi/gtest/src/json_rpc_tests.cpp
+++ b/libs/dfi/gtest/src/json_rpc_tests.cpp
@@ -68,6 +68,73 @@ extern "C" {
         dynFunction_destroy(dynFunc);
     }
 
+    void prepareCharTest(void) {
+        dyn_function_type *dynFunc = nullptr;
+        int rc = dynFunction_parseWithStr("setName(#am=handle;Pt)N", nullptr, 
&dynFunc);
+        ASSERT_EQ(0, rc);
+
+        char *result = nullptr;
+
+        void *handle = nullptr;
+        char *arg1 = strdup("hello char");
+        void *args[2];
+        args[0] = &handle;
+        args[1] = &arg1;
+
+        rc = jsonRpc_prepareInvokeRequest(dynFunc, "setName", args, &result);
+        ASSERT_EQ(0, rc);
+
+        ASSERT_TRUE(strstr(result, "\"setName\"") != nullptr);
+        ASSERT_TRUE(strstr(result, "hello char") != nullptr);
+
+        free(result);
+        dynFunction_destroy(dynFunc);
+    }
+
+    void prepareConstCharTest(void) {
+        dyn_function_type *dynFunc = nullptr;
+        int rc = 
dynFunction_parseWithStr("setConstName(#am=handle;P#const=true;t)N", nullptr, 
&dynFunc);
+        ASSERT_EQ(0, rc);
+
+        char *result = nullptr;
+
+        void *handle = nullptr;
+        const char *arg1 = "hello const char";
+        void *args[2];
+        args[0] = &handle;
+        args[1] = &arg1;
+
+        rc = jsonRpc_prepareInvokeRequest(dynFunc, "setConstName", args, 
&result);
+        ASSERT_EQ(0, rc);
+
+        ASSERT_TRUE(strstr(result, "\"setConstName\"") != nullptr);
+        ASSERT_TRUE(strstr(result, "hello const char") != nullptr);
+
+        free(result);
+        dynFunction_destroy(dynFunc);
+    }
+
+    void prepareTestFailed(void) {
+        dyn_function_type *dynFunc = nullptr;
+        int rc = dynFunction_parseWithStr("action(#am=handle;PP)N", nullptr, 
&dynFunc);
+        ASSERT_EQ(0, rc);
+
+        char *result = nullptr;
+
+        void *handle = nullptr;
+        void *arg1 = nullptr;
+        void *args[2];
+        args[0] = &handle;
+        args[1] = &arg1;
+
+        rc = jsonRpc_prepareInvokeRequest(dynFunc, "action", args, &result);
+        ASSERT_EQ(1, rc);
+        celix_err_printErrors(stderr, nullptr, nullptr);
+
+        free(result);
+        dynFunction_destroy(dynFunc);
+    }
+
     void handleTestPre(void) {
         dyn_function_type *dynFunc = nullptr;
         int rc = dynFunction_parseWithStr("add(#am=handle;PDD#am=pre;*D)N", 
nullptr, &dynFunc);
@@ -87,6 +154,26 @@ extern "C" {
         dynFunction_destroy(dynFunc);
     }
 
+
+    void handleTestInvalidReply(void) {
+        dyn_function_type *dynFunc = nullptr;
+        int rc = dynFunction_parseWithStr("add(#am=handle;PDD#am=pre;*D)N", 
nullptr, &dynFunc);
+        ASSERT_EQ(0, rc);
+
+        double result = -1.0;
+        double *out = &result;
+        void *args[4];
+        args[3] = &out;
+        int rsErrno = 0;
+        rc = jsonRpc_handleReply(dynFunc, "invalid", args, &rsErrno);
+        ASSERT_EQ(1, rc);
+        celix_err_printErrors(stderr, nullptr, nullptr);
+
+        dynFunction_destroy(dynFunc);
+    }
+
+
+
     int add(void*, double a, double b, double *result) {
         *result = a + b;
         return 0;
@@ -175,6 +262,8 @@ extern "C" {
     struct tst_serv_example4 {
         void *handle;
         int (*getName_example4)(void *, char** name);
+        int (*setName_example4)(void *, char* name);
+        int (*setConstName_example4)(void *, const char* name);
     };
 
     void callTestPreAllocated(void) {
@@ -251,6 +340,10 @@ extern "C" {
         rc = jsonRpc_call(intf, &serv, R"({"a": [1.0,2.0]})", &result);
         ASSERT_EQ(1, rc);
 
+        //request argument type mismatch
+        rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": 
[1.0]})", &result);
+        ASSERT_EQ(1, rc);
+
         dynInterface_destroy(intf);
     }
 
@@ -376,6 +469,23 @@ extern "C" {
         dynInterface_destroy(intf);
     }
 
+    void callTestUnknownRequest(void) {
+        dyn_interface_type *intf = nullptr;
+        FILE *desc = fopen("descriptors/example1.descriptor", "r");
+        ASSERT_TRUE(desc != nullptr);
+        int rc = dynInterface_parse(desc, &intf);
+        ASSERT_EQ(0, rc);
+        fclose(desc);
+
+        char *result = nullptr;
+        tst_serv serv {nullptr, addFailed, nullptr, nullptr, nullptr};
+
+        rc = jsonRpc_call(intf, &serv, R"({"m":"unknown", "a": [1.0,2.0]})", 
&result);
+        ASSERT_EQ(1, rc);
+
+        dynInterface_destroy(intf);
+    }
+
     static void handleTestOutputSequence() {
         dyn_interface_type *intf = nullptr;
         FILE *desc = fopen("descriptors/example2.descriptor", "r");
@@ -559,7 +669,7 @@ extern "C" {
         fclose(desc);
 
         char *result = nullptr;
-        tst_serv_example4 serv {nullptr, getName_example4};
+        tst_serv_example4 serv {nullptr, getName_example4, nullptr, nullptr};
 
         rc = jsonRpc_call(intf, &serv, R"({"m": "getName(V)t", "a": []})", 
&result);
         ASSERT_EQ(0, rc);
@@ -611,6 +721,222 @@ extern "C" {
         free(result);
         dynInterface_destroy(intf);
     }
+
+    void handleTestPreChar(void) {
+        dyn_function_type *dynFunc = nullptr;
+        int rc = dynFunction_parseWithStr("getName(#am=handle;P#am=pre;t)N", 
nullptr, &dynFunc);
+        ASSERT_EQ(0, rc);
+
+        const char *reply = "{\"r\":\"this is a test pre string\"}";
+        char result[32] = {0};
+        void *out = result;
+        void *args[2];
+        args[0] = nullptr;
+        args[1] = &out;
+        int rsErrno = 0;
+        rc = jsonRpc_handleReply(dynFunc, reply, args, &rsErrno);
+        ASSERT_EQ(0, rc);
+        ASSERT_EQ(0, rsErrno);
+        ASSERT_STREQ("this is a test pre string", result);
+
+        dynFunction_destroy(dynFunc);
+    }
+
+    void callTestChar(void) {
+        dyn_interface_type *intf = nullptr;
+        FILE *desc = fopen("descriptors/example4.descriptor", "r");
+        ASSERT_TRUE(desc != nullptr);
+        int rc = dynInterface_parse(desc, &intf);
+        ASSERT_EQ(0, rc);
+        fclose(desc);
+
+        char *result = nullptr;
+        tst_serv_example4 serv {nullptr, nullptr,[](void*, char *name)->int {
+            EXPECT_STREQ(name, "hello");
+            free(name);
+            return 0;
+        }, nullptr};
+
+        rc = jsonRpc_call(intf, &serv, R"({"m": "setName", "a":["hello"]})", 
&result);
+        ASSERT_EQ(0, rc);
+        free(result);
+
+        dynInterface_destroy(intf);
+    }
+
+    void callTestConstChar(void) {
+        dyn_interface_type *intf = nullptr;
+        FILE *desc = fopen("descriptors/example4.descriptor", "r");
+        ASSERT_TRUE(desc != nullptr);
+        int rc = dynInterface_parse(desc, &intf);
+        ASSERT_EQ(0, rc);
+        fclose(desc);
+
+        char *result = nullptr;
+        tst_serv_example4 serv {nullptr, nullptr, nullptr, [](void*, const 
char *name)->int {
+            EXPECT_STREQ(name, "hello const char");
+            return 0;
+        }};
+
+        rc = jsonRpc_call(intf, &serv, R"({"m": "setConstName", "a":["hello 
const char"]})", &result);
+        ASSERT_EQ(0, rc);
+        free(result);
+
+        dynInterface_destroy(intf);
+    }
+
+    enum example6_enum{
+            v1 = 1,
+            v2 = 2,
+    };
+
+    struct tst_CptData {
+        double d;
+        char *t;
+        struct tst_seq s;
+        enum example6_enum e;
+    };
+
+    struct tst_serv_example6 {
+        void *handle;
+        int (*cpt)(void *, struct tst_CptData *input, struct tst_CptData 
*output);
+    };
+
+    void testRequestBackwardCompatibility(void) {
+        dyn_interface_type *intf = nullptr;
+        FILE *desc = fopen("descriptors/example6.descriptor", "r");
+        ASSERT_TRUE(desc != nullptr);
+        int rc = dynInterface_parse(desc, &intf);
+        ASSERT_EQ(0, rc);
+        fclose(desc);
+
+
+        char *result = nullptr;
+
+        tst_serv_example6 serv {nullptr, nullptr};
+
+        serv.cpt = [](void *, struct tst_CptData *input, struct tst_CptData 
*)->int {
+            EXPECT_EQ(input->d , 0.0);
+            EXPECT_EQ(input->t , nullptr);
+            EXPECT_EQ(input->s.len , 0);
+            EXPECT_EQ(input->s.cap , 0);
+            EXPECT_EQ(input->s.buf , nullptr);
+            EXPECT_EQ(input->e , 0);
+            return 0;
+        };
+        rc = jsonRpc_call(intf, &serv, R"({"m": "compatibility", "a": [{}]})", 
&result);
+        ASSERT_EQ(0, rc);
+        free(result);
+
+        serv.cpt = [](void *, struct tst_CptData *input, struct tst_CptData 
*)->int {
+            EXPECT_EQ(input->d , 1.0);
+            EXPECT_EQ(input->t , nullptr);
+            EXPECT_EQ(input->s.len , 0);
+            EXPECT_EQ(input->s.cap , 0);
+            EXPECT_EQ(input->s.buf , nullptr);
+            EXPECT_EQ(input->e , 0);
+            return 0;
+        };
+        rc = jsonRpc_call(intf, &serv, R"({"m": "compatibility", "a": 
[{"d":1.0}]})", &result);
+        ASSERT_EQ(0, rc);
+        free(result);
+
+        serv.cpt = [](void *, struct tst_CptData *input, struct tst_CptData 
*)->int {
+            EXPECT_EQ(input->d , 1.0);
+            EXPECT_STREQ(input->t , "hello compatibility");
+            EXPECT_EQ(input->s.len , 0);
+            EXPECT_EQ(input->s.cap , 0);
+            EXPECT_EQ(input->s.buf , nullptr);
+            EXPECT_EQ(input->e , 0);
+            return 0;
+        };
+        rc = jsonRpc_call(intf, &serv, R"({"m": "compatibility", "a": 
[{"d":1.0, "t":"hello compatibility"}]})", &result);
+        ASSERT_EQ(0, rc);
+        free(result);
+
+        serv.cpt = [](void *, struct tst_CptData *input, struct tst_CptData 
*)->int {
+            EXPECT_EQ(input->d , 1.0);
+            EXPECT_STREQ(input->t , "hello compatibility");
+            EXPECT_EQ(input->s.len , 3);
+            EXPECT_EQ(input->s.cap , 3);
+            EXPECT_EQ(input->s.buf[0] , 1.0);
+            EXPECT_EQ(input->s.buf[1] , 2.0);
+            EXPECT_EQ(input->s.buf[2] , 3.0);
+            EXPECT_EQ(input->e , 0);
+            return 0;
+        };
+        rc = jsonRpc_call(intf, &serv, R"({"m": "compatibility", "a": 
[{"d":1.0, "t":"hello compatibility", "s":[1.0,2.0,3.0]}]})", &result);
+        ASSERT_EQ(0, rc);
+        free(result);
+
+
+        serv.cpt = [](void *, struct tst_CptData *input, struct tst_CptData 
*)->int {
+            EXPECT_EQ(input->d , 1.0);
+            EXPECT_STREQ(input->t , "hello compatibility");
+            EXPECT_EQ(input->s.len , 3);
+            EXPECT_EQ(input->s.cap , 3);
+            EXPECT_EQ(input->s.buf[0] , 1.0);
+            EXPECT_EQ(input->s.buf[1] , 2.0);
+            EXPECT_EQ(input->s.buf[2] , 3.0);
+            EXPECT_EQ(input->e , v2);
+            return 0;
+        };
+        rc = jsonRpc_call(intf, &serv, R"({"m": "compatibility", "a": 
[{"d":1.0, "t":"hello compatibility", "s":[1.0,2.0,3.0], "e":"v2"}]})", 
&result);
+        ASSERT_EQ(0, rc);
+        free(result);
+
+
+        dynInterface_destroy(intf);
+    }
+
+    void testResponseForwardCompatibility(void) {
+        dyn_interface_type *intf = nullptr;
+        FILE *desc = fopen("descriptors/example6.descriptor", "r");
+        ASSERT_TRUE(desc != nullptr);
+        int rc = dynInterface_parse(desc, &intf);
+        ASSERT_EQ(0, rc);
+        fclose(desc);
+
+        struct methods_head *head;
+        dynInterface_methods(intf, &head);
+        dyn_function_type *func = nullptr;
+        struct method_entry *entry = nullptr;
+        TAILQ_FOREACH(entry, head, entries) {
+            if (strcmp(entry->name, "compatibility") == 0) {
+                func = entry->dynFunc;
+                break;
+            }
+        }
+        ASSERT_TRUE(func != nullptr);
+
+        struct tst_CptData *cptData{nullptr};
+        void *out = &cptData;
+
+        void *args[3];
+        args[0] = nullptr;
+        args[1] = nullptr;
+        args[2] = &out;
+        int rsErrno = 0;
+
+        //provider has more reply
+        rc = jsonRpc_handleReply(func, R"({"r":{"d":1.0, "t":"hello 
compatibility", "s":[1.0,2.0,3.0], "e":"v1", "e2":"v2"}})", args, &rsErrno);
+        EXPECT_EQ(0, rc);
+        EXPECT_EQ(0, rsErrno);
+        EXPECT_NE(cptData , nullptr);
+        EXPECT_EQ(cptData->d , 1.0);
+        EXPECT_STREQ(cptData->t , "hello compatibility");
+        EXPECT_EQ(cptData->s.len , 3);
+        EXPECT_EQ(cptData->s.cap , 3);
+        EXPECT_EQ(cptData->s.buf[0] , 1.0);
+        EXPECT_EQ(cptData->s.buf[1] , 2.0);
+        EXPECT_EQ(cptData->s.buf[2] , 3.0);
+        EXPECT_EQ(cptData->e , v1);
+        free(cptData->t);
+        free(cptData->s.buf);
+        free(cptData);
+
+        dynInterface_destroy(intf);
+    }
 }
 
 class JsonRpcTests : public ::testing::Test {
@@ -627,10 +953,26 @@ TEST_F(JsonRpcTests, prepareTest) {
     prepareTest();
 }
 
+TEST_F(JsonRpcTests, prepareCharTest) {
+    prepareCharTest();
+}
+
+TEST_F(JsonRpcTests, prepareConstCharTest) {
+    prepareConstCharTest();
+}
+
+TEST_F(JsonRpcTests, prepareTestFailed) {
+    prepareTestFailed();
+}
+
 TEST_F(JsonRpcTests, handleTestPre) {
     handleTestPre();
 }
 
+TEST_F(JsonRpcTests, handleTestInvalidReply) {
+    handleTestInvalidReply();
+}
+
 TEST_F(JsonRpcTests, handleTestOut) {
     handleTestOut();
 }
@@ -651,6 +993,10 @@ TEST_F(JsonRpcTests, callTestInvalidRequest) {
     callTestInvalidRequest();
 }
 
+TEST_F(JsonRpcTests, callTestUnknownRequest) {
+    callTestUnknownRequest();
+}
+
 TEST_F(JsonRpcTests, handleOutSeq) {
     handleTestOutputSequence();
 }
@@ -663,6 +1009,10 @@ TEST_F(JsonRpcTests, handleOutChar) {
     handleTestOutChar();
 }
 
+TEST_F(JsonRpcTests, handlePreChar) {
+    handleTestPreChar();
+}
+
 TEST_F(JsonRpcTests, handleReplyError) {
     handleTestReplyError();
 }
@@ -681,4 +1031,20 @@ TEST_F(JsonRpcTests, handleTestMultiPreOut) {
 
 TEST_F(JsonRpcTests, handleTestMultiOut) {
     handleTestMultiOut();
-}
\ No newline at end of file
+}
+
+TEST_F(JsonRpcTests, callTestChar) {
+    callTestChar();
+}
+
+TEST_F(JsonRpcTests, callTestConstChar) {
+    callTestConstChar();
+}
+
+TEST_F(JsonRpcTests, testRequestBackwardCompatibility) {
+    testRequestBackwardCompatibility();
+}
+
+TEST_F(JsonRpcTests, testResponseForwardCompatibility) {
+    testResponseForwardCompatibility();
+}
diff --git a/libs/dfi/gtest/src/json_serializer_tests.cpp 
b/libs/dfi/gtest/src/json_serializer_tests.cpp
index 3b7b125d..ccfcedf9 100644
--- a/libs/dfi/gtest/src/json_serializer_tests.cpp
+++ b/libs/dfi/gtest/src/json_serializer_tests.cpp
@@ -32,6 +32,7 @@ extern "C" {
 #include "dyn_common.h"
 #include "dyn_type.h"
 #include "json_serializer.h"
+#include "celix_err.h"
 
 static void stdLog(void*, int level, const char *file, int line, const char 
*msg, ...) {
        va_list ap;
@@ -991,6 +992,89 @@ static void parseTests() {
     check_exampleA(inst);
     dynType_free(type, inst);
     dynType_destroy(type);
+
+    //parse ref by value
+    type = nullptr;
+    inst = nullptr;
+    rc = dynType_parseWithStr("Ttype={DD a b};ltype;", nullptr, nullptr, 
&type);
+    ASSERT_EQ(0, rc);
+    auto inputStr = R"({"a":1.0, "b":2.0})";
+    json_error_t error;
+    json_auto_t *input = json_loadb(inputStr, strlen(inputStr), 
JSON_DECODE_ANY, &error);
+    rc = jsonSerializer_deserializeJson(type, input, &inst);
+    ASSERT_EQ(0, rc);
+    struct {
+        double a;
+        double b;
+    } *data = static_cast<decltype(data)>(inst);
+    ASSERT_EQ(1.0, data->a);
+    ASSERT_EQ(2.0, data->b);
+    dynType_free(type, inst);
+    dynType_destroy(type);
+
+    //invalid input
+    type = nullptr;
+    inst = nullptr;
+    rc = dynType_parseWithStr("{DD a b}", nullptr, nullptr, &type);
+    ASSERT_EQ(0, rc);
+    rc = jsonSerializer_deserialize(type, "invalid", strlen("invalid"), &inst);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+    dynType_destroy(type);
+
+    //pointer type mismatch
+    rc = dynType_parseWithStr("{*t a}", nullptr, nullptr, &type);
+    ASSERT_EQ(0, rc);
+    inputStr = R"({"a":1.0})";
+    rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+    dynType_destroy(type);
+
+    //text type mismatch
+    rc = dynType_parseWithStr("{t a}", nullptr, nullptr, &type);
+    ASSERT_EQ(0, rc);
+    inputStr = R"({"a":1.0})";
+    rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+    dynType_destroy(type);
+
+    //enum type mismatch
+    rc = dynType_parseWithStr("{#v1=1;#v2=2;E a}", nullptr, nullptr, &type);
+    ASSERT_EQ(0, rc);
+    inputStr = R"({"a":1.0})";
+    rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+    dynType_destroy(type);
+
+    //enum value unknown
+    rc = dynType_parseWithStr("{#v1=1;#v2=2;E a}", nullptr, nullptr, &type);
+    ASSERT_EQ(0, rc);
+    inputStr = R"({"a":"v3"})";
+    rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+    dynType_destroy(type);
+
+    //sequence element type mismatch
+    rc = dynType_parseWithStr("[t", nullptr, nullptr, &type);
+    ASSERT_EQ(0, rc);
+    inputStr = R"([1.0, 2.0])";
+    rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+    dynType_destroy(type);
+
+    //unsupported untyped pointer
+    rc = dynType_parseWithStr("{P a}", nullptr, nullptr, &type);
+    ASSERT_EQ(0, rc);
+    inputStr = R"({"a":1.0})";
+    rc = jsonSerializer_deserialize(type, inputStr, strlen(inputStr), &inst);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+    dynType_destroy(type);
 }
 
 /*********** write example 1 ************************/
@@ -1396,6 +1480,88 @@ void writeAvprTest3(void) {
        free(result);
 }
 
+void writeEnum(void) {
+    dyn_type *type = nullptr;
+    char *result = nullptr;
+    int rc = dynType_parseWithStr(R"(#v1=1;#v2=2;E)", nullptr, nullptr, &type);
+    ASSERT_EQ(0, rc);
+    enum {
+        v1 = 1,
+        v2 = 2
+    }enumVal = v2;
+    rc = jsonSerializer_serialize(type, &enumVal, &result);
+    ASSERT_EQ(0, rc);
+    ASSERT_TRUE(strstr(result, R"(v2)") != nullptr);
+    free(result);
+    dynType_destroy(type);
+}
+
+void writeRefByVal(void) {
+    dyn_type *type = nullptr;
+    char *result = nullptr;
+    int rc = dynType_parseWithStr(R"(Ttype={DD a b};ltype;)", nullptr, 
nullptr, &type);
+    ASSERT_EQ(0, rc);
+    struct {
+        double a;
+        double b;
+    }input{1.0,2.0};
+    rc = jsonSerializer_serialize(type, &input, &result);
+    ASSERT_EQ(0, rc);
+    ASSERT_TRUE(strstr(result, R"("a":1.0)") != nullptr);
+    ASSERT_TRUE(strstr(result, R"("b":2.0)") != nullptr);
+    free(result);
+    dynType_destroy(type);
+}
+
+void writeSequenceFailed(void) {
+    dyn_type *type = nullptr;
+    char *result = nullptr;
+    int rc = dynType_parseWithStr(R"([D)", nullptr, nullptr, &type);
+    ASSERT_EQ(0, rc);
+    double input[] = {1.0,2.0};
+    struct {
+        uint32_t cap;
+        uint32_t len;
+        double *buf;
+    }seq{1,2,input};//cap < len
+    rc = jsonSerializer_serialize(type, &seq, &result);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+    dynType_destroy(type);
+}
+
+void writeComplexFailed(void) {
+    dyn_type *type = nullptr;
+    char *result = nullptr;
+    //has unnamed complex element
+    int rc = dynType_parseWithStr(R"({II a})", nullptr, nullptr, &type);
+    ASSERT_EQ(0, rc);
+    struct {
+        int32_t a;
+        int32_t b;
+    }input{1,2};
+    rc = jsonSerializer_serialize(type, &input, &result);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+    dynType_destroy(type);
+}
+
+void writeEnumFailed(void) {
+    dyn_type *type = nullptr;
+    char *result = nullptr;
+    int rc = dynType_parseWithStr(R"(#v1=1;#v2=2;E)", nullptr, nullptr, &type);
+    ASSERT_EQ(0, rc);
+    enum {
+        v1 = 1,
+        v2 = 2,
+        v3 = 3
+    }enumVal = v3;//schema only has v1 and v2
+    rc = jsonSerializer_serialize(type, &enumVal, &result);
+    ASSERT_EQ(1, rc);
+    celix_err_printErrors(stderr, nullptr, nullptr);
+    dynType_destroy(type);
+}
+
 } // extern "C"
 
 
@@ -1437,3 +1603,22 @@ TEST_F(JsonSerializerTests, WriteTest3) {
     writeAvprTest3();
 }
 
+TEST_F(JsonSerializerTests, WriteEnum) {
+    writeEnum();
+}
+
+TEST_F(JsonSerializerTests, WriteRefByVal) {
+    writeRefByVal();
+}
+
+TEST_F(JsonSerializerTests, WriteSequenceFailed) {
+    writeSequenceFailed();
+}
+
+TEST_F(JsonSerializerTests, WriteComplexFailed) {
+    writeComplexFailed();
+}
+
+TEST_F(JsonSerializerTests, WriteEnumFailed) {
+    writeEnumFailed();
+}
diff --git a/libs/dfi/src/dyn_avpr_type.c b/libs/dfi/src/dyn_avpr_type.c
index 9482c5ca..32c41fd8 100644
--- a/libs/dfi/src/dyn_avpr_type.c
+++ b/libs/dfi/src/dyn_avpr_type.c
@@ -691,7 +691,7 @@ static inline bool dynAvprType_metaEntrySetValue(struct 
meta_entry * meta_entry_
     if (values_array) {
         const char* enumValue = json_string_value(json_array_get(values_array, 
index));
         if (!enumValue) {
-            LOG_ERROR("MetaEntrySetValue: could not get the enum value from 
the \"EnumValues\" list at index %lu", index);
+            LOG_ERROR("MetaEntrySetValue: could not get the enum value from 
the \"EnumValues\" list at index %zu", index);
             return false;
         }
 
@@ -706,7 +706,7 @@ static inline bool dynAvprType_metaEntrySetValue(struct 
meta_entry * meta_entry_
         const size_t bufsize = sizeof(index) * CHAR_BIT + 1;
         meta_entry_ptr->value = calloc(bufsize, sizeof(char));
 
-        snprintf(meta_entry_ptr->value, bufsize, "%lu", (long unsigned int) 
index);
+        snprintf(meta_entry_ptr->value, bufsize, "%zu", index);
     }
 
     return true;
diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c
index 9b208280..9f702ce7 100644
--- a/libs/dfi/src/dyn_interface.c
+++ b/libs/dfi/src/dyn_interface.c
@@ -185,8 +185,8 @@ static int 
dynInterface_parseNameValueSection(dyn_interface_type *intf, FILE *st
     while (peek != ':' && peek != EOF) {
         ungetc(peek, stream);
 
-        char *name;
-        char *value;
+        char *name = NULL;
+        char *value = NULL;
         status = dynCommon_parseNameValue(stream, &name, &value);
 
         if (status == OK) {
@@ -230,7 +230,7 @@ static int dynInterface_parseTypes(dyn_interface_type 
*intf, FILE *stream) {
     while (peek != ':' && peek != EOF) {
         ungetc(peek, stream);
 
-        char *name;
+        char *name = NULL;
         status = dynCommon_parseName(stream, &name);
 
         if (status == OK) {
@@ -283,7 +283,7 @@ static int dynInterface_parseMethods(dyn_interface_type 
*intf, FILE *stream) {
     while (peek != ':' && peek != EOF) {
         ungetc(peek, stream);
 
-        char *id;
+        char *id = NULL;
         status = dynCommon_parseNameAlsoAccept(stream, ".();[{}/", &id);
 
         if (status == OK) {
diff --git a/libs/dfi/src/dyn_message.c b/libs/dfi/src/dyn_message.c
index 441a6061..0571be09 100644
--- a/libs/dfi/src/dyn_message.c
+++ b/libs/dfi/src/dyn_message.c
@@ -180,8 +180,8 @@ static int 
dynMessage_parseNameValueSection(dyn_message_type *msg, FILE *stream,
     while (peek != ':' && peek != EOF) {
         ungetc(peek, stream);
 
-        char *name;
-        char *value;
+        char *name = NULL;
+        char *value = NULL;
         status = dynCommon_parseNameValue(stream, &name, &value);
 
         if (status == OK) {
diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c
index cf84dff2..45f9d3e1 100644
--- a/libs/dfi/src/dyn_type.c
+++ b/libs/dfi/src/dyn_type.c
@@ -95,6 +95,8 @@ int dynType_parseWithStr(const char *descriptor, const char 
*name, struct types_
             int c = fgetc(stream);
             if (c != '\0' && c != EOF) {
                 status = PARSE_ERROR;
+                dynType_destroy(*type);
+                *type = NULL;
                 LOG_ERROR("Expected EOF got %c", c);
             }
         } 
@@ -294,6 +296,14 @@ static int dynType_parseComplex(FILE *stream, dyn_type 
*type) {
     if (status == OK) {
         TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) {
             count +=1;
+            struct complex_type_entry *entry2 = NULL;
+            for(entry2 = entry->entries.tqe_next; entry2 != NULL; entry2 = 
entry2->entries.tqe_next) {
+                if (entry2->name != NULL && entry->name != NULL && 
strcmp(entry2->name, entry->name) == 0) {
+                    status = PARSE_ERROR;
+                    LOG_ERROR("Error duplicate name '%s'", entry->name);
+                    break;
+                }
+            }
         }
     }
 
@@ -591,12 +601,16 @@ int dynType_alloc(dyn_type *type, void **bufLoc) {
 
 int dynType_complex_indexForName(dyn_type *type, const char *name) {
     assert(type->type == DYN_TYPE_COMPLEX);
+    if (name == NULL) {
+        return -1;
+    }
     int i = 0;
     int index = -1;
     struct complex_type_entry *entry = NULL;
     TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) {
-        if (strcmp(name, entry->name) == 0) {
+        if (entry->name != NULL && strcmp(name, entry->name) == 0) {
             index = i;
+            break;
         }
         i +=1;
     }
@@ -662,7 +676,7 @@ int dynType_sequence_alloc(dyn_type *type, void *inst, 
uint32_t cap) {
     struct generic_sequence *seq = inst;
     if (seq != NULL) {
         size_t size = dynType_size(type->sequence.itemType);
-        seq->buf = malloc(cap * size);
+        seq->buf = calloc(cap, size);
         if (seq->buf != NULL) {
             seq->cap = cap;
             seq->len = 0;
diff --git a/libs/dfi/src/json_rpc.c b/libs/dfi/src/json_rpc.c
index ad864c98..c2f7f43d 100644
--- a/libs/dfi/src/json_rpc.c
+++ b/libs/dfi/src/json_rpc.c
@@ -230,7 +230,7 @@ int jsonRpc_call(dyn_interface_type *intf, void *service, 
const char *request, c
                } else {
             json_object_set_new_nocheck(payload, "e", 
json_integer(funcCallStatus));
                }
-               response = json_dumps(payload, JSON_DECODE_ANY);
+               response = json_dumps(payload, JSON_COMPACT | 
JSON_ENCODE_ANY);//Should use JSON_COMPACT, it can reduce the size of the JSON 
string.
                json_decref(payload);
        }
 
@@ -285,12 +285,15 @@ int jsonRpc_prepareInvokeRequest(dyn_function_type *func, 
const char *id, void *
                }
        }
 
-       char *invokeStr = json_dumps(invoke, JSON_DECODE_ANY);
+       char *invokeStr = json_dumps(invoke, JSON_COMPACT | 
JSON_ENCODE_ANY);//Should use JSON_COMPACT, it can reduce the size of the JSON 
string.
        json_decref(invoke);
 
        if (status == OK) {
                *out = invokeStr;
-       }
+       } else {
+        *out = NULL;
+        free(invokeStr);
+    }
 
        return status;
 }
@@ -315,7 +318,7 @@ int jsonRpc_handleReply(dyn_function_type *func, const char 
*reply, void *args[]
                        rsError = json_object_get(replyJson, "e");
                        if(rsError != NULL) {
                                //get the invocation error of remote service 
function
-                               *rsErrno = json_integer_value(rsError);
+                               *rsErrno = (int)json_integer_value(rsError);
                 replyHasError = true;
                        }
                }
diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c
index c4668866..e24cc58d 100644
--- a/libs/dfi/src/json_serializer.c
+++ b/libs/dfi/src/json_serializer.c
@@ -128,8 +128,7 @@ static int jsonSerializer_parseObjectMember(dyn_type *type, 
const char *name, js
 
     int index = dynType_complex_indexForName(type, name);
     if (index < 0) {
-        LOG_ERROR("Cannot find index for member '%s'", name);
-        status = ERROR;
+        return OK;//We should ignore unknown name in request or response. 
Satisfy forward compatibility for responses.
     }
 
     if (status == OK) {
@@ -311,7 +310,7 @@ int jsonSerializer_serialize(dyn_type *type, const void* 
input, char **output) {
     status = jsonSerializer_serializeJson(type, input, &root);
 
     if (status == OK) {
-        *output = json_dumps(root, JSON_COMPACT);
+        *output = json_dumps(root, JSON_COMPACT | JSON_ENCODE_ANY);
         json_decref(root);
     }
 
@@ -411,7 +410,7 @@ static int jsonSerializer_writeAny(dyn_type *type, void* 
input, json_t **out) {
             break;
         case 'E':
             e = input;
-            jsonSerializer_writeEnum(type, *e, &val);
+            status = jsonSerializer_writeEnum(type, *e, &val);
             break;
         case '*' :
             status = dynType_typedPointer_getTypedType(type, &subType);
@@ -474,6 +473,9 @@ static int jsonSerializer_writeSequence(dyn_type *type, 
void *input, json_t **ou
 
     if (status == OK && array != NULL) {
         *out = array;
+    } else {
+        *out = NULL;
+        json_decref(array);
     }
 
     return status;
@@ -521,6 +523,9 @@ static int jsonSerializer_writeComplex(dyn_type *type, void 
*input, json_t **out
 
     if (status == OK && val != NULL) {
         *out = val;
+    } else {
+        *out = NULL;
+        json_decref(val);
     }
 
     return status;


Reply via email to