blob: 7257c7b4b67f2e84e575c27c0cf34d30743f163c [file] [log] [blame]
/* libnih
*
* test_list.c - test suite for nih/list.c
*
* Copyright © 2009 Scott James Remnant <scott@netsplit.com>.
* Copyright © 2009 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* 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 <nih/test.h>
#include <nih/macros.h>
#include <nih/alloc.h>
#include <nih/list.h>
void
test_init (void)
{
NihList entry;
/* Check that nih_list_init correctly initialises an empty list,
* with both pointers pointing back to the entry itself.
*/
TEST_FUNCTION ("nih_list_init");
nih_list_init (&entry);
TEST_EQ_P (entry.prev, &entry);
TEST_EQ_P (entry.next, &entry);
}
void
test_new (void)
{
NihList *list;
/* Check that nih_list_new allocates a new empty list with nih_alloc
* and that it is initialised with pointers pointing to itself. If
* allocation fails, we should get NULL returned.
*/
TEST_FUNCTION ("nih_list_new");
TEST_ALLOC_FAIL {
list = nih_list_new (NULL);
if (test_alloc_failed) {
TEST_EQ_P (list, NULL);
continue;
}
TEST_ALLOC_SIZE (list, sizeof (NihList));
TEST_EQ_P (list->prev, list);
TEST_EQ_P (list->next, list);
nih_free (list);
}
}
void
test_entry_new (void)
{
NihListEntry *list;
/* Check that nih_list_entry_new allocates a new empty list entry with
* nih_alloc and that it is initialised with pointers pointing to
* itself. If allocation fails, we should get NULL returned.
*/
TEST_FUNCTION ("nih_list_entry_new");
TEST_ALLOC_FAIL {
list = nih_list_entry_new (NULL);
if (test_alloc_failed) {
TEST_EQ_P (list, NULL);
continue;
}
TEST_ALLOC_SIZE (list, sizeof (NihListEntry));
TEST_EQ_P (list->entry.prev, &list->entry);
TEST_EQ_P (list->entry.next, &list->entry);
TEST_EQ_P (list->data, NULL);
nih_free (list);
}
}
void
test_add (void)
{
NihList *list, *entry1, *entry2, *ptr;
TEST_FUNCTION ("nih_list_add");
list = nih_list_new (NULL);
entry1 = nih_list_new (NULL);
entry2 = nih_list_new (NULL);
/* Check that nih_list_add can add a single entry to an empty list;
* the added entry should be returned and the pointers should all
* chain up.
*/
TEST_FEATURE ("with single-entry list");
ptr = nih_list_add (list, entry1);
TEST_EQ_P (ptr, entry1);
TEST_EQ_P (list->next, entry1);
TEST_EQ_P (entry1->next, list);
TEST_EQ_P (list->prev, entry1);
TEST_EQ_P (entry1->prev, list);
/* Check that we can now add another entry to that two entry list,
* and the pointers are still all right.
*/
TEST_FEATURE ("with multi-entry list");
nih_list_add (list, entry2);
TEST_EQ_P (list->next, entry1);
TEST_EQ_P (entry1->next, entry2);
TEST_EQ_P (entry2->next, list);
TEST_EQ_P (list->prev, entry2);
TEST_EQ_P (entry2->prev, entry1);
TEST_EQ_P (entry1->prev, list);
/* Check that we can use nih_list_add to swap two entries that are
* in the same list.
*/
TEST_FEATURE ("with two entries from same list");
nih_list_add (entry1, entry2);
TEST_EQ_P (list->next, entry2);
TEST_EQ_P (entry2->next, entry1);
TEST_EQ_P (entry1->next, list);
TEST_EQ_P (list->prev, entry1);
TEST_EQ_P (entry1->prev, entry2);
TEST_EQ_P (entry2->prev, list);
/* Check that we can rip an entry out of its list and place it in
* a new empty one.
*/
TEST_FEATURE ("with entry from other list");
ptr = nih_list_new (NULL);
nih_list_add (ptr, entry2);
TEST_EQ_P (list->next, entry1);
TEST_EQ_P (entry1->next, list);
TEST_EQ_P (list->prev, entry1);
TEST_EQ_P (entry1->prev, list);
TEST_EQ_P (ptr->next, entry2);
TEST_EQ_P (entry2->next, ptr);
TEST_EQ_P (ptr->prev, entry2);
TEST_EQ_P (entry2->prev, ptr);
nih_free (list);
nih_free (entry1);
nih_free (entry2);
nih_free (ptr);
}
void
test_add_after (void)
{
NihList *list, *entry1, *entry2, *ptr;
TEST_FUNCTION ("nih_list_add_after");
list = nih_list_new (NULL);
entry1 = nih_list_new (NULL);
entry2 = nih_list_new (NULL);
/* Check that nih_list_add_after can add a single entry to an empty
* list, the result should be the same as nih_list_add.
*/
TEST_FEATURE ("with single-entry list");
ptr = nih_list_add_after (list, entry1);
TEST_EQ_P (ptr, entry1);
TEST_EQ_P (list->next, entry1);
TEST_EQ_P (entry1->next, list);
TEST_EQ_P (list->prev, entry1);
TEST_EQ_P (entry1->prev, list);
/* Check that when adding an entry to a list with multiple entries,
* nih_list_add_after adds the entry immediately after the entry
* given, not before (as nih_list_add does)
*/
TEST_FEATURE ("with multi-entry list");
nih_list_add_after (list, entry2);
TEST_EQ_P (list->next, entry2);
TEST_EQ_P (entry2->next, entry1);
TEST_EQ_P (entry1->next, list);
TEST_EQ_P (list->prev, entry1);
TEST_EQ_P (entry1->prev, entry2);
TEST_EQ_P (entry2->prev, list);
/* Check that nih_list_add_after can be used to swap two entries
* around.
*/
TEST_FEATURE ("with two entries from same list");
nih_list_add_after (entry1, entry2);
TEST_EQ_P (list->next, entry1);
TEST_EQ_P (entry1->next, entry2);
TEST_EQ_P (entry2->next, list);
TEST_EQ_P (list->prev, entry2);
TEST_EQ_P (entry2->prev, entry1);
TEST_EQ_P (entry1->prev, list);
/* Check that nih_list_add_after can rip an entry out of its
* containing list, and add it to a new one.
*/
TEST_FEATURE ("with entry from other list");
ptr = nih_list_new (NULL);
nih_list_add_after (ptr, entry1);
TEST_EQ_P (list->next, entry2);
TEST_EQ_P (entry2->next, list);
TEST_EQ_P (list->prev, entry2);
TEST_EQ_P (entry2->prev, list);
TEST_EQ_P (ptr->next, entry1);
TEST_EQ_P (entry1->next, ptr);
TEST_EQ_P (ptr->prev, entry1);
TEST_EQ_P (entry1->prev, ptr);
nih_free (list);
nih_free (entry1);
nih_free (entry2);
nih_free (ptr);
}
void
test_empty (void)
{
NihList *list, *entry;
TEST_FUNCTION ("NIH_LIST_EMPTY");
/* Check that NIH_LIST_EMPTY is TRUE on an empty list */
TEST_FEATURE ("with empty list");
list = nih_list_new (NULL);
TEST_LIST_EMPTY (list);
/* Check that NIH_LIST_EMPTY is FALSE on a non-empty list */
TEST_FEATURE ("with non-empty list");
entry = nih_list_new (NULL);
nih_list_add (list, entry);
TEST_LIST_NOT_EMPTY (list);
TEST_LIST_NOT_EMPTY (entry);
nih_free (list);
nih_free (entry);
}
void
test_foreach (void)
{
NihList *list, *entry[3];
int i;
/* Check that NIH_LIST_FOREACH iterates the list correctly in
* order, visiting each entry.
*/
TEST_FUNCTION ("NIH_LIST_FOREACH");
list = nih_list_new (NULL);
entry[0] = nih_list_add (list, nih_list_new (NULL));
entry[1] = nih_list_add (list, nih_list_new (NULL));
entry[2] = nih_list_add (list, nih_list_new (NULL));
i = 0;
NIH_LIST_FOREACH (list, iter) {
if (i > 2)
TEST_FAILED ("wrong number of iterations, expected %d got %d",
3, i + 1);
if (iter != entry[i])
TEST_FAILED ("wrong list entry, expected %p got %p",
entry[i], iter);
i++;
}
nih_free (list);
nih_free (entry[0]);
nih_free (entry[1]);
nih_free (entry[2]);
}
void
test_foreach_safe (void)
{
NihList *list, *entry[3];
int i;
TEST_FUNCTION ("NIH_LIST_FOREACH_SAFE");
/* Check that NIH_LIST_FOREACH_SAFE iterates the list correctly in
* order, visiting each entry.
*/
TEST_FEATURE ("with ordinary iteration");
list = nih_list_new (NULL);
entry[0] = nih_list_add (list, nih_list_new (NULL));
entry[1] = nih_list_add (list, nih_list_new (NULL));
entry[2] = nih_list_add (list, nih_list_new (NULL));
i = 0;
NIH_LIST_FOREACH_SAFE (list, iter) {
if (i > 2)
TEST_FAILED ("wrong number of iterations, expected %d got %d",
3, i + 1);
if (iter != entry[i])
TEST_FAILED ("wrong list entry, expected %p got %p",
entry[i], iter);
i++;
}
nih_free (list);
nih_free (entry[0]);
nih_free (entry[1]);
nih_free (entry[2]);
/* Check that NIH_LIST_FOREACH_SAFE iterates the list correctly in
* order, visiting each entry; and that it's safe to remove entries
* while doing so.
*/
TEST_FEATURE ("with removal of visited node");
list = nih_list_new (NULL);
entry[0] = nih_list_add (list, nih_list_new (NULL));
entry[1] = nih_list_add (list, nih_list_new (NULL));
entry[2] = nih_list_add (list, nih_list_new (NULL));
i = 0;
NIH_LIST_FOREACH_SAFE (list, iter) {
if (i > 2)
TEST_FAILED ("wrong number of iterations, expected %d got %d",
3, i + 1);
if (iter != entry[i])
TEST_FAILED ("wrong list entry, expected %p got %p",
entry[i], iter);
nih_list_remove (entry[i]);
i++;
}
/* Check that the list is now empty */
TEST_LIST_EMPTY (list);
nih_free (list);
nih_free (entry[0]);
nih_free (entry[1]);
nih_free (entry[2]);
/* Check that NIH_LIST_FOREACH_SAFE iterates the list correctly in
* order, visiting each entry; and that it's safe to remove the
* next entry while doing so.
*/
TEST_FEATURE ("with removal of next node");
list = nih_list_new (NULL);
entry[0] = nih_list_add (list, nih_list_new (NULL));
entry[1] = nih_list_add (list, nih_list_new (NULL));
entry[2] = nih_list_add (list, nih_list_new (NULL));
i = 0;
NIH_LIST_FOREACH_SAFE (list, iter) {
if (i > 2)
TEST_FAILED ("wrong number of iterations, expected %d got %d",
3, i + 1);
if (iter != entry[i])
TEST_FAILED ("wrong list entry, expected %p got %p",
entry[i], iter);
if (i == 0)
nih_list_remove (entry[1]);
/* Next entry visited should be 2 */
i += 2;
}
nih_free (list);
nih_free (entry[0]);
nih_free (entry[1]);
nih_free (entry[2]);
}
void
test_remove (void)
{
NihList *list, *entry, *tail, *ptr;
TEST_FUNCTION ("nih_list_remove");
list = nih_list_new (NULL);
entry = nih_list_add (list, nih_list_new (NULL));
tail = nih_list_add (list, nih_list_new (NULL));
/* Check that nih_list_remove works, returning the entry that was
* removed and adjusting both sets of pointers in the lists.
*/
TEST_FEATURE ("with two-entry list");
ptr = nih_list_remove (entry);
TEST_EQ_P (ptr, entry);
TEST_EQ_P (list->next, tail);
TEST_EQ_P (tail->next, list);
TEST_EQ_P (list->prev, tail);
TEST_EQ_P (tail->prev, list);
TEST_EQ_P (entry->next, entry);
TEST_EQ_P (entry->prev, entry);
/* Check that nih_list_remove works if there is only one entry in the
* list that's not the head, the pointers should both curl in.
*/
TEST_FEATURE ("with one-entry list");
ptr = nih_list_remove (tail);
TEST_EQ_P (list->next, list);
TEST_EQ_P (list->prev, list);
TEST_EQ_P (tail->next, tail);
TEST_EQ_P (tail->prev, tail);
/* Check that it works on an empty list, this should do nothing. */
TEST_FEATURE ("with empty list");
ptr = nih_list_remove (tail);
TEST_EQ_P (tail->next, tail);
TEST_EQ_P (tail->prev, tail);
nih_free (list);
nih_free (entry);
nih_free (tail);
}
void
test_destroy (void)
{
NihList *list, *entry, *tail;
int ret;
/* Check that the function removes the entry from its containing
* list, it needn't bother updating the entry itself seeing as it's
* being freed anyway.
*/
TEST_FUNCTION ("nih_list_destroy");
list = nih_list_new (NULL);
entry = nih_list_add (list, nih_list_new (NULL));
tail = nih_list_add (list, nih_list_new (NULL));
ret = nih_list_destroy (entry);
TEST_EQ (ret, 0);
TEST_EQ_P (list->next, tail);
TEST_EQ_P (tail->next, list);
TEST_EQ_P (list->prev, tail);
TEST_EQ_P (tail->prev, list);
nih_free (entry);
nih_free (list);
nih_free (tail);
}
int
main (int argc,
char *argv[])
{
test_init ();
test_new ();
test_entry_new ();
test_add ();
test_add_after ();
test_empty ();
test_foreach ();
test_foreach_safe ();
test_remove ();
test_destroy ();
return 0;
}