diff --git a/CHANGELOG.md b/CHANGELOG.md
index 00d1ce31..0f643588 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,7 +13,7 @@ The git repositories are hosted at the following sites:
*
*
-## [Unreleased] - 2026-03-25
+## [Unreleased] - 2026-03-27
### Security
@@ -53,6 +53,8 @@ The git repositories are hosted at the following sites:
### Added
+* Added structured-view object subordinate-list add, remove, exist, same
+ API for interfacing as a list. Added purge API for unit testing. (#1283)
* Added ports/pico for Raspberry Pi Pico port of the BACnet stack
supporting both BACnet/IP and BACnet MS/TP. (#1232)
* Added new functions for duplicating and copying octet string buffers.
diff --git a/src/bacnet/basic/object/structured_view.c b/src/bacnet/basic/object/structured_view.c
index 9cee412b..b5ddef43 100644
--- a/src/bacnet/basic/object/structured_view.c
+++ b/src/bacnet/basic/object/structured_view.c
@@ -17,17 +17,9 @@
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/bacdcode.h"
-#include "bacnet/bacerror.h"
#include "bacnet/bacapp.h"
-#include "bacnet/bactext.h"
-#include "bacnet/apdu.h"
-#include "bacnet/npdu.h"
-#include "bacnet/abort.h"
#include "bacnet/proplist.h"
-#include "bacnet/property.h"
-#include "bacnet/reject.h"
#include "bacnet/rp.h"
-#include "bacnet/basic/services.h"
#include "bacnet/basic/sys/keylist.h"
/* me! */
#include "structured_view.h"
@@ -419,7 +411,7 @@ Structured_View_Subordinate_List_Size(struct object_data *pObject)
* @return pointer to the Subordinate_List element
*/
static BACNET_SUBORDINATE_DATA *
-Structured_View_Subordinate_List_Element_Add(OS_Keylist list, KEY key)
+Subordinate_List_Element_Add(OS_Keylist list, KEY key)
{
BACNET_SUBORDINATE_DATA *element = NULL;
int index;
@@ -441,8 +433,7 @@ Structured_View_Subordinate_List_Element_Add(OS_Keylist list, KEY key)
* @brief For a given object element, free the Subordinate_List element
* @param element - pointer to the Subordinate_List element
*/
-static void
-Structured_View_Subordinate_List_Element_Remove(OS_Keylist list, KEY key)
+static void Subordinate_List_Element_Remove(OS_Keylist list, KEY key)
{
BACNET_SUBORDINATE_DATA *element;
@@ -459,7 +450,7 @@ Structured_View_Subordinate_List_Element_Remove(OS_Keylist list, KEY key)
* @brief For a given object instance-number, free the Subordinate_List
* @param pObject - pointer to the object data
*/
-static void Structured_View_Subordinate_List_Free(struct object_data *pObject)
+static void Subordinate_List_Purge(struct object_data *pObject)
{
KEY key = 0;
int count = 0;
@@ -467,14 +458,161 @@ static void Structured_View_Subordinate_List_Free(struct object_data *pObject)
if (pObject) {
count = Keylist_Count(pObject->Subordinate_List);
while (count > 0) {
- Structured_View_Subordinate_List_Element_Remove(
- pObject->Subordinate_List, key);
+ Subordinate_List_Element_Remove(pObject->Subordinate_List, key);
key++;
count--;
}
}
}
+/**
+ * @brief For a given object instance-number, returns the number of
+ * Subordinate_List elements
+ * @param object_instance - object-instance number of the object
+ * @param array_index [in] array index requested:
+ * 0 to N for individual array members
+ * @return Subordinate_List element or NULL if not found
+ */
+static BACNET_SUBORDINATE_DATA *Subordinate_List_Element(
+ struct object_data *pObject, BACNET_ARRAY_INDEX array_index)
+{
+ BACNET_SUBORDINATE_DATA *subordinate_list = NULL;
+ KEY key = 0;
+
+ if (pObject) {
+ key = array_index;
+ subordinate_list = Keylist_Data(pObject->Subordinate_List, key);
+ }
+
+ return subordinate_list;
+}
+
+/**
+ * @brief For a given object instance-number, determine if an element exists
+ * in the Subordinate_List
+ * @param object_instance - object-instance number of the object
+ * @param element - pointer to the Subordinate_List element to be checked
+ * @return array index of the element if it exists,
+ * or BACNET_ARRAY_ALL if not found
+ */
+BACNET_ARRAY_INDEX Structured_View_Subordinate_List_Element_Exist(
+ uint32_t object_instance, BACNET_SUBORDINATE_DATA *element)
+{
+ struct object_data *pObject;
+ BACNET_SUBORDINATE_DATA *list_element;
+ unsigned count = 0;
+ BACNET_ARRAY_INDEX array_index = BACNET_ARRAY_ALL;
+
+ pObject = Keylist_Data(Object_List, object_instance);
+ if (pObject) {
+ if (element) {
+ count = Structured_View_Subordinate_List_Size(pObject);
+ for (array_index = 0; array_index < count; array_index++) {
+ list_element = Subordinate_List_Element(pObject, array_index);
+ if (Structured_View_Subordinate_List_Element_Same(
+ list_element, element)) {
+ break;
+ }
+ }
+ if (array_index >= count) {
+ array_index = BACNET_ARRAY_ALL;
+ }
+ }
+ }
+
+ return array_index;
+}
+
+/**
+ * @brief For a given object instance-number, add a unique element to the
+ * Subordinate_List
+ * @param object_instance - object-instance number of the object
+ * @param element - pointer to the Subordinate_List element to be added
+ * @return array index of the element if it was added successfully,
+ * or BACNET_ARRAY_ALL if not added
+ */
+BACNET_ARRAY_INDEX Structured_View_Subordinate_List_Element_Add(
+ uint32_t object_instance, BACNET_SUBORDINATE_DATA *element)
+{
+ struct object_data *pObject;
+ BACNET_ARRAY_INDEX array_index = BACNET_ARRAY_ALL;
+ KEY key = 0;
+ BACNET_SUBORDINATE_DATA *data = NULL;
+
+ pObject = Keylist_Data(Object_List, object_instance);
+ if (pObject) {
+ if (element) {
+ /* does this element already exist in the list? */
+ array_index = Structured_View_Subordinate_List_Element_Exist(
+ object_instance, element);
+ if (array_index == BACNET_ARRAY_ALL) {
+ /* append a copy of the element to the list */
+ key = Keylist_Next_Empty_Key(pObject->Subordinate_List, 0);
+ data = Subordinate_List_Element_Add(
+ pObject->Subordinate_List, key);
+ if (!data) {
+ array_index = BACNET_ARRAY_ALL;
+ } else {
+ memmove(data, element, sizeof(BACNET_SUBORDINATE_DATA));
+ if (element->Annotation) {
+ data->Annotation = bacnet_strdup(element->Annotation);
+ }
+ data->next = NULL;
+ array_index = key;
+ }
+ }
+ }
+ }
+
+ return array_index;
+}
+
+/**
+ * @brief For a given object instance-number, remove an element from the
+ * Subordinate_List
+ * @param object_instance - object-instance number of the object
+ * @param element - pointer to the Subordinate_List element to be removed
+ * @return array index of the element if it was removed successfully,
+ * or BACNET_ARRAY_ALL if not removed
+ */
+BACNET_ARRAY_INDEX Structured_View_Subordinate_List_Element_Remove(
+ uint32_t object_instance, BACNET_SUBORDINATE_DATA *element)
+{
+ BACNET_ARRAY_INDEX array_index = BACNET_ARRAY_ALL;
+ struct object_data *pObject;
+
+ pObject = Keylist_Data(Object_List, object_instance);
+ if (pObject) {
+ array_index = Structured_View_Subordinate_List_Element_Exist(
+ object_instance, element);
+ if (array_index != BACNET_ARRAY_ALL) {
+ Subordinate_List_Element_Remove(
+ pObject->Subordinate_List, array_index);
+ }
+ }
+
+ return array_index;
+}
+
+/**
+ * @brief For a given object instance-number, free the Subordinate_List
+ * @param object_instance - object-instance number of the object
+ * @return true if the Subordinate_List was purged successfully
+ */
+bool Structured_View_Subordinate_List_Purge(uint32_t object_instance)
+{
+ bool status = false;
+ struct object_data *pObject;
+
+ pObject = Keylist_Data(Object_List, object_instance);
+ if (pObject) {
+ Subordinate_List_Purge(pObject);
+ status = true;
+ }
+
+ return status;
+}
+
/**
* @brief For a given object instance-number, resize the Subordinate_List
* @param pObject - pointer to the object data
@@ -496,16 +634,15 @@ static BACNET_ERROR_CODE Structured_View_Subordinate_List_Resize(
/* free the elements at the tail of the list */
key = new_array_size;
while (key < old_array_size) {
- Structured_View_Subordinate_List_Element_Remove(
- pObject->Subordinate_List, key);
+ Subordinate_List_Element_Remove(pObject->Subordinate_List, key);
key++;
}
} else if (new_array_size > old_array_size) {
/* extend the list */
key = old_array_size;
while (key < new_array_size) {
- element = Structured_View_Subordinate_List_Element_Add(
- pObject->Subordinate_List, key);
+ element =
+ Subordinate_List_Element_Add(pObject->Subordinate_List, key);
if (!element) {
error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
break;
@@ -517,28 +654,6 @@ static BACNET_ERROR_CODE Structured_View_Subordinate_List_Resize(
return error_code;
}
-/**
- * @brief For a given object instance-number, returns the number of
- * Subordinate_List elements
- * @param object_instance - object-instance number of the object
- * @param array_index [in] array index requested:
- * 0 to N for individual array members
- * @return Subordinate_List element or NULL if not found
- */
-static BACNET_SUBORDINATE_DATA *Structured_View_Subordinate_List_Element(
- struct object_data *pObject, BACNET_ARRAY_INDEX array_index)
-{
- BACNET_SUBORDINATE_DATA *subordinate_list = NULL;
- KEY key = 0;
-
- if (pObject) {
- key = array_index;
- subordinate_list = Keylist_Data(pObject->Subordinate_List, key);
- }
-
- return subordinate_list;
-}
-
/**
* @brief Decode a BACnetARRAY property element to determine the length
* @param object_instance [in] BACnet network port object instance number
@@ -595,8 +710,7 @@ static BACNET_ERROR_CODE Structured_View_Subordinate_List_Member_Write(
len = bacnet_device_object_reference_decode(
apdu, apdu_size, &reference);
if (len > 0) {
- element = Structured_View_Subordinate_List_Element(
- pObject, array_index);
+ element = Subordinate_List_Element(pObject, array_index);
if (element) {
element->Device_Instance =
reference.deviceIdentifier.instance;
@@ -674,8 +788,7 @@ static BACNET_ERROR_CODE Structured_View_Subordinate_Annotation_Member_Write(
apdu, apdu_size, &annotation);
if (len > 0) {
if (characterstring_utf8_valid(&annotation)) {
- element = Structured_View_Subordinate_List_Element(
- pObject, array_index);
+ element = Subordinate_List_Element(pObject, array_index);
if (element) {
annotation_string =
characterstring_utf8_strdup(&annotation);
@@ -761,8 +874,7 @@ static BACNET_ERROR_CODE Structured_View_Subordinate_Node_Type_Member_Write(
if (node_type > BACNET_NODE_TYPE_MAX) {
error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
} else {
- element = Structured_View_Subordinate_List_Element(
- pObject, array_index);
+ element = Subordinate_List_Element(pObject, array_index);
if (element) {
element->Node_Type = node_type;
error_code = ERROR_CODE_SUCCESS;
@@ -837,8 +949,7 @@ static BACNET_ERROR_CODE Structured_View_Subordinate_Relationship_Member_Write(
if (relationship > BACNET_RELATIONSHIP_PROPRIETARY_MAX) {
error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
} else {
- element = Structured_View_Subordinate_List_Element(
- pObject, array_index);
+ element = Subordinate_List_Element(pObject, array_index);
if (element) {
element->Relationship = relationship;
error_code = ERROR_CODE_SUCCESS;
@@ -855,6 +966,48 @@ static BACNET_ERROR_CODE Structured_View_Subordinate_Relationship_Member_Write(
return error_code;
}
+/**
+ * @brief For a given object instance-number, returns the Subordinate_List
+ * as a linked list with next pointers properly set
+ * @param object_instance - object-instance number of the object
+ * @return pointer to the first element of the Subordinate_List, or NULL
+ */
+BACNET_SUBORDINATE_DATA *
+Structured_View_Subordinate_List(uint32_t object_instance)
+{
+ struct object_data *pObject;
+ BACNET_SUBORDINATE_DATA *first_element = NULL;
+ BACNET_SUBORDINATE_DATA *element;
+ BACNET_SUBORDINATE_DATA *prev_element = NULL;
+ unsigned int count = 0;
+ unsigned int i = 0;
+
+ pObject = Keylist_Data(Object_List, object_instance);
+ if (pObject) {
+ count = Structured_View_Subordinate_List_Size(pObject);
+ if (count > 0) {
+ /* Iterate through all elements and set up next pointers */
+ for (i = 0; i < count; i++) {
+ element = Subordinate_List_Element(pObject, i);
+ if (element) {
+ if (i == 0) {
+ /* First element */
+ first_element = element;
+ } else if (prev_element) {
+ /* Link previous element to current element */
+ prev_element->next = element;
+ }
+ /* Clear the next pointer for the current element */
+ element->next = NULL;
+ prev_element = element;
+ }
+ }
+ }
+ }
+
+ return first_element;
+}
+
/**
* @brief For a given object instance-number, sets the Subordinate_List
* @param object_instance - object-instance number of the object
@@ -869,13 +1022,12 @@ void Structured_View_Subordinate_List_Set(
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
- Structured_View_Subordinate_List_Free(pObject);
+ Subordinate_List_Purge(pObject);
/* walk the linked list and add to Keylist */
- key = 0;
element = subordinate_list;
while (element) {
- data = Structured_View_Subordinate_List_Element_Add(
- pObject->Subordinate_List, key);
+ key = Keylist_Next_Empty_Key(pObject->Subordinate_List, 0);
+ data = Subordinate_List_Element_Add(pObject->Subordinate_List, key);
if (data) {
memmove(data, element, sizeof(BACNET_SUBORDINATE_DATA));
if (element->Annotation) {
@@ -884,11 +1036,45 @@ void Structured_View_Subordinate_List_Set(
data->next = NULL;
}
element = element->next;
- key++;
}
}
}
+/**
+ * @brief For a given object instance-number, determine if two
+ * BACNET_SUBORDINATE_DATA elements are the same
+ * @param element1 - pointer to the first Subordinate_List element
+ * @param element2 - pointer to the second Subordinate_List element
+ * @return true if the contents of the elements are the same, false otherwise
+ */
+bool Structured_View_Subordinate_List_Element_Same(
+ BACNET_SUBORDINATE_DATA *element1, BACNET_SUBORDINATE_DATA *element2)
+{
+ if (element1 == element2) {
+ return true;
+ }
+ if (!element1 || !element2) {
+ return false;
+ }
+ if (element1->Device_Instance != element2->Device_Instance ||
+ element1->Object_Type != element2->Object_Type ||
+ element1->Object_Instance != element2->Object_Instance ||
+ element1->Node_Type != element2->Node_Type ||
+ element1->Relationship != element2->Relationship) {
+ return false;
+ }
+ if (element1->Annotation && element2->Annotation) {
+ if (strcmp(element1->Annotation, element2->Annotation) != 0) {
+ return false;
+ }
+ } else if (element1->Annotation || element2->Annotation) {
+ /* one is NULL and the other is not */
+ return false;
+ }
+
+ return true;
+}
+
/**
* @brief Convert an array of BACnetSubordinateData to linked list
* @param array pointer to element zero of the array
@@ -1028,8 +1214,7 @@ BACNET_SUBORDINATE_DATA *Structured_View_Subordinate_List_Member(
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
- element =
- Structured_View_Subordinate_List_Element(pObject, array_index);
+ element = Subordinate_List_Element(pObject, array_index);
}
return element;
@@ -1628,7 +1813,7 @@ static void Structured_View_Object_Free(struct object_data *pObject)
free(pObject->Description);
free(pObject->Node_Subtype);
free(pObject->Object_Name);
- Structured_View_Subordinate_List_Free(pObject);
+ Subordinate_List_Purge(pObject);
Keylist_Delete(pObject->Subordinate_List);
free(pObject);
}
diff --git a/src/bacnet/basic/object/structured_view.h b/src/bacnet/basic/object/structured_view.h
index 23b6d89e..0e8e71d0 100644
--- a/src/bacnet/basic/object/structured_view.h
+++ b/src/bacnet/basic/object/structured_view.h
@@ -98,10 +98,24 @@ BACNET_STACK_EXPORT
void Structured_View_Subordinate_List_Link_Array(
BACNET_SUBORDINATE_DATA *array, size_t size);
BACNET_STACK_EXPORT
+bool Structured_View_Subordinate_List_Element_Same(
+ BACNET_SUBORDINATE_DATA *element1, BACNET_SUBORDINATE_DATA *element2);
+BACNET_STACK_EXPORT
BACNET_SUBORDINATE_DATA *Structured_View_Subordinate_List_Member(
uint32_t object_instance, BACNET_ARRAY_INDEX array_index);
BACNET_STACK_EXPORT
unsigned int Structured_View_Subordinate_List_Count(uint32_t object_instance);
+BACNET_STACK_EXPORT
+BACNET_ARRAY_INDEX Structured_View_Subordinate_List_Element_Exist(
+ uint32_t object_instance, BACNET_SUBORDINATE_DATA *element);
+BACNET_STACK_EXPORT
+BACNET_ARRAY_INDEX Structured_View_Subordinate_List_Element_Add(
+ uint32_t object_instance, BACNET_SUBORDINATE_DATA *element);
+BACNET_STACK_EXPORT
+BACNET_ARRAY_INDEX Structured_View_Subordinate_List_Element_Remove(
+ uint32_t object_instance, BACNET_SUBORDINATE_DATA *element);
+BACNET_STACK_EXPORT
+bool Structured_View_Subordinate_List_Purge(uint32_t object_instance);
BACNET_STACK_EXPORT
BACNET_RELATIONSHIP
diff --git a/test/bacnet/basic/object/structured_view/src/main.c b/test/bacnet/basic/object/structured_view/src/main.c
index e114b12d..ec32ecac 100644
--- a/test/bacnet/basic/object/structured_view/src/main.c
+++ b/test/bacnet/basic/object/structured_view/src/main.c
@@ -31,6 +31,220 @@ static void Structured_View_Subordinate_List_Member_Same(
strcmp(list_member_a->Annotation, list_member_b->Annotation), 0, NULL);
}
+/**
+ * @brief Test Structured_View_Subordinate_List_Element_Same
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_subordinate_list_element_same)
+#else
+static void test_subordinate_list_element_same(void)
+#endif
+{
+ bool status = false;
+ BACNET_SUBORDINATE_DATA element_a = { 0,
+ OBJECT_ANALOG_INPUT,
+ 1,
+ "annotation-a",
+ BACNET_NODE_MEMBER,
+ BACNET_RELATIONSHIP_CONTAINS,
+ NULL };
+ BACNET_SUBORDINATE_DATA element_b = { 0,
+ OBJECT_ANALOG_INPUT,
+ 1,
+ "annotation-a",
+ BACNET_NODE_MEMBER,
+ BACNET_RELATIONSHIP_CONTAINS,
+ NULL };
+ BACNET_SUBORDINATE_DATA element_c = { 1,
+ OBJECT_BINARY_INPUT,
+ 2,
+ "annotation-c",
+ BACNET_NODE_COLLECTION,
+ BACNET_RELATIONSHIP_DEFAULT,
+ NULL };
+
+ /* identical elements */
+ status =
+ Structured_View_Subordinate_List_Element_Same(&element_a, &element_b);
+ zassert_true(status, NULL);
+
+ /* different elements */
+ status =
+ Structured_View_Subordinate_List_Element_Same(&element_a, &element_c);
+ zassert_false(status, NULL);
+
+ /* NULL element1 */
+ status = Structured_View_Subordinate_List_Element_Same(NULL, &element_b);
+ zassert_false(status, NULL);
+
+ /* NULL element2 */
+ status = Structured_View_Subordinate_List_Element_Same(&element_a, NULL);
+ zassert_false(status, NULL);
+
+ /* both NULL - returns true (both match "nothing") */
+ status = Structured_View_Subordinate_List_Element_Same(NULL, NULL);
+ zassert_true(status, NULL);
+
+ /* NULL vs non-NULL annotation */
+ element_b.Annotation = NULL;
+ status =
+ Structured_View_Subordinate_List_Element_Same(&element_a, &element_b);
+ zassert_false(status, NULL);
+}
+
+/**
+ * @brief Test Structured_View_Subordinate_List_Element_Exist,
+ * Structured_View_Subordinate_List_Element_Add, and
+ * Structured_View_Subordinate_List_Element_Remove
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(
+ tests_object_structured_view,
+ test_subordinate_list_element_add_remove_exist)
+#else
+static void test_subordinate_list_element_add_remove_exist(void)
+#endif
+{
+ const uint32_t instance = 456;
+ BACNET_ARRAY_INDEX array_index = 0;
+ BACNET_SUBORDINATE_DATA element_a = {
+ 0, OBJECT_ACCUMULATOR, 10,
+ "watts", BACNET_NODE_MEMBER, BACNET_RELATIONSHIP_CONTAINS,
+ NULL
+ };
+ BACNET_SUBORDINATE_DATA element_b = {
+ 0, OBJECT_LOAD_CONTROL, 20,
+ "demand", BACNET_NODE_MEMBER, BACNET_RELATIONSHIP_CONTAINS,
+ NULL
+ };
+ BACNET_SUBORDINATE_DATA element_not_added = {
+ 99, OBJECT_BINARY_INPUT, 99,
+ "not-added", BACNET_NODE_UNKNOWN, BACNET_RELATIONSHIP_DEFAULT,
+ NULL
+ };
+
+ Structured_View_Init();
+ Structured_View_Create(instance);
+
+ /* --- Structured_View_Subordinate_List_Element_Add --- */
+
+ /* add first element - should succeed and return index 0 */
+ array_index =
+ Structured_View_Subordinate_List_Element_Add(instance, &element_a);
+ zassert_not_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ /* add second element - should succeed */
+ array_index =
+ Structured_View_Subordinate_List_Element_Add(instance, &element_b);
+ zassert_not_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ /* adding a duplicate should return the existing index, not BACNET_ARRAY_ALL
+ */
+ array_index =
+ Structured_View_Subordinate_List_Element_Add(instance, &element_a);
+ zassert_equal(array_index, 0, NULL);
+
+ /* count should be 2 after adding two unique elements */
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 2, NULL);
+
+ /* --- Structured_View_Subordinate_List_Element_Exist --- */
+
+ /* element_a should be found at index 0 */
+ array_index =
+ Structured_View_Subordinate_List_Element_Exist(instance, &element_a);
+ zassert_equal(array_index, 0, NULL);
+
+ /* element_b should be found at index 1 */
+ array_index =
+ Structured_View_Subordinate_List_Element_Exist(instance, &element_b);
+ zassert_equal(array_index, 1, NULL);
+
+ /* element not in the list should return BACNET_ARRAY_ALL */
+ array_index = Structured_View_Subordinate_List_Element_Exist(
+ instance, &element_not_added);
+ zassert_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ /* invalid instance should return BACNET_ARRAY_ALL */
+ array_index = Structured_View_Subordinate_List_Element_Exist(
+ instance + 1000, &element_a);
+ zassert_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ /* --- Structured_View_Subordinate_List_Element_Remove --- */
+
+ /* remove element_a */
+ array_index =
+ Structured_View_Subordinate_List_Element_Remove(instance, &element_a);
+ zassert_not_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ /* element_a should no longer be found */
+ array_index =
+ Structured_View_Subordinate_List_Element_Exist(instance, &element_a);
+ zassert_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ /* count should now be 1 */
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 1, NULL);
+
+ /* removing an element that doesn't exist should return BACNET_ARRAY_ALL */
+ array_index = Structured_View_Subordinate_List_Element_Remove(
+ instance, &element_not_added);
+ zassert_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ Structured_View_Delete(instance);
+ Structured_View_Cleanup();
+}
+
+/**
+ * @brief Test Structured_View_Subordinate_List_Purge
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_subordinate_list_purge)
+#else
+static void test_subordinate_list_purge(void)
+#endif
+{
+ const uint32_t instance = 789;
+ bool status = false;
+ BACNET_SUBORDINATE_DATA test_subordinate_data[] = {
+ { 0, OBJECT_ACCUMULATOR, 1, "watts", BACNET_NODE_MEMBER,
+ BACNET_RELATIONSHIP_CONTAINS, NULL },
+ { 0, OBJECT_LOAD_CONTROL, 2, "demand", BACNET_NODE_MEMBER,
+ BACNET_RELATIONSHIP_CONTAINS, NULL },
+ { 0, OBJECT_CHANNEL, 3, "scene", BACNET_NODE_COLLECTION,
+ BACNET_RELATIONSHIP_CONTAINS, NULL },
+ };
+
+ Structured_View_Init();
+ Structured_View_Create(instance);
+
+ /* populate the list */
+ Structured_View_Subordinate_List_Link_Array(
+ test_subordinate_data, ARRAY_SIZE(test_subordinate_data));
+ Structured_View_Subordinate_List_Set(instance, &test_subordinate_data[0]);
+ zassert_equal(
+ Structured_View_Subordinate_List_Count(instance),
+ ARRAY_SIZE(test_subordinate_data), NULL);
+
+ /* purge - all elements should be gone */
+ status = Structured_View_Subordinate_List_Purge(instance);
+ zassert_true(status, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 0, NULL);
+
+ /* purge on invalid instance returns false */
+ status = Structured_View_Subordinate_List_Purge(instance + 1000);
+ zassert_false(status, NULL);
+
+ /* re-populate after purge to confirm the list is reusable */
+ Structured_View_Subordinate_List_Link_Array(
+ test_subordinate_data, ARRAY_SIZE(test_subordinate_data));
+ Structured_View_Subordinate_List_Set(instance, &test_subordinate_data[0]);
+ zassert_equal(
+ Structured_View_Subordinate_List_Count(instance),
+ ARRAY_SIZE(test_subordinate_data), NULL);
+
+ Structured_View_Delete(instance);
+ Structured_View_Cleanup();
+}
+
/**
* @brief Test
*/
@@ -161,6 +375,641 @@ static void test_object_structured_view(void)
Structured_View_Cleanup();
}
+/**
+ * @brief Test Structured_View_Property_Lists
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_property_lists)
+#else
+static void test_property_lists(void)
+#endif
+{
+ const int32_t *required = NULL;
+ const int32_t *optional = NULL;
+ const int32_t *proprietary = NULL;
+
+ /* all three pointers populated */
+ Structured_View_Property_Lists(&required, &optional, &proprietary);
+ zassert_not_null(required, NULL);
+ zassert_not_null(optional, NULL);
+ zassert_not_null(proprietary, NULL);
+
+ /* required list must contain PROP_OBJECT_IDENTIFIER (-1 terminated) */
+ {
+ const int32_t *p = required;
+ bool found_oid = false;
+ bool found_name = false;
+ bool found_node_type = false;
+ bool found_sublist = false;
+ while (*p != -1) {
+ if (*p == PROP_OBJECT_IDENTIFIER) {
+ found_oid = true;
+ }
+ if (*p == PROP_OBJECT_NAME) {
+ found_name = true;
+ }
+ if (*p == PROP_NODE_TYPE) {
+ found_node_type = true;
+ }
+ if (*p == PROP_SUBORDINATE_LIST) {
+ found_sublist = true;
+ }
+ p++;
+ }
+ zassert_true(found_oid, NULL);
+ zassert_true(found_name, NULL);
+ zassert_true(found_node_type, NULL);
+ zassert_true(found_sublist, NULL);
+ }
+
+ /* NULL pointers - must not crash */
+ Structured_View_Property_Lists(NULL, NULL, NULL);
+ Structured_View_Property_Lists(&required, NULL, NULL);
+ Structured_View_Property_Lists(NULL, &optional, NULL);
+ Structured_View_Property_Lists(NULL, NULL, &proprietary);
+}
+
+/**
+ * @brief Test Structured_View_Subordinate_List (raw head-pointer accessor)
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_subordinate_list)
+#else
+static void test_subordinate_list(void)
+#endif
+{
+ const uint32_t instance = 321;
+ BACNET_SUBORDINATE_DATA *head = NULL;
+ BACNET_SUBORDINATE_DATA list[] = {
+ { 0, OBJECT_ANALOG_INPUT, 1, "ai-1", BACNET_NODE_MEMBER,
+ BACNET_RELATIONSHIP_CONTAINS, NULL },
+ { 0, OBJECT_ANALOG_OUTPUT, 2, "ao-2", BACNET_NODE_MEMBER,
+ BACNET_RELATIONSHIP_CONTAINS, NULL },
+ };
+
+ Structured_View_Init();
+ Structured_View_Create(instance);
+
+ /* before population the list is empty */
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 0, NULL);
+
+ Structured_View_Subordinate_List_Link_Array(list, ARRAY_SIZE(list));
+ Structured_View_Subordinate_List_Set(instance, &list[0]);
+
+ /* Structured_View_Subordinate_List returns the head of the internal list */
+ head = Structured_View_Subordinate_List(instance);
+ zassert_not_null(head, NULL);
+ zassert_equal(head->Object_Type, OBJECT_ANALOG_INPUT, NULL);
+ zassert_equal(head->Object_Instance, 1, NULL);
+
+ /* invalid instance returns NULL */
+ zassert_is_null(Structured_View_Subordinate_List(instance + 9999), NULL);
+
+ Structured_View_Delete(instance);
+ Structured_View_Cleanup();
+}
+
+/**
+ * @brief Test the four subordinate element encode functions
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_subordinate_encode_functions)
+#else
+static void test_subordinate_encode_functions(void)
+#endif
+{
+ const uint32_t instance = 654;
+ uint8_t apdu[256];
+ int len = 0;
+ BACNET_SUBORDINATE_DATA list[] = {
+ { 1234, OBJECT_ANALOG_INPUT, 7, "energy", BACNET_NODE_MEMBER,
+ BACNET_RELATIONSHIP_CONTAINS, NULL },
+ { 0, OBJECT_BINARY_INPUT, 3, "status", BACNET_NODE_COLLECTION,
+ BACNET_RELATIONSHIP_DEFAULT, NULL },
+ };
+
+ Structured_View_Init();
+ Structured_View_Create(instance);
+ Structured_View_Subordinate_List_Link_Array(list, ARRAY_SIZE(list));
+ Structured_View_Subordinate_List_Set(instance, &list[0]);
+
+ /* --- Structured_View_Subordinate_List_Element_Encode --- */
+
+ /* valid index 0 - should produce a positive length */
+ len = Structured_View_Subordinate_List_Element_Encode(instance, 0, apdu);
+ zassert_true(len > 0, NULL);
+
+ /* valid index 1 */
+ len = Structured_View_Subordinate_List_Element_Encode(instance, 1, apdu);
+ zassert_true(len > 0, NULL);
+
+ /* out-of-range index returns BACNET_STATUS_ERROR */
+ len = Structured_View_Subordinate_List_Element_Encode(instance, 99, apdu);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ /* NULL apdu (length-only query) for index 0 */
+ len = Structured_View_Subordinate_List_Element_Encode(instance, 0, NULL);
+ zassert_true(len > 0, NULL);
+
+ /* --- Structured_View_Subordinate_Annotations_Element_Encode --- */
+
+ len = Structured_View_Subordinate_Annotations_Element_Encode(
+ instance, 0, apdu);
+ zassert_true(len > 0, NULL);
+
+ len = Structured_View_Subordinate_Annotations_Element_Encode(
+ instance, 1, apdu);
+ zassert_true(len > 0, NULL);
+
+ /* out-of-range */
+ len = Structured_View_Subordinate_Annotations_Element_Encode(
+ instance, 99, apdu);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ /* --- Structured_View_Subordinate_Node_Types_Element_Encode --- */
+
+ len = Structured_View_Subordinate_Node_Types_Element_Encode(
+ instance, 0, apdu);
+ zassert_true(len > 0, NULL);
+
+ len = Structured_View_Subordinate_Node_Types_Element_Encode(
+ instance, 1, apdu);
+ zassert_true(len > 0, NULL);
+
+ /* out-of-range */
+ len = Structured_View_Subordinate_Node_Types_Element_Encode(
+ instance, 99, apdu);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ /* --- Structured_View_Subordinate_Relationships_Element_Encode --- */
+
+ len = Structured_View_Subordinate_Relationships_Element_Encode(
+ instance, 0, apdu);
+ zassert_true(len > 0, NULL);
+
+ len = Structured_View_Subordinate_Relationships_Element_Encode(
+ instance, 1, apdu);
+ zassert_true(len > 0, NULL);
+
+ /* out-of-range */
+ len = Structured_View_Subordinate_Relationships_Element_Encode(
+ instance, 99, apdu);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ Structured_View_Delete(instance);
+ Structured_View_Cleanup();
+}
+
+/**
+ * @brief Test encode functions with invalid instance
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_encode_functions_invalid_instance)
+#else
+static void test_encode_functions_invalid_instance(void)
+#endif
+{
+ const uint32_t invalid_instance = 9999;
+ uint8_t apdu[256];
+ int len = 0;
+
+ Structured_View_Init();
+
+ /* All encode functions should return BACNET_STATUS_ERROR for invalid
+ * instance */
+
+ /* --- Subordinate_List_Element_Encode --- */
+ len = Structured_View_Subordinate_List_Element_Encode(
+ invalid_instance, 0, apdu);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ len = Structured_View_Subordinate_List_Element_Encode(
+ invalid_instance, 0, NULL);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ /* --- Subordinate_Annotations_Element_Encode --- */
+ len = Structured_View_Subordinate_Annotations_Element_Encode(
+ invalid_instance, 0, apdu);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ len = Structured_View_Subordinate_Annotations_Element_Encode(
+ invalid_instance, 0, NULL);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ /* --- Subordinate_Node_Types_Element_Encode --- */
+ len = Structured_View_Subordinate_Node_Types_Element_Encode(
+ invalid_instance, 0, apdu);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ len = Structured_View_Subordinate_Node_Types_Element_Encode(
+ invalid_instance, 0, NULL);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ /* --- Subordinate_Relationships_Element_Encode --- */
+ len = Structured_View_Subordinate_Relationships_Element_Encode(
+ invalid_instance, 0, apdu);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ len = Structured_View_Subordinate_Relationships_Element_Encode(
+ invalid_instance, 0, NULL);
+ zassert_equal(len, BACNET_STATUS_ERROR, NULL);
+
+ Structured_View_Cleanup();
+}
+
+/**
+ * @brief Test NULL annotation handling in subordinate list elements
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_subordinate_null_annotations)
+#else
+static void test_subordinate_null_annotations(void)
+#endif
+{
+ const uint32_t instance = 555;
+ BACNET_ARRAY_INDEX array_index = 0;
+ BACNET_SUBORDINATE_DATA element_with_annotation = {
+ 0,
+ OBJECT_ACCUMULATOR,
+ 10,
+ "has-annotation",
+ BACNET_NODE_MEMBER,
+ BACNET_RELATIONSHIP_CONTAINS,
+ NULL
+ };
+ BACNET_SUBORDINATE_DATA element_null_annotation = {
+ 0, OBJECT_ACCUMULATOR, 10,
+ NULL, BACNET_NODE_MEMBER, BACNET_RELATIONSHIP_CONTAINS,
+ NULL
+ };
+ BACNET_SUBORDINATE_DATA *test_member = NULL;
+
+ Structured_View_Init();
+ Structured_View_Create(instance);
+
+ /* Add element with annotation */
+ array_index = Structured_View_Subordinate_List_Element_Add(
+ instance, &element_with_annotation);
+ zassert_not_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ /* Retrieve and verify annotation is preserved */
+ test_member = Structured_View_Subordinate_List_Member(instance, 0);
+ zassert_not_null(test_member, NULL);
+ zassert_not_null(test_member->Annotation, NULL);
+
+ /* Add element with NULL annotation */
+ array_index = Structured_View_Subordinate_List_Element_Add(
+ instance, &element_null_annotation);
+ /* Should succeed with different annotation */
+ zassert_not_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ /* Verify NULL annotation is preserved */
+ test_member = Structured_View_Subordinate_List_Member(instance, 1);
+ zassert_not_null(test_member, NULL);
+ zassert_is_null(test_member->Annotation, NULL);
+
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 2, NULL);
+
+ Structured_View_Delete(instance);
+ Structured_View_Cleanup();
+}
+
+/**
+ * @brief Test boundary conditions for subordinate list operations
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_subordinate_list_boundaries)
+#else
+static void test_subordinate_list_boundaries(void)
+#endif
+{
+ const uint32_t instance = 888;
+ BACNET_ARRAY_INDEX array_index = 0;
+ BACNET_SUBORDINATE_DATA element = {
+ 0, OBJECT_ACCUMULATOR, 1,
+ "test", BACNET_NODE_MEMBER, BACNET_RELATIONSHIP_CONTAINS,
+ NULL
+ };
+ BACNET_SUBORDINATE_DATA *test_member = NULL;
+
+ Structured_View_Init();
+ Structured_View_Create(instance);
+
+ /* Test accessing beyond list boundaries */
+ test_member = Structured_View_Subordinate_List_Member(instance, 0);
+ zassert_is_null(test_member, NULL); /* Empty list */
+
+ test_member = Structured_View_Subordinate_List_Member(instance, 100);
+ zassert_is_null(test_member, NULL); /* Out of bounds */
+
+ /* Add one element */
+ array_index =
+ Structured_View_Subordinate_List_Element_Add(instance, &element);
+ zassert_not_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ /* Access valid index */
+ test_member = Structured_View_Subordinate_List_Member(instance, 0);
+ zassert_not_null(test_member, NULL);
+
+ /* Access index just beyond valid range */
+ test_member = Structured_View_Subordinate_List_Member(instance, 1);
+ zassert_is_null(test_member, NULL);
+
+ /* Test count after operations */
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 1, NULL);
+
+ /* Remove element and verify count */
+ array_index =
+ Structured_View_Subordinate_List_Element_Remove(instance, &element);
+ zassert_not_equal(array_index, BACNET_STATUS_ERROR, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 0, NULL);
+
+ Structured_View_Delete(instance);
+ Structured_View_Cleanup();
+}
+
+/**
+ * @brief Test sequential add-remove-add cycles
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_sequential_add_remove_cycles)
+#else
+static void test_sequential_add_remove_cycles(void)
+#endif
+{
+ const uint32_t instance = 777;
+ BACNET_ARRAY_INDEX array_index = 0;
+ BACNET_SUBORDINATE_DATA element1 = {
+ 0, OBJECT_ACCUMULATOR, 1,
+ "element-1", BACNET_NODE_MEMBER, BACNET_RELATIONSHIP_CONTAINS,
+ NULL
+ };
+ BACNET_SUBORDINATE_DATA element2 = {
+ 0, OBJECT_LOAD_CONTROL, 2,
+ "element-2", BACNET_NODE_MEMBER, BACNET_RELATIONSHIP_CONTAINS,
+ NULL
+ };
+
+ Structured_View_Init();
+ Structured_View_Create(instance);
+
+ /* Add element (should succeed) */
+ array_index =
+ Structured_View_Subordinate_List_Element_Add(instance, &element1);
+ zassert_not_equal(array_index, BACNET_ARRAY_ALL, NULL);
+ unsigned count1 = Structured_View_Subordinate_List_Count(instance);
+ zassert_equal(count1, 1, NULL);
+
+ /* Remove element (should succeed) */
+ array_index =
+ Structured_View_Subordinate_List_Element_Remove(instance, &element1);
+ zassert_not_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ /* Add another element */
+ array_index =
+ Structured_View_Subordinate_List_Element_Add(instance, &element2);
+ zassert_not_equal(array_index, BACNET_ARRAY_ALL, NULL);
+ unsigned count2 = Structured_View_Subordinate_List_Count(instance);
+ zassert_equal(count2, 1, NULL);
+
+ Structured_View_Delete(instance);
+ Structured_View_Cleanup();
+}
+
+/**
+ * @brief Test public API functions handle invalid instances gracefully
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_invalid_instance_validation)
+#else
+static void test_invalid_instance_validation(void)
+#endif
+{
+ const uint32_t invalid_instance = 5000;
+ char test_string[] = "test";
+ BACNET_DEVICE_OBJECT_REFERENCE test_ref = { { OBJECT_DEVICE, 1 },
+ { OBJECT_DEVICE, 1 } };
+
+ Structured_View_Init();
+
+ /* Valid instance check should return false for invalid instance */
+ bool is_valid = Structured_View_Valid_Instance(invalid_instance);
+ zassert_false(is_valid, NULL);
+
+ /* Operations on invalid instance should handle gracefully (not crash) */
+ /* These calls should not crash or cause undefined behavior */
+ (void)Structured_View_Subordinate_List_Count(invalid_instance);
+ (void)Structured_View_Subordinate_List_Member(invalid_instance, 0);
+ (void)Structured_View_Name_ASCII(invalid_instance);
+ (void)Structured_View_Description(invalid_instance);
+ (void)Structured_View_Node_Subtype(invalid_instance);
+ (void)Structured_View_Node_Type(invalid_instance);
+ (void)Structured_View_Default_Subordinate_Relationship(invalid_instance);
+ (void)Structured_View_Represents(invalid_instance);
+ (void)Structured_View_Context_Get(invalid_instance);
+
+ /* Write operations should fail for invalid instance */
+ bool result = Structured_View_Name_Set(invalid_instance, test_string);
+ zassert_false(result, NULL);
+
+ result = Structured_View_Description_Set(invalid_instance, test_string);
+ zassert_false(result, NULL);
+
+ result = Structured_View_Node_Subtype_Set(invalid_instance, test_string);
+ zassert_false(result, NULL);
+
+ result =
+ Structured_View_Node_Type_Set(invalid_instance, BACNET_NODE_MEMBER);
+ zassert_false(result, NULL);
+
+ result = Structured_View_Default_Subordinate_Relationship_Set(
+ invalid_instance, BACNET_RELATIONSHIP_CONTAINS);
+ zassert_false(result, NULL);
+
+ result = Structured_View_Represents_Set(invalid_instance, &test_ref);
+ zassert_false(result, NULL);
+
+ result = Structured_View_Subordinate_List_Purge(invalid_instance);
+ zassert_false(result, NULL);
+
+ Structured_View_Cleanup();
+}
+
+/**
+ * @brief Test state consistency after purge and re-populate
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_state_consistency_after_purge)
+#else
+static void test_state_consistency_after_purge(void)
+#endif
+{
+ const uint32_t instance = 999;
+ bool status = false;
+ BACNET_ARRAY_INDEX array_index = 0;
+ BACNET_SUBORDINATE_DATA test_data[] = {
+ { 0, OBJECT_ACCUMULATOR, 1, "acc-1", BACNET_NODE_MEMBER,
+ BACNET_RELATIONSHIP_CONTAINS, NULL },
+ { 0, OBJECT_LOAD_CONTROL, 2, "lc-2", BACNET_NODE_MEMBER,
+ BACNET_RELATIONSHIP_CONTAINS, NULL },
+ { 0, OBJECT_CHANNEL, 3, "ch-3", BACNET_NODE_COLLECTION,
+ BACNET_RELATIONSHIP_CONTAINS, NULL },
+ };
+
+ Structured_View_Init();
+ Structured_View_Create(instance);
+
+ /* Populate list */
+ Structured_View_Subordinate_List_Link_Array(
+ test_data, ARRAY_SIZE(test_data));
+ Structured_View_Subordinate_List_Set(instance, &test_data[0]);
+ zassert_equal(
+ Structured_View_Subordinate_List_Count(instance), ARRAY_SIZE(test_data),
+ NULL);
+
+ /* Verify element exists */
+ array_index =
+ Structured_View_Subordinate_List_Element_Exist(instance, &test_data[0]);
+ zassert_equal(array_index, 0, NULL);
+
+ /* Purge list */
+ status = Structured_View_Subordinate_List_Purge(instance);
+ zassert_true(status, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 0, NULL);
+
+ /* Verify element no longer exists after purge */
+ array_index =
+ Structured_View_Subordinate_List_Element_Exist(instance, &test_data[0]);
+ zassert_equal(array_index, BACNET_ARRAY_ALL, NULL);
+
+ /* Re-populate with same data */
+ Structured_View_Subordinate_List_Link_Array(
+ test_data, ARRAY_SIZE(test_data));
+ Structured_View_Subordinate_List_Set(instance, &test_data[0]);
+ zassert_equal(
+ Structured_View_Subordinate_List_Count(instance), ARRAY_SIZE(test_data),
+ NULL);
+
+ /* Verify elements are found again */
+ array_index =
+ Structured_View_Subordinate_List_Element_Exist(instance, &test_data[0]);
+ zassert_equal(array_index, 0, NULL);
+
+ array_index =
+ Structured_View_Subordinate_List_Element_Exist(instance, &test_data[1]);
+ zassert_equal(array_index, 1, NULL);
+
+ /* Purge again */
+ status = Structured_View_Subordinate_List_Purge(instance);
+ zassert_true(status, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 0, NULL);
+
+ Structured_View_Delete(instance);
+ Structured_View_Cleanup();
+}
+
+/**
+ * @brief Test Structured_View_Subordinate_List_Resize via Write_Property
+ * array_index==0 on PROP_SUBORDINATE_LIST and related array properties.
+ */
+#if defined(CONFIG_ZTEST_NEW_API)
+ZTEST(tests_object_structured_view, test_subordinate_list_resize)
+#else
+static void test_subordinate_list_resize(void)
+#endif
+{
+ const uint32_t instance = 1111;
+ bool status = false;
+ BACNET_WRITE_PROPERTY_DATA wp_data = { 0 };
+
+ Structured_View_Init();
+ Structured_View_Create(instance);
+
+ wp_data.object_type = OBJECT_STRUCTURED_VIEW;
+ wp_data.object_instance = instance;
+ wp_data.priority = BACNET_NO_PRIORITY;
+ wp_data.array_index = 0; /* index 0 means "write the array size" */
+
+ /* --- grow from 0 to 3 via PROP_SUBORDINATE_LIST --- */
+ wp_data.object_property = PROP_SUBORDINATE_LIST;
+ wp_data.application_data_len =
+ encode_application_unsigned(wp_data.application_data, 3);
+ status = Structured_View_Write_Property(&wp_data);
+ zassert_true(status, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 3, NULL);
+
+ /* --- grow from 3 to 5 --- */
+ wp_data.application_data_len =
+ encode_application_unsigned(wp_data.application_data, 5);
+ status = Structured_View_Write_Property(&wp_data);
+ zassert_true(status, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 5, NULL);
+
+ /* --- same size (no change) --- */
+ wp_data.application_data_len =
+ encode_application_unsigned(wp_data.application_data, 5);
+ status = Structured_View_Write_Property(&wp_data);
+ zassert_true(status, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 5, NULL);
+
+ /* --- shrink from 5 to 2 --- */
+ wp_data.application_data_len =
+ encode_application_unsigned(wp_data.application_data, 2);
+ status = Structured_View_Write_Property(&wp_data);
+ zassert_true(status, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 2, NULL);
+
+ /* --- shrink to zero --- */
+ wp_data.application_data_len =
+ encode_application_unsigned(wp_data.application_data, 0);
+ status = Structured_View_Write_Property(&wp_data);
+ zassert_true(status, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 0, NULL);
+
+ /* --- resize via PROP_SUBORDINATE_ANNOTATIONS independently --- */
+ /* first grow the list so the annotation resize has something to work with
+ */
+ wp_data.object_property = PROP_SUBORDINATE_LIST;
+ wp_data.application_data_len =
+ encode_application_unsigned(wp_data.application_data, 4);
+ status = Structured_View_Write_Property(&wp_data);
+ zassert_true(status, NULL);
+
+ wp_data.object_property = PROP_SUBORDINATE_ANNOTATIONS;
+ wp_data.application_data_len =
+ encode_application_unsigned(wp_data.application_data, 2);
+ status = Structured_View_Write_Property(&wp_data);
+ zassert_true(status, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 2, NULL);
+
+ /* --- resize via PROP_SUBORDINATE_NODE_TYPES --- */
+ wp_data.object_property = PROP_SUBORDINATE_NODE_TYPES;
+ wp_data.application_data_len =
+ encode_application_unsigned(wp_data.application_data, 3);
+ status = Structured_View_Write_Property(&wp_data);
+ zassert_true(status, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 3, NULL);
+
+ /* --- resize via PROP_SUBORDINATE_RELATIONSHIPS --- */
+ wp_data.object_property = PROP_SUBORDINATE_RELATIONSHIPS;
+ wp_data.application_data_len =
+ encode_application_unsigned(wp_data.application_data, 1);
+ status = Structured_View_Write_Property(&wp_data);
+ zassert_true(status, NULL);
+ zassert_equal(Structured_View_Subordinate_List_Count(instance), 1, NULL);
+
+ /* --- invalid instance returns false --- */
+ wp_data.object_instance = instance + 9999;
+ wp_data.object_property = PROP_SUBORDINATE_LIST;
+ wp_data.application_data_len =
+ encode_application_unsigned(wp_data.application_data, 2);
+ status = Structured_View_Write_Property(&wp_data);
+ zassert_false(status, NULL);
+
+ Structured_View_Delete(instance);
+ Structured_View_Cleanup();
+}
+
/**
* @}
*/
@@ -172,7 +1021,20 @@ void test_main(void)
{
ztest_test_suite(
tests_object_structured_view,
- ztest_unit_test(test_object_structured_view));
+ ztest_unit_test(test_object_structured_view),
+ ztest_unit_test(test_subordinate_list_element_same),
+ ztest_unit_test(test_subordinate_list_element_add_remove_exist),
+ ztest_unit_test(test_subordinate_list_purge),
+ ztest_unit_test(test_property_lists),
+ ztest_unit_test(test_subordinate_list),
+ ztest_unit_test(test_subordinate_encode_functions),
+ ztest_unit_test(test_encode_functions_invalid_instance),
+ ztest_unit_test(test_subordinate_null_annotations),
+ ztest_unit_test(test_subordinate_list_boundaries),
+ ztest_unit_test(test_sequential_add_remove_cycles),
+ ztest_unit_test(test_invalid_instance_validation),
+ ztest_unit_test(test_state_consistency_after_purge),
+ ztest_unit_test(test_subordinate_list_resize));
ztest_run_test_suite(tests_object_structured_view);
}