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

#include "psTest.h"
#include "pslib_strict.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);
static psS32 testListAddAfter(void);
static psS32 testListAddBefore(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},
                              {testListAddAfter,811,"psListAddAfter",0,false},
                              {testListAddBefore,811,"psListAddBefore",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).
    // Test requirement SDR-167
    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->n != 0) {
        psError(PS_ERR_UNKNOWN, true,"size of list wasn't zero for empty list.");
        return 3;
    }

    psFree(list);

    // Test requirement SDR-165
    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->n != 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 testListAddAfter(void)
{
    psList* list = NULL;
    psS32*  data = NULL;
    psS32*  data1 = NULL;
    psS32*  data2 = NULL;
    psListIterator *currentIterator = NULL;

    data = psAlloc(sizeof(psS32));
    *data = 1;
    data1 = psAlloc(sizeof(psS32));
    *data1 = 2;
    data2 = psAlloc(sizeof(psS32));
    *data2 = 3;

    list = psListAlloc(data);
    currentIterator = psListIteratorAlloc(list,PS_LIST_HEAD,true);

    // Add data after HEAD and verify data
    // Test requirement SDR-755
    if(!psListAddAfter(currentIterator,data1)) {
        psError(PS_ERR_UNKNOWN,true,"psListAddAfter failed to add item");
        return 1;
    }
    if(*(psS32*)list->head->next->data != 2) {
        psError(PS_ERR_UNKNOWN,true,"psListAddAfter did not add the item properly");
        return 2;
    }
    // Test requirement SDR-175
    if (psMemGetRefCounter(data1) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAddAfter didn't increment the data reference count.");
        return 20;
    }
    psFree(currentIterator);

    currentIterator = psListIteratorAlloc(list,PS_LIST_TAIL,true);

    // Add data after TAIL and verify data
    if(!psListAddAfter(currentIterator,data2)) {
        psError(PS_ERR_UNKNOWN,true,"psListAddAfter failed to add item");
        return 3;
    }
    if(*(psS32*)list->tail->data != 3) {
        psError(PS_ERR_UNKNOWN,true,"psListAddAfter did not add the item properly");
        return 4;
    }
    // Test requirement SDR-175
    if (psMemGetRefCounter(data2) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAddAfter didn't increment the data reference count.");
        return 40;
    }

    // Verify error message generated with data pointer is NULL
    psLogMsg(__func__,PS_LOG_INFO,"NULL data pointer should generate error message");
    if(psListAddAfter(currentIterator,NULL)) {
        psError(PS_ERR_UNKNOWN,true,"psListAddAfter should have generated error.");
        return 5;
    }

    // Verify error message generated with iterator NULL
    psLogMsg(__func__,PS_LOG_INFO,"NULL iterator should generate error message");
    if(psListAddAfter(NULL,data2)) {
        psError(PS_ERR_UNKNOWN,true,"psListAddAfter should have generated error.");
        return 6;
    }

    // Verify error message is generate with non-mutable iterator
    psLogMsg(__func__,PS_LOG_INFO,"Non-mutable list should generate error message");
    currentIterator->mutable = false;
    if(psListAddAfter(currentIterator,data2)) {
        psError(PS_ERR_UNKNOWN,true,"psListAddAfter should have generated error for non-mutable list add");
        return 7;
    }
    currentIterator->mutable = true;

    psFree(data);
    psFree(data1);
    psFree(data2);
    psFree(list);

    return 0;
}

