Skip to content

Commit

Permalink
Optimize jsonRpc_call.
Browse files Browse the repository at this point in the history
1. Add support for nullptr result for `am=out` parameter.
2. Fix memory leaks when `am=out` result fails to serialize.
3. Extract dynInterface_findMethod.
  • Loading branch information
PengZheng committed Jan 27, 2024
1 parent c39b581 commit 94903a2
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 139 deletions.
18 changes: 18 additions & 0 deletions libs/dfi/gtest/src/dyn_interface_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,21 @@ TEST_F(DynInterfaceTests, testEmptyMethod) {
dynInterface_destroy(dynIntf);
}

TEST_F(DynInterfaceTests, testFindMethod) {
int status = 0;
dyn_interface_type *dynIntf = NULL;
FILE *desc = fopen("descriptors/example1.descriptor", "r");
assert(desc != NULL);
status = dynInterface_parse(desc, &dynIntf);
ASSERT_EQ(0, status);
fclose(desc);

const struct method_entry* mInfo = dynInterface_findMethod(dynIntf, "add(DD)D");
ASSERT_TRUE(mInfo != NULL);
ASSERT_STREQ("add(DD)D", mInfo->id);

mInfo = dynInterface_findMethod(dynIntf, "add(D)D");
ASSERT_TRUE(mInfo == NULL);

dynInterface_destroy(dynIntf);
}
225 changes: 132 additions & 93 deletions libs/dfi/gtest/src/json_rpc_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@

#include <float.h>
#include <assert.h>
#include "celix_err.h"

