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

pnoltes pushed a commit to branch feature/685-properties-json-serialization
in repository https://gitbox.apache.org/repos/asf/celix.git

commit 99e503aa62a02173818a7d518c102f7dd8edaa12
Author: Pepijn Noltes <pnol...@apache.org>
AuthorDate: Sun Apr 14 16:54:16 2024 +0200

    gh-685: Add error injection test for properties encoding
---
 conanfile.py                                       |   4 +-
 libs/error_injector/jansson/CMakeLists.txt         |   2 +
 libs/error_injector/jansson/include/jansson_ei.h   |   2 +
 libs/error_injector/jansson/src/jansson_ei.cc      |  58 ++--
 .../error_injector/celix_version/CMakeLists.txt    |   1 +
 .../celix_version/include/celix_version_ei.h       |   2 +
 .../celix_version/src/celix_version_ei.cc          |   7 +
 libs/utils/gtest/CMakeLists.txt                    |   2 +
 .../PropertiesEncodingErrorInjectionTestSuite.cc   | 305 +++++++++++++++++++++
 .../utils/gtest/src/PropertiesEncodingTestSuite.cc |  30 +-
 libs/utils/src/properties.c                        |   3 +-
 libs/utils/src/properties_encoding.c               |  53 ++--
 12 files changed, 403 insertions(+), 66 deletions(-)

diff --git a/conanfile.py b/conanfile.py
index 609082c8..63d9ac00 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -309,7 +309,7 @@ class CelixConan(ConanFile):
             self.options['openssl'].shared = True
         if self.options.build_celix_dfi:
             self.options['libffi'].shared = True
-        if self.options.build_celix_dfi or self.options.build_celix_etcdlib:
+        if self.options.build_utils or  self.options.build_celix_dfi or 
self.options.build_celix_etcdlib:
             self.options['jansson'].shared = True
 
     def requirements(self):
@@ -332,7 +332,7 @@ class CelixConan(ConanFile):
             self.requires("civetweb/1.16")
         if self.options.build_celix_dfi:
             self.requires("libffi/[>=3.2.1 <4.0.0]")
-        if self.options.build_celix_dfi or self.options.build_celix_etcdlib:
+        if self.option.build_utils or self.options.build_celix_dfi or 
self.options.build_celix_etcdlib:
             self.requires("jansson/[>=2.12 <3.0.0]")
         if self.options.build_rsa_discovery_zeroconf:
             # TODO: To be replaced with mdnsresponder/1790.80.10, resolve some 
problems of mdnsresponder
diff --git a/libs/error_injector/jansson/CMakeLists.txt 
b/libs/error_injector/jansson/CMakeLists.txt
index b536956a..b093901d 100644
--- a/libs/error_injector/jansson/CMakeLists.txt
+++ b/libs/error_injector/jansson/CMakeLists.txt
@@ -35,5 +35,7 @@ target_link_options(jansson_ei INTERFACE
         LINKER:--wrap,json_integer
         LINKER:--wrap,json_string
         LINKER:--wrap,json_real
+        LINKER:--wrap,json_vsprintf
+        LINKER:--wrap,json_sprintf
         )
 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 60f04e45..167ace85 100644
--- a/libs/error_injector/jansson/include/jansson_ei.h
+++ b/libs/error_injector/jansson/include/jansson_ei.h
@@ -34,6 +34,8 @@ CELIX_EI_DECLARE(json_array_append_new, int);
 CELIX_EI_DECLARE(json_integer, json_t*);
 CELIX_EI_DECLARE(json_string, json_t*);
 CELIX_EI_DECLARE(json_real, json_t*);
