From 76e06eece9c8867ec80b7c4522c66ada53feca56 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonathan=20Sch=C3=B6bel?= <jonathan@xn--schbel-yxa.info>
Date: Sun, 4 Jun 2023 15:36:40 +0200
Subject: [PATCH] NodeFragment: added remove_attr methods

Two alternatives are provided: remove_attr and pop_attr. While the
former free's the Attr's data, the latter allocates a new Attr, to store
and return the data. Both functionality is provided by a single
(internal) static method.
---
 sefht.geany                   |   6 +-
 src/lib/sefht/node_fragment.c | 124 ++++++
 src/lib/sefht/node_fragment.h |  22 +
 tests/test_node_fragment.c    | 757 ++++++++++++++++++++++++++++++++++
 4 files changed, 906 insertions(+), 3 deletions(-)

diff --git a/sefht.geany b/sefht.geany
index 545bbc0..e1a0431 100644
--- a/sefht.geany
+++ b/sefht.geany
@@ -43,8 +43,8 @@ FILE_NAME_10=1550;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fp
 FILE_NAME_11=1562;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ffragment.h;0;8
 FILE_NAME_12=2022;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ffragment_data.c;0;8
 FILE_NAME_13=2558;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ffragment_class.c;0;8
-FILE_NAME_14=20283;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fnode_fragment.c;0;8
-FILE_NAME_15=6032;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fnode_fragment.h;0;8
+FILE_NAME_14=23169;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fnode_fragment.c;0;8
+FILE_NAME_15=6811;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fnode_fragment.h;0;8
 FILE_NAME_16=5204;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fattr.c;0;8
 FILE_NAME_17=3030;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fattr.h;0;8
 FILE_NAME_18=3044;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fattr_static.c;0;8
@@ -63,7 +63,7 @@ FILE_NAME_30=555;Make;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2
 FILE_NAME_31=218;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Fno_test.sh.in;0;8
 FILE_NAME_32=1085;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_cms.c;0;8
 FILE_NAME_33=3283;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_data.c;0;8
-FILE_NAME_34=34187;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_node_fragment.c;0;8
+FILE_NAME_34=109525;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_node_fragment.c;0;8
 FILE_NAME_35=15195;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_attr.c;0;8
 FILE_NAME_36=11068;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text.c;0;8
 FILE_NAME_37=5744;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator.c;0;8
diff --git a/src/lib/sefht/node_fragment.c b/src/lib/sefht/node_fragment.c
index be87771..81a92b9 100644
--- a/src/lib/sefht/node_fragment.c
+++ b/src/lib/sefht/node_fragment.c
@@ -898,6 +898,130 @@ SH_NodeFragment_append_attr_copy (struct SH_NodeFragment * fragment,
 	                         status);
 }
 
