/** @file  tst_psList.c
 *
 *  @brief Contains the tests for psList.[ch]
 *
 *
 *  @author Robert DeSonia, MHPCC
 *
 *  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2004-12-11 01:24:15 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 */

#include "psTest.h"
#include "pslib.h"

void printListInt(psList* list);


static psS32 testListAlloc(void);
static psS32 testListAdd(void);
static psS32 testListGet(void);
static psS32 testListRemove(void);
static psS32 testListConvert(void);
static psS32 testListIterator(void);
static psS32 testListFree(void);
static psS32 testListSort(void);

testDescription tests[] = {
                              {testListAlloc,487,"psListAlloc",0,false},
                              {testListAdd,488,"psListAdd",0,false},
                              {testListGet,489,"psListGet",0,false},
                              {testListRemove,490,"psListRemove",0,false},
                              {testListConvert,491,"psListConvert",0,false},
                              {testListIterator,494,"psListIterator",0,false},
                              {testListFree,627,"psListFree",0,false},
                              {testListSort,624,"psListSort",0,false},
                              {NULL}
                          };

psS32 main(psS32 argc, char* argv[])
{
    psLogSetLevel(PS_LOG_INFO);

    if (! runTestSuite(stderr,"psList",tests,argc,argv) ) {
        psError(PS_ERR_UNKNOWN,true,"One or more tests failed");
        return 1;
    }
    return 0;
}

psS32 testListAlloc(void)
{
    psList* list;
    psS32 ref;
    float* data;

    psLogMsg(__func__,PS_LOG_INFO,"psListAlloc shall create a psList with either 0 or 1 element.");

    data = psAlloc(sizeof(float));

    // if psListAlloc is invoked with a NULL parameter, it shall return an
    // empty psList struct with head=tail=NULL and n=0. Otherwise, it shall
    // return a psList of one element (head=tail=data, n=1).

    list = psListAlloc(NULL);
    if (list == NULL) {
        psError(PS_ERR_UNKNOWN, true,"psListAlloc failed to return a list.");
        return 1;
    }

    if (list->head != NULL || list->tail != NULL) {
        psError(PS_ERR_UNKNOWN, true,"head and/or tail was not NULL for empty list.");
        return 2;
    }

    if (list->size != 0) {
        psError(PS_ERR_UNKNOWN, true,"size of list wasn't zero for empty list.");
        return 3;
    }

    psFree(list);

    list = psListAlloc(data);
    if (list == NULL) {
        psError(PS_ERR_UNKNOWN, true,"psListAlloc failed to return a list.");
        return 4;
    }

    if (list->head == NULL || list->tail == NULL) {
        psError(PS_ERR_UNKNOWN, true,"head and/or tail was NULL for one-element new list.");
        return 5;
    }

    if (list->head->data != data || list->tail->data != data) {
        psError(PS_ERR_UNKNOWN, true,"head and/or tail didn't point to data's node for one-element new list.");
        return 6;
    }

    if (list->size != 1) {
        psError(PS_ERR_UNKNOWN, true,"size of list wasn't correctly set for new, one-element list.");
        return 7;
    }

    ref = psMemGetRefCounter(data);
    if (ref != 2) {
        psError(PS_ERR_UNKNOWN, true,"psList didn't increment reference count of data (%d.",ref);
        return 8;
    }


    psFree(list);

    psFree(data);

    return 0;
}