+CELIX_EI_DECLARE(json_vsprintf,json_t*);
+CELIX_EI_DECLARE(json_sprintf, 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 57033c0c..1d820629 100644
--- a/libs/error_injector/jansson/src/jansson_ei.cc
+++ b/libs/error_injector/jansson/src/jansson_ei.cc
@@ -23,69 +23,87 @@
 
 extern "C" {
 
-size_t __real_json_array_size(const json_t *array);
+size_t __real_json_array_size(const json_t* array);
 CELIX_EI_DEFINE(json_array_size, size_t)
-size_t __wrap_json_array_size(const json_t *array) {
+size_t __wrap_json_array_size(const json_t* array) {
     CELIX_EI_IMPL(json_array_size);
     return __real_json_array_size(array);
 }
 
-char *__real_json_dumps(const json_t *json, size_t flags);
+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) {
+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);
+json_t* __real_json_object(void);
 CELIX_EI_DEFINE(json_object, json_t*)
-json_t *__wrap_json_object(void) {
+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);
+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;
+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);
+json_t* __real_json_array(void);
 CELIX_EI_DEFINE(json_array, json_t*)
-json_t *__wrap_json_array(void) {
+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);
+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;
+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);
+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) {
+json_t* __wrap_json_integer(json_int_t value) {
     CELIX_EI_IMPL(json_integer);
     return __real_json_integer(value);
 }
 
-json_t *__real_json_string(const char *value);
+json_t* __real_json_string(const char* value);
 CELIX_EI_DEFINE(json_string, json_t*)
-json_t *__wrap_json_string(const char *value) {
+json_t* __wrap_json_string(const char* value) {
     CELIX_EI_IMPL(json_string);
     return __real_json_string(value);
 }
 
-json_t *__real_json_real(double value);
+json_t* __real_json_real(double value);
 CELIX_EI_DEFINE(json_real, json_t*)
-json_t *__wrap_json_real(double value) {
+json_t* __wrap_json_real(double value) {
     CELIX_EI_IMPL(json_real);
     return __real_json_real(value);
 }
 
+json_t* __real_json_vsprintf(const char* fmt, va_list ap);
+CELIX_EI_DEFINE(json_vsprintf, json_t*)
+json_t* __wrap_json_vsprintf(const char* fmt, va_list ap) {
+    CELIX_EI_IMPL(json_vsprintf);
+    return __real_json_vsprintf(fmt, ap);
+}
+
+json_t* __real_json_sprintf(const char* fmt, ...);
+CELIX_EI_DEFINE(json_sprintf, json_t*)
+json_t* __wrap_json_sprintf(const char* fmt, ...) {
+    CELIX_EI_IMPL(json_sprintf);
+    va_list args;
+    va_start(args, fmt);
+    json_t* obj = __real_json_vsprintf(fmt, args);
+    va_end(args);
+    return obj;
+}
+
 }
\ No newline at end of file
diff --git a/libs/utils/error_injector/celix_version/CMakeLists.txt 
b/libs/utils/error_injector/celix_version/CMakeLists.txt
index ed7aadcd..81886a5f 100644
--- a/libs/utils/error_injector/celix_version/CMakeLists.txt
+++ b/libs/utils/error_injector/celix_version/CMakeLists.txt
@@ -24,5 +24,6 @@ target_link_options(version_ei INTERFACE
         LINKER:--wrap,celix_version_createVersionFromString
         LINKER:--wrap,celix_version_parse
         LINKER:--wrap,celix_version_copy
+        LINKER:--wrap,celix_version_toString
 )
 add_library(Celix::version_ei ALIAS version_ei)
diff --git a/libs/utils/error_injector/celix_version/include/celix_version_ei.h 
b/libs/utils/error_injector/celix_version/include/celix_version_ei.h
index a8231857..e5d510f7 100644
--- a/libs/utils/error_injector/celix_version/include/celix_version_ei.h
+++ b/libs/utils/error_injector/celix_version/include/celix_version_ei.h
@@ -31,6 +31,8 @@ CELIX_EI_DECLARE(celix_version_parse, celix_status_t);
 
 CELIX_EI_DECLARE(celix_version_copy, celix_version_t*);
 
+CELIX_EI_DECLARE(celix_version_toString, char*);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/utils/error_injector/celix_version/src/celix_version_ei.cc 
b/libs/utils/error_injector/celix_version/src/celix_version_ei.cc
index eefad50d..ddcdf9f8 100644
--- a/libs/utils/error_injector/celix_version/src/celix_version_ei.cc
+++ b/libs/utils/error_injector/celix_version/src/celix_version_ei.cc
@@ -41,4 +41,11 @@ celix_version_t* __wrap_celix_version_copy(const 
celix_version_t* version) {
     return __real_celix_version_copy(version);
 }
 
+char* __real_celix_version_toString(const celix_version_t* version);
+CELIX_EI_DEFINE(celix_version_toString, char*);
+char* __wrap_celix_version_toString(const celix_version_t* version) {
+    CELIX_EI_IMPL(celix_version_toString);
+    return __real_celix_version_toString(version);
+}
+
 }
\ No newline at end of file
diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt
index 11c6ec00..08be2b58 100644
--- a/libs/utils/gtest/CMakeLists.txt
+++ b/libs/utils/gtest/CMakeLists.txt
@@ -127,6 +127,7 @@ if (EI_TESTS)
             src/VersionErrorInjectionTestSuite.cc
             src/HashMapErrorInjectionTestSuite.cc
             src/FilterErrorInjectionTestSuite.cc
+            src/PropertiesEncodingErrorInjectionTestSuite.cc
     )
     target_link_libraries(test_utils_with_ei PRIVATE
             Celix::zip_ei
@@ -143,6 +144,7 @@ if (EI_TESTS)
             Celix::long_hash_map_ei
             Celix::version_ei
             Celix::array_list_ei
+            Celix::jansson_ei
             GTest::gtest GTest::gtest_main
     )
     target_include_directories(test_utils_with_ei PRIVATE ../src) #for 