psS32 testListAddBefore(void)
{
    psList* list = NULL;
    psS32*  data1 = NULL;
    psS32*  data2 = NULL;
    psListIterator *currentIterator = NULL;

    data1 = psAlloc(sizeof(psS32));
    *data1 = 2;
    data2 = psAlloc(sizeof(psS32));
    *data2 = 3;

    list = psListAlloc(NULL);
    currentIterator = psListIteratorAlloc(list,PS_LIST_HEAD,true);

    // Add data before HEAD and verify data
    // Test requirement SDR-756
    if(!psListAddBefore(currentIterator,data1)) {
        psError(PS_ERR_UNKNOWN,true,"psListAddBefore failed to add item");
        return 1;
    }
    if(*(psS32*)list->head->data != 2) {
        psError(PS_ERR_UNKNOWN,true,"psListAddBefore did not add the item properly");
        return 2;
    }
    // Test requirement SDR-175
    if (psMemGetRefCounter(data1) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAddBefore didn't increment the data reference count.");
        return 20;
    }
    psFree(currentIterator);

    currentIterator = psListIteratorAlloc(list,PS_LIST_TAIL,true);

    // Add data after TAIL and verify data
    if(!psListAddBefore(currentIterator,data2)) {
        psError(PS_ERR_UNKNOWN,true,"psListAddBefore failed to add item");
        return 3;
    }
    if(*(psS32*)list->tail->prev->data != 3) {
        psError(PS_ERR_UNKNOWN,true,"psListAddBefore did not add the item properly");
        return 4;
    }
    // Test requirement SDR-175
    if (psMemGetRefCounter(data2) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAddBefore didn't increment the data reference count.");
        return 40;
    }

    // Verify error message generated with data pointer is NULL
    psLogMsg(__func__,PS_LOG_INFO,"NULL data pointer should generate error message");
    if(psListAddBefore(currentIterator,NULL)) {
        psError(PS_ERR_UNKNOWN,true,"psListAddBefore should have generated error.");
        return 5;
    }

    // Verify error message generated with iterator NULL
    psLogMsg(__func__,PS_LOG_INFO,"NULL iterator should generate error message");
    if(psListAddBefore(NULL,data2)) {
        psError(PS_ERR_UNKNOWN,true,"psListAddBefore should have generated error.");
        return 6;
    }

    // Verify error message is generate with non-mutable iterator
    psLogMsg(__func__,PS_LOG_INFO,"Non-mutable list should generate error message");
    currentIterator->mutable = false;
    if(psListAddBefore(currentIterator,data2)) {
        psError(PS_ERR_UNKNOWN,true,"psListAddBefore should have generated error for non-mutable list add");
        return 7;
    }
    currentIterator->mutable = true;

    psFree(data1);
    psFree(data2);
    psFree(list);

    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)
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for using NULL list.");
    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->n != 1) {
        psError(PS_ERR_UNKNOWN, true,"psListAlloc didn't create a list properly.");
        return 2;
    }

    //  2. data is NULL (error, list should not grow)
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error msg to add NULL data");
    if (psListAdd(list, PS_LIST_HEAD,NULL)) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd successfully added a NULL data item?");
        return 40;
    }
    if ( list->n != 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;
    }
    // Test requirement SDR-175
    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->n != 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;
    }
    // Test requirement SDR-175
    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->n != 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;
    }
    // Test requirement SDR-175
    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->n != 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;
    }
    // Test requirement SDR-175
    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->n != 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;
    }
    // Test requirment SDR-175
    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->n != 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;
    // Test requirment SDR-169
    if ( ! psListAdd(list,-2,data) ) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd failed to add data to 2 position.");
        return 32;
    }

    // Test requirment SDR-175
    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->n != 6  || *(psS32 *)list->tail->prev->prev->data != 7) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't insert to position #2.");
        return 11;
    }

    // Test requirement SDR-757
    data = psAlloc(sizeof(psS32));
    *data = 8;
    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;
    }
    // Test requirment SDR-175
    if (psMemGetRefCounter(data) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListAdd didn't increment the data reference count.");
        return 29;
    }
    if(list->n != 7 || *(psS32 *)list->tail->data != 8) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN,true,"psListAdd didn't place added item at tail.");
        return 12;
    }

    psFree(data);

    psFree(list);

    return 0;
}

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

    psListIterator* iter = psListIteratorAlloc(list,PS_LIST_HEAD,true);

    while ( (data=(psS32*)psListGetAndIncrement(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;
    }

    data = (psS32*)psListGet(list,-2);
    if (data == NULL || *data !=2) {
        psError(PS_ERR_UNKNOWN,true,"psListGet failed with location=-2");
        return 9;
    }

    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)
    psS32* data1 = (psS32 *)psListGet(list,PS_LIST_HEAD);
    psMemIncrRefCounter(data1);
    // Test requirement SDR-172, SDR-173
    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->n != --items) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
        return 1;
    }
    // Test requirement SDR-176
    if (psMemGetRefCounter(data1) != 1) {
        psError(PS_ERR_UNKNOWN, true,"psListRemove didn't decrement the data reference count.");
        return 20;
    }
    psMemDecrRefCounter(data1);

    // 3. which is PS_LIST_TAIL (remove last element of list)
    data = psListGet(list,PS_LIST_TAIL);
    // Test requirement SDR-173
    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->n != --items) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
        return 1;
    }

    data = psListGet(list,-2);
    // Test requirement SDR-173
    if ( (! psListRemove(list,-2)) ||
            (psListGet(list,-2) == data) ) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Failed to remove from location -2");
        return 11;
    }

    if (list->n != --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);
    // Test requirement SDR-762
    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->n != --items) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
        return 1;
    }

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

    if (list->n != --items) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Didn't decrement size properly to %d.",items);
        return 1;
    }
    // Test requirement SDR-176
    if (psMemGetRefCounter(data2) != 1) {
        psError(PS_ERR_UNKNOWN, true,"psListRemoveData didn't decrement the data reference count.");
        return 20;
    }
    psMemDecrRefCounter(data2);

    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->n != --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");
    // Test requirement SDR-764
    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->n != 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->n != 0) {
        printListInt(list);
        psError(PS_ERR_UNKNOWN, true,"Didn't remove all the elements.");
        return 1;
    }

    psLogMsg(__func__,PS_LOG_INFO,"NULL list in psListRemoveData should generate error message.");
    if(psListRemoveData(NULL,data)) {
        psError(PS_ERR_UNKNOWN,true,"psListRemoveData should have generated an error with NULL list");
        return 2;
    }

    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->n != 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->n != 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,true);
    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
    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;
    }

    // Attempt to get next with NULL iterator
    if ( psListGetAndIncrement(NULL) != NULL ) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL when NULL iterator specified");
        return 24;
    }

    // Attempt to get previous with NULL iterator
    if( psListGetAndDecrement(NULL) != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL when NULL iterator specified");
        return 25;
    }

    // 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*)psListGetAndIncrement(iter) != 0) {
        psError(PS_ERR_UNKNOWN, true,"psListGetNext didn't move cursor to next.");
        return 8;
    }
    if (*(psS32*)psListGetAndIncrement(iter) != 1) {
        psError(PS_ERR_UNKNOWN, true,"psListGetNext didn't move cursor to next.");
        return 9;
    }
    if (*(psS32*)psListGetAndIncrement(iter) != 2) {
        psError(PS_ERR_UNKNOWN, true,"psListGetNext didn't move cursor to next.");
        return 10;
    }

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

    psListIteratorSet(iter,PS_LIST_TAIL); // works according to an above test
    if (*(psS32*)psListGetAndIncrement(iter) != 14) {
        psError(PS_ERR_UNKNOWN, true,"psListGetNext moved cursor beyond tail.");
        return 16;
    }
    if ((psListGetAndIncrement(iter) != NULL) || (!iter->offEnd)) {
        psError(PS_ERR_UNKNOWN, true,"psListGetNext moved cursor beyond tail.");
        return 17;
    }
    if(psListGetAndDecrement(iter) != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psListGetPrevious did not return NULL when offEnd is true");
        return 18;
    }
    if(iter->offEnd) {
        psError(PS_ERR_UNKNOWN,true,"psListGetPrevious did not move back onto the list.");
        return 19;
    }
    if(*(psS32*)psListGetAndDecrement(iter) != 14) {
        psError(PS_ERR_UNKNOWN,true,"psListGetPrevious did not return correct value of tail.");
        return 20;
    }

    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->n != 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,true);
    while( (fValue=psListGetAndIncrement(iter)) != NULL ) {
        printf(" %.1f",*fValue);
    }
    printf(" ]\n");

    list = psListSort(list,psCompareF32Ptr);

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

    psListIteratorSet(iter,PS_LIST_HEAD);
    float* prevFValue = psListGetAndIncrement(iter);
    while( (fValue=psListGetAndIncrement(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=psListGetAndIncrement(iter)) != NULL ) {
        printf(" %.1f",*fValue);
    }
    printf(" ]\n");

    psListIteratorSet(iter,PS_LIST_HEAD);
    prevFValue = psListGetAndIncrement(iter);
    while( (fValue=psListGetAndIncrement(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,true);
    while( (uValue=psListGetAndIncrement(iter)) != NULL ) {
        printf(" %d",*uValue);
    }
    printf(" ]\n");

    list = psListSort(list,psCompareU32Ptr);

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

    psListIteratorSet(iter,PS_LIST_HEAD);
    psU32* prevUValue = psListGetAndIncrement(iter);
    while( (uValue=psListGetAndIncrement(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=psListGetAndIncrement(iter)) != NULL ) {
        printf(" %d",*uValue);
    }
    printf(" ]\n");

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

    psFree(list);

    return 0;
}