psS32 testListAdd(void)
{
    psList* list = NULL;
    psS32* data = NULL;

    psLogMsg(__func__,PS_LOG_INFO,"psListAdd shall add an element to list");

    /*
        psListAdd(list,data,where) should be tested in the instance where:

        1. list is NULL (error)
        2. data is NULL (error, list should not grow)
        3. where is PS_LIST_HEAD or PS_LIST_TAIL
        4. where is not PS_LIST_* but <0
        5. where is >0
    */

    data = psAlloc(sizeof(psS32));
    *data = 1;

    //  1. list is NULL (error)
    if (psListAdd(NULL,PS_LIST_HEAD,data)) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd was given a NULL list, but returned a true/success.");
        return 1;
    }

    list = psListAlloc(data);
    psFree(data);
    if (list->size != 1) {
        psError(PS_ERR_UNKNOWN, true,"psListAlloc didn't create a list properly.");
        return 2;
    }

    //  2. data is NULL (error, list should not grow)
    if (psListAdd(list, PS_LIST_HEAD,NULL)) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd successfully added a NULL data item?");
        return 40;
    }
    if ( list->size != 1) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd with a NULL data element changed the list size or returned success.");
        return 3;
    }

    //  3. where is PS_LIST_HEAD or PS_LIST_TAIL
    data = psAlloc(sizeof(psS32));
    *data = 2;
    if ( ! psListAdd(list,PS_LIST_HEAD,data) ) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add a data item to head.");
        return 21;
    }

    if (psMemGetRefCounter(data) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
        return 20;
    }
    psFree(data);

    // verify that the size incremented
    if (list->size != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the size by one");
        return 4;
    }

    // verify that the head is the inserted data item
    if (*(psS32*)list->head->data != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd with PS_LIST_HEAD didn't insert at the head.");
        return 5;
    }

    data = psAlloc(sizeof(psS32));
    *data = 3;
    if ( ! psListAdd(list,PS_LIST_TAIL,data) ) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add a data item to tail.");
        return 21;
    }

    if (psMemGetRefCounter(data) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
        return 22;
    }
    psFree(data);
    // verify that the size incremented
    if (list->size != 3) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the size by 1");
        return 6;
    }

    // verify that the head is still the same
    if (*(psS32*)list->head->data != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd with PS_LIST_TAIL modified the head.");
        return 7;
    }

    // verify that the tail is the data item inserted
    if (*(psS32*)list->tail->data != 3) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd with PS_LIST_TAIL didn't insert at the tail.");
        return 8;
    }

    // 4. where is not PS_LIST_* but <0

    data = psAlloc(sizeof(psS32));
    *data = 4;
    psLogMsg(__func__,PS_LOG_INFO,"Following should error with invalid insert location");

    if ( psListAdd(list,-10,data) ) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd successfully added data to a -10 position?");
        return 30;
    }
    if (psMemGetRefCounter(data) != 1) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd incremented the data reference count.");
        return 24;
    }
    psFree(data);
    // verify that the size wasn't incremented
    if (list->size != 3) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't insert to head when where was invalid.");
        return 9;
    }

    // 5. where is >0
    data = psAlloc(sizeof(psS32));
    *data = 5;

    if ( ! psListAdd(list,1,data) ) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add data to 1 position.");
        return 30;
    }

    if (psMemGetRefCounter(data) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
        return 25;
    }
    psFree(data);
    // verify that the size incremented
    if (list->size != 4 || *(psS32*)list->head->next->data != 5) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't insert to position #1.");
        return 10;
    }

    data = psAlloc(sizeof(psS32));
    *data = 6;
    if ( ! psListAdd(list,3,data) ) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add data to 4 position.");
        return 31;
    }
    if (psMemGetRefCounter(data) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
        return 26;
    }
    psFree(data);
    // verify that the size incremented
    if (list->size != 5  || *(psS32 *)list->head->next->next->next->data != 6) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't insert to position #4.");
        return 50;
    }

    data = psAlloc(sizeof(psS32));
    *data = 7;
    if ( ! psListAdd(list,2,data) ) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add data to 2 position.");
        return 32;
    }
    if (psMemGetRefCounter(data) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
        return 28;
    }
    psFree(data);
    // verify that the size incremented
    if (list->size != 6  || *(psS32 *)list->head->next->next->data != 7) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't insert to position #2.");
        return 11;
    }

    data = psAlloc(sizeof(psS32));
    *data = 7;
    psLogMsg(__func__,PS_LOG_INFO,"Following should be a warning.");

    if ( ! psListAdd(list,9,data) ) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add data to a 9 position?");
        return 30;
    }

    if (psMemGetRefCounter(data) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
        return 29;
    }

    psFree(data);

    // verify that the size incremented
    if (list->size != 7) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't insert to position #9.");
        return 12;
    }

    psFree(list);

    return 0;
}

void printListInt(psList* list)
{
    psS32* data = NULL;
    psBool first = true;

    psListIterator* iter = psListIteratorAlloc(list,PS_LIST_HEAD);

    while ( (data=(psS32*)psListGetNext(iter)) != NULL ) {
        if (!first) {
            printf(", %d",*(psS32*)data);
        } else {
            printf("%d",*(psS32*)data);
            first = false;
        }
    }

    printf(".\n");
}


