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

pnoltes pushed a commit to branch feature/type_support_for_properties
in repository https://gitbox.apache.org/repos/asf/celix.git


The following commit(s) were added to 
refs/heads/feature/type_support_for_properties by this push:
     new cb0a8ba9 #509: Improve some error handling in celix properties.
cb0a8ba9 is described below

commit cb0a8ba933113f2098f69bbb1ffb79b330175b3c
Author: Pepijn Noltes <[email protected]>
AuthorDate: Fri Nov 10 14:10:52 2023 +0100

    #509: Improve some error handling in celix properties.
    
    Also:
      - Add some clarification of when error messaged are pushed to celix err
        in the celix properties header documentation.
---
 .../gtest/src/PropertiesErrorInjectionTestSuite.cc | 22 ++++++++++-
 libs/utils/gtest/src/PropertiesTestSuite.cc        | 34 ++++++++++++++++
 libs/utils/include/celix_properties.h              | 29 +++++++++++++-
 libs/utils/src/properties.c                        | 45 +++++++++++++++++-----
 4 files changed, 117 insertions(+), 13 deletions(-)

diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc 
b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc
index 7deaa39b..3172a8c2 100644
--- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc
+++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc
@@ -37,13 +37,15 @@ class PropertiesErrorInjectionTestSuite : public 
::testing::Test {
   public:
     PropertiesErrorInjectionTestSuite() = default;
     ~PropertiesErrorInjectionTestSuite() override {
+        celix_err_resetErrors();
         celix_ei_expect_malloc(nullptr, 0, nullptr);
-        celix_ei_expect_celix_stringHashMap_create(nullptr, 0, nullptr);
+        celix_ei_expect_celix_stringHashMap_createWithOptions(nullptr, 0, 
nullptr);
         celix_ei_expect_celix_utils_strdup(nullptr, 0, nullptr);
         celix_ei_expect_fopen(nullptr, 0, nullptr);
         celix_ei_expect_fputc(nullptr, 0, 0);
         celix_ei_expect_fseek(nullptr, 0, 0);
         celix_ei_expect_celix_utils_strdup(nullptr, 0, nullptr);
+        celix_ei_expect_celix_stringHashMap_put(nullptr, 0, 0);
     }
 
     /**
@@ -77,10 +79,26 @@ TEST_F(PropertiesErrorInjectionTestSuite, 
CreateFailureTest) {
 
 TEST_F(PropertiesErrorInjectionTestSuite, CopyFailureTest) {
     // C API
+
+    //Given a celix properties object with more entries than the optimization 
cache
     celix_autoptr(celix_properties_t) prop = celix_properties_create();
     ASSERT_NE(nullptr, prop);
+    fillOptimizationCache(prop);
+    celix_properties_set(prop, "additionalKey", "value");
+
+    // When a hash map create error injection is set for 
celix_properties_create
     
celix_ei_expect_celix_stringHashMap_createWithOptions((void*)celix_properties_create,
 0, nullptr);
+    // Then the celix_properties_copy call fails
     ASSERT_EQ(nullptr, celix_properties_copy(prop));
+    ASSERT_EQ(1, celix_err_getErrorCount());
+    celix_err_resetErrors();
+
+    // When a malloc error injection is set for celix_properties_allocEntry 
(during set)
+    celix_ei_expect_malloc((void*)celix_properties_allocEntry, 0, nullptr);
+    // Then the celix_properties_copy call fails
+    ASSERT_EQ(nullptr, celix_properties_copy(prop));
+    ASSERT_GE(celix_err_getErrorCount(), 1);
+    celix_err_resetErrors();
 
     // C++ API
     const celix::Properties cxxProp{};
@@ -94,7 +112,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, SetFailureTest) {
     celix_autoptr(celix_properties_t) props = celix_properties_create();
     fillOptimizationCache(props);
 
-    // When a malloc error injection is set for celix_properties_set (during 
alloc entry)
+    // When a malloc error injection is set for celix_properties_allocEntry 
(during set)
     celix_ei_expect_malloc((void*)celix_properties_allocEntry, 0, nullptr);
     // Then the celix_properties_set call fails
     ASSERT_EQ(celix_properties_set(props, "key", "value"), CELIX_ENOMEM);
diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc 
b/libs/utils/gtest/src/PropertiesTestSuite.cc
index 1a0ae825..675b9086 100644
--- a/libs/utils/gtest/src/PropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/PropertiesTestSuite.cc
@@ -22,11 +22,15 @@
 #include "celix_properties.h"
 #include "celix_utils.h"
 #include "properties.h"
+#include "celix_err.h"
 
 using ::testing::MatchesRegex;
 
 class PropertiesTestSuite : public ::testing::Test {
 public:
+  PropertiesTestSuite() {
+      celix_err_resetErrors();
+  }
 };
 
 
@@ -649,3 +653,33 @@ TEST_F(PropertiesTestSuite, PropertiesEqualsTest) {
     celix_properties_setDouble(prop2, "key5", 42.0);
     EXPECT_FALSE(celix_properties_equals(prop1, prop2)); //different types
 }
+
+TEST_F(PropertiesTestSuite, PropertiesNullArgumentsTest) {
+    //Silently ignore nullptr properties arguments for set* and copy functions
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_set(nullptr, "key", "value"));
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_setLong(nullptr, "key", 1));
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_setDouble(nullptr, "key", 1.0));
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBool(nullptr, "key", true));
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersion(nullptr, "key", 
nullptr));
+    celix_autoptr(celix_properties_t) copy = celix_properties_copy(nullptr);
+    EXPECT_NE(nullptr, copy);
+}
+
+TEST_F(PropertiesTestSuite, InvalidArgumentsTest) {
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+    celix_autoptr(celix_version_t) version = celix_version_create(1,2,3, 
nullptr);
+
+    //Key cannot be nullptr and set functions should fail
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_set(props, nullptr, 
"value"));
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setLong(props, nullptr, 
1));
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setDouble(props, 
nullptr, 1.0));
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setBool(props, nullptr, 
true));
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setVersion(props, 
nullptr, version));
+    EXPECT_EQ(5, celix_err_getErrorCount());
+
+    celix_err_resetErrors();
+    //Set without copy should fail if a key or value is nullptr
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setWithoutCopy(props, 
nullptr, strdup("value")));
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setWithoutCopy(props, 
strdup("key"), nullptr));
+    EXPECT_EQ(2, celix_err_getErrorCount());
+}
diff --git a/libs/utils/include/celix_properties.h 
b/libs/utils/include/celix_properties.h
index f106b081..edc5a684 100644
--- a/libs/utils/include/celix_properties.h
+++ b/libs/utils/include/celix_properties.h
@@ -117,6 +117,8 @@ typedef struct celix_properties_iterator {
 /**
  * @brief Create a new empty property set.
  *
+ * This function will push a error msg to celix_err if the return value is 
NULL.
+ *
  * @return A new empty property set.
  */
 CELIX_UTILS_EXPORT celix_properties_t* celix_properties_create();
@@ -133,6 +135,8 @@ CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_properties_t, 
celix_properties_destroy)
 /**
  * @brief Load properties from a file.
  *
+ * This function will push a error msg to celix_err if the return value is 
NULL.
+ *
  * @param[in] filename The name of the file to load properties from.
  * @return A property set containing the properties from the file.
  * @retval NULL If an error occurred (e.g. file not found).
@@ -142,6 +146,8 @@ CELIX_UTILS_EXPORT celix_properties_t* 
celix_properties_load(const char* filenam
 /**
  * @brief Load properties from a stream.
  *
+ * This function will push a error msg to celix_err if the return value is 
NULL.
+ *
  * @param[in,out] stream The stream to load properties from.
  * @return A property set containing the properties from the stream.
  * @retval NULL If an error occurred (e.g. invalid format).
@@ -151,6 +157,8 @@ CELIX_UTILS_EXPORT celix_properties_t* 
celix_properties_loadWithStream(FILE* str
 /**
  * @brief Load properties from a string.
  *
+ * This function will push a error msg to celix_err if the return value is 
NULL.
+ *
  * @param[in] input The string to load properties from.
  * @return A property set containing the properties from the string.
  * @retval NULL If an error occurred (e.g. invalid format).
@@ -162,6 +170,8 @@ CELIX_UTILS_EXPORT celix_properties_t* 
celix_properties_loadFromString(const cha
  *
  * @note Properties values are always stored as string values, regardless of 
their actual underlining types.
  *
+ * This function will push a error msg to celix_err if the return code is not 
CELIX_SUCCESS.
+ *
  * @param[in] properties The property set to store.
  * @param[in] file The name of the file to store the properties to.
  * @param[in] header An optional - single line - header to write to the file 
before the properties.
@@ -207,6 +217,8 @@ CELIX_UTILS_EXPORT celix_properties_value_type_e 
celix_properties_getType(const
 /**
  * @brief Set the value of a property.
  *
+ * This function will push a error msg to celix_err if the return code is not 
CELIX_SUCCESS.
+ *
  * @param[in] properties The property set to modify.
  * @param[in] key The key of the property to set.
  * @param[in] value The value to set the property to.
@@ -219,6 +231,8 @@ CELIX_UTILS_EXPORT celix_status_t 
celix_properties_set(celix_properties_t* prope
 /**
  * @brief Set the value of a property without copying the value string.
  *
+ * This function will push a error msg to celix_err if the return code is not 
CELIX_SUCCESS.
+ *
  * @param[in] properties The property set to modify.
  * @param[in] key The key of the property to set. This string will be used 
directly, so it must not be freed or
 *                 modified after calling this function.
@@ -233,6 +247,9 @@ CELIX_UTILS_EXPORT celix_status_t 
celix_properties_setWithoutCopy(celix_properti
 
 /**
  * @brief Set the value of a property based on the provided property entry, 
maintaining underlying type.
+ *
+ * This function will push a error msg to celix_err if the return code is not 
CELIX_SUCCESS.
+ *
  * @param[in] properties The property set to modify.
  * @param[in] key The key of the property to set.
  * @param[in] entry The entry to set the property to. The entry will be 
copied, so it can be freed after calling
@@ -252,7 +269,9 @@ CELIX_UTILS_EXPORT celix_status_t 
celix_properties_setEntry(celix_properties_t*
 CELIX_UTILS_EXPORT void celix_properties_unset(celix_properties_t* properties, 
const char* key);
 
 /**
- * @brief Make a copy of a property set.
+ * @brief Make a copy of a properties set.
+ *
+ * This function will push a error msg to celix_err if the return value is 
NULL.
  *
  * @param[in] properties The property set to copy.
  * @return A copy of the given property set.
@@ -276,6 +295,8 @@ celix_properties_getAsLong(const celix_properties_t* 
properties, const char* key
 /**
  * @brief Set the value of a property to a long integer.
  *
+ * This function will push a error msg to celix_err if the return code is not 
CELIX_SUCCESS.
+ *
  * @param[in] properties The property set to modify.
  * @param[in] key The key of the property to set.
  * @param[in] value The long value to set the property to.
@@ -300,6 +321,8 @@ celix_properties_getAsBool(const celix_properties_t* 
properties, const char* key
 /**
  * @brief Set the value of a property to a boolean.
  *
+ * This function will push a error msg to celix_err if the return code is not 
CELIX_SUCCESS.
+ *
  * @param[in] properties The property set to modify.
  * @param[in] key The key of the property to set.
  * @param[in] val The boolean value to set the property to.
@@ -310,6 +333,8 @@ CELIX_UTILS_EXPORT celix_status_t 
celix_properties_setBool(celix_properties_t* p
 /**
  * @brief Set the value of a property to a double.
  *
+ * This function will push a error msg to celix_err if the return code is not 
CELIX_SUCCESS.
+ *
  * @param[in] properties The property set to modify.
  * @param[in] key The key of the property to set.
  * @param[in] val The double value to set the property to.
@@ -337,6 +362,7 @@ celix_properties_getAsDouble(const celix_properties_t* 
properties, const char* k
  * @brief Set the value of a property as a Celix version.
  *
  * This function will make a copy of the provided celix_version_t object and 
store it in the property set.
+ * This function will push a error msg to celix_err if the return code is not 
CELIX_SUCCESS.
  *
  * @param[in] properties The property set to modify.
  * @param[in] key The key of the property to set.
@@ -352,6 +378,7 @@ CELIX_UTILS_EXPORT celix_status_t 
celix_properties_setVersion(celix_properties_t
  *
  * This function will store a reference to the provided celix_version_t object 
in the property set and takes
  * ownership of the provided version.
+ * This function will push a error msg to celix_err if the return code is not 
CELIX_SUCCESS.
  *
  * @param[in] properties The property set to modify.
  * @param[in] key The key of the property to set.
diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c
index 0d987bb2..c5381922 100644
--- a/libs/utils/src/properties.c
+++ b/libs/utils/src/properties.c
@@ -265,12 +265,16 @@ static celix_properties_entry_t* 
celix_properties_createEntry(celix_properties_t
                                                               celix_version_t* 
versionValue) {
     celix_properties_entry_t* entry = celix_properties_allocEntry(properties);
     if (entry == NULL) {
+        celix_err_pushf("Cannot allocate property entry");
+        celix_version_destroy(versionValue);
         return NULL;
     }
 
     celix_status_t status =
         celix_properties_fillEntry(properties, entry, strValue, longValue, 
doubleValue, boolValue, versionValue);
     if (status != CELIX_SUCCESS) {
+        celix_err_pushf("Cannot fill property entry");
+        celix_version_destroy(versionValue);
         if (entry >= properties->entriesBuffer &&
             entry <= (properties->entriesBuffer + 
CELIX_PROPERTIES_OPTIMIZATION_ENTRIES_BUFFER_SIZE)) {
             // entry is part of the properties entries buffer -> nop.
@@ -308,11 +312,14 @@ static celix_status_t 
celix_properties_createAndSetEntry(celix_properties_t* pro
                                                          const bool* boolValue,
                                                          celix_version_t* 
versionValue) {
     if (!properties) {
+        celix_version_destroy(versionValue);
         return CELIX_SUCCESS; // silently ignore
     }
+
     if (!key) {
-        celix_err_push("Cannot set property with NULL key");
-        return CELIX_SUCCESS; // print error and ignore
+        celix_version_destroy(versionValue);
+        celix_err_pushf("Cannot set property with NULL key");
+        return CELIX_ILLEGAL_ARGUMENT;
     }
 
     celix_properties_entry_t* entry =
@@ -616,25 +623,37 @@ celix_status_t celix_properties_store(celix_properties_t* 
properties, const char
 
 celix_properties_t* celix_properties_copy(const celix_properties_t* 
properties) {
     celix_properties_t* copy = celix_properties_create();
-    if (properties == NULL) {
+
+    if (!copy) {
+        celix_err_push("Failed to create properties copy");
+        return NULL;
+    }
+
+    if (!properties) {
         return copy;
     }
 
     CELIX_PROPERTIES_ITERATE(properties, iter) {
+        celix_status_t status;
         if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) {
-            celix_properties_set(copy, iter.key, iter.entry.value);
+            status = celix_properties_set(copy, iter.key, iter.entry.value);
         } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) {
-            celix_properties_setLong(copy, iter.key, 
iter.entry.typed.longValue);
+            status = celix_properties_setLong(copy, iter.key, 
iter.entry.typed.longValue);
         } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) 
{
-            celix_properties_setDouble(copy, iter.key, 
iter.entry.typed.doubleValue);
+            status = celix_properties_setDouble(copy, iter.key, 
iter.entry.typed.doubleValue);
         } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) {
-            celix_properties_setBool(copy, iter.key, 
iter.entry.typed.boolValue);
+            status = celix_properties_setBool(copy, iter.key, 
iter.entry.typed.boolValue);
         } else /*version*/ {
             assert(iter.entry.valueType == 
CELIX_PROPERTIES_VALUE_TYPE_VERSION);
-            celix_properties_setVersion(copy, iter.key, 
iter.entry.typed.versionValue);
+            status = celix_properties_setVersion(copy, iter.key, 
iter.entry.typed.versionValue);
+        }
+        if (status != CELIX_SUCCESS) {
+            celix_err_pushf("Failed to copy property %s", iter.key);
+            celix_properties_destroy(copy);
+            return NULL;
         }
     }
-    return copy;
+    return celix_steal_ptr(copy);
 }
 
 celix_properties_value_type_e celix_properties_getType(const 
celix_properties_t* properties, const char* key) {
@@ -663,7 +682,13 @@ celix_status_t celix_properties_set(celix_properties_t* 
properties, const char*
 }
 
 celix_status_t celix_properties_setWithoutCopy(celix_properties_t* properties, 
char* key, char* value) {
-    if (properties != NULL && key != NULL && value != NULL) {
+    if (properties) {
+        if (!key || !value) {
+            celix_err_push("Failed to set (without copy) property. Key or 
value is NULL.");
+            free(key);
+            free(value);
+            return CELIX_ILLEGAL_ARGUMENT;
+        }
         celix_properties_entry_t* entry = 
celix_properties_createEntryWithNoCopy(properties, value);
         if (!entry) {
             celix_err_push("Failed to create entry for property.");

Reply via email to