version_private (needs refactoring of test)
diff --git a/libs/utils/gtest/src/PropertiesEncodingErrorInjectionTestSuite.cc 
b/libs/utils/gtest/src/PropertiesEncodingErrorInjectionTestSuite.cc
new file mode 100644
index 00000000..9e2d3bc3
--- /dev/null
+++ b/libs/utils/gtest/src/PropertiesEncodingErrorInjectionTestSuite.cc
@@ -0,0 +1,305 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "celix_err.h"
+#include "celix_properties.h"
+
+#include "celix_array_list_ei.h"
+#include "celix_utils_ei.h"
+#include "celix_version_ei.h"
+#include "jansson_ei.h"
+#include "malloc_ei.h"
+#include "stdio_ei.h"
+
+class PropertiesEncodingErrorInjectionTestSuite : public ::testing::Test {
+  public:
+    PropertiesEncodingErrorInjectionTestSuite() = default;
+
+    ~PropertiesEncodingErrorInjectionTestSuite() override {
+        celix_ei_expect_json_object(nullptr, 0, nullptr);
+        celix_ei_expect_open_memstream(nullptr, 0, nullptr);
+        celix_ei_expect_celix_utils_writeOrCreateString(nullptr, 0, nullptr);
+        celix_ei_expect_json_object_set_new(nullptr, 0, -1);
+        celix_ei_expect_json_sprintf(nullptr, 0, nullptr);
+        celix_ei_expect_celix_version_toString(nullptr, 0, nullptr);
+        celix_ei_expect_malloc(nullptr, 0, nullptr);
+        celix_ei_expect_celix_arrayList_createWithOptions(nullptr, 0, nullptr);
+        celix_ei_expect_celix_arrayList_addString(nullptr, 0, CELIX_SUCCESS);
+
+    }
+};
+
+TEST_F(PropertiesEncodingErrorInjectionTestSuite, SaveErrorTest) {
+    //Given a dummy properties object
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+    celix_properties_set(props, "key", "value");
+
+    //When an error injected is prepared for json_object() from saveToStream
+    celix_ei_expect_json_object((void*)celix_properties_saveToStream, 0, 
nullptr);
+
+    //And a dummy stream is created
+    FILE* stream = fopen("/dev/null", "w");
+
+    //When I call celix_properties_saveToStream
+    celix_status_t status = celix_properties_saveToStream(props, stream, 0);
+
+    //Then I expect an error
+    EXPECT_EQ(CELIX_ENOMEM, status);
+    fclose(stream);
+
+    //When an error injected is prepared for open_memstream()n from save
+    celix_ei_expect_open_memstream((void*)celix_properties_saveToString, 0, 
nullptr);
+
+    //When I call celix_properties_saveToString
+    char* out = nullptr;
+    status = celix_properties_saveToString(props, 0, &out);
+
+    //Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    //And I expect 2 error messages in celix_err
+    EXPECT_EQ(2, celix_err_getErrorCount());
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
+}
+
+TEST_F(PropertiesEncodingErrorInjectionTestSuite, EncodeErrorTest) {
+    // Given a dummy properties object
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+    celix_properties_set(props, "key/with/slash", "value");
+    celix_properties_set(props, "key-with-out-slash", "value");
+
+    // When an error injected is prepared for 
celix_utils_writeOrCreateString() from celix_properties_saveToString
+    
celix_ei_expect_celix_utils_writeOrCreateString((void*)celix_properties_saveToString,
 2, nullptr);
+
+    // And I call celix_properties_saveToString using NESTED encoding 
(whitebox-knowledge)
+    char* out = nullptr;
+    auto status = celix_properties_saveToString(props, 
CELIX_PROPERTIES_ENCODE_NESTED, &out);
+
+    // Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    // When an error injected is prepared for json_object() from 
celix_properties_saveToString
+    celix_ei_expect_json_object((void*)celix_properties_saveToString, 2, 
nullptr);
+
+    // And I call celix_properties_saveToString using NESTED encoding 
(whitebox-knowledge)
+    status = celix_properties_saveToString(props, 
CELIX_PROPERTIES_ENCODE_NESTED, &out);
+
+    // Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    // When an error injected is prepared for json_object_set_new() from 
celix_properties_saveToString
+    celix_ei_expect_json_object_set_new((void*)celix_properties_saveToString, 
2, -1);
+
+    // And I call celix_properties_saveToString using NESTED encoding 
(whitebox-knowledge)
+    status = celix_properties_saveToString(props, 
CELIX_PROPERTIES_ENCODE_NESTED, &out);
+
+    // Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    // When an error injected is prepared for json_string() from 
celix_properties_saveToString
+    celix_ei_expect_json_string((void*)celix_properties_saveToString, 3, 
nullptr);
+
+    // And I call celix_properties_saveToString using NESTED encoding 
(whitebox-knowledge)
+    status = celix_properties_saveToString(props, 
CELIX_PROPERTIES_ENCODE_NESTED, &out);
+
+    // Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    // When an error injected is prepared for json_object_set_new() from 
celix_properties_saveToString
+    celix_ei_expect_json_object_set_new((void*)celix_properties_saveToString, 
3, -1);
+
+    // And I call celix_properties_saveToString using FLAT encoding 
(whitebox-knowledge)
+    status = celix_properties_saveToString(props, 
CELIX_PROPERTIES_ENCODE_FLAT, &out);
+
+    // Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    // And I expect 5 error message in celix_err
+    EXPECT_EQ(5, celix_err_getErrorCount());
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
+}
+
+TEST_F(PropertiesEncodingErrorInjectionTestSuite, EncodeArrayErrorTest) {
+    // Given a dummy properties object with an array
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+    auto* arr = celix_arrayList_createStringArray();
+    celix_arrayList_addString(arr, "value1");
+    celix_arrayList_addString(arr, "value2");
+    celix_properties_assignArrayList(props, "key", arr);
+
+    // When an error injected is prepared for json_array() from 
celix_properties_saveToString
+    celix_ei_expect_json_array((void*)celix_properties_saveToString, 4, 
nullptr);
+
+    // And I call celix_properties_saveToString
+    char* out = nullptr;
+    auto status = celix_properties_saveToString(props, 0, &out);
+
+    // Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+
+    //When an error injected is prepared for json_array_append_new() from 
loadFromString2
+    
celix_ei_expect_json_array_append_new((void*)celix_properties_saveToString, 4, 
-1);
+
+    //And I call celix_properties_saveToString
+    status = celix_properties_saveToString(props, 0, &out);
+
+    //Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    //When an error injected is prepared for json_string() from loadFromString2
+    celix_ei_expect_json_string((void*)celix_properties_saveToString, 5, 
nullptr);
+
+    //And I call celix_properties_saveToString
+    status = celix_properties_saveToString(props, 0, &out);
+
+    //Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    // And I expect 3 error message in celix_err
+    EXPECT_EQ(3, celix_err_getErrorCount());
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
+}
+
+TEST_F(PropertiesEncodingErrorInjectionTestSuite, EncodeVersionErrorTest) {
+    // Given a dummy properties object with a version
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+    auto* version = celix_version_create(1, 2, 3, "qualifier");
+    celix_properties_assignVersion(props, "key", version);
+
+    // When an error injected is prepared for json_sprintf() from 
celix_properties_saveToString
+    celix_ei_expect_json_sprintf((void*)celix_properties_saveToString, 4, 
nullptr);
+
+    // And I call celix_properties_saveToString
+    char* out = nullptr;
+    auto status = celix_properties_saveToString(props, 0, &out);
+
+    // Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    // When an error injected is prepared for celix_version_toString() from 
celix_properties_saveToString
+    
celix_ei_expect_celix_version_toString((void*)celix_properties_saveToString, 4, 
nullptr);
+
+    // And I call celix_properties_saveToString
+    status = celix_properties_saveToString(props, 0, &out);
+
+    // Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    // And I expect 2 error message in celix_err
+    EXPECT_EQ(2, celix_err_getErrorCount());
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
+}
+
+TEST_F(PropertiesEncodingErrorInjectionTestSuite, LoadErrorTest) {
+    //Given a dummy json string
+    const char* json = R"({"key":"value"})";
+
+    //When an error injected is prepared for fmemopen() from loadFromString2
+    celix_ei_expect_fmemopen((void*)celix_properties_loadFromString2, 0, 
nullptr);
+
+    //When I call celix_properties_loadFromString
+    celix_properties_t* props;
+    auto status = celix_properties_loadFromString2(json, 0, &props);
+
+    //Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    //And I expect 1 error message in celix_err
+    EXPECT_EQ(1, celix_err_getErrorCount());
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
+}
+
+TEST_F(PropertiesEncodingErrorInjectionTestSuite, DecodeErrorTest) {
+    //Given a dummy json string
+    const char* json = R"({"key":"value", "object": {"key":"value"}})";
+
+    //When an error injected is prepared for 
celix_properties_create()->malloc() from celix_properties_loadFromString2
+    celix_ei_expect_malloc((void*)celix_properties_loadFromString2, 3, 
nullptr);
+
+    //When I call celix_properties_loadFromString
+    celix_properties_t* props;
+    auto status = celix_properties_loadFromString2(json, 0, &props);
+
+    //Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    //When an error injected is prepared for celix_utils_writeOrCreateString() 
from celix_properties_loadFromString2
+    
celix_ei_expect_celix_utils_writeOrCreateString((void*)celix_properties_loadFromString2,
 3, nullptr);
+
+    //When I call celix_properties_loadFromString
+    status = celix_properties_loadFromString2(json, 0, &props);
+
+    //Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    //And I expect 2 error message in celix_err
+    EXPECT_EQ(2, celix_err_getErrorCount());
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
+}
+
+TEST_F(PropertiesEncodingErrorInjectionTestSuite, DecodeArrayErrorTest) {
+    //Given a dummy json string
+    const char* json = R"({"key":["value1", "value2"]})";
+
+    // When an error injected is prepared for 
celix_arrayList_createWithOptions() from celix_properties_loadFromString2
+    
celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_loadFromString2,
 4, nullptr);