psS32 testListGet(void)
{
    psList* list = NULL;
    psS32* data;

    /*
     psListGet(list,which) shall be tested with the following instances

        1. list is NULL.
        2. which>0 and which<list.n.
        3. which>list.n.
        4. which=PS_LIST_HEAD
        5. which=PS_LIST_NEXT
        6. which=PS_LIST_TAIL
        7. which=PS_LIST_PREV
        8. which<0 and not any PS_LIST_* values
    */

    //  1. list is NULL.
    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error");
    if (psListGet(list,PS_LIST_HEAD) != NULL) {
        psError(PS_ERR_UNKNOWN, true,"psListGet didn't return NULL given a NULL list.");
        return 1;
    };

    // create a list
    data = psAlloc(sizeof(psS32));
    *data = 0;
    list = psListAlloc(data);
    psFree(data);

    data = psAlloc(sizeof(psS32));
    *data = 1;
    psListAdd(list,PS_LIST_TAIL,data);
    psFree(data);

    data = psAlloc(sizeof(psS32));
    *data = 2;
    psListAdd(list,PS_LIST_TAIL,data);
    psFree(data);

    data = psAlloc(sizeof(psS32));
    *data = 3;
    psListAdd(list,PS_LIST_TAIL,data);
    psFree(data);

    //  2. which>0 and which<list.n.
    data = (psS32*)psListGet(list,3);
    if (data == NULL || *data != 3) {
        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=3");
        return 2;
    }
    data = (psS32*)psListGet(list,1);
    if (data == NULL || *data != 1) {
        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=1");
        return 3;
    }

    //  3. which>=list.n.
    data = (psS32*)psListGet(list,5);
    if (data != NULL) {
        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=5");
        return 4;
    }
    data = (psS32*)psListGet(list,4);
    if (data != NULL) {
        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=4");
        return 5;
    }

    //  4. which=PS_LIST_HEAD
    data = (psS32*)psListGet(list,PS_LIST_HEAD);
    if (data == NULL || *data != 0) {
        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=PS_LIST_HEAD");
        return 6;
    }

    //  6. which=PS_LIST_TAIL
    data = (psS32*)psListGet(list,PS_LIST_TAIL);
    if (data == NULL || *data != 3) {
        psError(PS_ERR_UNKNOWN, true,"psListGet failed with which=PS_LIST_TAIL");
        return 8;
    }

    psFree(list);

    return 0;
}