extern "C" {
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
Expand All @@ -37,7 +37,11 @@ extern "C" {
#include "dyn_function.h"
#include "json_serializer.h"
#include "json_rpc.h"
#include "celix_compiler.h"
#include "celix_errno.h"
#include "celix_err.h"

#include <jansson.h>


void prepareTest(void) {
Expand Down Expand Up @@ -372,16 +376,8 @@ extern "C" {
ASSERT_EQ(0, rc);
fclose(desc);

const struct methods_head* head = dynInterface_methods(intf);
dyn_function_type *func = nullptr;
struct method_entry *entry = nullptr;
TAILQ_FOREACH(entry, head, entries) {
if (strcmp(dynFunction_getName(entry->dynFunc), "stats") == 0) {
func = entry->dynFunc;
break;
}
}
ASSERT_TRUE(func != nullptr);
dyn_function_type *func = dynInterface_findMethod(intf, "stats([D)LStatsResult;")->dynFunc;
assert(func != nullptr);

const char *reply = R"({"r":{"input":[1.0,2.0],"max":2.0,"average":1.5,"min":1.0}})";

Expand Down Expand Up @@ -413,16 +409,8 @@ extern "C" {
ASSERT_EQ(0, rc);
fclose(desc);

const struct methods_head* head = dynInterface_methods(intf);
dyn_function_type *func = nullptr;
struct method_entry *entry = nullptr;
TAILQ_FOREACH(entry, head, entries) {
if (strcmp(dynFunction_getName(entry->dynFunc), "stats") == 0) {
func = entry->dynFunc;
break;
}
}
ASSERT_TRUE(func != nullptr);
dyn_function_type *func = dynInterface_findMethod(intf, "stats([D)LStatsResult;")->dynFunc;
assert(func != nullptr);

const char *reply = R"({})";

Expand Down Expand Up @@ -451,16 +439,9 @@ extern "C" {
ASSERT_EQ(0, rc);
fclose(desc);

const struct methods_head* head = dynInterface_methods(intf);
dyn_function_type *func = nullptr;
struct method_entry *entry = nullptr;
TAILQ_FOREACH(entry, head, entries) {
if (strcmp(dynFunction_getName(entry->dynFunc), "add") == 0) {
func = entry->dynFunc;
break;
}
}
ASSERT_TRUE(func != nullptr);

dyn_function_type *func = dynInterface_findMethod(intf, "add(DD)D")->dynFunc;
assert(func != nullptr);

const char *reply = R"({"e":33554433})";

Expand Down Expand Up @@ -509,19 +490,8 @@ extern "C" {
ASSERT_EQ(0, rc);
fclose(desc);

const struct methods_head* head = dynInterface_methods(intf);
dyn_function_type *func = nullptr;
struct method_entry *entry = nullptr;
TAILQ_FOREACH(entry, head, entries) {
if (strcmp(dynFunction_getName(entry->dynFunc), "items") == 0) {
func = entry->dynFunc;
break;
}
}
ASSERT_TRUE(func != nullptr);

//dyn_type *arg = dynFunction_argumentTypeForIndex(func, 1);
//dynType_print(arg, stdout);
dyn_function_type *func = dynInterface_findMethod(intf, "example1")->dynFunc;
assert(func != nullptr);

const char *reply = R"({"r":[{"a":1.0,"b":1.5},{"a":2.0,"b":2.5}]})";

Expand Down Expand Up @@ -561,16 +531,8 @@ extern "C" {
ASSERT_EQ(0, rc);
fclose(desc);

const struct methods_head* head = dynInterface_methods(intf);
dyn_function_type *func = nullptr;
struct method_entry *entry = nullptr;
TAILQ_FOREACH(entry, head, entries) {
if (strcmp(dynFunction_getName(entry->dynFunc), "action") == 0) {
func = entry->dynFunc;
break;
}
}
ASSERT_TRUE(func != nullptr);
dyn_function_type *func = dynInterface_findMethod(intf, "action(V)")->dynFunc;
assert(func != nullptr);

const char *reply = R"({})";

Expand Down Expand Up @@ -613,17 +575,8 @@ extern "C" {
ASSERT_EQ(0, rc);
fclose(desc);

const struct methods_head* head = dynInterface_methods(intf);
dyn_function_type *func = nullptr;
struct method_entry *entry = nullptr;
TAILQ_FOREACH(entry, head, entries) {
if (strcmp(dynFunction_getName(entry->dynFunc), "getName") == 0) {
func = entry->dynFunc;
break;
}
}

ASSERT_TRUE(func != nullptr);
dyn_function_type *func = dynInterface_findMethod(intf, "getName(V)t")->dynFunc;
assert(func != nullptr);

const char *reply = R"({"r": "this is a test string"})";
char *result = nullptr;
Expand All @@ -633,11 +586,9 @@ extern "C" {
args[0] = nullptr;
args[1] = &out;

if (func != nullptr) { // Check needed just to satisfy Coverity
int rsErrno = 0;
jsonRpc_handleReply(func, reply, args, &rsErrno);
ASSERT_EQ(0, rsErrno);
}
int rsErrno = 0;
jsonRpc_handleReply(func, reply, args, &rsErrno);
ASSERT_EQ(0, rsErrno);

ASSERT_STREQ("this is a test string", result);

Expand Down Expand Up @@ -746,25 +697,47 @@ TEST_F(JsonRpcTests, handleTestOut) {
handleTestOut();
}

TEST_F(JsonRpcTests, handleTestNullOut) {
TEST_F(JsonRpcTests, handleTestNullOutResult) {
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);

const struct methods_head* head = dynInterface_methods(intf);
dyn_function_type *func = nullptr;
struct method_entry *entry = nullptr;
TAILQ_FOREACH(entry, head, entries) {
if (strcmp(dynFunction_getName(entry->dynFunc), "stats") == 0) {
func = entry->dynFunc;
break;
}
}
dyn_function_type *func = dynInterface_findMethod(intf, "stats([D)LStatsResult;")->dynFunc;
ASSERT_TRUE(func != nullptr);

const char *reply = R"({"r":null})";

void *args[3];
args[0] = nullptr;
args[1] = nullptr;
args[2] = nullptr;

struct tst_StatsResult *result = nullptr;
void *out = &result;
args[2] = &out;

int rsErrno = 0;
rc = jsonRpc_handleReply(func, reply, args, &rsErrno);
ASSERT_EQ(0, rc);
ASSERT_EQ(0, rsErrno);
ASSERT_EQ(nullptr, result);
dynInterface_destroy(intf);
}

TEST_F(JsonRpcTests, handleTestNullOut) {
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);

dyn_function_type *func = dynInterface_findMethod(intf, "stats([D)LStatsResult;")->dynFunc;
assert(func != nullptr);

const char *reply = R"({"r":{"input":[1.0,2.0],"max":2.0,"average":1.5,"min":1.0}})";

void *args[3];
Expand Down Expand Up @@ -813,6 +786,32 @@ TEST_F(JsonRpcTests, callOut) {
callTestOutput();
}


TEST_F(JsonRpcTests, callOutNullResult) {
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, nullptr, nullptr, nullptr, [](void*, struct tst_seq, struct tst_StatsResult **out)->int {
assert(out != nullptr);
assert(*out == nullptr);
*out = nullptr;
return 0;
}};

rc = jsonRpc_call(intf, &serv, R"({"m":"stats([D)LStatsResult;", "a": [[1.0,2.0]]})", &result);
ASSERT_EQ(0, rc);

json_auto_t* replyJson = json_loads(result, JSON_DECODE_ANY, nullptr);
EXPECT_TRUE(json_is_null(json_object_get(replyJson, "r")));
free(result);
dynInterface_destroy(intf);
}

TEST_F(JsonRpcTests, callOutReference) {
dyn_interface_type *intf = nullptr;
FILE *desc = fopen("descriptors/example7.descriptor", "r");
Expand Down Expand Up @@ -844,14 +843,63 @@ TEST_F(JsonRpcTests, handleOutSeq) {
handleTestOutputSequence();
}

TEST_F(JsonRpcTests, callTestOutChar) {
TEST_F(JsonRpcTests, callTestOutText) {
callTestOutChar();
}

TEST_F(JsonRpcTests, handleOutChar) {
TEST_F(JsonRpcTests, callTestOutNullTextResult) {
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, [](void *, char** result)->int {
*result = nullptr;
return 0;
}, nullptr, nullptr};

rc = jsonRpc_call(intf, &serv, R"({"m": "getName(V)t", "a": []})", &result);
ASSERT_EQ(0, rc);

json_auto_t* replyJson = json_loads(result, JSON_DECODE_ANY, nullptr);
EXPECT_TRUE(json_is_null(json_object_get(replyJson, "r")));
free(result);
dynInterface_destroy(intf);
}

TEST_F(JsonRpcTests, handleOutText) {
handleTestOutChar();
}

TEST_F(JsonRpcTests, handleNullOutTextResult) {
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);

dyn_function_type *func = dynInterface_findMethod(intf, "getName(V)t")->dynFunc;
assert(func != nullptr);

const char *reply = R"({"r":null})";
char *result = nullptr;
void *out = &result;

void *args[2];
args[0] = nullptr;
args[1] = &out;

int rsErrno = 0;
jsonRpc_handleReply(func, reply, args, &rsErrno);
ASSERT_EQ(0, rsErrno);
EXPECT_EQ(nullptr, result);
dynInterface_destroy(intf);
}

TEST_F(JsonRpcTests, handleInvalidOutChar) {

dyn_interface_type *intf = nullptr;
Expand All @@ -861,17 +909,8 @@ TEST_F(JsonRpcTests, handleInvalidOutChar) {
ASSERT_EQ(0, rc);
fclose(desc);

const struct methods_head* head = dynInterface_methods(intf);
dyn_function_type *func = nullptr;
struct method_entry *entry = nullptr;
TAILQ_FOREACH(entry, head, entries) {
if (strcmp(dynFunction_getName(entry->dynFunc), "getName") == 0) {
func = entry->dynFunc;
break;
}
}

ASSERT_TRUE(func != nullptr);
dyn_function_type *func = dynInterface_findMethod(intf, "getName(V)t")->dynFunc;
assert(func != nullptr);

const char *reply = R"({"r": 12345})";
char *result = nullptr;
Expand Down
2 changes: 1 addition & 1 deletion libs/dfi/include/dyn_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ extern "C" {
* Dyn function argument meta (am) as meta info, with the following possible values
* am=handle #void pointer for the handle
* am=pre #output pointer with memory pre-allocated, it should be pointer to trivial types, check `dynType_isTrivial` for more info.
* am=out #output pointer, it should be pointer to text or double pointer to serializable types
* am=out #output pointer, it should be pointers to text or pointer to serializable point type
*
* Without meta info the argument is considered to be a standard argument, which can be of any serializable type.
*
Expand Down
10 changes: 10 additions & 0 deletions libs/dfi/include/dyn_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ CELIX_DFI_EXPORT const struct methods_head* dynInterface_methods(const dyn_inter
*/
CELIX_DFI_EXPORT int dynInterface_nrOfMethods(const dyn_interface_type* intf);

/**
* @brief Finds and returns the method_entry structure for a given method id in the dynamic interface type instance.
* The dynamic interface type instance is the owner of the returned method_entry structure and it should not be freed.
*
* @param[in] intf The dynamic interface type instance.
* @param[in] id The id of the method to find.
* @return The method_entry structure for the given method id, or NULL if no matching method is found.
*/
CELIX_DFI_EXPORT const struct method_entry* dynInterface_findMethod(const dyn_interface_type* intf, const char* id);


#ifdef __cplusplus
}
Expand Down
10 changes: 10 additions & 0 deletions libs/dfi/src/dyn_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,13 @@ int dynInterface_nrOfMethods(const dyn_interface_type* intf) {
struct method_entry* last = TAILQ_LAST(&intf->methods, methods_head);
return last == NULL ? 0 : (last->index+1);
}

const struct method_entry* dynInterface_findMethod(const dyn_interface_type* intf, const char* id) {
const struct method_entry* entry = NULL;
TAILQ_FOREACH(entry, &intf->methods, entries) {
if (strcmp(entry->id, id) == 0) {
break;
}
}
return entry;
}
Loading

0 comments on commit 94903a2

Please sign in to comment.