diff --git a/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc index 309d54d06..f808d3af8 100644 --- a/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc @@ -20,7 +20,6 @@ #include #include "celix_array_list.h" -#include "celix_array_list_ei.h" #include "celix_err.h" #include "celix_utils_ei.h" #include "celix_version_ei.h" @@ -34,10 +33,28 @@ class ArrayListErrorInjectionTestSuite : public ::testing::Test { celix_ei_expect_calloc(nullptr, 0, nullptr); celix_ei_expect_celix_utils_strdup(nullptr, 0, nullptr); celix_ei_expect_celix_version_copy(nullptr, 0, nullptr); + celix_err_resetErrors(); } }; -TEST_F(ArrayListErrorInjectionTestSuite, TestAddFunctions) { +TEST_F(ArrayListErrorInjectionTestSuite, CreateTest) { + //Given an error is injected for calloc (used for the array struct) + celix_ei_expect_calloc(CELIX_EI_UNKNOWN_CALLER, 1, nullptr); + //Then creating an array list should fail + EXPECT_EQ(nullptr, celix_arrayList_create()); + //And an error is logged to the celix_err + EXPECT_EQ(1, celix_err_getErrorCount()); + + //Given an error is injected for malloc (used for the element data) + celix_ei_expect_malloc(CELIX_EI_UNKNOWN_CALLER, 0, nullptr); + //Then creating an array list should fail + EXPECT_EQ(nullptr, celix_arrayList_create()); + //And an error is logged to the celix_err + EXPECT_EQ(2, celix_err_getErrorCount()); +} + + +TEST_F(ArrayListErrorInjectionTestSuite, AddFunctionsTest) { //Given an array list with a capacity of 10 (whitebox knowledge) auto* list = celix_arrayList_create(); @@ -53,6 +70,8 @@ TEST_F(ArrayListErrorInjectionTestSuite, TestAddFunctions) { //Then adding an element should fail EXPECT_EQ(CELIX_ENOMEM, celix_arrayList_addInt(list, 10)); EXPECT_EQ(10, celix_arrayList_size(list)); + //And an error is logged to the celix_err + EXPECT_EQ(1, celix_err_getErrorCount()); celix_arrayList_destroy(list); } @@ -70,7 +89,8 @@ TEST_F(ArrayListErrorInjectionTestSuite, AddStringAndAddVersionFailureTest) { // When an error is injected for celix_version_copy celix_ei_expect_celix_version_copy((void*)celix_arrayList_addVersion, 0, nullptr); // Then adding a version should fail - EXPECT_EQ(CELIX_ENOMEM, celix_arrayList_addVersion(versionList, NULL)); + celix_autoptr(celix_version_t) version = celix_version_createVersionFromString("1.0.0"); + EXPECT_EQ(CELIX_ENOMEM, celix_arrayList_addVersion(versionList, version)); } TEST_F(ArrayListErrorInjectionTestSuite, CopyArrayListFailureTest) { diff --git a/libs/utils/gtest/src/ArrayListTestSuite.cc b/libs/utils/gtest/src/ArrayListTestSuite.cc index 0bc61c26f..e820f714e 100644 --- a/libs/utils/gtest/src/ArrayListTestSuite.cc +++ b/libs/utils/gtest/src/ArrayListTestSuite.cc @@ -528,3 +528,67 @@ TEST_F(ArrayListTestSuite, AutoCleanupTest) { celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); EXPECT_NE(nullptr, list); } + +TEST_F(ArrayListTestSuite, AddNullTest) { + // Given an undefined type, string, string ref and version list + celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_autoptr(celix_array_list_t) stringList = celix_arrayList_createStringArray(); + celix_autoptr(celix_array_list_t) stringRefList = celix_arrayList_createStringRefArray(); + celix_autoptr(celix_array_list_t) versionList = celix_arrayList_createVersionArray(); + + // When adding a null value to the lists + celix_arrayList_add(list, nullptr); + celix_arrayList_addString(stringList, nullptr); + celix_arrayList_addString(stringRefList, nullptr); + celix_arrayList_addVersion(versionList, nullptr); + + // Then the lists contain the null value + EXPECT_EQ(1, celix_arrayList_size(list)); + EXPECT_EQ(nullptr, celix_arrayList_get(list, 0)); + EXPECT_EQ(1, celix_arrayList_size(stringList)); + EXPECT_EQ(nullptr, celix_arrayList_getString(stringList, 0)); + EXPECT_EQ(1, celix_arrayList_size(stringRefList)); + EXPECT_EQ(nullptr, celix_arrayList_getString(stringRefList, 0)); + EXPECT_EQ(1, celix_arrayList_size(versionList)); + EXPECT_EQ(nullptr, celix_arrayList_getVersion(versionList, 0)); + + // When copying the lists + celix_autoptr(celix_array_list_t) listCopy = celix_arrayList_copy(list); + celix_autoptr(celix_array_list_t) stringListCopy = celix_arrayList_copy(stringList); + celix_autoptr(celix_array_list_t) stringRefListCopy = celix_arrayList_copy(stringRefList); + celix_autoptr(celix_array_list_t) versionListCopy = celix_arrayList_copy(versionList); + + // Then the copied lists contain the null value + EXPECT_EQ(1, celix_arrayList_size(listCopy)); + EXPECT_EQ(nullptr, celix_arrayList_get(listCopy, 0)); + EXPECT_EQ(1, celix_arrayList_size(stringListCopy)); + EXPECT_EQ(nullptr, celix_arrayList_getString(stringListCopy, 0)); + EXPECT_EQ(1, celix_arrayList_size(stringRefListCopy)); + EXPECT_EQ(nullptr, celix_arrayList_getString(stringRefListCopy, 0)); + EXPECT_EQ(1, celix_arrayList_size(versionListCopy)); + EXPECT_EQ(nullptr, celix_arrayList_getVersion(versionListCopy, 0)); +} + +TEST_F(ArrayListTestSuite, AssignNullTest) { + // Given a string and version list + celix_autoptr(celix_array_list_t) stringList = celix_arrayList_createStringArray(); + celix_autoptr(celix_array_list_t) versionList = celix_arrayList_createVersionArray(); + + // When assigning a null value to the lists + celix_arrayList_assignString(stringList, nullptr); + celix_arrayList_assignVersion(versionList, nullptr); + + // Then the lists contain the null value + EXPECT_EQ(1, celix_arrayList_size(stringList)); + EXPECT_EQ(nullptr, celix_arrayList_getString(stringList, 0)); + EXPECT_EQ(1, celix_arrayList_size(versionList)); + EXPECT_EQ(nullptr, celix_arrayList_getVersion(versionList, 0)); + + // When copying the lists + celix_autoptr(celix_array_list_t) stringListCopy = celix_arrayList_copy(stringList); + celix_autoptr(celix_array_list_t) versionListCopy = celix_arrayList_copy(versionList); + + // Then the copied lists contain the null value + EXPECT_EQ(1, celix_arrayList_size(stringListCopy)); + EXPECT_EQ(nullptr, celix_arrayList_getString(stringListCopy, 0)); +} diff --git a/libs/utils/include/celix_array_list.h b/libs/utils/include/celix_array_list.h index 716aa1946..a35d87a69 100644 --- a/libs/utils/include/celix_array_list.h +++ b/libs/utils/include/celix_array_list.h @@ -177,16 +177,20 @@ typedef struct celix_array_list_create_options { * * The remove, equals and compare callback will be NULL. * - * @deprecated Use celix_arrayList_createWithOptions instead. + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_DEPRECATED_EXPORT -celix_array_list_t* celix_arrayList_create() __attribute__((deprecated("use create typed array list instead"))); +celix_array_list_t* celix_arrayList_create(); /** * @brief Creates a new empty array list with a pointer element type where the array list is not the owner of the * pointers. * * The remove, equals and compare callback will be NULL. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createPointerArray(); @@ -196,6 +200,9 @@ celix_array_list_t* celix_arrayList_createPointerArray(); * * The remove callback will be configured to free the string, and equals and compare callback will be configured for * string comparison. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createStringArray(); @@ -206,6 +213,9 @@ celix_array_list_t* celix_arrayList_createStringArray(); * * The remove callback will be configured to NULL, and equals and compare callback will be configured for * string comparison. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createStringRefArray(); @@ -215,6 +225,9 @@ celix_array_list_t* celix_arrayList_createStringRefArray(); * * The remove callback will be configured to NULL, and equals and compare callback will be configured for * integer comparison. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createIntArray(); @@ -224,6 +237,9 @@ celix_array_list_t* celix_arrayList_createIntArray(); * * The remove callback will be configured to NULL, and equals and compare callback will be configured for * integer comparison. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createLongArray(); @@ -233,6 +249,9 @@ celix_array_list_t* celix_arrayList_createLongArray(); * * The remove callback will be configured to NULL, and equals and compare callback will be configured for * unsigned integer comparison. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createUIntArray(); @@ -242,6 +261,9 @@ celix_array_list_t* celix_arrayList_createUIntArray(); * * The remove callback will be configured to NULL, and equals and compare callback will be configured for * unsigned integer comparison. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createULongArray(); @@ -251,6 +273,9 @@ celix_array_list_t* celix_arrayList_createULongArray(); * * The remove callback will be configured to NULL, and equals and compare callback will be configured for * float comparison. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createFloatArray(); @@ -260,6 +285,9 @@ celix_array_list_t* celix_arrayList_createFloatArray(); * * The remove callback will be configured to NULL, and equals and compare callback will be configured for * double comparison. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createDoubleArray(); @@ -269,6 +297,9 @@ celix_array_list_t* celix_arrayList_createDoubleArray(); * * The remove callback will be configured to NULL, and equals and compare callback will be configured for * boolean comparison. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createBoolArray(); @@ -278,6 +309,9 @@ celix_array_list_t* celix_arrayList_createBoolArray(); * * The remove callback will be configured to NULL, and equals and compare callback will be configured for * unsigned integer comparison. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createSizeArray(); @@ -287,6 +321,9 @@ celix_array_list_t* celix_arrayList_createSizeArray(); * * The remove callback will be configured to free a celix version, and equals and compare callback will be configured * for celix version comparison. + * + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createVersionArray(); @@ -299,6 +336,8 @@ celix_array_list_t* celix_arrayList_createVersionArray(); * The provided callbacks in the options will override the default callbacks. * * @param opts The create options, only used during the creation of the array list. + * @return A new empty array list or NULL if the array list could not be created. If NULL is returned an error message + * is logged to celix_err. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createWithOptions(const celix_array_list_create_options_t* opts); diff --git a/libs/utils/src/array_list.c b/libs/utils/src/array_list.c index d35f40dfc..094a313cf 100644 --- a/libs/utils/src/array_list.c +++ b/libs/utils/src/array_list.c @@ -158,23 +158,20 @@ static void celix_arrayList_callRemovedCallback(celix_array_list_t *list, int in } static celix_status_t celix_arrayList_ensureCapacity(celix_array_list_t* list, int capacity) { - celix_status_t status = CELIX_SUCCESS; celix_array_list_entry_t *newList; list->modCount++; size_t oldCapacity = list->capacity; if (capacity > oldCapacity) { size_t newCapacity = (oldCapacity * 3) / 2 + 1; - if (newCapacity < capacity) { - newCapacity = capacity; - } newList = realloc(list->elementData, sizeof(celix_array_list_entry_t) * newCapacity); - if (newList != NULL) { - list->capacity = newCapacity; - list->elementData = newList; + if (!newList) { + celix_err_push("Failed to reallocate memory for elementData"); + return CELIX_ENOMEM; } - status = newList == NULL ? CELIX_ENOMEM : CELIX_SUCCESS; + list->capacity = newCapacity; + list->elementData = newList; } - return status; + return CELIX_SUCCESS; } static void celix_arrayList_setTypeSpecificCallbacks(celix_array_list_t* list) { @@ -238,31 +235,34 @@ static void celix_arrayList_setTypeSpecificCallbacks(celix_array_list_t* list) { celix_array_list_t* celix_arrayList_createWithOptions(const celix_array_list_create_options_t* opts) { celix_autofree celix_array_list_t *list = calloc(1, sizeof(*list)); - if (list) { - list->capacity = 10; - list->elementData = malloc(sizeof(celix_array_list_entry_t) * list->capacity); - if (!list->elementData) { - celix_err_push("Failed to allocate memory for elementData"); - return NULL; - } + if (!list) { + celix_err_push("Failed to allocate memory for list"); + return NULL; + } - list->elementType = opts->elementType; - celix_arrayList_setTypeSpecificCallbacks(list); + list->capacity = 10; + list->elementData = malloc(sizeof(celix_array_list_entry_t) * list->capacity); + if (!list->elementData) { + celix_err_push("Failed to allocate memory for elementData"); + return NULL; + } - //if opts contains callbacks, use them and override the default ones - if (opts->simpleRemovedCallback) { - list->simpleRemovedCallback = opts->simpleRemovedCallback; - } - if (opts->removedCallback) { - list->removedCallback = opts->removedCallback; - list->removedCallbackData = opts->removedCallbackData; - } - if (opts->equalsCallback) { - list->equalsCallback = opts->equalsCallback; - } - if (opts->compareCallback) { - list->compareCallback = opts->compareCallback; - } + list->elementType = opts->elementType; + celix_arrayList_setTypeSpecificCallbacks(list); + + //if opts contains callbacks, use them and override the default ones + if (opts->simpleRemovedCallback) { + list->simpleRemovedCallback = opts->simpleRemovedCallback; + } + if (opts->removedCallback) { + list->removedCallback = opts->removedCallback; + list->removedCallbackData = opts->removedCallbackData; + } + if (opts->equalsCallback) { + list->equalsCallback = opts->equalsCallback; + } + if (opts->compareCallback) { + list->compareCallback = opts->compareCallback; } return celix_steal_ptr(list); } @@ -439,7 +439,7 @@ celix_status_t celix_arrayList_addString(celix_array_list_t* list, const char* v list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED); celix_array_list_entry_t entry; memset(&entry, 0, sizeof(entry)); - if (list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING) { + if (list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING && val) { entry.stringVal = celix_utils_strdup(val); if (entry.stringVal == NULL) { return CELIX_ENOMEM; @@ -535,7 +535,7 @@ celix_status_t celix_arrayList_addVersion(celix_array_list_t* list, const celix_ list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED); celix_array_list_entry_t entry; memset(&entry, 0, sizeof(entry)); - if (list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION) { + if (list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION && value) { entry.versionVal = celix_version_copy(value); if (entry.versionVal == NULL) { return CELIX_ENOMEM; @@ -739,7 +739,6 @@ celix_array_list_t* celix_arrayList_copy(const celix_array_list_t* list) { opts.simpleRemovedCallback = list->simpleRemovedCallback; celix_autoptr(celix_array_list_t) copy = celix_arrayList_createWithOptions(&opts); if (!copy) { - celix_err_push("Failed to create copy list. Out of memory."); return NULL; }