Skip to content
Snippets Groups Projects
test_validator_attr.c 57 KiB
Newer Older
/*
 * test_validator_attr.c
 *
 * Copyright 2023 Jonathan Schöbel <jonathan@xn--schbel-yxa.info>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 *
 */


#include <check.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>


/* lower SIZE_MAX as we try to reach it */
#include <limits.h>
#undef SIZE_MAX
#define SIZE_MAX 10 * sizeof (struct attr_info)

#include "macro.h"
#include "status.h"


/* override HTML spec */
#include "validator_html.h"

const struct HTML_TAG_DEFINITION HTML_TAGS[] = {
	{"html"},
	{"aside"},
	{"a"},
};

const struct HTML_ATTR_DEFINITION HTML_ATTRS[] = {
	{"lang", &HTML_TAGS[0], 2},
	{"href", &HTML_TAGS[2], 1},
	{"class", &HTML_TAGS[3], 1},
	{"id", NULL, 0},
	{"class", NULL, 0},
};

#define HTML5_TAGS HTML_TAGS
#define HTML5_ATTRS HTML_ATTRS


/* C file is needed, because we want to override SIZE_MAX */
#include "validator.c"


START_TEST(test_validator_no_status)
{
	struct SH_Validator * validator;

	validator = SH_Validator_new (NULL);
	ck_assert_ptr_ne (NULL, validator);

	ck_assert_int_eq (0, validator->attr_n);

	SH_Validator_free (validator);
}
END_TEST

START_TEST(test_validator_with_status)
{
	struct SH_Status status;
	struct SH_Validator * validator;

	_status_preinit (status);
	validator = SH_Validator_new (&status);
	ck_assert_ptr_ne (NULL, validator);
	ck_assert_int_eq (status.status, SUCCESS);

	ck_assert_int_eq (0, validator->attr_n);

	SH_Validator_free (validator);
}
END_TEST