psS32 testListRemove(void)
{
    psList* list = NULL;
    psS32* data;
    int items = 15;

    /*
        psListRemove(list,data,which) should be tested under the following conditions:

        1. list is NULL
        2. which is PS_LIST_HEAD (remove first element of list)
        3. which is PS_LIST_TAIL (remove last element of list)
        4. which is PS_LIST_NEXT (element right of cursor [which should be head,tail, and neither])
        5. which is PS_LIST_PREV (element left of cursor [which should be head,tail, and neither])
        6. which is PS_LIST_UNKNOWN and data=NULL (error)
        7. which is PS_LIST_UNKNOWN and data=[head,tail,other list items]
        8. which is PS_LIST_UNKNOWN and data!=NULL and data!=any element in list

        In all conditions that are not an error, list.n shall be decremented and only the specified element
        shall be removed. After each step, list.n should be checked to verify that it is the true
        number of elements in list.
    */

    //  1. list is NULL.
    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error");
    if (psListRemove(list,PS_LIST_HEAD)) {
        psError(PS_ERR_UNKNOWN, true,"psListRemove didn't return false given a NULL list.");
        return 1;
    };

    // create a list
    list = psListAlloc(NULL);

    for (psS32 lcv=0;lcv<items;lcv++) {
        data = psAlloc(sizeof(psS32));
        *data = lcv;
        psListAdd(list,PS_LIST_TAIL,data);
        psMemDecrRefCounter(data);
    }


    // 2. which is PS_LIST_HEAD (remove first element of list)
    data = psListGet(list,PS_LIST_HEAD);
    if ( (! psListRemove(list,PS_LIST_HEAD)) ||
            (psListGet(list,PS_LIST_HEAD) == data) ) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Failed to remove PS_LIST_HEAD");
        return 1;
    }

    if (list->size != --items) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
        return 1;
    }

    // 3. which is PS_LIST_TAIL (remove last element of list)
    data = psListGet(list,PS_LIST_TAIL);
    if ( (! psListRemove(list,PS_LIST_TAIL)) ||
            (psListGet(list,PS_LIST_TAIL) == data) ) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Failed to remove PS_LIST_TAIL");
        return 1;
    }

    if (list->size != --items) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
        return 1;
    }

    // 6. psListRemoveData where data=NULL (error)
    psLogMsg(__func__,PS_LOG_INFO,"Next message should be an error");
    if (psListRemoveData(list,NULL)) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"removed something for PS_LIST_UNKNOWN with data=NULL");
        return 1;
    }

    // 7. which is PS_LIST_UNKNOWN and data=[head,tail,other list items]
    data = psListGet(list,PS_LIST_HEAD);
    if ( (! psListRemoveData(list,data)) ||
            (psListGet(list,PS_LIST_HEAD) == data) ) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Failed to remove PS_LIST_UNKNOWN @ PS_LIST_HEAD");
        return 1;
    }

    if (list->size != --items) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
        return 1;
    }

    data = psListGet(list,PS_LIST_TAIL);
    if ( (!psListRemoveData(list,data)) ||
            (psListGet(list,PS_LIST_TAIL) == data) ) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Failed to remove PS_LIST_UNKNOWN @ PS_LIST_TAIL");
        return 1;
    }

    if (list->size != --items) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
        return 1;
    }

    data = psListGet(list,1);
    if ( (! psListRemoveData(list,data))||
            (psListGet(list,1) == data) ) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Failed to remove data @ which=1");
        return 1;
    }

    if (list->size != --items) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
        return 1;
    }

    // 8. data!=NULL and data!=any element in list
    psLogMsg(__func__,PS_LOG_INFO,"Next message should be an error");
    if ( psListRemoveData(list,data) ) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"removed something for PS_LIST_UNKNOWN with previously removed item");
        return 1;
    }

    if (list->size != items) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"size not %d, as expected.",items);
        return 1;
    }

    // clear out the list
    while (items > 1) {
        psListRemove(list,PS_LIST_HEAD);
        items--;
    }

    data = psListGet(list,PS_LIST_HEAD);
    if ( (! psListRemove(list,PS_LIST_HEAD)) ||
            (psListGet(list,PS_LIST_HEAD) == data) ) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Failed to remove last element");
        return 1;
    }

    if (list->size != 0) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Didn't remove all the elements.");
        return 1;
    }

    psFree(list);

    return 0;

}