+
+    //When I call celix_properties_loadFromString
+    celix_properties_t* props;
+    auto status = celix_properties_loadFromString2(json, 0, &props);
+
+    //Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    // When an error injected is prepared for celix_arrayList_addString() from 
celix_properties_loadFromString2
+    
celix_ei_expect_celix_arrayList_addString((void*)celix_properties_loadFromString2,
 4, ENOMEM);
+
+    //When I call celix_properties_loadFromString
+    status = celix_properties_loadFromString2(json, 0, &props);
+
+    //Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    // And I expect 0 error message in celix_err. Note because errors are 
injected for celix_array_list_t, celix_err is
+    // not used
+    EXPECT_EQ(0, celix_err_getErrorCount());
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
+}
+
+TEST_F(PropertiesEncodingErrorInjectionTestSuite, DecodeVersionErrorTest) {
+    // Given a dummy json version string
+    const char* json = R"({"key":"version<1.2.3.qualifier>"})";
+
+    // When an error injected is prepared for 
celix_utils_writeOrCreateString() from celix_properties_loadFromString2
+    
celix_ei_expect_celix_utils_writeOrCreateString((void*)celix_properties_loadFromString2,
 4, nullptr);
+
+    // And I call celix_properties_loadFromString
+    celix_properties_t* props;
+    auto status = celix_properties_loadFromString2(json, 0, &props);
+
+    // Then I expect an error
+    EXPECT_EQ(ENOMEM, status);
+
+    // And I expect 1 error message in celix_err
+    EXPECT_EQ(1, celix_err_getErrorCount());
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
+}
diff --git a/libs/utils/gtest/src/PropertiesEncodingTestSuite.cc 
b/libs/utils/gtest/src/PropertiesEncodingTestSuite.cc
index b854e183..bece2147 100644
--- a/libs/utils/gtest/src/PropertiesEncodingTestSuite.cc
+++ b/libs/utils/gtest/src/PropertiesEncodingTestSuite.cc
@@ -17,16 +17,14 @@
  * under the License.
  */
 