+static inline
+bool
+remove_attr (struct SH_NodeFragment * fragment, size_t position,
+             /*@out@*/ /*@null@*/ SH_Attr * attr_ptr,
+             /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@modifies fragment->attrs@*/
+	/*@modifies fragment->attr_s@*/
+	/*@modifies fragment->attr_n@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	size_t index;
+	size_t new_size;
+	typeof (fragment->attrs) new_attrs;
+	SH_Attr attr;
+
+	Attr_move (&attr, &(fragment->attrs[position]));
+
+	for (index = position+1; index < fragment->attr_n; index++)
+	{
+		fragment->attrs[index-1] = fragment->attrs[index];
+	}
+
+	new_size = get_attr_alloc_size (fragment->attr_n - 1);
+	if (new_size < fragment->attr_s)
+	{
+		new_attrs = realloc (fragment->attrs, new_size
+		                      * sizeof (*new_attrs));
+		if ((0 != new_size) && (NULL == new_attrs))
+		{
+			for (index = fragment->attr_n-1;
+			     index > position;
+			     index--)
+			{
+				fragment->attrs[index] =
+				fragment->attrs[index-1];
+			}
+			fragment->attrs[position] = attr;
+
+			set_status (status, E_ALLOC, 4,
+			            "realloc failed.\n");
+			return FALSE;
+		}
+
+		fragment->attrs = new_attrs;
+		fragment->attr_s = new_size;
+	}
+
+	fragment->attr_n--;
+
+	if (NULL == attr_ptr)
+	{
+		Attr_free (&attr);
+	}
+	else
+	{
+		*attr_ptr = attr;
+	}
+
+	set_success (status);
+	return TRUE;
+}
+
+bool
+SH_NodeFragment_remove_attr (struct SH_NodeFragment * fragment,
+                             size_t position,
+                             /*@out@*/ /*@null@*/
+                             struct SH_Status * status)
+	/*@modifies fragment->attrs@*/
+	/*@modifies fragment->attr_s@*/
+	/*@modifies fragment->attr_n@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	if (position >= fragment->attr_n)
+	{
+		set_status (status, E_VALUE, 2,
+		            "index out of range.\n");
+		return FALSE;
+	}
+
+	return remove_attr (fragment, position, NULL, status);
+}
+
+/*@null@*/
+/*@only@*/
+SH_Attr *
+SH_NodeFragment_pop_attr (struct SH_NodeFragment * fragment,
+                          size_t position,
+                          /*@out@*/ /*@null@*/
+                          struct SH_Status * status)
+	/*@modifies fragment->attrs@*/
+	/*@modifies fragment->attr_s@*/
+	/*@modifies fragment->attr_n@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	SH_Attr * attr;
+
+	if (position >= fragment->attr_n)
+	{
+		set_status (status, E_VALUE, 2,
+		            "index out of range.\n");
+		return NULL;
+	}
+
+	attr = malloc (sizeof (SH_Attr));
+	if (NULL == attr)
+	{
+		set_status (status, E_ALLOC, 3, "malloc failed");
+		return NULL;
+	}
+
+	if (!remove_attr (fragment, position, attr, status))
+	{
+		return NULL;
+	}
+
+	return attr;
+}
+
 size_t
 SH_NodeFragment_count_childs (const struct SH_NodeFragment * fragment)
 	/*@*/
diff --git a/src/lib/sefht/node_fragment.h b/src/lib/sefht/node_fragment.h
index eb07c6e..a12cd21 100644
--- a/src/lib/sefht/node_fragment.h
+++ b/src/lib/sefht/node_fragment.h
@@ -197,6 +197,28 @@ SH_NodeFragment_append_attr_copy (SH_NodeFragment * fragment,
 	/*@modifies fileSystem@*/
 	/*@modifies status@*/;
 
+bool
+SH_NodeFragment_remove_attr (SH_NodeFragment * fragment,
+                             size_t position,
+                             /*@out@*/ /*@null@*/
+                             struct SH_Status * status)
+	/*@modifies fragment@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+/*@null@*/
+/*@only@*/
+SH_Attr *
+SH_NodeFragment_pop_attr (SH_NodeFragment * fragment,
+                          size_t position,
+                          /*@out@*/ /*@null@*/
+                          struct SH_Status * status)
+	/*@modifies fragment@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
 /*@null@*/
 /*@only@*/
 char *
diff --git a/tests/test_node_fragment.c b/tests/test_node_fragment.c
index 0a583c4..7aa1e3b 100644
--- a/tests/test_node_fragment.c
+++ b/tests/test_node_fragment.c
@@ -1583,6 +1583,755 @@ START_TEST(test_node_fragment_attr_insert_insert_copy_with_status)
 }
 END_TEST
 
