diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index b714aa7ae..961a24112 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -170,13 +170,20 @@ TEST_F(DynTypeTests, AssignTest1) { int32_t val1 = 2; int32_t val2 = 4; int32_t val3 = 8; + EXPECT_EQ(0, dynType_complex_indexForName(type, "a")); dynType_complex_setValueAt(type, 0, &inst, &val1); ASSERT_EQ(2, inst.a); + + EXPECT_EQ(1, dynType_complex_indexForName(type, "b")); dynType_complex_setValueAt(type, 1, &inst, &val2); ASSERT_EQ(4, inst.b); + + EXPECT_EQ(2, dynType_complex_indexForName(type, "c")); dynType_complex_setValueAt(type, 2, &inst, &val3); ASSERT_EQ(8, inst.c); + EXPECT_EQ(-1, dynType_complex_indexForName(type, nullptr)); + EXPECT_EQ(-1, dynType_complex_indexForName(type, "none")); dynType_destroy(type); } diff --git a/libs/dfi/gtest/src/json_serializer_ei_tests.cc b/libs/dfi/gtest/src/json_serializer_ei_tests.cc index c1a538166..4c7e9feba 100644 --- a/libs/dfi/gtest/src/json_serializer_ei_tests.cc +++ b/libs/dfi/gtest/src/json_serializer_ei_tests.cc @@ -31,6 +31,12 @@ class JsonSerializerErrorInjectionTestSuite : public ::testing::Test { public: JsonSerializerErrorInjectionTestSuite() = default; ~JsonSerializerErrorInjectionTestSuite() override { + celix_ei_expect_json_integer(nullptr, 0, nullptr); + celix_ei_expect_json_array_append_new(nullptr, 0, 0); + celix_ei_expect_json_array(nullptr, 0, nullptr); + celix_ei_expect_json_object_set_new(nullptr, 0, 0); + celix_ei_expect_json_object(nullptr, 0, nullptr); + celix_ei_expect_json_dumps(nullptr, 0, nullptr); celix_ei_expect_json_array_size(nullptr, 0, 0); celix_ei_expect_strdup(nullptr, 0, nullptr); celix_ei_expect_calloc(nullptr, 0, nullptr); @@ -38,7 +44,7 @@ class JsonSerializerErrorInjectionTestSuite : public ::testing::Test { } }; -TEST_F(JsonSerializerErrorInjectionTestSuite, SerilizationError) { +TEST_F(JsonSerializerErrorInjectionTestSuite, DeserilizationError) { int rc; dyn_type *type; void *inst; @@ -92,4 +98,75 @@ TEST_F(JsonSerializerErrorInjectionTestSuite, SerilizationError) { EXPECT_STREQ("Error cannot deserialize json. Input is '[\"hello\", \"world\"]'", celix_err_popLastError()); EXPECT_STREQ("Error allocating memory for seq buf", celix_err_popLastError()); dynType_destroy(type); -} \ No newline at end of file +} + +struct test_struct { + double a; + double b; +}; + +struct test_seq { + uint32_t cap; + uint32_t len; + double* buf; +}; + +TEST_F(JsonSerializerErrorInjectionTestSuite, SerilizationError) { + 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; + celix_ei_expect_json_dumps((void*)jsonSerializer_serialize, 0, nullptr); + rc = jsonSerializer_serialize(type, &enumVal, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); + + test_struct ex {1.0, 2.0}; + rc = dynType_parseWithStr(R"({DD a b})", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + celix_ei_expect_json_object((void*)jsonSerializer_serialize, 3, nullptr); + rc = jsonSerializer_serialize(type, &ex, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); + + rc = dynType_parseWithStr(R"({DD a b})", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + celix_ei_expect_json_object_set_new((void*)jsonSerializer_serialize, 3, -1); + rc = jsonSerializer_serialize(type, &ex, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); + + double arr[1] = {1.0}; + test_seq seq {1, 1, arr}; + rc = dynType_parseWithStr(R"([D)", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + celix_ei_expect_json_array((void*)jsonSerializer_serialize, 3, nullptr); + rc = jsonSerializer_serialize(type, &seq, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); + + rc = dynType_parseWithStr(R"([D)", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + celix_ei_expect_json_array_append_new((void*)jsonSerializer_serialize, 3, -1); + rc = jsonSerializer_serialize(type, &seq, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); + + int32_t intVal = 12345; + rc = dynType_parseWithStr(R"(I)", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + celix_ei_expect_json_integer((void*)jsonSerializer_serialize, 2, nullptr); + rc = jsonSerializer_serialize(type, &intVal, &result); + EXPECT_NE(0, rc); + EXPECT_EQ(nullptr, result); + dynType_destroy(type); +} diff --git a/libs/dfi/gtest/src/json_serializer_tests.cpp b/libs/dfi/gtest/src/json_serializer_tests.cpp index abf82b800..449ab6fe0 100644 --- a/libs/dfi/gtest/src/json_serializer_tests.cpp +++ b/libs/dfi/gtest/src/json_serializer_tests.cpp @@ -34,6 +34,8 @@ extern "C" { #include "json_serializer.h" #include "celix_err.h" +#include + /*********** example 1 ************************/ /** struct type ******************************/ const char *example1_descriptor = "{DJISF a b c d e}"; @@ -408,7 +410,7 @@ static void parseTests() { ASSERT_EQ(0, rc); rc = jsonSerializer_deserialize(type, example5_input, strlen(example5_input), &inst); ASSERT_EQ(0, rc); - check_example5(inst); + check_example5(inst); dynType_free(type, inst); dynType_destroy(type); @@ -874,3 +876,42 @@ TEST_F(JsonSerializerTests, WriteComplexFailed) { TEST_F(JsonSerializerTests, WriteEnumFailed) { writeEnumFailed(); } + +TEST_F(JsonSerializerTests, WriteDoublePointer) { + dyn_type *type; + int rc; + double val = 1.0; + double* valp = &val; + double** valpp = &valp; + char *result = nullptr; + + rc = dynType_parseWithStr("**D", nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + rc = jsonSerializer_serialize(type, &valpp, &result); + EXPECT_NE(0, rc); + EXPECT_STREQ("Error cannot serialize pointer to pointer", celix_err_popLastError()); + dynType_destroy(type); +} + +TEST_F(JsonSerializerTests, SerializationDeserilizationTest) { + dyn_type *type; + void *inst; + int rc; + + type = nullptr; + inst = nullptr; + rc = dynType_parseWithStr(example5_descriptor, nullptr, nullptr, &type); + ASSERT_EQ(0, rc); + json_auto_t* root = json_loads(example5_input, JSON_DECODE_ANY, NULL); + ASSERT_NE(nullptr, root); + rc = jsonSerializer_deserializeJson(type, root, &inst); + ASSERT_EQ(0, rc); + check_example5(inst); + + json_auto_t* result = nullptr; + rc = jsonSerializer_serializeJson(type, inst, &result); + ASSERT_EQ(0, rc); + EXPECT_TRUE(json_equal(root, result)); + dynType_free(type, inst); + dynType_destroy(type); +} diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index 7ae474719..4e17386d5 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -311,7 +311,7 @@ CELIX_DFI_EXPORT int dynType_sequence_reserve(const dyn_type* type, void* inst, * @return 0 if successful. * @retval 1 if the index is out of bounds. */ -CELIX_DFI_EXPORT int dynType_sequence_locForIndex(const dyn_type* type, void* seqLoc, uint32_t index, void** valLoc); +CELIX_DFI_EXPORT int dynType_sequence_locForIndex(const dyn_type* type, const void* seqLoc, uint32_t index, void** valLoc); /** * @brief Increase the length of the sequence by one and return the value location for the last element. diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index d9ce0206f..af950efdb 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -690,9 +690,9 @@ uint32_t dynType_sequence_length(const void *seqLoc) { return seq->len; } -int dynType_sequence_locForIndex(const dyn_type* type, void* seqLoc, uint32_t index, void** out) { +int dynType_sequence_locForIndex(const dyn_type* type, const void* seqLoc, uint32_t index, void** out) { assert(type->type == DYN_TYPE_SEQUENCE); - struct generic_sequence* seq = seqLoc; + const struct generic_sequence* seq = seqLoc; size_t itemSize = dynType_size(type->sequence.itemType); diff --git a/libs/dfi/src/json_serializer.c b/libs/dfi/src/json_serializer.c index c271a0277..f6f964f9b 100644 --- a/libs/dfi/src/json_serializer.c +++ b/libs/dfi/src/json_serializer.c @@ -20,7 +20,6 @@ #include "json_serializer.h" #include "dyn_type.h" #include "dyn_type_common.h" -#include "dyn_interface.h" #include "celix_err.h" #include @@ -34,9 +33,9 @@ static int jsonSerializer_parseSequence(const dyn_type* seq, json_t* array, void static int jsonSerializer_parseAny(const dyn_type* type, void* input, json_t* val); static int jsonSerializer_parseEnum(const dyn_type* type, const char* enum_name, int32_t* out); -static int jsonSerializer_writeAny(const dyn_type* type, void* input, json_t** val); -static int jsonSerializer_writeComplex(const dyn_type* type, void* input, json_t** val); -static int jsonSerializer_writeSequence(const dyn_type* type, void* input, json_t** out); +static int jsonSerializer_writeAny(const dyn_type* type, const void* input, json_t** val); +static int jsonSerializer_writeComplex(const dyn_type* type, const void* input, json_t** val); +static int jsonSerializer_writeSequence(const dyn_type* type, const void* input, json_t** out); static int jsonSerializer_writeEnum(const dyn_type* type, int32_t enum_value, json_t** out); @@ -246,17 +245,13 @@ static int jsonSerializer_parseSequence(const dyn_type* seq, json_t* array, void } int jsonSerializer_serialize(const dyn_type* type, const void* input, char** output) { - int status = OK; - - json_t* root = NULL; - status = jsonSerializer_serializeJson(type, input, &root); - - if (status == OK) { - *output = json_dumps(root, JSON_COMPACT | JSON_ENCODE_ANY); - json_decref(root); + int status; + json_auto_t* root = NULL; + if ((status = jsonSerializer_serializeJson(type, input, &root)) != OK) { + return status; } - - return status; + *output = json_dumps(root, JSON_COMPACT | JSON_ENCODE_ANY); + return *output != NULL ? OK : ERROR; } static int jsonSerializer_parseEnum(const dyn_type* type, const char* enum_name, int32_t* out) { @@ -278,89 +273,72 @@ int jsonSerializer_serializeJson(const dyn_type* type, const void* input, json_t while (real->type == DYN_TYPE_REF) { real = real->ref.ref; } - return jsonSerializer_writeAny(real, (void*)input /*TODO update static function to take const void**/, out); + return jsonSerializer_writeAny(real, input, out); } -static int jsonSerializer_writeAny(const dyn_type *type, void* input, json_t **out) { +static int jsonSerializer_writeAny(const dyn_type* type, const void* input, json_t** out) { int status = OK; int descriptor = dynType_descriptorType(type); - json_t *val = NULL; - const dyn_type *subType = NULL; - - bool *z; //Z - float *f; //F - double *d; //D - char *b; //B - int *n; //N - int16_t *s; //S - int32_t *i; //I - int32_t *e; //E - int64_t *l; //J - uint8_t *ub; //b - uint16_t *us; //s - uint32_t *ui; //i - uint64_t *ul; //j + json_auto_t* val = NULL; + const dyn_type* subType = NULL; switch (descriptor) { case 'Z' : - z = input; - val = json_boolean((bool)*z); + val = json_boolean(*(const bool*)input); break; case 'B' : - b = input; - val = json_integer((json_int_t)*b); + val = json_integer((json_int_t)*(const char*)input); break; case 'S' : - s = input; - val = json_integer((json_int_t)*s); + val = json_integer((json_int_t)*(const int16_t*)input); break; case 'I' : - i = input; - val = json_integer((json_int_t)*i); + val = json_integer((json_int_t)*(const int32_t*)input); break; case 'J' : - l = input; - val = json_integer((json_int_t)*l); + val = json_integer((json_int_t)*(const int64_t*)input); break; case 'b' : - ub = input; - val = json_integer((json_int_t)*ub); + val = json_integer((json_int_t)*(const uint8_t*)input); break; case 's' : - us = input; - val = json_integer((json_int_t)*us); + val = json_integer((json_int_t)*(const uint16_t*)input); break; case 'i' : - ui = input; - val = json_integer((json_int_t)*ui); + val = json_integer((json_int_t)*(const uint32_t *)input); break; case 'j' : - ul = input; - val = json_integer((json_int_t)*ul); + val = json_integer((json_int_t)*(const uint64_t*)input); break; case 'N' : - n = input; - val = json_integer((json_int_t)*n); + val = json_integer((json_int_t)*(const int*)input); break; case 'F' : - f = input; - val = json_real((double) *f); + val = json_real((double) *(const float*)input); break; case 'D' : - d = input; - val = json_real(*d); + val = json_real(*(const double*)input); break; case 't' : val = json_string(*(const char **) input); break; case 'E': - e = input; - status = jsonSerializer_writeEnum(type, *e, &val); + status = jsonSerializer_writeEnum(type, *(const int32_t*)input, &val); break; case '*' : subType = dynType_typedPointer_getTypedType(type); - status = jsonSerializer_writeAny(subType, *(void **)input, &val); + if (dynType_ffiType(subType) != &ffi_type_pointer) { + const void* inputValue = *(const void**)input; + if (inputValue) { + status = jsonSerializer_writeAny(subType, inputValue, &val); + } else { + val = json_null(); + } + } else { + status = ERROR; + celix_err_pushf("Error cannot serialize pointer to pointer"); + } break; case '{' : status = jsonSerializer_writeComplex(type, input, &val); @@ -373,96 +351,76 @@ static int jsonSerializer_writeAny(const dyn_type *type, void* input, json_t **o status = ERROR; break; } - - if (status == OK && val != NULL) { - *out = val; + if (status != OK) { + return status; } - - return status; + *out = celix_steal_ptr(val); + return *out != NULL ? OK : ERROR; } -static int jsonSerializer_writeSequence(const dyn_type *type, void *input, json_t **out) { +static int jsonSerializer_writeSequence(const dyn_type* type, const void* input, json_t** out) { assert(dynType_type(type) == DYN_TYPE_SEQUENCE); - int status = OK; - json_t *array = json_array(); - const dyn_type *itemType = dynType_sequence_itemType(type); + json_auto_t* array = json_array(); + if (array == NULL) { + return ERROR; + } + const dyn_type* itemType = dynType_sequence_itemType(type); uint32_t len = dynType_sequence_length(input); - uint32_t i = 0; - void *itemLoc = NULL; - json_t *item = NULL; - for (i = 0; i < len; i += 1) { - item = NULL; - status = dynType_sequence_locForIndex(type, input, i, &itemLoc); - if (status == OK) { - status = jsonSerializer_writeAny(itemType, itemLoc, &item); - if (status == OK) { - json_array_append(array, item); - json_decref(item); - } + for (uint32_t i = 0; i < len; i += 1) { + int status = OK; + void* itemLoc = NULL; + json_t* item = NULL; + if ((status = dynType_sequence_locForIndex(type, input, i, &itemLoc)) != OK) { + celix_err_push("Cannot serialize invalid sequence"); + return status; } - - if (status != OK) { - break; + if ((status = jsonSerializer_writeAny(itemType, itemLoc, &item)) != OK) { + return status; + } + if ((json_array_append_new(array, item)) != 0) { + return ERROR; } } - if (status == OK && array != NULL) { - *out = array; - } else { - *out = NULL; - json_decref(array); - } - - return status; + *out = celix_steal_ptr(array); + return OK; } -static int jsonSerializer_writeComplex(const dyn_type *type, void *input, json_t **out) { +static int jsonSerializer_writeComplex(const dyn_type* type, const void* input, json_t** out) { assert(dynType_type(type) == DYN_TYPE_COMPLEX); - int status = OK; - json_t *val = json_object(); - struct complex_type_entry *entry = NULL; - const struct complex_type_entries_head *entries = dynType_complex_entries(type); - int index = -1; + json_auto_t* val = json_object(); + if (val == NULL) { + return ERROR; + } + struct complex_type_entry* entry = NULL; + const struct complex_type_entries_head* entries = dynType_complex_entries(type); + int index = 0; TAILQ_FOREACH(entry, entries, entries) { - void *subLoc = NULL; - json_t *subVal = NULL; + int status; + void* subLoc = NULL; + json_t* subVal = NULL; const dyn_type* subType = NULL; - index = dynType_complex_indexForName(type, entry->name); - if (index < 0) { - celix_err_pushf("Cannot find index for member '%s'", entry->name); - status = ERROR; - } - if(status == OK){ - subLoc = dynType_complex_valLocAt(type, index, input); - } - if (status == OK) { - subType = dynType_complex_dynTypeAt(type, index); - } - if (status == OK) { - status = jsonSerializer_writeAny(subType, subLoc, &subVal); + if (entry->name == NULL) { + celix_err_push("Unamed field unsupported"); + return ERROR; } - if (status == OK) { - json_object_set(val, entry->name, subVal); - json_decref(subVal); + subLoc = dynType_complex_valLocAt(type, index, (void*)input); + subType = dynType_complex_dynTypeAt(type, index); + if ((status = jsonSerializer_writeAny(subType, subLoc, &subVal)) != OK) { + return status; } - - if (status != OK) { - break; + if (json_object_set_new(val, entry->name, subVal) != 0) { + return ERROR; } + index++; } - if (status == OK && val != NULL) { - *out = val; - } else { - *out = NULL; - json_decref(val); - } - - return status; + *out = celix_steal_ptr(val); + return OK; } static int jsonSerializer_writeEnum(const dyn_type* type, int32_t enum_value, json_t **out) { @@ -476,7 +434,7 @@ static int jsonSerializer_writeEnum(const dyn_type* type, int32_t enum_value, js TAILQ_FOREACH(entry, &type->metaProperties, entries) { if (0 == strcmp(enum_value_str, entry->value)) { *out = json_string((const char*)entry->name); - return OK; + return *out != NULL ? OK : ERROR; } } diff --git a/libs/error_injector/jansson/CMakeLists.txt b/libs/error_injector/jansson/CMakeLists.txt index 73fec4035..9365037ac 100644 --- a/libs/error_injector/jansson/CMakeLists.txt +++ b/libs/error_injector/jansson/CMakeLists.txt @@ -20,9 +20,18 @@ find_package(jansson REQUIRED) add_library(jansson_ei STATIC src/jansson_ei.cc) target_include_directories(jansson_ei PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) -target_link_libraries(jansson_ei PUBLIC Celix::error_injector jansson::jansson) +target_link_libraries(jansson_ei + PUBLIC Celix::error_injector jansson::jansson + PRIVATE Celix::utils #for celix_cleanup.h +) target_link_options(jansson_ei INTERFACE LINKER:--wrap,json_array_size + LINKER:--wrap,json_dumps + LINKER:--wrap,json_object + LINKER:--wrap,json_object_set_new + LINKER:--wrap,json_array + LINKER:--wrap,json_array_append_new + LINKER:--wrap,json_integer ) add_library(Celix::jansson_ei ALIAS jansson_ei) diff --git a/libs/error_injector/jansson/include/jansson_ei.h b/libs/error_injector/jansson/include/jansson_ei.h index a33693719..504638529 100644 --- a/libs/error_injector/jansson/include/jansson_ei.h +++ b/libs/error_injector/jansson/include/jansson_ei.h @@ -26,6 +26,12 @@ extern "C" { #include "celix_error_injector.h" CELIX_EI_DECLARE(json_array_size, size_t); +CELIX_EI_DECLARE(json_dumps, char*); +CELIX_EI_DECLARE(json_object, json_t*); +CELIX_EI_DECLARE(json_object_set_new, int); +CELIX_EI_DECLARE(json_array, json_t*); +CELIX_EI_DECLARE(json_array_append_new, int); +CELIX_EI_DECLARE(json_integer, json_t*); #ifdef __cplusplus } diff --git a/libs/error_injector/jansson/src/jansson_ei.cc b/libs/error_injector/jansson/src/jansson_ei.cc index df3a8ed02..cca486d7e 100644 --- a/libs/error_injector/jansson/src/jansson_ei.cc +++ b/libs/error_injector/jansson/src/jansson_ei.cc @@ -18,6 +18,8 @@ */ #include "jansson_ei.h" +#include "celix_cleanup.h" +#include extern "C" { @@ -28,4 +30,48 @@ size_t __wrap_json_array_size(const json_t *array) { return __real_json_array_size(array); } +char* __real_json_dumps(const json_t *json, size_t flags); +CELIX_EI_DEFINE(json_dumps, char*) +char* __wrap_json_dumps(const json_t *json, size_t flags) { + CELIX_EI_IMPL(json_dumps); + return __real_json_dumps(json, flags); +} + +json_t* __real_json_object(void); +CELIX_EI_DEFINE(json_object, json_t*) +json_t* __wrap_json_object(void) { + CELIX_EI_IMPL(json_object); + return __real_json_object(); +} + +int __real_json_object_set_new(json_t *object, const char *key, json_t *value); +CELIX_EI_DEFINE(json_object_set_new, int) +int __wrap_json_object_set_new(json_t *object, const char *key, json_t *value) { + json_auto_t* val = value; + CELIX_EI_IMPL(json_object_set_new); + return __real_json_object_set_new(object, key, celix_steal_ptr(val)); +} + +json_t* __real_json_array(void); +CELIX_EI_DEFINE(json_array, json_t*) +json_t* __wrap_json_array(void) { + CELIX_EI_IMPL(json_array); + return __real_json_array(); +} + +int __real_json_array_append_new(json_t *array, json_t *value); +CELIX_EI_DEFINE(json_array_append_new, int) +int __wrap_json_array_append_new(json_t *array, json_t *value) { + json_auto_t *val = value; + CELIX_EI_IMPL(json_array_append_new); + return __real_json_array_append_new(array, celix_steal_ptr(val)); +} + +json_t* __real_json_integer(json_int_t value); +CELIX_EI_DEFINE(json_integer, json_t*) +json_t* __wrap_json_integer(json_int_t value) { + CELIX_EI_IMPL(json_integer); + return __real_json_integer(value); +} + } \ No newline at end of file