START_TEST(test_validator_copy_no_status)
{
	struct SH_Validator * validator;
	struct SH_Validator * copy;

	/* setup */
	validator = SH_Validator_new_html5 (NULL);
	ck_assert_ptr_ne (NULL, validator);

	result = SH_Validator_register_attr (validator, NULL, "id",
	ck_assert_int_eq (TRUE, result);
	result = SH_Validator_register_attr (validator, "html", "lang",
	                                     NULL);
	ck_assert_int_eq (TRUE, result);

	result = SH_Validator_register_attr (validator, "p", "lang",
	                                     NULL);
	ck_assert_int_eq (TRUE, result);

	/* test */
	copy = SH_Validator_copy (validator, NULL);
	ck_assert_ptr_ne (NULL, copy);

	ck_assert_ptr_ne (NULL, copy->attrs);
	ck_assert_ptr_ne (validator->attrs, copy->attrs);
	ck_assert_int_eq (validator->attr_n, copy->attr_n);

	#define TEST_INT(I) ck_assert_int_eq (validator->I, copy->I);
	#define TEST_PTR(P) ck_assert_ptr_eq (validator->P, copy->P);
	#define TEST_STR(S) ck_assert_ptr_ne (validator->S, copy->S);  \
	                    ck_assert_str_eq (validator->S, copy->S);

	for (size_t index = 0; index < copy->attr_n; index++)
	{
		TEST_STR(attrs[index].name)

		TEST_INT(attrs[index].tag_n)
		for (size_t j = 0; j < copy->attrs[index].tag_n; j++)
		{
			TEST_PTR(attrs[index].tags[j].name)
		}

		if (0 == validator->attrs[index].tag_n)
		{
			TEST_PTR(attrs[index].tags);
		}
	}
	#undef TEST_INT
	#undef TEST_STR

	SH_Validator_free (copy);
	SH_Validator_free (validator);
}
END_TEST

START_TEST(test_validator_copy_with_status)
{
	struct SH_Status status;
	struct SH_Validator * validator;
	struct SH_Validator * copy;

	/* setup */
	validator = SH_Validator_new_html5 (NULL);
	ck_assert_ptr_ne (NULL, validator);

	result = SH_Validator_register_attr (validator, NULL, "id",
	ck_assert_int_eq (TRUE, result);
	result = SH_Validator_register_attr (validator, "html", "lang",
	                                     NULL);
	ck_assert_int_eq (TRUE, result);

	result = SH_Validator_register_attr (validator, "p", "lang",
	                                     NULL);
	ck_assert_int_eq (TRUE, result);

	/* test */
	_status_preinit (status);
	copy = SH_Validator_copy (validator, &status);
	ck_assert_ptr_ne (NULL, copy);
	ck_assert_int_eq (status.status, SUCCESS);

	ck_assert_ptr_ne (NULL, copy->attrs);
	ck_assert_ptr_ne (validator->attrs, copy->attrs);
	ck_assert_int_eq (validator->attr_n, copy->attr_n);

	#define TEST_INT(I) ck_assert_int_eq (validator->I, copy->I);
	#define TEST_PTR(P) ck_assert_ptr_eq (validator->P, copy->P);
	#define TEST_STR(S) ck_assert_ptr_ne (validator->S, copy->S);  \
	                    ck_assert_str_eq (validator->S, copy->S);

	for (size_t index = 0; index < copy->attr_n; index++)
	{
		TEST_STR(attrs[index].name)

		TEST_INT(attrs[index].tag_n)
		for (size_t j = 0; j < copy->attrs[index].tag_n; j++)
		{
			TEST_PTR(attrs[index].tags[j].name)
		}

		if (0 == validator->attrs[index].tag_n)
		{
			TEST_PTR(attrs[index].tags);
		}
	}
	#undef TEST_INT
	#undef TEST_STR

	SH_Validator_free (copy);
	SH_Validator_free (validator);
}
END_TEST

START_TEST(test_validator_spec_no_status)
{
	struct SH_Validator * validator;

	/* test */
	validator = SH_Validator_new_html5 (NULL);
	ck_assert_ptr_ne (NULL, validator);

	ck_assert_ptr_ne (NULL, validator->attrs);
	ck_assert_int_eq (4, validator->attr_n);

	#define attrs validator->attrs

	/* lang, 2 tags */
	ck_assert_str_eq ("lang", HTML_ATTRS[0].attr);
	ck_assert_ptr_ne (HTML_ATTRS[0].attr, attrs[3].name);
	ck_assert_str_eq (HTML_ATTRS[0].attr, attrs[3].name);
	ck_assert_int_eq (HTML_ATTRS[0].tag_n, attrs[3].tag_n);
	ck_assert_ptr_ne (NULL, attrs[3].tags);
	#define TEST_STR(S1, S2) ck_assert_ptr_ne (S1, S2);            \
	                         ck_assert_str_eq (S1, S2);
	/* The storage order depends on the relative position of memory,
	 * allocated by different malloc calls. This can change and
	 * thus must be determined. */
	if (attrs[3].tags[0].name[0]
	  > attrs[3].tags[1].name[0])
		TEST_STR (HTML_TAGS[0].tag, attrs[3].tags[0].name);
		TEST_STR (HTML_TAGS[1].tag, attrs[3].tags[1].name);
		TEST_STR (HTML_TAGS[1].tag, attrs[3].tags[0].name);
		TEST_STR (HTML_TAGS[0].tag, attrs[3].tags[1].name);

	/* href, 1 tag */
	ck_assert_str_eq ("href", HTML_ATTRS[1].attr);
	ck_assert_ptr_ne (HTML_ATTRS[1].attr, attrs[1].name);
	ck_assert_str_eq (HTML_ATTRS[1].attr, attrs[1].name);
	ck_assert_int_eq (HTML_ATTRS[1].tag_n, attrs[1].tag_n);
	ck_assert_ptr_ne (NULL, attrs[1].tags);

	ck_assert_ptr_ne (HTML_TAGS[2].tag, attrs[1].tags[0].name);
	ck_assert_str_eq (HTML_TAGS[2].tag, attrs[1].tags[0].name);

	/* class, global, overwrite */
	ck_assert_str_eq ("class", HTML_ATTRS[2].attr);
	ck_assert_ptr_ne (HTML_ATTRS[2].attr, attrs[0].name);
	ck_assert_str_eq (HTML_ATTRS[2].attr, attrs[0].name);
	ck_assert_int_eq (0, attrs[0].tag_n);
	ck_assert_ptr_eq (NULL, attrs[0].tags);

	/* id, global */
	ck_assert_str_eq ("id", HTML_ATTRS[3].attr);
	ck_assert_ptr_ne (HTML_ATTRS[3].attr, attrs[2].name);
	ck_assert_str_eq (HTML_ATTRS[3].attr, attrs[2].name);
	ck_assert_int_eq (0, attrs[2].tag_n);
	ck_assert_ptr_eq (NULL, attrs[2].tags);

	#undef attrs

	/* cleanup */
	SH_Validator_free (validator);
}
END_TEST

START_TEST(test_validator_spec_with_status)
{
	struct SH_Status status;
	struct SH_Validator * validator;

	/* test */
	_status_preinit (status);
	validator = SH_Validator_new_html5 (&status);
	ck_assert_ptr_ne (NULL, validator);
	ck_assert_int_eq (SUCCESS, status.status);

	ck_assert_ptr_ne (NULL, validator->attrs);
	ck_assert_int_eq (4, validator->attr_n);

	#define attrs validator->attrs

	/* lang, 2 tags */
	ck_assert_str_eq ("lang", HTML_ATTRS[0].attr);
	ck_assert_ptr_ne (HTML_ATTRS[0].attr, attrs[3].name);
	ck_assert_str_eq (HTML_ATTRS[0].attr, attrs[3].name);
	ck_assert_int_eq (HTML_ATTRS[0].tag_n, attrs[3].tag_n);
	ck_assert_ptr_ne (NULL, attrs[3].tags);
	#define TEST_STR(S1, S2) ck_assert_ptr_ne (S1, S2);            \
	                         ck_assert_str_eq (S1, S2);
	/* The storage order depends on the relative position of memory,
	 * allocated by different malloc calls. This can change and
	 * thus must be determined. */
	if (attrs[3].tags[0].name[0]
	  > attrs[3].tags[1].name[0])
		TEST_STR (HTML_TAGS[0].tag, attrs[3].tags[0].name);
		TEST_STR (HTML_TAGS[1].tag, attrs[3].tags[1].name);
		TEST_STR (HTML_TAGS[1].tag, attrs[3].tags[0].name);
		TEST_STR (HTML_TAGS[0].tag, attrs[3].tags[1].name);

	/* href, 1 tag */
	ck_assert_str_eq ("href", HTML_ATTRS[1].attr);
	ck_assert_ptr_ne (HTML_ATTRS[1].attr, attrs[1].name);
	ck_assert_str_eq (HTML_ATTRS[1].attr, attrs[1].name);
	ck_assert_int_eq (HTML_ATTRS[1].tag_n, attrs[1].tag_n);
	ck_assert_ptr_ne (NULL, attrs[1].tags);

	ck_assert_ptr_ne (HTML_TAGS[2].tag, attrs[1].tags[0].name);
	ck_assert_str_eq (HTML_TAGS[2].tag, attrs[1].tags[0].name);

	/* class, global, overwrite */
	ck_assert_str_eq ("class", HTML_ATTRS[2].attr);
	ck_assert_ptr_ne (HTML_ATTRS[2].attr, attrs[0].name);
	ck_assert_str_eq (HTML_ATTRS[2].attr, attrs[0].name);
	ck_assert_int_eq (0, attrs[0].tag_n);
	ck_assert_ptr_eq (NULL, attrs[0].tags);

	/* id, global */
	ck_assert_str_eq ("id", HTML_ATTRS[3].attr);
	ck_assert_ptr_ne (HTML_ATTRS[3].attr, attrs[2].name);
	ck_assert_str_eq (HTML_ATTRS[3].attr, attrs[2].name);
	ck_assert_int_eq (0, attrs[2].tag_n);
	ck_assert_ptr_eq (NULL, attrs[2].tags);

	#undef attrs

	/* cleanup */
	SH_Validator_free (validator);
}
END_TEST

START_TEST(test_validator_register_no_status)
{
	struct SH_Validator * validator;
	const char * tag1 = "html";
	const char * tag2 = "foobarbaz"; // mustn't be registered yet
	const char * tag3 = "body";
	const char * tag4 = "nav";
	const char * tag5 = "aside";
	const char * attr1 = "id";
	const char * attr2 = "class";
	const char * attr3 = "name";
	const char * attr4 = "src";
	const char * attr5 = "content";
	const char * attr6 = "lang";
	char * attrN;
	validator = SH_Validator_new (NULL);
	ck_assert_ptr_ne (NULL, validator);

	/* test - register */
	result = SH_Validator_register_attr (validator, tag1, attr1,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (1, validator->attr_n);
	ck_assert_ptr_ne (attr1, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[0].name);
	ck_assert_int_eq (1, validator->attrs[0].tag_n);
	ck_assert_ptr_ne (tag1, validator->attrs[0].tags[0].name);
	ck_assert_str_eq (tag1, validator->attrs[0].tags[0].name);
	/* test - duplicate registration */
	result = SH_Validator_register_attr (validator, tag1, attr1,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (1, validator->attr_n);
	ck_assert_ptr_ne (attr1, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[0].name);
	ck_assert_int_eq (1, validator->attrs[0].tag_n);
	ck_assert_ptr_ne (tag1, validator->attrs[0].tags[0].name);
	ck_assert_str_eq (tag1, validator->attrs[0].tags[0].name);

	/* test - automatic tag register */
	result = SH_Validator_register_attr (validator, tag2, attr1,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (1, validator->attr_n);
	ck_assert_ptr_ne (attr1, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[0].name);
	#define TEST_STR(S1, S2) ck_assert_ptr_ne (S1, S2);            \
	                         ck_assert_str_eq (S1, S2);
	ck_assert_int_eq (2, validator->attrs[0].tag_n);
	/* The storage order depends on the relative position of memory,
	 * allocated by different malloc calls. This can change and
	 * thus must be determined. */
	if (validator->attrs[0].tags[0].name[0]
	  > validator->attrs[0].tags[1].name[0])
	{
		TEST_STR(tag1, validator->attrs[0].tags[0].name)
		TEST_STR(tag2, validator->attrs[0].tags[1].name)
		TEST_STR(tag2, validator->attrs[0].tags[0].name)
		TEST_STR(tag1, validator->attrs[0].tags[1].name)
	#undef TEST_STR
	/* test - order (attr) */
	result = SH_Validator_register_attr (validator, tag1, attr3,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);
	result = SH_Validator_register_attr (validator, tag1, attr4,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);
	result = SH_Validator_register_attr (validator, tag1, attr5,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (4, validator->attr_n);
	ck_assert_str_eq (attr5, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[1].name);
	ck_assert_str_eq (attr3, validator->attrs[2].name);
	ck_assert_str_eq (attr4, validator->attrs[3].name);
	result = SH_Validator_register_attr (validator, tag2, attr4,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);
	result = SH_Validator_register_attr (validator, tag3, attr4,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);
	result = SH_Validator_register_attr (validator, tag4, attr4,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);
	result = SH_Validator_register_attr (validator, tag5, attr4,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);
	#define attr validator->attrs[3]
	ck_assert_int_eq (5, attr.tag_n);
	for (size_t i = 0; i < attr.tag_n-1; i++)
		ck_assert_int_lt ((size_t)attr.tags[i].name,
		                  (size_t)attr.tags[i+1].name);
	/* test - change to global attr */
	result = SH_Validator_register_attr (validator, NULL, attr1,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);

	ck_assert_int_eq (4, validator->attr_n);
	ck_assert_str_eq (attr5, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[1].name);
	ck_assert_str_eq (attr3, validator->attrs[2].name);
	ck_assert_str_eq (attr4, validator->attrs[3].name);

	ck_assert_int_eq (0, validator->attrs[1].tag_n);
	ck_assert_ptr_eq (NULL, validator->attrs[1].tags);

	/* test - new global attr */
	result = SH_Validator_register_attr (validator, NULL, attr6,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);

	ck_assert_int_eq (5, validator->attr_n);
	ck_assert_str_eq (attr5, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[1].name);
	ck_assert_str_eq (attr6, validator->attrs[2].name);
	ck_assert_str_eq (attr3, validator->attrs[3].name);
	ck_assert_str_eq (attr4, validator->attrs[4].name);

	ck_assert_int_eq (0, validator->attrs[2].tag_n);
	ck_assert_ptr_eq (NULL, validator->attrs[2].tags);

	/* test - existing global attr */
	result = SH_Validator_register_attr (validator, tag1, attr6,
	                                     NULL);
	ck_assert_int_eq (TRUE, result);

	ck_assert_int_eq (5, validator->attr_n);
	ck_assert_str_eq (attr5, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[1].name);
	ck_assert_str_eq (attr6, validator->attrs[2].name);
	ck_assert_str_eq (attr3, validator->attrs[3].name);
	ck_assert_str_eq (attr4, validator->attrs[4].name);

	ck_assert_int_eq (0, validator->attrs[2].tag_n);
	ck_assert_ptr_eq (NULL, validator->attrs[2].tags);

	/* test - overflow detection */
	/* make method fail by filling with garbage until
	 * upper boundary is reached */

	/* ensure enough space  inside string*/
	/* log10 +1 = number length */
	/* +4 "attr" */
	/* +1 '\0' */
	/* = +5 */
	attrN = malloc (((int) floor (log10 ((double) SIZE_MAX))) + 6);
	ck_assert_ptr_ne (NULL, attrN);

	/* fill with garbage */
	do
	{
		sprintf (attrN, "attr%zu", validator->attr_n);
	}
	while (SH_Validator_register_attr (validator, tag1, attrN, NULL));
	/* test overflow */
	result = SH_Validator_register_attr (validator, tag1, attr2,
	                                     NULL);
	ck_assert_int_eq (FALSE, result);
	ck_assert_int_eq (10, validator->attr_n);

	/* cleanup */
	/* also free garbage created for overflow test */
	validator->attr_n = 10;
	SH_Validator_free (validator);
}

START_TEST(test_validator_register_with_status)
{
	struct SH_Status status;
	struct SH_Validator * validator;
	const char * tag1 = "html";
	const char * tag2 = "foobarbaz"; // mustn't be registered yet
	const char * tag3 = "body";
	const char * tag4 = "nav";
	const char * tag5 = "aside";
	const char * attr1 = "id";
	const char * attr2 = "class";
	const char * attr3 = "name";
	const char * attr4 = "src";
	const char * attr5 = "content";
	const char * attr6 = "lang";
	char * attrN;
	validator = SH_Validator_new (NULL);
	ck_assert_ptr_ne (NULL, validator);

	/* test - register */
	_status_preinit (status);
	result = SH_Validator_register_attr (validator, tag1, attr1,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);

	ck_assert_int_eq (1, validator->attr_n);
	ck_assert_ptr_ne (attr1, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[0].name);
	ck_assert_int_eq (1, validator->attrs[0].tag_n);
	ck_assert_ptr_ne (tag1, validator->attrs[0].tags[0].name);
	ck_assert_str_eq (tag1, validator->attrs[0].tags[0].name);
	/* test - duplicate registration */
	_status_preinit (status);
	result = SH_Validator_register_attr (validator, tag1, attr1,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);

	ck_assert_int_eq (1, validator->attr_n);
	ck_assert_ptr_ne (attr1, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[0].name);
	ck_assert_int_eq (1, validator->attrs[0].tag_n);
	ck_assert_ptr_ne (tag1, validator->attrs[0].tags[0].name);
	ck_assert_str_eq (tag1, validator->attrs[0].tags[0].name);

	/* test - automatic tag register */
	_status_preinit (status);
	result = SH_Validator_register_attr (validator, tag2, attr1,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);

	ck_assert_int_eq (1, validator->attr_n);
	ck_assert_ptr_ne (attr1, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[0].name);
	#define TEST_STR(S1, S2) ck_assert_ptr_ne (S1, S2);            \
	                         ck_assert_str_eq (S1, S2);
	ck_assert_int_eq (2, validator->attrs[0].tag_n);
	/* The storage order depends on the relative position of memory,
	 * allocated by different malloc calls. This can change and
	 * thus must be determined. */
	if (validator->attrs[0].tags[0].name[0]
	  > validator->attrs[0].tags[1].name[0])
	{
		TEST_STR(tag1, validator->attrs[0].tags[0].name)
		TEST_STR(tag2, validator->attrs[0].tags[1].name)
		TEST_STR(tag2, validator->attrs[0].tags[0].name)
		TEST_STR(tag1, validator->attrs[0].tags[1].name)
	_status_preinit (status);
	result = SH_Validator_register_attr (validator, tag1, attr3,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);

	_status_preinit (status);
	result = SH_Validator_register_attr (validator, tag1, attr4,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);

	_status_preinit (status);
	result = SH_Validator_register_attr (validator, tag1, attr5,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);
	ck_assert_int_eq (4, validator->attr_n);
	ck_assert_str_eq (attr5, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[1].name);
	ck_assert_str_eq (attr3, validator->attrs[2].name);
	ck_assert_str_eq (attr4, validator->attrs[3].name);
	/* test - order (tag) */
	_status_preinit (status);
	result = SH_Validator_register_attr (validator, tag2, attr4,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);
	result = SH_Validator_register_attr (validator, tag3, attr4,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);
	result = SH_Validator_register_attr (validator, tag4, attr4,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);
	result = SH_Validator_register_attr (validator, tag5, attr4,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);

	ck_assert_int_eq (5, validator->attrs[3].tag_n);

	#define attr validator->attrs[3]
	ck_assert_int_eq (5, attr.tag_n);
	for (size_t i = 0; i < attr.tag_n-1; i++)
		ck_assert_int_lt ((size_t)attr.tags[i].name,
		                  (size_t)attr.tags[i+1].name);
	/* test - change to global attr */
	_status_preinit (status);
	result = SH_Validator_register_attr (validator, NULL, attr1,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);

	ck_assert_int_eq (4, validator->attr_n);
	ck_assert_str_eq (attr5, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[1].name);
	ck_assert_str_eq (attr3, validator->attrs[2].name);
	ck_assert_str_eq (attr4, validator->attrs[3].name);

	ck_assert_int_eq (0, validator->attrs[1].tag_n);
	ck_assert_ptr_eq (NULL, validator->attrs[1].tags);

	/* test - new global attr */
	_status_preinit (status);
	result = SH_Validator_register_attr (validator, NULL, attr6,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);

	ck_assert_int_eq (5, validator->attr_n);
	ck_assert_str_eq (attr5, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[1].name);
	ck_assert_str_eq (attr6, validator->attrs[2].name);
	ck_assert_str_eq (attr3, validator->attrs[3].name);
	ck_assert_str_eq (attr4, validator->attrs[4].name);

	ck_assert_int_eq (0, validator->attrs[2].tag_n);
	ck_assert_ptr_eq (NULL, validator->attrs[2].tags);

	/* test - existing global attr */
	_status_preinit (status);
	result = SH_Validator_register_attr (validator, tag1, attr6,
	                                     &status);
	ck_assert_int_eq (TRUE, result);
	ck_assert_int_eq (SUCCESS, status.status);

	ck_assert_int_eq (5, validator->attr_n);
	ck_assert_str_eq (attr5, validator->attrs[0].name);
	ck_assert_str_eq (attr1, validator->attrs[1].name);
	ck_assert_str_eq (attr6, validator->attrs[2].name);
	ck_assert_str_eq (attr3, validator->attrs[3].name);
	ck_assert_str_eq (attr4, validator->attrs[4].name);

	ck_assert_int_eq (0, validator->attrs[2].tag_n);
	ck_assert_ptr_eq (NULL, validator->attrs[2].tags);

	/* test - overflow detection */
	/* make method fail by filling with garbage until
	 * upper boundary is reached */

	/* ensure enough space  inside string*/
	/* log10 +1 = number length */
	/* +4 "attr" */
	/* +1 '\0' */
	/* = +5 */
	attrN = malloc (((int) floor (log10 ((double) SIZE_MAX))) + 6);
	ck_assert_ptr_ne (NULL, attrN);

	/* fill with garbage */
	do
	{
		sprintf (attrN, "attr%zu", validator->attr_n);
	}
	while (SH_Validator_register_attr (validator, tag1, attrN, NULL));
	/* test overflow */
	_status_preinit (status);
	result = SH_Validator_register_attr (validator, tag1, attr2,
	                                     &status);
	ck_assert_int_eq (FALSE, result);
	ck_assert_int_eq (E_DOMAIN, status.status);

	ck_assert_int_eq (10, validator->attr_n);

	/* cleanup */
	/* also free garbage created for overflow test */
	validator->attr_n = 10;
	SH_Validator_free (validator);
}

START_TEST(test_validator_deregister_no_status)
{
	struct SH_Validator * validator;
	bool result;

	/* setup */
	validator = SH_Validator_new (NULL);
	ck_assert_ptr_ne (NULL, validator);

	#define REGISTER SH_Validator_register_attr
	result = REGISTER (validator, "html", "attr", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, "html", "id", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, "html", "name", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, "html", "class", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, "body", "attr", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, "body", "id", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, "body", "name", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, "body", "class", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, NULL, "v", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, NULL, "w", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, NULL, "x", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, NULL, "y", NULL);
	ck_assert_int_eq (TRUE, result);
	result = REGISTER (validator, NULL, "z", NULL);
	ck_assert_int_eq (TRUE, result);
	#undef REGISTER

	/* test - consistency */
	ck_assert_int_eq (9, validator->attr_n);
	ck_assert_str_eq ("attr", validator->attrs[0].name);
	ck_assert_str_eq ("class", validator->attrs[1].name);
	ck_assert_str_eq ("id", validator->attrs[2].name);
	ck_assert_str_eq ("name", validator->attrs[3].name);
	ck_assert_str_eq ("v", validator->attrs[4].name);
	ck_assert_str_eq ("w", validator->attrs[5].name);
	ck_assert_str_eq ("x", validator->attrs[6].name);
	ck_assert_str_eq ("y", validator->attrs[7].name);
	ck_assert_str_eq ("z", validator->attrs[8].name);
	ck_assert_int_eq (2, validator->attrs[0].tag_n);
	ck_assert_int_eq (2, validator->attrs[1].tag_n);
	ck_assert_int_eq (2, validator->attrs[2].tag_n);
	ck_assert_int_eq (2, validator->attrs[3].tag_n);
	ck_assert_int_eq (0, validator->attrs[4].tag_n);
	ck_assert_int_eq (0, validator->attrs[5].tag_n);
	ck_assert_int_eq (0, validator->attrs[6].tag_n);
	ck_assert_int_eq (0, validator->attrs[7].tag_n);
	ck_assert_int_eq (0, validator->attrs[8].tag_n);
	ck_assert_ptr_ne (NULL, validator->attrs[0].tags);
	ck_assert_ptr_ne (NULL, validator->attrs[1].tags);
	ck_assert_ptr_ne (NULL, validator->attrs[2].tags);
	ck_assert_ptr_ne (NULL, validator->attrs[3].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[4].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[5].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[6].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[7].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[8].tags);
	#define STR_EQ ck_assert_str_eq
	/* The storage order depends on the relative position of memory,
	 * allocated by different malloc calls. This can change and
	 * thus must be determined. */
	if (validator->attrs[0].tags[0].name[0]
	  > validator->attrs[0].tags[1].name[0])
	{
		STR_EQ ("html", validator->attrs[0].tags[0].name);
		STR_EQ ("html", validator->attrs[1].tags[0].name);
		STR_EQ ("html", validator->attrs[2].tags[0].name);
		STR_EQ ("html", validator->attrs[3].tags[0].name);
		STR_EQ ("body", validator->attrs[0].tags[1].name);
		STR_EQ ("body", validator->attrs[1].tags[1].name);
		STR_EQ ("body", validator->attrs[2].tags[1].name);
		STR_EQ ("body", validator->attrs[3].tags[1].name);
		STR_EQ ("html", validator->attrs[0].tags[1].name);
		STR_EQ ("html", validator->attrs[1].tags[1].name);
		STR_EQ ("html", validator->attrs[2].tags[1].name);
		STR_EQ ("html", validator->attrs[3].tags[1].name);
		STR_EQ ("body", validator->attrs[0].tags[0].name);
		STR_EQ ("body", validator->attrs[1].tags[0].name);
		STR_EQ ("body", validator->attrs[2].tags[0].name);
		STR_EQ ("body", validator->attrs[3].tags[0].name);
	#undef STR_EQ
	/* test - existent attr */
	result = SH_Validator_deregister_attr (validator, "html", "attr",
	                                       NULL);
	ck_assert_int_eq (TRUE, result);

	/* test - non existent tag */
	result = SH_Validator_deregister_attr (validator, "html", "attr",
	                                       NULL);
	ck_assert_int_eq (FALSE, result);

	/* test - existent attr, auto remove */
	result = SH_Validator_deregister_attr (validator, "body", "attr",
	                                       NULL);
	ck_assert_int_eq (TRUE, result);

	ck_assert_int_eq (8, validator->attr_n);
	ck_assert_str_eq ("class", validator->attrs[0].name);
	ck_assert_str_eq ("id", validator->attrs[1].name);
	ck_assert_str_eq ("name", validator->attrs[2].name);
	ck_assert_str_eq ("v", validator->attrs[3].name);
	ck_assert_str_eq ("w", validator->attrs[4].name);
	ck_assert_str_eq ("x", validator->attrs[5].name);
	ck_assert_str_eq ("y", validator->attrs[6].name);
	ck_assert_str_eq ("z", validator->attrs[7].name);
	ck_assert_int_eq (2, validator->attrs[0].tag_n);
	ck_assert_int_eq (2, validator->attrs[1].tag_n);
	ck_assert_int_eq (2, validator->attrs[2].tag_n);
	ck_assert_int_eq (0, validator->attrs[3].tag_n);
	ck_assert_int_eq (0, validator->attrs[4].tag_n);
	ck_assert_int_eq (0, validator->attrs[5].tag_n);
	ck_assert_int_eq (0, validator->attrs[6].tag_n);
	ck_assert_int_eq (0, validator->attrs[7].tag_n);
	ck_assert_ptr_ne (NULL, validator->attrs[0].tags);
	ck_assert_ptr_ne (NULL, validator->attrs[1].tags);
	ck_assert_ptr_ne (NULL, validator->attrs[2].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[3].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[4].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[5].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[6].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[7].tags);
	/* test - non existent attr */
	result = SH_Validator_deregister_attr (validator, "html", "attr",
	                                       NULL);
	ck_assert_int_eq (FALSE, result);

	/* test - existent attr, total remove */
	result = SH_Validator_deregister_attr (validator, NULL, "name",
	                                       NULL);
	ck_assert_int_eq (TRUE, result);

	/* test - non existent attr, total remove */
	result = SH_Validator_deregister_attr (validator, NULL, "attr",
	                                       NULL);
	ck_assert_int_eq (FALSE, result);

	/* test - consistency */
	ck_assert_int_eq (7, validator->attr_n);
	ck_assert_str_eq ("class", validator->attrs[0].name);
	ck_assert_str_eq ("id", validator->attrs[1].name);
	ck_assert_str_eq ("v", validator->attrs[2].name);
	ck_assert_str_eq ("w", validator->attrs[3].name);
	ck_assert_str_eq ("x", validator->attrs[4].name);
	ck_assert_str_eq ("y", validator->attrs[5].name);
	ck_assert_str_eq ("z", validator->attrs[6].name);
	ck_assert_int_eq (2, validator->attrs[0].tag_n);
	ck_assert_int_eq (2, validator->attrs[1].tag_n);
	ck_assert_int_eq (0, validator->attrs[2].tag_n);
	ck_assert_int_eq (0, validator->attrs[3].tag_n);
	ck_assert_int_eq (0, validator->attrs[4].tag_n);
	ck_assert_int_eq (0, validator->attrs[5].tag_n);
	ck_assert_int_eq (0, validator->attrs[6].tag_n);
	ck_assert_ptr_ne (NULL, validator->attrs[0].tags);
	ck_assert_ptr_ne (NULL, validator->attrs[1].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[2].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[3].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[4].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[5].tags);
	ck_assert_ptr_eq (NULL, validator->attrs[6].tags);

	#define STR_EQ ck_assert_str_eq
	/* The storage order depends on the relative position of memory,
	 * allocated by different malloc calls. This can change and
	 * thus must be determined. */
	if (validator->attrs[0].tags[0].name[0]
	  > validator->attrs[0].tags[1].name[0])
	{
		STR_EQ ("html", validator->attrs[0].tags[0].name);
		STR_EQ ("html", validator->attrs[1].tags[0].name);
		STR_EQ ("body", validator->attrs[0].tags[1].name);
		STR_EQ ("body", validator->attrs[1].tags[1].name);
		STR_EQ ("html", validator->attrs[0].tags[1].name);
		STR_EQ ("html", validator->attrs[1].tags[1].name);
		STR_EQ ("body", validator->attrs[0].tags[0].name);
		STR_EQ ("body", validator->attrs[1].tags[0].name);
	#undef STR_EQ


	/* test - existent tag, total remove */
	result = SH_Validator_deregister_attr (validator, "html", NULL,
	                                       NULL);
	ck_assert_int_eq (TRUE, result);

	/* test - non existent tag, total remove */
	result = SH_Validator_deregister_attr (validator, "nav", NULL,