1309 lines
39 KiB
C
1309 lines
39 KiB
C
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h> // for EXIT_FAILURE
|
|
#include <list.h>
|
|
|
|
// Macro to compute length of a C array
|
|
#define LEN(x) (sizeof(x)/sizeof(x[0]))
|
|
|
|
/*
|
|
* Test program for the list implementation in list.c. This version
|
|
* does not use a free-handler, i.e., the list will not be responsible
|
|
* for deallocating any allocated elements stored in it. Instead, the
|
|
* responsibility remains with the user of the list.
|
|
*
|
|
* Author: Niclas Borlin (niclas.borlin@cs.umu.se).
|
|
*
|
|
* Version information:
|
|
* v1.0 2023-01-21: First public version.
|
|
* v1.1 2023-03-21: Removed empty if statements in test code.
|
|
* v1.2 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal.
|
|
* v1.3 2024-03-13: Added explicit create/kill functions.
|
|
*/
|
|
|
|
#define VERSION "v1.3"
|
|
#define VERSION_DATE "2024-03-13"
|
|
|
|
/*
|
|
* Function to compare the values stored in the list.
|
|
*/
|
|
bool value_equal(int v1, int v2)
|
|
{
|
|
return v1 == v2;
|
|
}
|
|
|
|
/**
|
|
* int_create() - make a dynamic copy of an integer
|
|
* @i: Integer to copy
|
|
*
|
|
* Returns: A pointer to a dynamic copy of i
|
|
*/
|
|
int *int_create(int i)
|
|
{
|
|
// Allocate memory for an integer and set the value
|
|
int *v=malloc(sizeof(*v));
|
|
*v = i;
|
|
return v;
|
|
}
|
|
|
|
// Return the memory used by the integer.
|
|
void int_kill(void *v)
|
|
{
|
|
int *p=v;
|
|
free(p);
|
|
}
|
|
|
|
/*
|
|
* empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer.
|
|
* Precondition: None.
|
|
*/
|
|
void empty_returns_non_null(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting empty_returns_non_null()...");
|
|
|
|
// Create the list without a free-handler.
|
|
list *l = list_empty(NULL);
|
|
|
|
// l should be non-NULL
|
|
if (l == NULL) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* empty_is_empty() - Test that the list_empty() list is empty.
|
|
* Precondition: list_empty() returns non-null.
|
|
*/
|
|
void empty_is_empty(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting empty_is_empty()...");
|
|
|
|
// Create the list without a free-handler.
|
|
list *l = list_empty(NULL);
|
|
|
|
// The list returned by empty() should be is_empty()
|
|
if (!list_is_empty(l)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* empty_first_end() - Test that first() == end() on an empty list.
|
|
* Precondition: list_is_empty(l) == false.
|
|
*/
|
|
void empty_first_end(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting empty_first_end()...");
|
|
|
|
// Create the list without a free-handler.
|
|
list *l = list_empty(NULL);
|
|
|
|
// first(l) should be == end(l) for an empty list
|
|
if (!(list_pos_is_equal(l, list_first(l), list_end(l)))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* kill_all_element_values() - kill all element values stored in the list
|
|
* @l - List to operate on
|
|
*
|
|
* Returns: A pointer to the modified list
|
|
*
|
|
* The function traverses the list and calls int_kill for the value (void *)
|
|
* stored in each element. WARNING: This should only be executed
|
|
* immediately before the list is killed since all element values are
|
|
* UNDEFINED after the call.
|
|
*/
|
|
list *kill_all_element_values(list *l)
|
|
{
|
|
// Iterate over all elements
|
|
list_pos p=list_first(l);
|
|
while (!list_pos_is_equal(l, p, list_end(l))) {
|
|
// Read the stored pointer value
|
|
void *q = list_inspect(l, p);
|
|
// Free its memory
|
|
int_kill(q);
|
|
// Advance to the next element
|
|
p = list_next(l, p);
|
|
}
|
|
return l;
|
|
}
|
|
|
|
/**
|
|
* create_one_element_list() - Create a list with one element
|
|
* @v: Value to insert
|
|
*
|
|
* Preconditions: list_empty() and list_first() works.
|
|
*
|
|
* Returns: A list with one element with the value v.
|
|
*
|
|
* The list is created by inserting an element at the first position
|
|
* of an empty list.
|
|
*/
|
|
list *create_one_element_list(int v)
|
|
{
|
|
// Create the list
|
|
list *l = list_empty(NULL);
|
|
// Make a dynamic copy of v.
|
|
int *q = int_create(v);
|
|
// Insert an element at the only position in the empty list
|
|
list_insert(l, q, list_first(l));
|
|
|
|
return l;
|
|
}
|
|
|
|
/**
|
|
* create_two_element_list() - Create a list with two elements
|
|
* @v1: Value to insert first
|
|
* @v2: Value to insert at the end
|
|
*
|
|
* Preconditions: list_empty(), list_first(), list_next(), list_insert() works.
|
|
*
|
|
* Returns: A list with two elements with the values v1 and v2 in that order.
|
|
*
|
|
* The the element with value v2 is inserted after the element with value v1.
|
|
*/
|
|
list *create_two_element_list(int v1, int v2)
|
|
{
|
|
// Create the list
|
|
list *l = list_empty(NULL);
|
|
// Make dynamic copies of v1 and v2
|
|
int *q1 = int_create(v1);
|
|
int *q2 = int_create(v2);
|
|
// Insert the first element
|
|
list_pos p = list_insert(l, q1, list_first(l));
|
|
// Insert the second element *after* the first
|
|
list_insert(l, q2, list_next(l, p));
|
|
|
|
return l;
|
|
}
|
|
|
|
/*
|
|
* one_element_list_is_nonempty() - Test that a list with one element is not empty.
|
|
* Precondition: list_empty() returns non-null.
|
|
* first(l) == end(l)
|
|
*/
|
|
void one_element_list_is_nonempty(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting one_element_list_is_nonempty()...");
|
|
|
|
// Create a one-element list storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
// One-element list should be non-empty
|
|
if (list_is_empty(l)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* one_element_list_has_first_neq_end() - Test that a list with one element has first() != end().
|
|
* Precondition: list_empty(), list_first() works
|
|
*/
|
|
void one_element_list_has_first_neq_end(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting one_element_list_has_first_neq_end()...");
|
|
|
|
// Create a one-element list storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
// One-element list should have first() != end()
|
|
if (list_pos_is_equal(l, list_first(l), list_end(l))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: one-element list has first() == end()\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos.
|
|
* Precondition: list_empty(), list_first() works
|
|
*/
|
|
void insert_first_returns_correct_pos(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting insert_first_returns_correct_pos()...");
|
|
|
|
// Create the list
|
|
list *l = list_empty(NULL);
|
|
// Create a dynamic integer
|
|
int *q = int_create(24);
|
|
// Insert an element with an arbitrary value
|
|
list_pos p = list_insert(l, q, list_first(l));
|
|
|
|
// The returned position should be first in the modified list
|
|
if (!list_pos_is_equal(l, p, list_first(l))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: position returned by insert() != first\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* inserted_element_has_correct_value() - Test that the inserted value can be recovered.
|
|
* Precondition: list_empty(), list_first() works, list_insert() returns correct pos.
|
|
*/
|
|
void inserted_element_has_correct_value(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting inserted_element_has_correct_value()...");
|
|
|
|
// Decide an arbitrary value to insert
|
|
int val = 24;
|
|
// Create a one-element list with the value
|
|
list *l = create_one_element_list(24);
|
|
|
|
// The value at the returned position should be the one we inserted
|
|
int *stored_ptr = list_inspect(l, list_first(l));
|
|
int stored_value = *stored_ptr;
|
|
|
|
if (!value_equal(stored_value, val)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", stored_value, val);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* next_does_something() - Test that next returns another position than its argument.
|
|
* Precondition: list_empty(), list_first() works,
|
|
*/
|
|
void next_does_something(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting next_does_something()...");
|
|
|
|
// Create a one-element list storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
list_pos p = list_first(l);
|
|
|
|
// Next(p) should be != p
|
|
if (list_pos_is_equal(l, list_next(l, p), p)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected next(p) != p\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end().
|
|
* Precondition: list_empty(), list_first(), list_end() works
|
|
*/
|
|
void one_element_list_next_eq_end(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting one_element_list_next_eq_end()...");
|
|
|
|
// Create a one-element list storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
// Next(First()) should be == End(l)
|
|
if (!list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected next(p) == end(l)\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* prev_does_something() - Test that prev returns another position than its argument.
|
|
* Precondition: list_empty(), list_first(), list_end() works
|
|
*/
|
|
void prev_does_something(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting prev_does_something()...");
|
|
|
|
// Create a one-element list storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
list_pos p = list_end(l);
|
|
|
|
// Prev(p) should be != p
|
|
if (list_pos_is_equal(l, list_prev(l, p), p)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected prev(p) != p\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* one_element_list_prev_end_eq_first() - Test that prev on
|
|
* end() in a one-element list returns first().
|
|
* Precondition: list_empty(), list_first(), list_end() works
|
|
*/
|
|
void one_element_list_prev_end_eq_first(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting one_element_list_prev_eq_end()...");
|
|
|
|
// Create a one-element list storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
// Prev(End()) should be == First(l)
|
|
if (!list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected prev(p) == end(l)\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/**
|
|
* prev_is_inv_next() - Check if prev is inverse of next
|
|
* @l: A list with a least one element
|
|
* @p: A position, not equal to end
|
|
*
|
|
* Returns: True if Prev(l, Next(l, p)) == p
|
|
*/
|
|
bool prev_is_inv_next(const list *l, const list_pos p)
|
|
{
|
|
return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p);
|
|
}
|
|
|
|
/**
|
|
* next_is_inv_prev() - Check if next is inverse of prev
|
|
* @l: A list with a least one element
|
|
* @p: A position, not equal to first
|
|
*
|
|
* Returns: True if Next(l, Prev(l, p)) == p
|
|
*/
|
|
bool next_is_inv_prev(const list *l, const list_pos p)
|
|
{
|
|
return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p);
|
|
}
|
|
|
|
/*
|
|
* one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a
|
|
* one-element list.
|
|
* Precondition: list_empty(), list_first(), list_end() works
|
|
*/
|
|
void one_element_list_prev_is_inv_next(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting one_element_list_prev_is_inv_next()...");
|
|
|
|
// Create a one-element list storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
// Check that prev is inverse of next on first()
|
|
if (!prev_is_inv_next(l, list_first(l))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a
|
|
* one-element list.
|
|
* Precondition: list_empty(), list_first(), list_end() works
|
|
*/
|
|
void one_element_list_next_is_inv_prev(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting one_element_list_next_is_inv_prev()...");
|
|
|
|
// Create a one-element list storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
// Check that next is inverse of prev on end()
|
|
if (!next_is_inv_prev(l, list_end(l))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* insert_remove_is_empty() - Test that insert followed by remove gives empty list.
|
|
* Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works
|
|
*/
|
|
void insert_remove_is_empty(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting insert_remove_is_empty()...");
|
|
|
|
// Create a one-element list storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
// Free stored pointer before removing element
|
|
int *q = list_inspect(l, list_first(l));
|
|
int_kill(q);
|
|
|
|
// Remove only element
|
|
list_remove(l, list_first(l));
|
|
|
|
// Check that list is empty
|
|
if (!list_is_empty(l)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* insert_remove_returns_end() - Test that insert followed by remove gives empty list.
|
|
* Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works
|
|
*/
|
|
void insert_remove_returns_end(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting insert_remove_returns_end()...");
|
|
|
|
// Create a one-element list storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
// Free stored pointer before removing element
|
|
int *q = list_inspect(l, list_first(l));
|
|
int_kill(q);
|
|
|
|
// Remove only element
|
|
list_pos p = list_remove(l, list_first(l));
|
|
|
|
// Check that remove(first()) on one-element list returns end()
|
|
if (!list_pos_is_equal(l, list_end(l), p)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: remove(insert()) did not return end()\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* one_element_list_check_insert_first_pos() - Check return value of insert(first()) on
|
|
* one-element list
|
|
*
|
|
* Precondition: list_first(), list_end(), list_next(), list_prev(), works
|
|
*/
|
|
void one_element_list_check_insert_first_pos(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting one_element_list_check_insert_first_pos()...");
|
|
|
|
// Create a two-element lists storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
// Make dynamic copy of value to insert
|
|
int *q = int_create(30);
|
|
|
|
list_pos p = list_insert(l, q, list_first(l));
|
|
|
|
// Returned position should be == first(l)
|
|
if (!list_pos_is_equal(l, p, list_first(l))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: insert(first()) on one-element list did not "
|
|
"return correct position\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* one_element_list_check_insert_end_pos() - Check return value of insert(end()) on
|
|
* one-element list
|
|
* Precondition: list_first(), list_end(), list_next(), list_prev(), works
|
|
*/
|
|
void one_element_list_check_insert_end_pos(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting one_element_list_check_insert_end_pos()...");
|
|
|
|
// Create a two-element lists storing an arbitrary value
|
|
list *l = create_one_element_list(24);
|
|
|
|
// Make dynamic copy of element value to insert
|
|
int *q = int_create(30);
|
|
|
|
list_pos p = list_insert(l, q, list_end(l));
|
|
|
|
// Returned position should be == prev(end(l))
|
|
if (!list_pos_is_equal(l, p, list_prev(l, list_end(l)))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: insert(end()) on one-element list did not return "
|
|
"correct position\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* two_element_list_check_links() - Test next(first()) and prev(end()) are equal on
|
|
* two-element list
|
|
* Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works
|
|
*/
|
|
void two_element_list_check_links(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting two_element_list_check_links()...");
|
|
|
|
// Create a two-element lists storing an arbitrary value
|
|
list *l = create_two_element_list(24, 30);
|
|
|
|
// Check prev(next()) on each valid position...
|
|
|
|
// ...start with first
|
|
if (!prev_is_inv_next(l, list_first(l))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: prev(next()) failed on first()\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// ...then middle
|
|
if (!prev_is_inv_next(l, list_next(l, list_first(l)))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Check next(prev()) on each valid position...
|
|
|
|
// ...start with end
|
|
if (!next_is_inv_prev(l, list_end(l))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: next(prev()) failed on end()\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// ...then middle
|
|
if (!prev_is_inv_next(l, list_prev(l, list_end(l)))) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* one_element_list_insert_end() - Insert a value at end() of a one-element list and check
|
|
* the order
|
|
* Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works
|
|
*/
|
|
void one_element_list_insert_end(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting one_element_list_insert_end()...");
|
|
|
|
// Numbers in the expected order
|
|
int v[2] = { 11, 12 };
|
|
|
|
// Create a one-element lists with 11
|
|
list *l = create_one_element_list(v[0]);
|
|
|
|
// Make dynamic copy of element to insert
|
|
int *q1 = int_create(v[1]);
|
|
|
|
// Insert new element at end
|
|
list_insert(l, q1, list_end(l));
|
|
|
|
// Check that we got the expected element order
|
|
list_pos p = list_first(l);
|
|
|
|
for (int i = 0; i < LEN(v); i++) {
|
|
int *stored_ptr = list_inspect(l, p);
|
|
int stored_value = *stored_ptr;
|
|
|
|
if (!value_equal(v[i], stored_value)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n",
|
|
v[i], stored_value);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
p = list_next(l, p);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* one_element_list_insert_first() - Insert a value at first() of a one-element list and
|
|
* check the order
|
|
* Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works
|
|
*/
|
|
void one_element_list_insert_first(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting one_element_list_insert_first()...");
|
|
|
|
// Numbers in the expected order
|
|
int v[2] = { 11, 12 };
|
|
|
|
// Create a one-element lists with 12
|
|
list *l = create_one_element_list(v[1]);
|
|
|
|
// Make dynamic copy of element to insert
|
|
int *q0 = int_create(v[0]);
|
|
|
|
// Insert new element at first
|
|
list_insert(l, q0, list_first(l));
|
|
|
|
// Check that we got the expected element order
|
|
list_pos p = list_first(l);
|
|
|
|
for (int i = 0; i < LEN(v); i++) {
|
|
int *stored_ptr = list_inspect(l, p);
|
|
int stored_value = *stored_ptr;
|
|
|
|
if (!value_equal(v[i], stored_value)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n",
|
|
v[i], stored_value);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
p = list_next(l, p);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* two_element_list_insert_end() - Insert a value at end() of a two-element list and check
|
|
* the order
|
|
* Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works
|
|
*/
|
|
void two_element_list_insert_end(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting two_element_list_insert_end()...");
|
|
|
|
// Numbers in the expected order
|
|
int v[3] = { 11, 12, 13 };
|
|
|
|
// Create a two-element lists with 11
|
|
list *l = create_two_element_list(v[0],v[1]);
|
|
|
|
// Make dynamic copy of value to insert
|
|
int *q2 = int_create(v[2]);
|
|
|
|
// Insert new element at end
|
|
list_insert(l, q2, list_end(l));
|
|
|
|
// Check that we got the expected element order
|
|
list_pos p = list_first(l);
|
|
|
|
for (int i = 0; i < LEN(v); i++) {
|
|
int *stored_ptr = list_inspect(l, p);
|
|
int stored_value = *stored_ptr;
|
|
|
|
if (!value_equal(v[i], stored_value)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n",
|
|
v[i], stored_value);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
p = list_next(l, p);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* two_element_list_insert_first() - Insert a value at first() of a two-element list and
|
|
* check the order
|
|
* Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works
|
|
*/
|
|
void two_element_list_insert_first(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting two_element_list_insert_first()...");
|
|
|
|
// Numbers in the expected order
|
|
int v[3] = { 11, 12, 13 };
|
|
|
|
// Create a two-element lists with 11
|
|
list *l = create_two_element_list(v[1],v[2]);
|
|
|
|
// Make dynamic copy of value to insert
|
|
int *q0 = int_create(v[0]);
|
|
|
|
// Insert new element at first
|
|
list_insert(l, q0, list_first(l));
|
|
|
|
// Check that we got the expected element order
|
|
list_pos p = list_first(l);
|
|
|
|
for (int i = 0; i < LEN(v); i++) {
|
|
int *stored_ptr = list_inspect(l, p);
|
|
int stored_value = *stored_ptr;
|
|
|
|
if (!value_equal(v[i], stored_value)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n",
|
|
v[i], stored_value);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
p = list_next(l, p);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* two_element_list_insert_middle() - Insert a value in the middle of a two-element list
|
|
* and check the order
|
|
* Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works
|
|
*/
|
|
void two_element_list_insert_middle(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting two_element_list_insert_middle()...");
|
|
|
|
// Numbers in the expected order
|
|
int v[3] = { 11, 12, 13 };
|
|
|
|
// Create a two-element lists with 11
|
|
list *l = create_two_element_list(v[0],v[2]);
|
|
|
|
// Make dynamic copy of value to insert
|
|
int *q1 = int_create(v[1]);
|
|
|
|
// Insert new element at next(first())
|
|
list_insert(l, q1, list_next(l, list_first(l)));
|
|
|
|
// Check that we got the expected element order
|
|
list_pos p = list_first(l);
|
|
|
|
for (int i = 0; i < LEN(v); i++) {
|
|
int *stored_ptr = list_inspect(l, p);
|
|
int stored_value = *stored_ptr;
|
|
|
|
if (!value_equal(v[i], stored_value)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n",
|
|
v[i], stored_value);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
p = list_next(l, p);
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order
|
|
* Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works
|
|
*/
|
|
void n_element_list_insert_end(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting n_element_list_insert_end()...");
|
|
|
|
// Start with an empty list
|
|
list *l = list_empty(NULL);
|
|
|
|
// n is the number of elements in the list
|
|
for (int n = 1; n <= 5; n++) {
|
|
// Make dynamic copy of value to insert
|
|
int *q = int_create(n);
|
|
|
|
// insert the value n at the end of the list
|
|
list_insert(l, q, list_end(l));
|
|
|
|
// Check that the element values are in the expected order, 1, ..., n
|
|
list_pos p = list_first(l);
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
int *stored_ptr = list_inspect(l, p);
|
|
int stored_value = *stored_ptr;
|
|
|
|
if (!value_equal(i + 1, stored_value)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n",
|
|
i + 1, stored_value);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
p = list_next(l, p);
|
|
}
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* n_element_list_insert_first() - Insert a value at first() of a n-element list and check
|
|
* the order
|
|
* Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works
|
|
*/
|
|
void n_element_list_insert_first(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting n_element_list_insert_first()...");
|
|
|
|
// Start with an empty list
|
|
list *l = list_empty(NULL);
|
|
|
|
// n is the number of elements in the list
|
|
for (int n = 1; n <= 5; n++) {
|
|
// Make dynamic copy of value to insert
|
|
int *q = int_create(n);
|
|
|
|
// insert the value n at the beginning of the list
|
|
list_insert(l, q, list_first(l));
|
|
|
|
// Check that the element values are in the expected order n, n-1, ..., 1
|
|
list_pos p = list_first(l);
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
int *stored_ptr = list_inspect(l, p);
|
|
int stored_value = *stored_ptr;
|
|
|
|
if (!value_equal(n - i, stored_value)) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: expected %d, got %d after "
|
|
"insert(first())\n", n - i, stored_value);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
p = list_next(l, p);
|
|
}
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/**
|
|
* count_list_elements_forwards() - Failsafely count the number of elements in a list from front()
|
|
* @l: A list with any number of elements
|
|
* @max: The maximum number of elements to visit
|
|
*
|
|
* Returns: The number of elements in the list if <= max, otherwise -1.
|
|
*
|
|
* Traverses the list with next() from front() until end() is
|
|
* encountered. If more than max elements are encountered, bail out
|
|
* and return -1.
|
|
*/
|
|
int count_list_elements_forwards(const list *l, int max)
|
|
{
|
|
int n = 0;
|
|
list_pos p = list_first(l);
|
|
while (!list_pos_is_equal(l, p, list_end(l))) {
|
|
n++;
|
|
if (n > max) {
|
|
// We've gone past the limit, bail out
|
|
return -1;
|
|
}
|
|
p = list_next(l, p);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* count_list_elements_backwards() - Failsafely count the number of elements in a list from end()
|
|
* @l: A list with any number of elements
|
|
* @max: The maximum number of elements to visit
|
|
*
|
|
* Returns: The number of elements in the list if <= max, otherwise -1.
|
|
*
|
|
* Traverses the list with prev() from end() until first() is
|
|
* encountered. If more than max elements are encountered, bail out
|
|
* and return -1.
|
|
*/
|
|
int count_list_elements_backwards(const list *l, int max)
|
|
{
|
|
int n = 0;
|
|
list_pos p = list_end(l);
|
|
while (!list_pos_is_equal(l, p, list_first(l))) {
|
|
n++;
|
|
if (n > max) {
|
|
// We've gone past the limit, bail out
|
|
return -1;
|
|
}
|
|
p = list_prev(l, p);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* insert_and_count_forwards() - Check element count from first() after repeated inserts
|
|
* Precondition: list_empty(), list_first(), list_end(), list_next() works
|
|
*/
|
|
void insert_and_count_forwards(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting insert_and_count_forwards()...");
|
|
|
|
// Start with empty list
|
|
list *l = list_empty(NULL);
|
|
|
|
for (int n=0; n<5; n++) {
|
|
// Check the actual count.
|
|
// Use max=10 as a safeguard against infinite loops
|
|
int c = count_list_elements_forwards(l, 10);
|
|
if (c != n) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: count_forwards returned %d elements, "
|
|
"expected %d\n", c, n);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
// Make dynamic copy of value to insert
|
|
int *q = int_create(n);
|
|
|
|
// Insert one more element
|
|
list_insert(l, q, list_first(l));
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* insert_and_count_backwards() - Check element count from end() after repeated inserts
|
|
* Precondition: list_empty(), list_first(), list_end(), list_prev() works
|
|
*/
|
|
void insert_and_count_backwards(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting insert_and_count_backwards()...");
|
|
|
|
// Start with empty list
|
|
list *l = list_empty(NULL);
|
|
|
|
for (int n=0; n<5; n++) {
|
|
// Check the actual count.
|
|
// Use max=10 as a safeguard against infinite loops
|
|
int c = count_list_elements_backwards(l, 10);
|
|
if (c != n) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: count_backwards returned %d elements, "
|
|
"expected %d\n", c, n);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
// Make dynamic copy of value to insert
|
|
int *q = int_create(n);
|
|
|
|
// Insert one more element
|
|
list_insert(l, q, list_first(l));
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* remove_and_count_forwards() - Check element count from first() after repeated removes
|
|
* Precondition: list_empty(), list_first(), list_end(), list_next() works
|
|
*/
|
|
void remove_and_count_forwards(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting remove_and_count_forwards()...");
|
|
|
|
// Create a five-element list
|
|
list *l = list_empty(NULL);
|
|
for (int i = 0; i < 5; i++) {
|
|
// Make dynamic copy of value to insert
|
|
int *q = int_create(i);
|
|
|
|
list_insert(l, q, list_first(l));
|
|
}
|
|
|
|
for (int n=5; n>=0; n--) {
|
|
// Check the actual count.
|
|
// Use max=10 as a safeguard against infinite loops
|
|
int c = count_list_elements_forwards(l, 10);
|
|
if (c != n) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: count_forwards returned %d elements, "
|
|
"expected %d\n", c, n);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (n > 0) {
|
|
// Free stored pointer before removing element
|
|
int *q = list_inspect(l, list_first(l));
|
|
int_kill(q);
|
|
|
|
// Remove one element unless the list is empty
|
|
list_remove(l, list_first(l));
|
|
}
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
/*
|
|
* remove_and_count_backwards() - Check element count from first() after repeated removes
|
|
* Precondition: list_empty(), list_first(), list_end(), list_next() works
|
|
*/
|
|
void remove_and_count_backwards(void)
|
|
{
|
|
// Print a starting message
|
|
fprintf(stderr,"Starting remove_and_count_backwards()...");
|
|
|
|
// Create a five-element list
|
|
list *l = list_empty(NULL);
|
|
for (int i = 0; i < 5; i++) {
|
|
// Make dynamic copy of value to insert
|
|
int *q = int_create(i);
|
|
|
|
list_insert(l, q, list_first(l));
|
|
}
|
|
|
|
for (int n=5; n>=0; n--) {
|
|
// Check the actual count.
|
|
// Use max=10 as a safeguard against infinite loops
|
|
int c = count_list_elements_backwards(l, 10);
|
|
if (c != n) {
|
|
// Fail with error message
|
|
fprintf(stderr, "FAIL: count_backwards returned %d elements, "
|
|
"expected %d\n", c, n);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (n > 0) {
|
|
// Free stored pointer before removing element
|
|
int *q = list_inspect(l, list_first(l));
|
|
int_kill(q);
|
|
|
|
// Remove one element unless the list is empty
|
|
list_remove(l, list_first(l));
|
|
}
|
|
}
|
|
|
|
// Everything went well, clean up
|
|
fprintf(stderr,"cleaning up...");
|
|
|
|
// De-allocate all element values stored in the list before killing the list
|
|
l = kill_all_element_values(l);
|
|
list_kill(l);
|
|
fprintf(stderr,"done.\n");
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
printf("%s, %s %s: Test program for the generic list.\n"
|
|
"Does not use any free-handler to handle deallocation of dynamic memory.\n",
|
|
__FILE__, VERSION, VERSION_DATE);
|
|
printf("Uses code base version %s.\n\n", CODE_BASE_VERSION);
|
|
|
|
empty_returns_non_null();
|
|
empty_is_empty();
|
|
empty_first_end();
|
|
one_element_list_is_nonempty();
|
|
one_element_list_has_first_neq_end();
|
|
insert_first_returns_correct_pos();
|
|
inserted_element_has_correct_value();
|
|
next_does_something();
|
|
one_element_list_next_eq_end();
|
|
prev_does_something();
|
|
one_element_list_prev_end_eq_first();
|
|
one_element_list_prev_is_inv_next();
|
|
one_element_list_next_is_inv_prev();
|
|
insert_remove_is_empty();
|
|
insert_remove_returns_end();
|
|
one_element_list_check_insert_first_pos();
|
|
one_element_list_check_insert_end_pos();
|
|
two_element_list_check_links();
|
|
one_element_list_insert_end();
|
|
one_element_list_insert_first();
|
|
insert_and_count_forwards();
|
|
insert_and_count_backwards();
|
|
remove_and_count_forwards();
|
|
remove_and_count_backwards();
|
|
two_element_list_insert_end();
|
|
two_element_list_insert_first();
|
|
two_element_list_insert_middle();
|
|
n_element_list_insert_end();
|
|
n_element_list_insert_first();
|
|
|
|
fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n");
|
|
return 0;
|
|
}
|