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;
