blob: acec88b87fb90ba41019b5d74a392aa34e875cbe [file] [log] [blame]
/*
* Copyright (c) 2019 LK Trusty Authors. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <lk/list.h>
#include "gtest/gtest.h"
/**
* CheckListArray - check that a list contains specific items.
* @list: The list to check.
* @items: Array with expected items.
* @count: Number if entries in @items, and expected number of entries in
* @list.
*
* Check that @list contains the all the entries in @items, in the same order,
* and no other entries. Also check that head, tail, next and prev functions
* return the expected entries in @items.
*/
static void CheckListArray(struct list_node *list, struct list_node *items[],
int count) {
struct list_node *node;
int i = 0;
struct list_node *head_item = count ? items[0] : NULL;
EXPECT_EQ(list_peek_head(list), head_item) << "List has wrong head";
struct list_node *tail_item = count ? items[count - 1] : NULL;
EXPECT_EQ(list_peek_tail(list), tail_item) << "List has wrong tail";
list_for_every(list, node) {
ASSERT_GT(count, i) << "List has more items than expected";
EXPECT_EQ(node, items[i]) << "Wrong entry as index " << i;
struct list_node *prev_item = i > 0 ? items[i - 1] : NULL;
EXPECT_EQ(list_prev(list, node), prev_item) <<
"List has wrong back pointer at index " << i;
struct list_node *next_item = i < count - 1 ? items[i + 1] : NULL;
EXPECT_EQ(list_next(list, node), next_item) <<
"List has wrong next pointer at index " << i;
i++;
}
EXPECT_EQ(i, count) << "List has fewer items than expected";
}
/**
* CheckList - Helper macro to call CheckListArray.
*/
#define CheckList(list, items...) { \
SCOPED_TRACE("CheckList"); \
struct list_node *itemarray[] = {items}; \
CheckListArray(list, itemarray, countof(itemarray)); \
}
/**
* DefineEmptyList - Helper macro to define and initialize a list.
*/
#define DefineEmptyList(list) \
struct list_node list = LIST_INITIAL_VALUE(list);
/**
* DefineAndAddListItem - Helper macro to define and initialize a list entry and
* add it to a list.
*/
#define DefineAndAddListItem(list, item) \
struct list_node item = LIST_INITIAL_CLEARED_VALUE; \
list_add_tail(&list, &item);
/**
* DefineListWith1Item - Helper macro to define and initialize a list with 1
* item.
*/
#define DefineListWith1Item(list, item1) \
DefineEmptyList(list) \
DefineAndAddListItem(list, item1) \
CheckList(&list, &item1)
/**
* DefineListWith2Items - Helper macro to define and initialize a list with 2
* items.
*/
#define DefineListWith2Items(list, item1, item2) \
DefineListWith1Item(list, item1) \
DefineAndAddListItem(list, item2) \
CheckList(&list, &item1, &item2)
/**
* DefineListWith3Items - Helper macro to define and initialize a list with 3
* items.
*/
#define DefineListWith3Items(list, item1, item2, item3) \
DefineListWith2Items(list, item1, item2) \
DefineAndAddListItem(list, item3) \
CheckList(&list, &item1, &item2, &item3)
/* Smoke test 3 list init methods */
TEST(ListTest, ListInitialValue) {
struct list_node list = LIST_INITIAL_VALUE(list);
EXPECT_TRUE(list_in_list(&list));
EXPECT_TRUE(list_is_empty(&list));
CheckList(&list);
}
TEST(ListTest, ListInitialClearedValue) {
struct list_node list = LIST_INITIAL_CLEARED_VALUE;
EXPECT_FALSE(list_in_list(&list));
}
TEST(ListTest, ListInitialize) {
struct list_node list;
list_initialize(&list);
EXPECT_TRUE(list_in_list(&list));
EXPECT_TRUE(list_is_empty(&list));
}
/* Test 4 list add methods */
TEST(ListTest, ListAddHead) {
struct list_node list = LIST_INITIAL_VALUE(list);
struct list_node item1 = LIST_INITIAL_CLEARED_VALUE;
struct list_node item2 = LIST_INITIAL_CLEARED_VALUE;
list_add_head(&list, &item1);
CheckList(&list, &item1);
list_add_head(&list, &item2);
CheckList(&list, &item2, &item1);
}
TEST(ListTest, ListAddAfterLast) {
DefineListWith1Item(list, item1);
struct list_node item2 = LIST_INITIAL_CLEARED_VALUE;
list_add_after(&item1, &item2);
CheckList(&list, &item1, &item2);
}
TEST(ListTest, ListAddAfterNotLast) {
DefineListWith2Items(list, item1, item2);
struct list_node item3 = LIST_INITIAL_CLEARED_VALUE;
list_add_after(&item1, &item3);
CheckList(&list, &item1, &item3, &item2);
}
TEST(ListTest, ListAddTail) {
struct list_node list = LIST_INITIAL_VALUE(list);
struct list_node item1 = LIST_INITIAL_CLEARED_VALUE;
struct list_node item2 = LIST_INITIAL_CLEARED_VALUE;
list_add_tail(&list, &item1);
CheckList(&list, &item1);
list_add_tail(&list, &item2);
CheckList(&list, &item1, &item2);
}
TEST(ListTest, ListAddBeforeHead) {
DefineListWith1Item(list, item1);
struct list_node item2 = LIST_INITIAL_CLEARED_VALUE;
list_add_before(&item1, &item2);
CheckList(&list, &item2, &item1);
}
TEST(ListTest, ListAddBeforeNotHead) {
DefineListWith2Items(list, item1, item2);
struct list_node item3 = LIST_INITIAL_CLEARED_VALUE;
list_add_before(&item2, &item3);
CheckList(&list, &item1, &item3, &item2);
}
/* Test list delete 4 possible configurations of prev and next entries */
TEST(ListTest, ListDeleteOnly) {
DefineListWith1Item(list, item);
list_delete(&item);
EXPECT_FALSE(list_in_list(&item));
CheckList(&list);
}
TEST(ListTest, ListDeleteFirst) {
DefineListWith2Items(list, item1, item2);
list_delete(&item1);
EXPECT_FALSE(list_in_list(&item1));
CheckList(&list, &item2);
}
TEST(ListTest, ListDeleteLast) {
DefineListWith2Items(list, item1, item2);
list_delete(&item2);
EXPECT_FALSE(list_in_list(&item2));
CheckList(&list, &item1);
}
TEST(ListTest, ListDeleteMiddle) {
DefineListWith3Items(list, item1, item2, item3);
list_delete(&item2);
EXPECT_FALSE(list_in_list(&item2));
CheckList(&list, &item1, &item3);
}
/*
* Test list_remove_head with 3 possible configurations of empty list, head is
* last entry, and head is not the last entry.
*/
TEST(ListTest, ListRemoveHead) {
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_remove_head(&list);
EXPECT_EQ(node, &item1);
CheckList(&list, &item2);
node = list_remove_head(&list);
EXPECT_EQ(node, &item2);
CheckList(&list);
node = list_remove_head(&list);
EXPECT_EQ(node, nullptr);
CheckList(&list);
}
/*
* Test list_remove_tail with 3 possible configurations of empty list, tail is
* first entry, and tail is not the first entry.
*/
TEST(ListTest, ListRemoveTail) {
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_remove_tail(&list);
EXPECT_EQ(node, &item2);
CheckList(&list, &item1);
node = list_remove_tail(&list);
EXPECT_EQ(node, &item1);
CheckList(&list);
node = list_remove_tail(&list);
EXPECT_EQ(node, nullptr);
CheckList(&list);
}
/*
* Test list_peek_head with and without entries in the list.
* Use 2 entries to separate tail and head in non-empty test.
*/
TEST(ListTest, ListPeekHeadEmpty) {
struct list_node list = LIST_INITIAL_VALUE(list);
struct list_node *node = list_peek_head(&list);
EXPECT_EQ(node, nullptr);
}
TEST(ListTest, ListPeekHeadNotEmpty) {
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_peek_head(&list);
EXPECT_EQ(node, &item1);
}
/*
* Test list_peek_tail with and without entries in the list.
* Use 2 entries to separate tail and head in non-empty test.
*/
TEST(ListTest, ListPeekTailEmpty) {
struct list_node list = LIST_INITIAL_VALUE(list);
struct list_node *node = list_peek_tail(&list);
EXPECT_EQ(node, nullptr);
}
TEST(ListTest, ListPeekTailNotEmpty) {
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_peek_tail(&list);
EXPECT_EQ(node, &item2);
}
/* Test list navigation functions. */
TEST(ListTest, ListPrevHead) {
/*
* Calling list_prev on the head should return NULL.
*/
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_prev(&list, &item1);
EXPECT_EQ(node, nullptr);
}
TEST(ListTest, ListPrevNotHead) {
/*
* Calling list_prev on entries other than the head should return the
* previous entry.
*/
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_prev(&list, &item2);
EXPECT_EQ(node, &item1);
}
TEST(ListTest, ListPrevWrapHead) {
/*
* Calling list_prev_wrap on the head should return the tail.
*/
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_prev_wrap(&list, &item1);
EXPECT_EQ(node, &item2);
}
TEST(ListTest, ListPrevWrapNotHead) {
/*
* Calling list_prev_wrap on entries other than the head should return the
* previous entry (same as list_prev).
*/
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_prev_wrap(&list, &item2);
EXPECT_EQ(node, &item1);
}
TEST(ListTest, ListPrevWrapEmpty) {
/*
* Calling list_prev_wrap on the list itself, instead of an entry does not
* appear to be a useful operation, but the implemention allows it and
* returns NULL in that case.
*/
DefineEmptyList(list);
struct list_node *node = list_prev_wrap(&list, &list);
EXPECT_EQ(node, nullptr);
}
TEST(ListTest, ListNextHead) {
/*
* Calling list_next on the tail should return NULL.
*/
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_next(&list, &item2);
EXPECT_EQ(node, nullptr);
}
TEST(ListTest, ListNextNotTail) {
/*
* Calling list_next on entries other than the tail should return the next
* entry.
*/
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_next(&list, &item1);
EXPECT_EQ(node, &item2);
}
TEST(ListTest, ListNextWrapTail) {
/*
* Calling list_next_wrap on the tail should return the head.
*/
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_next_wrap(&list, &item2);
EXPECT_EQ(node, &item1);
}
TEST(ListTest, ListNextWrapNotTail) {
/*
* Calling list_next_wrap on entries other than the tail should return the
* next entry (same as list_next).
*/
DefineListWith2Items(list, item1, item2);
struct list_node *node = list_next_wrap(&list, &item1);
EXPECT_EQ(node, &item2);
}
TEST(ListTest, ListNextWrapEmpty) {
/*
* Calling list_next_wrap on the list itself, instead of an entry does not
* appear to be a useful operation, but the implemention allows it and
* returns NULL in that case.
*/
DefineEmptyList(list);
struct list_node *node = list_next_wrap(&list, &list);
EXPECT_EQ(node, nullptr);
}
/* Test list iterators */
TEST(ListTest, ListForEvery) {
/*
* list_for_every should return every entry one by one.
*/
DefineListWith2Items(list, item1, item2);
struct list_node *itemarray[] = {&item1, &item2};
size_t i = 0;
struct list_node *node;
list_for_every(&list, node) {
ASSERT_LT(i, countof(itemarray));
EXPECT_EQ(node, itemarray[i]);
i++;
}
EXPECT_EQ(i, countof(itemarray));
}
TEST(ListTest, ListForEverySafe) {
/*
* list_for_every_safe should return every entry one by one even if the
* current entry is removed (not tested here).
*/
DefineListWith2Items(list, item1, item2);
struct list_node *itemarray[] = {&item1, &item2};
size_t i = 0;
struct list_node *node;
struct list_node *tmp_node;
list_for_every_safe(&list, node, tmp_node) {
ASSERT_LT(i, countof(itemarray));
EXPECT_EQ(node, itemarray[i]);
i++;
}
EXPECT_EQ(i, countof(itemarray));
}
TEST(ListTest, ListForEverySafeDeleteAll) {
/*
* list_for_every_safe should return every entry one by one even if the
* current entry is removed (tested here).
*/
DefineListWith2Items(list, item1, item2);
struct list_node *itemarray[] = {&item1, &item2};
size_t i = 0;
struct list_node *node;
struct list_node *tmp_node;
list_for_every_safe(&list, node, tmp_node) {
ASSERT_LT(i, countof(itemarray));
EXPECT_EQ(node, itemarray[i]);
list_delete(node);
i++;
}
EXPECT_EQ(i, countof(itemarray));
CheckList(&list);
}
TEST(ListTest, ListForEverySafeDeleteSome) {
/*
* list_for_every_safe should return every entry one by one even if the
* current entry is removed (tested here).
*/
DefineListWith3Items(list, item1, item2, item3);
struct list_node *itemarray[] = {&item1, &item2, &item3};
size_t i = 0;
struct list_node *node;
struct list_node *tmp_node;
list_for_every_safe(&list, node, tmp_node) {
ASSERT_LT(i, countof(itemarray));
EXPECT_EQ(node, itemarray[i]);
if (i != 1) {
list_delete(node);
}
i++;
}
EXPECT_EQ(i, countof(itemarray));
CheckList(&list, &item2);
}
/* Test list empty and count functions */
TEST(ListTest, ListIsEmptyEmpty) {
DefineEmptyList(list);
EXPECT_TRUE(list_is_empty(&list));
}
TEST(ListTest, ListIsEmptyNotEmpty) {
DefineListWith1Item(list, item);
EXPECT_FALSE(list_is_empty(&list));
}
TEST(ListTest, ListLengthEmpty) {
DefineEmptyList(list);
EXPECT_EQ(list_length(&list), 0U);
}
TEST(ListTest, ListLength1) {
DefineListWith1Item(list, item);
EXPECT_EQ(list_length(&list), 1U);
}
TEST(ListTest, ListLength2) {
DefineListWith2Items(list, item1, item2);
EXPECT_EQ(list_length(&list), 2U);
}
/* Test list splice operations */
TEST(ListTest, ListSpliceTailBothEmpty) {
DefineEmptyList(list1);
DefineEmptyList(list2);
list_splice_tail(&list1, &list2);
CheckList(&list1);
CheckList(&list2);
}
TEST(ListTest, ListSpliceTailSrcEmpty) {
DefineListWith2Items(list1, item1, item2);
DefineEmptyList(list2);
list_splice_tail(&list1, &list2);
CheckList(&list1, &item1, &item2);
CheckList(&list2);
}
TEST(ListTest, ListSpliceTailSrc1Item) {
DefineListWith2Items(list1, item1, item2);
DefineListWith1Item(list2, item3);
list_splice_tail(&list1, &list2);
CheckList(&list1, &item1, &item2, &item3);
CheckList(&list2);
}
TEST(ListTest, ListSpliceTailDestEmpty) {
DefineEmptyList(list1);
DefineListWith2Items(list2, item1, item2);
list_splice_tail(&list1, &list2);
CheckList(&list1, &item1, &item2);
CheckList(&list2);
}
TEST(ListTest, ListSpliceTailDest1Item) {
DefineListWith1Item(list1, item1);
DefineListWith2Items(list2, item2, item3);
list_splice_tail(&list1, &list2);
CheckList(&list1, &item1, &item2, &item3);
CheckList(&list2);
}
TEST(ListTest, ListSpliceTail) {
DefineListWith2Items(list1, item1, item2);
DefineListWith2Items(list2, item3, item4);
list_splice_tail(&list1, &list2);
CheckList(&list1, &item1, &item2, &item3, &item4);
CheckList(&list2);
}
TEST(ListTest, ListSpliceHeadBothEmpty) {
DefineEmptyList(list1);
DefineEmptyList(list2);
list_splice_head(&list1, &list2);
CheckList(&list1);
CheckList(&list2);
}
TEST(ListTest, ListSpliceHeadSrcEmpty) {
DefineListWith2Items(list1, item1, item2);
DefineEmptyList(list2);
list_splice_head(&list1, &list2);
CheckList(&list1, &item1, &item2);
CheckList(&list2);
}
TEST(ListTest, ListSpliceHeadSrc1Item) {
DefineListWith2Items(list1, item2, item3);
DefineListWith1Item(list2, item1);
list_splice_head(&list1, &list2);
CheckList(&list1, &item1, &item2, &item3);
CheckList(&list2);
}
TEST(ListTest, ListSpliceHeadDestEmpty) {
DefineEmptyList(list1);
DefineListWith2Items(list2, item1, item2);
list_splice_head(&list1, &list2);
CheckList(&list1, &item1, &item2);
CheckList(&list2);
}
TEST(ListTest, ListSpliceHeadDest1Item) {
DefineListWith1Item(list1, item3);
DefineListWith2Items(list2, item1, item2);
list_splice_head(&list1, &list2);
CheckList(&list1, &item1, &item2, &item3);
CheckList(&list2);
}
TEST(ListTest, ListSpliceHead) {
DefineListWith2Items(list1, item3, item4);
DefineListWith2Items(list2, item1, item2);
list_splice_head(&list1, &list2);
CheckList(&list1, &item1, &item2, &item3, &item4);
CheckList(&list2);
}
TEST(ListTest, ListSpliceAfter) {
DefineListWith2Items(list1, item1, item4);
DefineListWith2Items(list2, item2, item3);
list_splice_after(&item1, &list2);
CheckList(&list1, &item1, &item2, &item3, &item4);
CheckList(&list2);
}
TEST(ListTest, ListSpliceBefore) {
DefineListWith2Items(list1, item1, item4);
DefineListWith2Items(list2, item2, item3);
list_splice_before(&item4, &list2);
CheckList(&list1, &item1, &item2, &item3, &item4);
CheckList(&list2);
}