psS32 testListConvert(void)
{

    psList* list = NULL;
    psArray* arr = NULL;
    psS32* data;

    /*
        array=psListToArray(list) shall take each element of the list, increment
        their reference, and insert them into the returned fresh psPtr array.
        The list shall not be changed, except for the element reference increment.
    */

    // test dlist -> array

    // create a list
    list = psListAlloc(NULL);
    for (psS32 lcv=0;lcv<15;lcv++) {
        data = psAlloc(sizeof(psS32));
        *data = lcv;
        psListAdd(list,PS_LIST_TAIL,data);
        psMemDecrRefCounter(data);
    }

    arr = psListToArray(list);

    if (arr->n != 15 || list->size != 15) {
        psError(PS_ERR_UNKNOWN, true,"The created array didn't have the proper size");
        return 1;
    }
    for (psS32 i=0;i<arr->n;i++) {
        if (i != *(psS32*)arr->data[i]) {
            psError(PS_ERR_UNKNOWN, true,"Element %d of array is incorrect (%d).",
                    i,*(psS32*)arr->data[i]);
            return 1;
        }
        if (i != *(psS32*)psListGet(list,i)) {
            psError(PS_ERR_UNKNOWN, true,"Element %d of list is incorrect (%d).",
                    i,*(psS32*)arr->data[i]);
            return 1;
        }
        if (psMemGetRefCounter(arr->data[i]) != 2) {
            psError(PS_ERR_UNKNOWN, true,"Element %d had wrong reference count (%d).",
                    i,psMemGetRefCounter(arr->data[i]));
            return 1;
        }
    }

    psFree(arr);
    psFree(list);

    // test array -> dlist

    // create an array
    arr = psArrayAlloc(15);
    arr->n = arr->nalloc;
    for (psS32 lcv=0;lcv<15;lcv++) {
        data = psAlloc(sizeof(psS32));
        *data = lcv;
        arr->data[lcv] = data;
    }

    list = psArrayToList(arr);

    if (arr->n != 15 || list->size != 15) {
        psError(PS_ERR_UNKNOWN, true,"The created array didn't have the proper size");
        return 1;
    }
    for (psS32 i=0;i<arr->n;i++) {
        if (i != *(psS32*)arr->data[i]) {
            psError(PS_ERR_UNKNOWN, true,"Element %d of array is incorrect (%d).",
                    i,*(psS32*)arr->data[i]);
            return 1;
        }
        if (i != *(psS32*)psListGet(list,i)) {
            psError(PS_ERR_UNKNOWN, true,"Element %d of list is incorrect (%d).",
                    i,*(psS32*)arr->data[i]);
            return 1;
        }
        if (psMemGetRefCounter(arr->data[i]) != 2) {
            psError(PS_ERR_UNKNOWN, true,"Element %d had wrong reference count (%d).",
                    i,psMemGetRefCounter(arr->data[i]));
            return 1;
        }
    }

    psFree(arr);
    psFree(list);

    // now, make sure if input array/list is NULL, output is NULL

    arr = psListToArray(NULL);
    if (arr != NULL) {
        psError(PS_ERR_UNKNOWN, true,"psListToArray didn't return NULL when given NULL");
        return 1;
    }

    list = psArrayToList(NULL);
    if (list != NULL) {
        psError(PS_ERR_UNKNOWN, true,"psArrayToList didn't return NULL when given NULL");
        return 1;
    }

    // now, see what happens with a zero-size array/list
    arr = psArrayAlloc(1);
    arr->n = 0;
    list = psArrayToList(arr);
    if (list == NULL) {
        psError(PS_ERR_UNKNOWN, true,"psArrayToList didn't create an empty list from an "
                "empty array.");
        return 1;
    }
    psFree(arr);
    psFree(list);

    list = psListAlloc(NULL);
    arr = psListToArray(list);
    if (arr == NULL) {
        psError(PS_ERR_UNKNOWN, true,"psArrayToList didn't create an empty array from an "
                "empty list.");
        return 1;
    }
    psFree(arr);
    psFree(list);

    return 0;
}

