blob: 93fe19a27aea2cb765bd327a6e5457d5611aaff0 [file] [log] [blame]
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include "doubly-linked-list.h"
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
/* Implementation */
typedef int T;
typedef struct ListNodeType
{
T value;
struct ListNodeType *next;
struct ListNodeType *prev;
} ListNodeType;
ListNodeType * l_new_node (T value)
{
ListNodeType *n = malloc (sizeof (ListNodeType));
n->next = NULL;
n->prev = NULL;
n->value = value;
return n;
}
typedef struct LinkedListWrapperType
{
ListNodeType *first;
ListNodeType *last;
size_t size;
} LinkedListWrapperType;
int compare_nodes (const ListNodeType *n1, const ListNodeType *n2)
{
if (n1->value == n2->value)
return 0;
else if (n1->value < n2->value)
return -1;
else
return 1;
}
LINKED_LIST_MUTATIVE_OPS_PROTOTYPE (LinkedListWrapperType, ListNodeType, static);
LINKED_LIST_MERGE_SORT_PROTOTYPE (LinkedListWrapperType, ListNodeType, static);
LINKED_LIST_MUTATIVE_OPS_DECL (LinkedListWrapperType, ListNodeType, static)
LINKED_LIST_MERGE_SORT_DECL (LinkedListWrapperType, ListNodeType, static)
ListNodeType * find_last_node (ListNodeType *head)
{
if (head == NULL)
return NULL;
ListNodeType *n = head;
while (n->next != NULL)
n = n->next;
return n;
}
void l_print (ListNodeType *node)
{
for (ListNodeType *l = node; l != NULL; l = l->next)
printf ("%d ", l->value);
printf ("\n");
}
void l_reverse_print (ListNodeType *last_node)
{
for (ListNodeType *l = last_node; l != NULL; l = l->prev)
printf ("%d ", l->value);
printf ("\n");
}
struct test_data_t
{
T const *content;
size_t size;
};
bool run_test (const struct test_data_t *expect,
LinkedListWrapperType *current,
bool reversed)
{
ListNodeType *node = (reversed) ? current->last : current->first;
bool passed = true;
for (int i=0; i<expect->size && node != NULL; ++i)
{
if (reversed)
{
if (expect->content[expect->size - 1 - i] != node->value)
{
printf ("FAIL: mismatching expected (%d) VS current (%d).\n",
expect->content[expect->size - 1 - i], node->value);
passed = false;
}
if (node->prev == NULL && current->first != node)
{
printf ("FAIL: first is not matching the first node.\n");
passed = false;
}
}
else
{
if (expect->content[i] != node->value)
{
printf ("FAIL: mismatching expected (%d) VS current (%d).\n",
expect->content[i], node->value);
passed = false;
}
if (node->next == NULL && current->last != node)
{
printf ("FAIL: last_ is not matching the last node.\n");
passed = false;
}
}
if (!passed)
return false;
if (reversed)
node = node->prev;
else
node = node->next;
}
if (node != NULL)
{
printf ("FAIL: the list is longer than expected.\n");
passed = false;
}
if (expect->size != current->size)
{
printf ("FAIL: size (%ld) is not matching the real size of the list (%ld).\n",
current->size, expect->size);
passed = false;
}
return passed;
}
bool check(const char *op,
const struct test_data_t *expect,
LinkedListWrapperType *wrapper)
{
bool success = true;
bool res;
#define DUMP_LIST 0
if (DUMP_LIST)
l_print (wrapper->first);
res = run_test (expect, wrapper, false);
printf ("%s: test-linked-list::%s: check forward conformity\n",
res ? "PASS": "FAIL", op);
success &= res;
if (DUMP_LIST)
l_reverse_print (wrapper->last);
res = run_test (expect, wrapper, true);
printf ("%s: test-linked-list::%s: check backward conformity\n",
res ? "PASS": "FAIL", op);
success &= res;
if (DUMP_LIST)
printf("\n");
return success;
}
const int EXPECT_0 [] = { 10, 4, 3, 1, 9, 2 };
const int EXPECT_1 [] = { 1, 2, 3, 4, 9, 10 };
const int EXPECT_2 [] = { 11, 1, 2, 3, 4, 9, 10 };
const int EXPECT_3 [] = { 11, 1, 2, 3, 4, 9, 8, 10 };
const int EXPECT_4 [] = { 11, 2, 3, 4, 9, 8, 10 };
const int EXPECT_5 [] = { 10, 2, 3, 4, 9, 8, 11 };
const int EXPECT_6 [] = { 10, 3, 2, 4, 9, 8, 11 };
const int EXPECT_7 [] = { 10, 9, 2, 4, 3, 8, 11 };
const int EXPECT_8 [] = { 2, 3, 4, 8, 9, 10, 11 };
const int EXPECT_9 [] = { 3, 4, 8, 9, 10, 11 };
const int EXPECT_10 [] = { 3, 4, 8, 9, 10 };
const struct test_data_t test_data[] = {
{ .content = EXPECT_0, .size = sizeof(EXPECT_0) / sizeof(EXPECT_0[0]) },
{ .content = EXPECT_1, .size = sizeof(EXPECT_1) / sizeof(EXPECT_1[0]) },
{ .content = EXPECT_2, .size = sizeof(EXPECT_2) / sizeof(EXPECT_2[0]) },
{ .content = EXPECT_3, .size = sizeof(EXPECT_3) / sizeof(EXPECT_3[0]) },
{ .content = EXPECT_4, .size = sizeof(EXPECT_4) / sizeof(EXPECT_4[0]) },
{ .content = EXPECT_5, .size = sizeof(EXPECT_5) / sizeof(EXPECT_5[0]) },
{ .content = EXPECT_6, .size = sizeof(EXPECT_6) / sizeof(EXPECT_6[0]) },
{ .content = EXPECT_7, .size = sizeof(EXPECT_7) / sizeof(EXPECT_7[0]) },
{ .content = EXPECT_8, .size = sizeof(EXPECT_8) / sizeof(EXPECT_8[0]) },
{ .content = EXPECT_9, .size = sizeof(EXPECT_9) / sizeof(EXPECT_9[0]) },
{ .content = EXPECT_10, .size = sizeof(EXPECT_10) / sizeof(EXPECT_10[0]) },
};
int main (void)
{
int failures = 0;
LinkedListWrapperType wrapper = {
.first = NULL,
.last = NULL,
.size = 0,
};
/* Append nodes. */
LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (10));
LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (4));
LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (3));
LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (1));
LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (9));
LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (2));
failures += ! check ("append", &test_data[0], &wrapper);
/* Sort nodes (without updating wrapper). */
wrapper.first =
LINKED_LIST_MERGE_SORT_(ListNodeType) (wrapper.first, compare_nodes);
wrapper.last = find_last_node (wrapper.first);
failures += ! check ("sort", &test_data[1], &wrapper);
/* Save a reference to this node for later. */
ListNodeType *n_to_remove = wrapper.first;
/* Prepend node. */
LINKED_LIST_PREPEND(ListNodeType) (&wrapper, l_new_node (11));
failures += ! check ("prepend", &test_data[2], &wrapper);
/* Insert node. */
LINKED_LIST_INSERT_BEFORE(ListNodeType) (&wrapper, l_new_node (8), wrapper.last);
failures += ! check ("insert_before", &test_data[3], &wrapper);
/* Remove a node. */
LINKED_LIST_REMOVE(ListNodeType) (&wrapper, n_to_remove);
failures += ! check ("remove", &test_data[4], &wrapper);
/* Swap first and last. */
LINKED_LIST_SWAP(ListNodeType) (&wrapper, wrapper.first, wrapper.last);
failures += ! check ("swap first and last", &test_data[5], &wrapper);
/* Swap adjacent nodes. */
LINKED_LIST_SWAP(ListNodeType) (&wrapper, wrapper.first->next,
wrapper.first->next->next);
failures += ! check ("swap adjacent nodes", &test_data[6], &wrapper);
/* Swap non-adjacent nodes, but neither first nor last. */
LINKED_LIST_SWAP(ListNodeType) (&wrapper, wrapper.first->next,
wrapper.first->next->next->next->next);
failures += ! check ("swap non-adjacent nodes", &test_data[7], &wrapper);
/* Sort nodes. */
LINKED_LIST_MERGE_SORT(ListNodeType) (&wrapper, compare_nodes);
failures += ! check ("sort", &test_data[8], &wrapper);
/* Pop front. */
LINKED_LIST_POP_FRONT(ListNodeType) (&wrapper);
failures += ! check ("pop_front", &test_data[9], &wrapper);
/* Pop back. */
LINKED_LIST_POP_BACK(ListNodeType) (&wrapper);
failures += ! check ("pop_back", &test_data[10], &wrapper);
exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
}