-#include <cmath>
 #include <gtest/gtest.h>
+#include <cmath>
 #include <jansson.h>
 
 #include "celix_err.h"
 #include "celix_properties.h"
 #include "celix_stdlib_cleanup.h"
 
-using ::testing::MatchesRegex;
-
 class PropertiesSerializationTestSuite : public ::testing::Test {
   public:
     PropertiesSerializationTestSuite() { celix_err_resetErrors(); }
@@ -189,7 +187,7 @@ TEST_F(PropertiesSerializationTestSuite, 
SaveEmptyArrayTest) {
 
     //And at least one error message is added to celix_err
     EXPECT_GE(celix_err_getErrorCount(), 1);
-    celix_err_printErrors(stderr, "Error: ", "\n");
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
 }
 
 TEST_F(PropertiesSerializationTestSuite, SaveJPathKeysTest) {
@@ -349,7 +347,7 @@ TEST_F(PropertiesSerializationTestSuite, 
SavePropertiesWithKeyCollision) {
 
     //And at least one error message is added to celix_err
     EXPECT_GE(celix_err_getErrorCount(), 1);
-    celix_err_printErrors(stderr, "Error: ", "\n");
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
 }
 
 TEST_F(PropertiesSerializationTestSuite, 
SavePropertiesWithAndWithoutStrictFlagTest) {
@@ -373,7 +371,7 @@ TEST_F(PropertiesSerializationTestSuite, 
SavePropertiesWithAndWithoutStrictFlagT
 
     //And at least one error message is added to celix_err
     EXPECT_GE(celix_err_getErrorCount(), 1);
-    celix_err_printErrors(stderr, "Error: ", "\n");
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
 }
 
 TEST_F(PropertiesSerializationTestSuite, SavePropertiesWithPrettyPrintTest) {
@@ -410,7 +408,7 @@ TEST_F(PropertiesSerializationTestSuite, 
SaveWithInvalidStreamTest) {
     EXPECT_EQ(2, celix_err_getErrorCount());
     fclose(readStream);
 
-    celix_err_printErrors(stderr, "Error: ", "\n");
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
 }
 
 TEST_F(PropertiesSerializationTestSuite, LoadEmptyPropertiesTest) {
@@ -563,11 +561,11 @@ TEST_F(PropertiesSerializationTestSuite, 
LoadPropertiesWithInvalidInputTest) {
         auto status = celix_properties_loadFromStream(stream, 0, &props);
 
         //Then loading fails
-        EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, status);
+        EXPECT_NE(CELIX_SUCCESS, status);
 
         //And at least one error message is added to celix_err
         EXPECT_GE(celix_err_getErrorCount(), 1);
-        celix_err_printErrors(stderr, "Error: ", "\n");
+        celix_err_printErrors(stderr, "Test Error: ", "\n");
 
         fclose(stream);
     }
@@ -661,7 +659,7 @@ TEST_F(PropertiesSerializationTestSuite, 
LoadPropertiesWithDuplicatesTest) {
 
     // And at least one error message is added to celix_err
     EXPECT_GE(celix_err_getErrorCount(), 1);
-    celix_err_printErrors(stderr, "Error: ", "\n");
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
 }
 
 TEST_F(PropertiesSerializationTestSuite, LoadPropertiesEscapedSlashesTest) {
@@ -713,7 +711,7 @@ TEST_F(PropertiesSerializationTestSuite, 
LoadPropertiesEscapedSlashesTest) {
 
     // And at least one error message is added to celix_err
     EXPECT_GE(celix_err_getErrorCount(), 1);
-    celix_err_printErrors(stderr, "Error: ", "\n");
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
 
     // When decoding the properties from a string using a flag that allows 
collisions
     celix_autoptr(celix_properties_t) props3 = nullptr;
@@ -724,7 +722,7 @@ TEST_F(PropertiesSerializationTestSuite, 
LoadPropertiesEscapedSlashesTest) {
 
     // And at least one error message is added to celix_err
     EXPECT_GE(celix_err_getErrorCount(), 1);
-    celix_err_printErrors(stderr, "Error: ", "\n");
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
 }
 
 TEST_F(PropertiesSerializationTestSuite, 
LoadPropertiesWithAndWithoutStrictFlagTest) {
@@ -770,7 +768,7 @@ TEST_F(PropertiesSerializationTestSuite, 
LoadPropertiesWithAndWithoutStrictFlagT
 
         //And at least one error message is added to celix_err
         EXPECT_GE(celix_err_getErrorCount(), 1);
-        celix_err_printErrors(stderr, "Error: ", "\n");
+        celix_err_printErrors(stderr, "Test Error: ", "\n");
 
         fclose(stream);
     }
@@ -795,7 +793,7 @@ TEST_F(PropertiesSerializationTestSuite, 
LoadPropertiesWithSlashesInTheKeysTest)
     // When loading the properties from the stream
     celix_autoptr(celix_properties_t) props = nullptr;
     auto status = celix_properties_loadFromStream(stream, 0, &props);
-    celix_err_printErrors(stderr, "Error: ", "\n");
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
     ASSERT_EQ(CELIX_SUCCESS, status);
 
     // Then the properties object contains the nested objects
@@ -821,7 +819,7 @@ TEST_F(PropertiesSerializationTestSuite, 
LoadPropertiesWithInvalidVersionsTest)
 
     // And at least one error message is added to celix_err
     EXPECT_GE(celix_err_getErrorCount(), 1);
-    celix_err_printErrors(stderr, "Error: ", "\n");
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
 
     // Given a JSON object with an invalid version strings, that are not 
recognized as versions
     jsonInput =
@@ -857,7 +855,7 @@ TEST_F(PropertiesSerializationTestSuite, 
LoadWithInvalidStreamTest) {
 
     fclose(stream);
     free(buf);
-    celix_err_printErrors(stderr, "Error: ", "\n");
+    celix_err_printErrors(stderr, "Test Error: ", "\n");
 }
 
 TEST_F(PropertiesSerializationTestSuite, SaveAndLoadFlatProperties) {
diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c
index 024d849d..10787e23 100644
--- a/libs/utils/src/properties.c
+++ b/libs/utils/src/properties.c
@@ -334,6 +334,8 @@ celix_properties_t* celix_properties_create() {
             free(props);
             props = NULL;
         }
+    } else {
+        celix_err_push("Cannot allocate memory for properties");
     }
     return props;
 }
@@ -451,7 +453,6 @@ celix_properties_t* celix_properties_loadWithStream(FILE* 
file) {
 
     celix_autoptr(celix_properties_t) props = celix_properties_create();
     if (!props) {
-        celix_err_push("Failed to create properties");
         return NULL;
     }
 
diff --git a/libs/utils/src/properties_encoding.c 
b/libs/utils/src/properties_encoding.c
index 087590f7..0b5f189c 100644
--- a/libs/utils/src/properties_encoding.c
+++ b/libs/utils/src/properties_encoding.c
@@ -35,12 +35,12 @@ static celix_status_t celix_properties_versionToJson(const 
celix_version_t* vers
     celix_autofree char* versionStr = celix_version_toString(version);
     if (!versionStr) {
         celix_err_push("Failed to create version string.");
-        return CELIX_ENOMEM;
+        return ENOMEM;
     }
     *out = json_sprintf("version<%s>", versionStr);
     if (!*out) {
         celix_err_push("Failed to create json string.");
-        return CELIX_ENOMEM;
+        return ENOMEM;
     }
     return CELIX_SUCCESS;
 }
@@ -72,7 +72,7 @@ static celix_status_t 
celix_properties_arrayElementEntryValueToJson(celix_array_
     }
     if (!*out) {
         celix_err_push("Failed to create json value.");
-        return CELIX_ENOMEM;
+        return ENOMEM;
     }
     return CELIX_SUCCESS;
 }
@@ -90,10 +90,10 @@ static celix_status_t 
celix_properties_arrayEntryValueToJson(const char* key,
         return CELIX_SUCCESS; // empty array -> treat as unset property
     }
 
-    json_t* array = json_array();
+    json_auto_t* array = json_array();
     if (!array) {
         celix_err_push("Failed to create json array.");
-        return CELIX_ENOMEM;
+        return ENOMEM;
     }
 
     for (int i = 0; i < celix_arrayList_size(entry->typed.arrayValue); ++i) {
@@ -102,7 +102,6 @@ static celix_status_t 
celix_properties_arrayEntryValueToJson(const char* key,
         json_t* jsonValue;
         celix_status_t status = 
celix_properties_arrayElementEntryValueToJson(elType, arrayEntry, &jsonValue);
         if (status != CELIX_SUCCESS) {
-            json_decref(array);
             return status;
         } else if (!jsonValue) {
             // ignore unset values
@@ -110,13 +109,12 @@ static celix_status_t 
celix_properties_arrayEntryValueToJson(const char* key,
             int rc = json_array_append_new(array, jsonValue);
             if (rc != 0) {
                 celix_err_push("Failed to append json string to array.");
-                json_decref(array);
-                return CELIX_ENOMEM;
+                return ENOMEM;
             }
         }
     }
 
-    *out = array;
+    *out = celix_steal_ptr(array);
     return CELIX_SUCCESS;
 }
 
@@ -146,14 +144,14 @@ celix_properties_entryValueToJson(const char* key, const 
celix_properties_entry_
         return celix_properties_arrayEntryValueToJson(key, entry, flags, out);
     default:
         // LCOV_EXCL_START
-        celix_err_pushf("Invalid properties entry type %d.", entry->valueType);
+        celix_err_pushf("Unexpected properties entry type %d.", 
entry->valueType);
         return CELIX_ILLEGAL_ARGUMENT;
         // LCOV_EXCL_STOP
     }
 
     if (!*out) {
         celix_err_pushf("Failed to create json value for key '%s'.", key);
-        return CELIX_ENOMEM;
+        return ENOMEM;
     }
     return CELIX_SUCCESS;
 }
@@ -175,7 +173,7 @@ static celix_status_t 
celix_properties_addJsonValueToJson(json_t* value, const c
     int rc = json_object_set_new(obj, key, value);
     if (rc != 0) {
         celix_err_push("Failed to set json object");
-        return CELIX_ENOMEM;
+        return ENOMEM;
     }
     return CELIX_SUCCESS;
 }
@@ -205,7 +203,7 @@ static celix_status_t 
celix_properties_addPropertiesEntryToJson(const celix_prop
         celix_auto(celix_utils_string_guard_t) strGuard = 
celix_utils_stringGuard_init(buf, name);
         if (!name) {
             celix_err_push("Failed to create name string");
-            return CELIX_ENOMEM;
+            return ENOMEM;
         }
         json_t* subObj = json_object_get(jsonObj, name);
         if (!subObj || !json_is_object(subObj)) {
@@ -216,12 +214,12 @@ static celix_status_t 
celix_properties_addPropertiesEntryToJson(const celix_prop
             subObj = json_object();
             if (!subObj) {
                 celix_err_push("Failed to create json object");
-                return CELIX_ENOMEM;
+                return ENOMEM;
             }
             int rc = json_object_set_new(jsonObj, name, subObj);
             if (rc != 0) {
                 celix_err_push("Failed to set json object");
-                return CELIX_ENOMEM;
+                return ENOMEM;
             }
         }
 
@@ -242,6 +240,7 @@ celix_status_t celix_properties_saveToStream(const 
celix_properties_t* propertie
     json_t* root = json_object();
     if (!root) {
         celix_err_push("Failed to create json object");
+        return ENOMEM;
     }
 
     if (!(encodeFlags & CELIX_PROPERTIES_ENCODE_FLAT) && !(encodeFlags & 
CELIX_PROPERTIES_ENCODE_NESTED)) {
@@ -295,7 +294,7 @@ celix_status_t celix_properties_saveToString(const 
celix_properties_t* propertie
     FILE* stream = open_memstream(&buffer, &size);
     if (!stream) {
         celix_err_push("Failed to open memstream.");
-        return CELIX_FILE_IO_EXCEPTION;
+        return ENOMEM;
     }
 
     celix_status_t status = celix_properties_saveToStream(properties, stream, 
encodeFlags);
@@ -310,13 +309,13 @@ static celix_status_t celix_properties_parseVersion(const 
char* value, celix_ver
     // precondition: value is a valid version string (8 chars prefix and 1 
char suffix)
     *out = NULL;;
     char buf[32];
-    char* corrected = celix_utils_writeOrCreateString(buf, sizeof(buf), 
"%.*s", (int)strlen(value) - 9, value + 8);
-    celix_auto(celix_utils_string_guard_t) guard = 
celix_utils_stringGuard_init(buf, corrected);
-    if (!corrected) {
-        celix_err_push("Failed to create corrected version string.");
+    char* extracted = celix_utils_writeOrCreateString(buf, sizeof(buf), 
"%.*s", (int)strlen(value) - 9, value + 8);
+    celix_auto(celix_utils_string_guard_t) guard = 
celix_utils_stringGuard_init(buf, extracted);
+    if (!extracted) {
+        celix_err_push("Failed to create extracted version string.");
         return ENOMEM;
     }
-    celix_status_t status = celix_version_parse(corrected, out);
+    celix_status_t status = celix_version_parse(extracted, out);
     if (status != CELIX_SUCCESS) {
         celix_err_push("Failed to parse version string.");
         return status;
@@ -412,7 +411,7 @@ celix_properties_decodeArray(celix_properties_t* props, 
const char* key, const j
     opts.elementType = elType;
     celix_autoptr(celix_array_list_t) array = 
celix_arrayList_createWithOptions(&opts);
     if (!array) {
-        return CELIX_ENOMEM;
+        return ENOMEM;
     }
 
     json_t* value;
@@ -439,7 +438,7 @@ celix_properties_decodeArray(celix_properties_t* props, 
const char* key, const j
         }
         default:
             // LCOV_EXCL_START
-            celix_err_pushf("Invalid array list element type %d for key %s.", 
elType, key);
+            celix_err_pushf("Unexpected array list element type %d for key 
%s.", elType, key);
             return CELIX_ILLEGAL_ARGUMENT;
             // LCOV_EXCL_STOP
         }
@@ -488,7 +487,7 @@ celix_properties_decodeValue(celix_properties_t* props, 
const char* key, json_t*
             celix_auto(celix_utils_string_guard_t) strGuard = 
celix_utils_stringGuard_init(buf, combinedKey);
             if (!combinedKey) {
                 celix_err_push("Failed to create sub key.");
-                return CELIX_ENOMEM;
+                return ENOMEM;
             }
             status = celix_properties_decodeValue(props, combinedKey, 
fieldValue, flags);
             if (status != CELIX_SUCCESS) {
@@ -514,7 +513,7 @@ celix_properties_decodeValue(celix_properties_t* props, 
const char* key, json_t*
         return CELIX_SUCCESS;
     } else {
         // LCOV_EXCL_START
-        celix_err_pushf("Invalid json value type for key '%s'.", key);
+        celix_err_pushf("Unexpected json value type for key '%s'.", key);
         return CELIX_ILLEGAL_ARGUMENT;
         // LCOV_EXCL_STOP
     }
@@ -529,7 +528,7 @@ static celix_status_t 
celix_properties_decodeFromJson(json_t* obj, int flags, ce
 
     celix_autoptr(celix_properties_t) props = celix_properties_create();
     if (!props) {
-        return CELIX_ENOMEM;
+        return ENOMEM;
     }
 
     const char* key;
@@ -574,7 +573,7 @@ celix_status_t celix_properties_loadFromString2(const char* 
input, int decodeFla
     FILE* stream = fmemopen((void*)input, strlen(input), "r");
     if (!stream) {
         celix_err_push("Failed to open memstream.");
-        return CELIX_FILE_IO_EXCEPTION;
+        return ENOMEM;
     }
     celix_status_t status = celix_properties_loadFromStream(stream, 
decodeFlags, out);
     fclose(stream);


Reply via email to