psS32 testListIterator(void)
{
    psList* list = NULL;
    psS32* data;

    psLogMsg(__func__,PS_LOG_INFO," psListSetIterator/psListGetNext/psListGetPrev"
             " shall move the list cursor to the specified location");

    /*
            psDlistSetIterator(list,where) shall:

            1. output error message and do nothing if list=NULL
            2. set list.cursor to list.head if where=PS_LIST_HEAD
            3. set list.cursor to list.tail if where=PS_LIST_TAIL
            4. set list.cursor to list.cursor->next if where=PS_LIST_NEXT
            5. set list.cursor to list.cursor->prev if where=PS_LIST_PREV
            6. leave list.cursor untouched if where=PS_LIST_UNKNOWN or PS_LIST_CURRENT

            psDlistGetNext(list) shall be functionally equivalent to
            psDlistSetIterator(list,PS_LIST_NEXT) but returns list.cursor.

            psDlistGetPrev(list) shall be functionally equivalent to
            psDlistSetIterator(list,PS_LIST_PREV) but returns list.cursor.
    */

    // create a list
    list = psListAlloc(NULL);
    for (psS32 lcv=0;lcv<15;lcv++) {
        data = psAlloc(sizeof(psS32));
        *data = lcv;
        psListAdd(list,PS_LIST_TAIL,data);
        psFree(data);
    }

    psListIterator* iter = psListIteratorAlloc(list,PS_LIST_HEAD);
    if (iter == NULL) {
        psError(PS_ERR_UNKNOWN, true,"Failed to make an iterator.");
        return 22;
    }

    // 1. output error message and do nothing if iterator=NULL
    psLogMsg(__func__,PS_LOG_INFO,"Following should error with "
             "'Unexpected null pointer'");
    psListIteratorSet(NULL,PS_LIST_HEAD);
    if (psListIteratorSet(NULL,PS_LIST_HEAD)) {
        psError(PS_ERR_UNKNOWN, true,"Success while setting position of a NULL iterator?");
        return 23;
    }


    // 3. set list.cursor to list.tail if where=PS_LIST_TAIL

    if (!psListIteratorSet(iter,PS_LIST_TAIL) ||
            *(psS32*)iter->cursor->data != 14 ||
            iter->index != 14) {
        psError(PS_ERR_UNKNOWN, true,"Didn't successfully move cursor to tail.");
        return 1;
    }

    // 2. set list.cursor to list.head if where=PS_LIST_HEAD
    if (!psListIteratorSet(iter,PS_LIST_HEAD) ||
            *(psS32*)iter->cursor->data != 0 ||
            iter->index != 0) {
        psError(PS_ERR_UNKNOWN, true,"Didn't successfully move cursor to head.");
        return 2;
    }

    // test psListGetPrevious/Next
    if (*(psS32*)psListGetNext(iter) != 0) {
        psError(PS_ERR_UNKNOWN, true,"psListGetNext didn't move cursor to next.");
        return 8;
    }
    if (*(psS32*)psListGetNext(iter) != 1) {
        psError(PS_ERR_UNKNOWN, true,"psListGetNext didn't move cursor to next.");
        return 9;
    }
    if (*(psS32*)psListGetNext(iter) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListGetNext didn't move cursor to next.");
        return 10;
    }

    if (*(psS32*)psListGetPrevious(iter) != 3) {
        psError(PS_ERR_UNKNOWN, true,"psListGetPrevious didn't move cursor to previous.");
        return 11;
    }
    if (*(psS32*)psListGetPrevious(iter) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListGetPrevious didn't move cursor to previous.");
        return 12;
    }
    if (*(psS32*)psListGetPrevious(iter) != 1) {
        psError(PS_ERR_UNKNOWN, true,"psListGetPrevious didn't move cursor to previous..");
        return 13;
    }
    if (*(psS32*)psListGetPrevious(iter) != 0) {
        psError(PS_ERR_UNKNOWN, true,"psListGetPrevious didn't move cursor to previous..");
        return 14;
    }
    if (psListGetPrevious(iter) != NULL) {
        psError(PS_ERR_UNKNOWN, true,"psListGetPrevious moved cursor beyond head.");
        return 15;
    }

    psListIteratorSet(iter,PS_LIST_TAIL); // works according to an above test
    if (*(psS32*)psListGetNext(iter) != 14) {
        psError(PS_ERR_UNKNOWN, true,"psListGetNext moved cursor beyond tail.");
        return 16;
    }
    if (psListGetNext(iter) != NULL) {
        psError(PS_ERR_UNKNOWN, true,"psListGetNext moved cursor beyond tail.");
        return 17;
    }

    psFree(list);

    return 0;
}

psS32 testListFree(void)
{
    float* data[15];
    psList* list;

    /*
    Call psDlistAlloc to create a doubly linked list.

    Create several data items and add them to the list.

    Verify the data item's reference counter are incremented after adding to the list.
    (not really needed, as psListAdd is tested elsewhere)
    */

    list = psListAlloc(NULL);
    for (psS32 lcv=0;lcv<15;lcv++) {
        data[lcv] = psAlloc(sizeof(psS32));
        *data[lcv] = lcv;
        psListAdd(list,PS_LIST_TAIL,data[lcv]);
        if (psMemGetRefCounter(data[lcv]) != 2) {
            psError(PS_ERR_UNKNOWN, true,"Reference counter for data was not incremented");
            return 1;
        }
    }

    /*
    Verify items are within the list.
    (not needed, as psListAdd is tested elsewhere, but check anyway.)
    */
    if (list->size != 15) {
        psError(PS_ERR_UNKNOWN, true,"List wasn't populated as expected?");
        return 2;
    }

    /*
    Call psDlistFree with NULL specified as the elemFree function.
    */

    psFree(list);

    /*
    Verify the list is deallocated but not the data items.
    (accomplished by checking for memory leaks)

    Verify the data item's reference counters are decremented by one compared to when added to the list.
    (technically, this could be accomplished by checking memory leaks too, but I checked anyway)
    */

    for (psS32 i=0;i<15;i++) {
        if (psMemGetRefCounter(data[i]) != 1) {
            psError(PS_ERR_UNKNOWN, true,"pslistFree didn't decrement the data item's reference counter");
            return 3;
        }
        psFree(data[i]);
    }

    return 0;
}