+START_TEST(test_node_fragment_attr_remove1_no_status)
+{
+	SH_Attr * attr;
+	SH_Attr * attr1;
+	SH_Attr * attr2;
+	SH_Attr * attr3;
+	SH_Attr * attr4;
+	SH_Attr * attr5;
+	SH_Attr * attr6;
+	SH_Data * data;
+	struct SH_NodeFragment * fragment;
+	bool result;
+
+	/* setup */
+	data = SH_Data_new (NULL);
+	ck_assert_ptr_ne (NULL, data);
+
+	attr = malloc (sizeof (SH_Attr));
+	ck_assert_ptr_ne (NULL, attr);
+
+	attr1 = SH_Attr_new ("name1", "value1", NULL);
+	ck_assert_ptr_ne (NULL, attr1);
+
+	attr2 = SH_Attr_new ("name2", "value2", NULL);
+	ck_assert_ptr_ne (NULL, attr2);
+
+	attr3 = SH_Attr_new ("name3", "value3", NULL);
+	ck_assert_ptr_ne (NULL, attr3);
+
+	attr4 = SH_Attr_new ("name4", "value4", NULL);
+	ck_assert_ptr_ne (NULL, attr4);
+
+	attr5 = SH_Attr_new ("name5", "value5", NULL);
+	ck_assert_ptr_ne (NULL, attr5);
+
+	attr6 = SH_Attr_new ("name6", "value6", NULL);
+	ck_assert_ptr_ne (NULL, attr6);
+
+	fragment = (SH_NodeFragment *)SH_NodeFragment_new ("html", data,
+	                                                   NULL);
+	ck_assert_ptr_ne (NULL, fragment);
+
+
+	result = SH_NodeFragment_append_attr (fragment, attr1, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr2, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr3, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr4, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr5, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr6, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+
+	/* test1 */
+	ck_assert_int_eq (6, fragment->attr_n);
+	ck_assert_int_le (6, fragment->attr_s);
+	ck_assert_int_eq (2*ATTR_CHUNK, fragment->attr_s);
+
+	result = remove_attr (fragment, 0, attr, NULL);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr1->name, attr->name);
+	ck_assert_ptr_eq (attr1->value, attr->value);
+
+	ck_assert_int_eq (5, fragment->attr_n);
+	ck_assert_int_le (5, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr2->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr2->value, fragment->attrs[0].value);
+
+	/* test2 */
+	result = remove_attr (fragment, 0, attr, NULL);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr2->name, attr->name);
+	ck_assert_ptr_eq (attr2->value, attr->value);
+
+	ck_assert_int_eq (4, fragment->attr_n);
+	ck_assert_int_le (4, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr3->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr3->value, fragment->attrs[0].value);
+
+	/* test3 */
+	result = remove_attr (fragment, 0, attr, NULL);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr3->name, attr->name);
+	ck_assert_ptr_eq (attr3->value, attr->value);
+
+	ck_assert_int_eq (3, fragment->attr_n);
+	ck_assert_int_le (3, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr4->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr4->value, fragment->attrs[0].value);
+
+	/* test4 */
+	result = remove_attr (fragment, 0, attr, NULL);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr4->name, attr->name);
+	ck_assert_ptr_eq (attr4->value, attr->value);
+
+	ck_assert_int_eq (2, fragment->attr_n);
+	ck_assert_int_le (2, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr5->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr5->value, fragment->attrs[0].value);
+
+	/* test5 */
+	result = remove_attr (fragment, 0, attr, NULL);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr5->name, attr->name);
+	ck_assert_ptr_eq (attr5->value, attr->value);
+
+	ck_assert_int_eq (1, fragment->attr_n);
+	ck_assert_int_le (1, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr6->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr6->value, fragment->attrs[0].value);
+
+	/* test6 */
+	result = remove_attr (fragment, 0, attr, NULL);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr6->name, attr->name);
+	ck_assert_ptr_eq (attr6->value, attr->value);
+
+	ck_assert_int_eq (0, fragment->attr_n);
+	ck_assert_int_le (0, fragment->attr_s);
+	ck_assert_int_eq (0, fragment->attr_s);
+
+	/* cleanup */
+	SH_NodeFragment_free (fragment);
+	SH_Data_free (data);
+	SH_Attr_free (attr1);
+	SH_Attr_free (attr2);
+	SH_Attr_free (attr3);
+	SH_Attr_free (attr4);
+	SH_Attr_free (attr5);
+	SH_Attr_free (attr6);
+	free (attr);
+}
+END_TEST
+
+START_TEST(test_node_fragment_attr_remove1_with_status)
+{
+	struct SH_Status status;
+	SH_Attr * attr;
+	SH_Attr * attr1;
+	SH_Attr * attr2;
+	SH_Attr * attr3;
+	SH_Attr * attr4;
+	SH_Attr * attr5;
+	SH_Attr * attr6;
+	SH_Data * data;
+	struct SH_NodeFragment * fragment;
+	bool result;
+
+	/* setup */
+	data = SH_Data_new (NULL);
+	ck_assert_ptr_ne (NULL, data);
+
+	attr = malloc (sizeof (SH_Attr));
+	ck_assert_ptr_ne (NULL, attr);
+
+	attr1 = SH_Attr_new ("name1", "value1", NULL);
+	ck_assert_ptr_ne (NULL, attr1);
+
+	attr2 = SH_Attr_new ("name2", "value2", NULL);
+	ck_assert_ptr_ne (NULL, attr2);
+
+	attr3 = SH_Attr_new ("name3", "value3", NULL);
+	ck_assert_ptr_ne (NULL, attr3);
+
+	attr4 = SH_Attr_new ("name4", "value4", NULL);
+	ck_assert_ptr_ne (NULL, attr4);
+
+	attr5 = SH_Attr_new ("name5", "value5", NULL);
+	ck_assert_ptr_ne (NULL, attr5);
+
+	attr6 = SH_Attr_new ("name6", "value6", NULL);
+	ck_assert_ptr_ne (NULL, attr6);
+
+	fragment = (SH_NodeFragment *)SH_NodeFragment_new ("html", data,
+	                                                   NULL);
+	ck_assert_ptr_ne (NULL, fragment);
+
+
+	result = SH_NodeFragment_append_attr (fragment, attr1, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr2, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr3, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr4, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr5, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr6, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+
+	/* test1 */
+	ck_assert_int_eq (6, fragment->attr_n);
+	ck_assert_int_le (6, fragment->attr_s);
+	ck_assert_int_eq (2*ATTR_CHUNK, fragment->attr_s);
+
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, attr, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr1->name, attr->name);
+	ck_assert_ptr_eq (attr1->value, attr->value);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (5, fragment->attr_n);
+	ck_assert_int_le (5, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr2->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr2->value, fragment->attrs[0].value);
+
+	/* test2 */
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, attr, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr2->name, attr->name);
+	ck_assert_ptr_eq (attr2->value, attr->value);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (4, fragment->attr_n);
+	ck_assert_int_le (4, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr3->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr3->value, fragment->attrs[0].value);
+
+	/* test3 */
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, attr, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr3->name, attr->name);
+	ck_assert_ptr_eq (attr3->value, attr->value);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (3, fragment->attr_n);
+	ck_assert_int_le (3, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr4->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr4->value, fragment->attrs[0].value);
+
+	/* test4 */
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, attr, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr4->name, attr->name);
+	ck_assert_ptr_eq (attr4->value, attr->value);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (2, fragment->attr_n);
+	ck_assert_int_le (2, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr5->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr5->value, fragment->attrs[0].value);
+
+	/* test5 */
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, attr, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr5->name, attr->name);
+	ck_assert_ptr_eq (attr5->value, attr->value);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (1, fragment->attr_n);
+	ck_assert_int_le (1, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr6->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr6->value, fragment->attrs[0].value);
+
+	/* test6 */
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, attr, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_ptr_eq (attr6->name, attr->name);
+	ck_assert_ptr_eq (attr6->value, attr->value);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (0, fragment->attr_n);
+	ck_assert_int_le (0, fragment->attr_s);
+	ck_assert_int_eq (0, fragment->attr_s);
+
+	/* cleanup */
+	SH_NodeFragment_free (fragment);
+	SH_Data_free (data);
+	SH_Attr_free (attr1);
+	SH_Attr_free (attr2);
+	SH_Attr_free (attr3);
+	SH_Attr_free (attr4);
+	SH_Attr_free (attr5);
+	SH_Attr_free (attr6);
+	free (attr);
+}
+END_TEST
+
+START_TEST(test_node_fragment_attr_remove2_no_status)
+{
+	SH_Attr * attr1;
+	SH_Attr * attr2;
+	SH_Attr * attr3;
+	SH_Attr * attr4;
+	SH_Attr * attr5;
+	SH_Attr * attr6;
+	SH_Data * data;
+	struct SH_NodeFragment * fragment;
+	bool result;
+
+	/* setup */
+	data = SH_Data_new (NULL);
+	ck_assert_ptr_ne (NULL, data);
+
+	attr1 = SH_Attr_new ("name1", "value1", NULL);
+	ck_assert_ptr_ne (NULL, attr1);
+
+	attr2 = SH_Attr_new ("name2", "value2", NULL);
+	ck_assert_ptr_ne (NULL, attr2);
+
+	attr3 = SH_Attr_new ("name3", "value3", NULL);
+	ck_assert_ptr_ne (NULL, attr3);
+
+	attr4 = SH_Attr_new ("name4", "value4", NULL);
+	ck_assert_ptr_ne (NULL, attr4);
+
+	attr5 = SH_Attr_new ("name5", "value5", NULL);
+	ck_assert_ptr_ne (NULL, attr5);
+
+	attr6 = SH_Attr_new ("name6", "value6", NULL);
+	ck_assert_ptr_ne (NULL, attr6);
+
+	fragment = (SH_NodeFragment *)SH_NodeFragment_new ("html", data,
+	                                                   NULL);
+	ck_assert_ptr_ne (NULL, fragment);
+
+
+	result = SH_NodeFragment_append_attr (fragment, attr1, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr2, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr3, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr4, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr5, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr6, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+
+	/* test1 */
+	ck_assert_int_eq (6, fragment->attr_n);
+	ck_assert_int_le (6, fragment->attr_s);
+	ck_assert_int_eq (2*ATTR_CHUNK, fragment->attr_s);
+
+	result = remove_attr (fragment, 0, NULL, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	ck_assert_int_eq (5, fragment->attr_n);
+	ck_assert_int_le (5, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr2->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr2->value, fragment->attrs[0].value);
+
+	/* test2 */
+	result = remove_attr (fragment, 0, NULL, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	ck_assert_int_eq (4, fragment->attr_n);
+	ck_assert_int_le (4, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr3->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr3->value, fragment->attrs[0].value);
+
+	/* test3 */
+	result = remove_attr (fragment, 0, NULL, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	ck_assert_int_eq (3, fragment->attr_n);
+	ck_assert_int_le (3, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr4->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr4->value, fragment->attrs[0].value);
+
+	/* test4 */
+	result = remove_attr (fragment, 0, NULL, NULL);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_int_eq (2, fragment->attr_n);
+	ck_assert_int_le (2, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr5->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr5->value, fragment->attrs[0].value);
+
+	/* test5 */
+	result = remove_attr (fragment, 0, NULL, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	ck_assert_int_eq (1, fragment->attr_n);
+	ck_assert_int_le (1, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr6->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr6->value, fragment->attrs[0].value);
+
+	/* test6 */
+	result = remove_attr (fragment, 0, NULL, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	ck_assert_int_eq (0, fragment->attr_n);
+	ck_assert_int_le (0, fragment->attr_s);
+	ck_assert_int_eq (0, fragment->attr_s);
+
+	/* cleanup */
+	SH_NodeFragment_free (fragment);
+	SH_Data_free (data);
+	free (attr1);
+	free (attr2);
+	free (attr3);
+	free (attr4);
+	free (attr5);
+	free (attr6);
+}
+END_TEST
+
+START_TEST(test_node_fragment_attr_remove2_with_status)
+{
+	struct SH_Status status;
+	SH_Attr * attr1;
+	SH_Attr * attr2;
+	SH_Attr * attr3;
+	SH_Attr * attr4;
+	SH_Attr * attr5;
+	SH_Attr * attr6;
+	SH_Data * data;
+	struct SH_NodeFragment * fragment;
+	bool result;
+
+	/* setup */
+	data = SH_Data_new (NULL);
+	ck_assert_ptr_ne (NULL, data);
+
+	attr1 = SH_Attr_new ("name1", "value1", NULL);
+	ck_assert_ptr_ne (NULL, attr1);
+
+	attr2 = SH_Attr_new ("name2", "value2", NULL);
+	ck_assert_ptr_ne (NULL, attr2);
+
+	attr3 = SH_Attr_new ("name3", "value3", NULL);
+	ck_assert_ptr_ne (NULL, attr3);
+
+	attr4 = SH_Attr_new ("name4", "value4", NULL);
+	ck_assert_ptr_ne (NULL, attr4);
+
+	attr5 = SH_Attr_new ("name5", "value5", NULL);
+	ck_assert_ptr_ne (NULL, attr5);
+
+	attr6 = SH_Attr_new ("name6", "value6", NULL);
+	ck_assert_ptr_ne (NULL, attr6);
+
+	fragment = (SH_NodeFragment *)SH_NodeFragment_new ("html", data,
+	                                                   NULL);
+	ck_assert_ptr_ne (NULL, fragment);
+
+
+	result = SH_NodeFragment_append_attr (fragment, attr1, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr2, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr3, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr4, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr5, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_NodeFragment_append_attr (fragment, attr6, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+
+	/* test1 */
+	ck_assert_int_eq (6, fragment->attr_n);
+	ck_assert_int_le (6, fragment->attr_s);
+	ck_assert_int_eq (2*ATTR_CHUNK, fragment->attr_s);
+
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, NULL, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (5, fragment->attr_n);
+	ck_assert_int_le (5, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr2->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr2->value, fragment->attrs[0].value);
+
+	/* test2 */
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, NULL, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (4, fragment->attr_n);
+	ck_assert_int_le (4, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr3->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr3->value, fragment->attrs[0].value);
+
+	/* test3 */
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, NULL, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (3, fragment->attr_n);
+	ck_assert_int_le (3, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr4->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr4->value, fragment->attrs[0].value);
+
+	/* test4 */
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, NULL, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (2, fragment->attr_n);
+	ck_assert_int_le (2, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr5->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr5->value, fragment->attrs[0].value);
+
+	/* test5 */
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, NULL, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (1, fragment->attr_n);
+	ck_assert_int_le (1, fragment->attr_s);
+	ck_assert_int_eq (ATTR_CHUNK, fragment->attr_s);
+	ck_assert_ptr_eq (attr6->name, fragment->attrs[0].name);
+	ck_assert_ptr_eq (attr6->value, fragment->attrs[0].value);
+
+	/* test6 */
+	_status_preinit (status);
+	result = remove_attr (fragment, 0, NULL, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (0, fragment->attr_n);
+	ck_assert_int_le (0, fragment->attr_s);
+	ck_assert_int_eq (0, fragment->attr_s);
+
+	/* cleanup */
+	SH_NodeFragment_free (fragment);
+	SH_Data_free (data);
+	free (attr1);
+	free (attr2);
+	free (attr3);
+	free (attr4);
+	free (attr5);
+	free (attr6);
+}
+END_TEST
+
+START_TEST(test_node_fragment_attr_remove_remove_no_status)
+{
+	SH_Attr * attr;
+	SH_Data * data;
+	struct SH_NodeFragment * fragment;
+	bool result;
+
+	/* setup */
+	attr = SH_Attr_new ("name", "value", NULL);
+	ck_assert_ptr_ne (NULL, attr);
+
+	data = SH_Data_new (NULL);
+	ck_assert_ptr_ne (NULL, data);
+
+	fragment = (SH_NodeFragment *)SH_NodeFragment_new ("html", data,
+	                                                   NULL);
+	ck_assert_ptr_ne (NULL, fragment);
+
+	result = SH_NodeFragment_append_attr (fragment, attr, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	/* test - fail */
+	result = SH_NodeFragment_remove_attr (fragment, 1, NULL);
+	ck_assert_int_eq (FALSE, result);
+
+	/* test - success */
+	result = SH_NodeFragment_remove_attr (fragment, 0, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	/* cleanup */
+	SH_NodeFragment_free (fragment);
+	SH_Data_free (data);
+	free (attr);
+}
+END_TEST
+
+START_TEST(test_node_fragment_attr_remove_remove_with_status)
+{
+	struct SH_Status status;
+	SH_Attr * attr;
+	SH_Data * data;
+	struct SH_NodeFragment * fragment;
+	bool result;
+
+	/* setup */
+	attr = SH_Attr_new ("name", "value", NULL);
+	ck_assert_ptr_ne (NULL, attr);
+
+	data = SH_Data_new (NULL);
+	ck_assert_ptr_ne (NULL, data);
+
+	fragment = (SH_NodeFragment *)SH_NodeFragment_new ("html", data,
+	                                                   NULL);
+	ck_assert_ptr_ne (NULL, fragment);
+
+	result = SH_NodeFragment_append_attr (fragment, attr, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	/* test - fail */
+	_status_preinit (status);
+	result = SH_NodeFragment_remove_attr (fragment, 1, &status);
+	ck_assert_int_eq (FALSE, result);
+	ck_assert_int_eq (E_VALUE, status.status);
+
+	/* test - success */
+	_status_preinit (status);
+	result = SH_NodeFragment_remove_attr (fragment, 0, &status);
+	ck_assert_int_eq (TRUE, result);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	/* cleanup */
+	SH_NodeFragment_free (fragment);
+	SH_Data_free (data);
+	free (attr);
+}
+END_TEST
+
+START_TEST(test_node_fragment_attr_remove_pop_no_status)
+{
+	SH_Attr * attr;
+	SH_Attr * attr_;
+	SH_Data * data;
+	struct SH_NodeFragment * fragment;
+	bool result;
+
+	/* setup */
+	attr = SH_Attr_new ("name", "value", NULL);
+	ck_assert_ptr_ne (NULL, attr);
+
+	data = SH_Data_new (NULL);
+	ck_assert_ptr_ne (NULL, data);
+
+	fragment = (SH_NodeFragment *)SH_NodeFragment_new ("html", data,
+	                                                   NULL);
+	ck_assert_ptr_ne (NULL, fragment);
+
+	result = SH_NodeFragment_append_attr (fragment, attr, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	/* test - fail */
+	attr_ = SH_NodeFragment_pop_attr (fragment, 1, NULL);
+	ck_assert_ptr_eq (NULL, attr_);
+
+	/* test - success */
+	attr_ = SH_NodeFragment_pop_attr (fragment, 0, NULL);
+	ck_assert_ptr_ne (NULL, attr_);
+	ck_assert_ptr_eq (attr->name, attr_->name);
+	ck_assert_ptr_eq (attr->value, attr_->value);
+	SH_Attr_free (attr_);
+
+	/* cleanup */
+	SH_NodeFragment_free (fragment);
+	SH_Data_free (data);
+	free (attr);
+}
+END_TEST
+
+START_TEST(test_node_fragment_attr_remove_pop_with_status)
+{
+	struct SH_Status status;
+	SH_Attr * attr;
+	SH_Attr * attr_;
+	SH_Data * data;
+	struct SH_NodeFragment * fragment;
+	bool result;
+
+	/* setup */
+	attr = SH_Attr_new ("name", "value", NULL);
+	ck_assert_ptr_ne (NULL, attr);
+
+	data = SH_Data_new (NULL);
+	ck_assert_ptr_ne (NULL, data);
+
+	fragment = (SH_NodeFragment *)SH_NodeFragment_new ("html", data,
+	                                                   NULL);
+	ck_assert_ptr_ne (NULL, fragment);
+
+	result = SH_NodeFragment_append_attr (fragment, attr, NULL);
+	ck_assert_int_eq (TRUE, result);
+
+	/* test - fail */
+	_status_preinit (status);
+	attr_ = SH_NodeFragment_pop_attr (fragment, 1, &status);
+	ck_assert_ptr_eq (NULL, attr_);
+	ck_assert_int_eq (E_VALUE, status.status);
+
+	/* test - success */
+	_status_preinit (status);
+	attr_ = SH_NodeFragment_pop_attr (fragment, 0, &status);
+	ck_assert_ptr_ne (NULL, attr_);
+	ck_assert_int_eq (SUCCESS, status.status);
+	ck_assert_ptr_eq (attr->name, attr_->name);
+	ck_assert_ptr_eq (attr->value, attr_->value);
+	SH_Attr_free (attr_);
+
+	/* cleanup */
+	SH_NodeFragment_free (fragment);
+	SH_Data_free (data);
+	free (attr);
+}
+END_TEST
+
 START_TEST(test_node_fragment_child_alloc)
 {
 	#define alloc_size get_child_alloc_size
@@ -2989,6 +3738,14 @@ Suite * fragment_suite (void)
 	tcase_add_test (tc_core, test_node_fragment_attr_insert_insert_new_with_status);
 	tcase_add_test (tc_core, test_node_fragment_attr_insert_insert_copy_no_status);
 	tcase_add_test (tc_core, test_node_fragment_attr_insert_insert_copy_with_status);
+	tcase_add_test (tc_core, test_node_fragment_attr_remove1_no_status);
+	tcase_add_test (tc_core, test_node_fragment_attr_remove1_with_status);
+	tcase_add_test (tc_core, test_node_fragment_attr_remove2_no_status);
+	tcase_add_test (tc_core, test_node_fragment_attr_remove2_with_status);
+	tcase_add_test (tc_core, test_node_fragment_attr_remove_remove_no_status);
+	tcase_add_test (tc_core, test_node_fragment_attr_remove_remove_with_status);
+	tcase_add_test (tc_core, test_node_fragment_attr_remove_pop_no_status);
+	tcase_add_test (tc_core, test_node_fragment_attr_remove_pop_with_status);
 	tcase_add_test (tc_core, test_node_fragment_child_alloc);
 	tcase_add_test (tc_core, test_node_fragment_child);
 	tcase_add_test (tc_core, test_node_fragment_get_child);
-- 
GitLab