psS32 testListSort(void)
{
    psList* list;
    psListIterator* iter;
    float* fValue;
    psU32* uValue;

    list = psListAlloc(NULL);

    for (psS32 lcv=0;lcv<15;lcv++) {
        float* data = psAlloc(sizeof(psS32));
        if (lcv < 7) {
            *data = 13-lcv*2;
        } else {
            *data = lcv*2-12;
        }
        psListAdd(list,PS_LIST_TAIL,data);
        psFree(data);

    }

    printf("original list = [");
    iter = psListIteratorAlloc(list,PS_LIST_HEAD);
    while( (fValue=psListGetNext(iter)) != NULL ) {
        printf(" %.1f",*fValue);
    }
    printf(" ]\n");

    list = psListSort(list,psCompareF32Ptr);

    printf("sorted list = [");
    psListIteratorSet(iter,PS_LIST_HEAD);
    while( (fValue=psListGetNext(iter)) != NULL ) {
        printf(" %.1f",*fValue);
    }
    printf(" ]\n");

    psListIteratorSet(iter,PS_LIST_HEAD);
    float* prevFValue = psListGetNext(iter);
    while( (fValue=psListGetNext(iter)) != NULL ) {
        if (*prevFValue > *fValue) {
            psError(PS_ERR_UNKNOWN, true,"Hey, the list wasn't sorted!?");
            return 1;
        }
    }

    list = psListSort(list,psCompareDescendingF32Ptr);

    printf("descending sort list = [");
    psListIteratorSet(iter,PS_LIST_HEAD);
    while( (fValue=psListGetNext(iter)) != NULL ) {
        printf(" %.1f",*fValue);
    }
    printf(" ]\n");

    psListIteratorSet(iter,PS_LIST_HEAD);
    prevFValue = psListGetNext(iter);
    while( (fValue=psListGetNext(iter)) != NULL ) {
        if (*prevFValue < *fValue) {
            psError(PS_ERR_UNKNOWN, true,"Hey, the list wasn't sorted!?");
            return 2;
        }
    }

    psFree(list);

    /********** OK, now do the same thing with ints. *************/

    list = psListAlloc(NULL);

    for (psS32 lcv=0;lcv<15;lcv++) {
        psU32* data = psAlloc(sizeof(psS32));
        if (lcv < 7) {
            *data = 13-lcv*2;
        } else {
            *data = lcv*2-12;
        }
        psListAdd(list,PS_LIST_TAIL,data);
        psFree(data);

    }

    printf("original list = [");
    iter = psListIteratorAlloc(list,PS_LIST_HEAD);
    while( (uValue=psListGetNext(iter)) != NULL ) {
        printf(" %d",*uValue);
    }
    printf(" ]\n");

    list = psListSort(list,psCompareU32Ptr);

    printf("sorted list = [");
    psListIteratorSet(iter,PS_LIST_HEAD);
    while( (uValue=psListGetNext(iter)) != NULL ) {
        printf(" %d",*uValue);
    }
    printf(" ]\n");

    psListIteratorSet(iter,PS_LIST_HEAD);
    psU32* prevUValue = psListGetNext(iter);
    while( (uValue=psListGetNext(iter)) != NULL ) {
        if (*prevUValue > *uValue) {
            psError(PS_ERR_UNKNOWN, true,"Hey, the list wasn't sorted!?");
            return 3;
        }
    }

    list = psListSort(list,psCompareDescendingU32Ptr);

    printf("descending sort list = [");
    psListIteratorSet(iter,PS_LIST_HEAD);
    while( (uValue=psListGetNext(iter)) != NULL ) {
        printf(" %d",*uValue);
    }
    printf(" ]\n");

    psListIteratorSet(iter,PS_LIST_HEAD);
    prevUValue = psListGetNext(iter);
    while( (uValue=psListGetNext(iter)) != NULL ) {
        if (*prevUValue < *uValue) {
            psError(PS_ERR_UNKNOWN, true,"Hey, the list wasn't sorted!?");
            return 4;
        }
    }

    psFree(list);

    return 0;
}
