#include <stdlib.h>

#include "cunit/cyrunit.h"
#include "ptrarray.h"

#define PTR0        ((void *)0xcafebabe)
#define PTR1        ((void *)0xcafebabf)
#define PTR2        ((void *)0xcafebac0)
#define PTR3        ((void *)0xcafebac1)
#define PTR4        ((void *)0xcafebac2)
#define PTR5        ((void *)0xcafebac3)

#define PTR0REP     ((void *)0xc0ffee00)
#define PTR0REP2    ((void *)0xc0ffee01)
#define PTR2REP     ((void *)0xc0ffee02)

#define PTRNOTHERE  ((void *)0xdefaced0)

static void test_fini_null(void)
{
    /* _fini(NULL) is harmless */
    ptrarray_fini(NULL);
    /* _free(NULL) is harmless */
    ptrarray_free(NULL);
}

static void test_auto(void)
{
    ptrarray_t pa = PTRARRAY_INITIALIZER;
    void *p1;
    void *p2;

    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, -1));

    p1 = PTR0;
    ptrarray_append(&pa, p1);
    CU_ASSERT_EQUAL(pa.count, 1);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), p1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, -1), p1);

    p2 = PTR0;
    ptrarray_append(&pa, p2);
    CU_ASSERT_EQUAL(pa.count, 2);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), p1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), p2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, -1), p2);

    ptrarray_fini(&pa);
    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT_EQUAL(pa.alloc, 0);
    CU_ASSERT_PTR_NULL(pa.data);
}

static void test_heap(void)
{
    ptrarray_t *pa = ptrarray_new();
    void *p1;
    void *p2;

    CU_ASSERT_EQUAL(pa->count, 0);
    CU_ASSERT(pa->alloc >= pa->count);
    CU_ASSERT_PTR_NULL(ptrarray_nth(pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(pa, -1));

    p1 = PTR0;
    ptrarray_append(pa, p1);
    CU_ASSERT_EQUAL(pa->count, 1);
    CU_ASSERT(pa->alloc >= pa->count);
    CU_ASSERT_PTR_NOT_NULL(pa->data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(pa, 0), p1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(pa, -1), p1);

    p2 = PTR1;
    ptrarray_append(pa, p2);
    CU_ASSERT_EQUAL(pa->count, 2);
    CU_ASSERT(pa->alloc >= pa->count);
    CU_ASSERT_PTR_NOT_NULL(pa->data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(pa, 0), p1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(pa, 1), p2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(pa, -1), p2);

    ptrarray_free(pa);
}

static void test_set(void)
{
    ptrarray_t pa = PTRARRAY_INITIALIZER;

    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, -1));

    ptrarray_append(&pa, PTR0);
    CU_ASSERT_EQUAL(pa.count, 1);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);

    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);

    ptrarray_set(&pa, 0, PTR0REP);
    CU_ASSERT_EQUAL(pa.count, 1);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0REP);

    ptrarray_set(&pa, -1, PTR0REP2);
    CU_ASSERT_EQUAL(pa.count, 1);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0REP2);

    ptrarray_append(&pa, PTR1);
    ptrarray_append(&pa, PTR2);
    ptrarray_append(&pa, PTR3);
    ptrarray_append(&pa, PTR4);
    CU_ASSERT_EQUAL(pa.count, 5);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0REP2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 3), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 4), PTR4);

    ptrarray_set(&pa, 2, PTR2REP);
    CU_ASSERT_EQUAL(pa.count, 5);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0REP2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR2REP);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 3), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 4), PTR4);

    ptrarray_fini(&pa);
}

static void test_insert(void)
{
    ptrarray_t pa = PTRARRAY_INITIALIZER;

    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, -1));

    ptrarray_insert(&pa, 0, PTR0);
    CU_ASSERT_EQUAL(pa.count, 1);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);

    ptrarray_insert(&pa, -1, PTR1);
    CU_ASSERT_EQUAL(pa.count, 2);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR0);

    ptrarray_insert(&pa, 0, PTR2);
    CU_ASSERT_EQUAL(pa.count, 3);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR0);

    ptrarray_insert(&pa, -1, PTR3);
    CU_ASSERT_EQUAL(pa.count, 4);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 3), PTR0);

    ptrarray_insert(&pa, 2, PTR4);
    CU_ASSERT_EQUAL(pa.count, 5);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR4);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 3), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 4), PTR0);

    ptrarray_fini(&pa);
}

/* test that _set() and _insert() of a bad
 * index will fail silently and leave no side effects */
static void test_bad_index(void)
{
    ptrarray_t pa = PTRARRAY_INITIALIZER;

    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, -1));

    /* when the ptrarray is empty, -1 is a bad index */

    ptrarray_set(&pa, -1, PTR0);
    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, -1));

    ptrarray_insert(&pa, -1, PTR0);
    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, -1));

    /* a negative number larger than the (non-zero) count is a bad index */
    ptrarray_append(&pa, PTR1);
    ptrarray_append(&pa, PTR2);

    ptrarray_set(&pa, -4, PTR0);
    CU_ASSERT_EQUAL(pa.count, 2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, -1), PTR2);

    ptrarray_insert(&pa, -4, PTR0);
    CU_ASSERT_EQUAL(pa.count, 2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, -1), PTR2);

    ptrarray_fini(&pa);
}

static void test_remove(void)
{
    ptrarray_t pa = PTRARRAY_INITIALIZER;
    void *p;

    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, -1));

    ptrarray_append(&pa, PTR0);
    ptrarray_append(&pa, PTR1);
    ptrarray_append(&pa, PTR2);
    ptrarray_append(&pa, PTR3);
    ptrarray_append(&pa, PTR4);
    CU_ASSERT_EQUAL(pa.count, 5);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 3), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 4), PTR4);

    p = ptrarray_remove(&pa, 2);
    CU_ASSERT_PTR_EQUAL(p, PTR2);
    CU_ASSERT_EQUAL(pa.count, 4);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 3), PTR4);

    p = ptrarray_remove(&pa, 0);
    CU_ASSERT_PTR_EQUAL(p, PTR0);
    CU_ASSERT_EQUAL(pa.count, 3);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR4);

    p = ptrarray_remove(&pa, -1);
    CU_ASSERT_PTR_EQUAL(p, PTR4);
    CU_ASSERT_EQUAL(pa.count, 2);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR3);

    p = ptrarray_remove(&pa, 1);
    CU_ASSERT_PTR_EQUAL(p, PTR3);
    CU_ASSERT_EQUAL(pa.count, 1);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR1);

    p = ptrarray_remove(&pa, 0);
    CU_ASSERT_PTR_EQUAL(p, PTR1);
    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));

    p = ptrarray_remove(&pa, 0);
    CU_ASSERT_PTR_NULL(p);

    ptrarray_fini(&pa);
}

static void test_takevf(void)
{
    ptrarray_t *pa;
    void **pp;

    pa = ptrarray_new();
    CU_ASSERT_PTR_NOT_NULL(pa);
    ptrarray_append(pa, PTR0);
    ptrarray_append(pa, PTR1);
    ptrarray_append(pa, PTR2);
    ptrarray_append(pa, PTR3);
    ptrarray_append(pa, PTR4);
    CU_ASSERT_EQUAL(pa->count, 5);
    CU_ASSERT(pa->alloc >= pa->count);
    CU_ASSERT_PTR_NOT_NULL(pa->data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(pa, 2), PTR2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(pa, 3), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(pa, 4), PTR4);

    pp = ptrarray_takevf(pa);
    /* note: takevf frees the ptrarray itself */
    CU_ASSERT_PTR_NOT_NULL(pp);
    CU_ASSERT_PTR_EQUAL(pp[0], PTR0);
    CU_ASSERT_PTR_EQUAL(pp[1], PTR1);
    CU_ASSERT_PTR_EQUAL(pp[2], PTR2);
    CU_ASSERT_PTR_EQUAL(pp[3], PTR3);
    CU_ASSERT_PTR_EQUAL(pp[4], PTR4);
    CU_ASSERT_PTR_NULL(pp[5]);

    free(pp);
}

static void test_truncate(void)
{
    ptrarray_t pa = PTRARRAY_INITIALIZER;

    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, -1));

    ptrarray_append(&pa, PTR0);
    ptrarray_append(&pa, PTR1);
    ptrarray_append(&pa, PTR2);
    ptrarray_append(&pa, PTR3);
    ptrarray_append(&pa, PTR4);
    CU_ASSERT_EQUAL(pa.count, 5);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 3), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 4), PTR4);

    /* expand the array */
    ptrarray_truncate(&pa, 7);
    CU_ASSERT_EQUAL(pa.count, 7);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 3), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 4), PTR4);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 5));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 6));

    /* truncate to the existing size is a no-op */
    ptrarray_truncate(&pa, 7);
    CU_ASSERT_EQUAL(pa.count, 7);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 3), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 4), PTR4);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 5));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 6));

    /* shrink the array */
    ptrarray_truncate(&pa, 4);
    CU_ASSERT_EQUAL(pa.count, 4);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 3), PTR3);

    /* shrink the array harder */
    ptrarray_truncate(&pa, 3);
    CU_ASSERT_EQUAL(pa.count, 3);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR2);

    /* shrink the array to nothing */
    ptrarray_truncate(&pa, 0);
    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT(pa.alloc >= pa.count);
    /* whether pa.data is NULL is undefined at this time */
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, -1));

    ptrarray_fini(&pa);
}

static void test_find(void)
{
    ptrarray_t pa = PTRARRAY_INITIALIZER;
    int i;

    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, -1));

    ptrarray_append(&pa, PTR0);
    ptrarray_append(&pa, PTR1);
    ptrarray_append(&pa, PTR2);
    ptrarray_append(&pa, PTR3);
    ptrarray_append(&pa, PTR0);
    ptrarray_append(&pa, PTR4);
    CU_ASSERT_EQUAL(pa.count, 6);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 2), PTR2);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 3), PTR3);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 4), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 5), PTR4);

    /* search for something which isn't there */
    i = ptrarray_find(&pa, PTRNOTHERE, 0);
    CU_ASSERT_EQUAL(i, -1);

    /* search for something which isn't there, starting off the end */
    i = ptrarray_find(&pa, PTRNOTHERE, 7);
    CU_ASSERT_EQUAL(i, -1);

    /* search for something which is there */
    i = ptrarray_find(&pa, PTR1, 0);
    CU_ASSERT_EQUAL(i, 1);
    i = ptrarray_find(&pa, PTR1, i+1);
    CU_ASSERT_EQUAL(i, -1);

    /* search for something which is there, starting off the end */
    i = ptrarray_find(&pa, PTR1, 7);
    CU_ASSERT_EQUAL(i, -1);

    /* search for something which is there multiple times */
    i = ptrarray_find(&pa, PTR0, 0);
    CU_ASSERT_EQUAL(i, 0);
    i = ptrarray_find(&pa, PTR0, i+1);
    CU_ASSERT_EQUAL(i, 4);
    i = ptrarray_find(&pa, PTR0, i+1);
    CU_ASSERT_EQUAL(i, -1);

    ptrarray_fini(&pa);
}

static void test_add(void)
{
    ptrarray_t pa = PTRARRAY_INITIALIZER;

    CU_ASSERT_EQUAL(pa.count, 0);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, 0));
    CU_ASSERT_PTR_NULL(ptrarray_nth(&pa, -1));

    /* _add() on an empty array appends */
    ptrarray_add(&pa, PTR0);
    CU_ASSERT_EQUAL(pa.count, 1);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);

    /* _add() of an item already present is a no-op */
    ptrarray_add(&pa, PTR0);
    CU_ASSERT_EQUAL(pa.count, 1);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);

    /* _add() of an item not already present appends */
    ptrarray_add(&pa, PTR1);
    CU_ASSERT_EQUAL(pa.count, 2);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);

    /* _add() of an item already present is a no-op */
    ptrarray_add(&pa, PTR0);
    CU_ASSERT_EQUAL(pa.count, 2);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);

    ptrarray_add(&pa, PTR1);
    CU_ASSERT_EQUAL(pa.count, 2);
    CU_ASSERT(pa.alloc >= pa.count);
    CU_ASSERT_PTR_NOT_NULL(pa.data);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 0), PTR0);
    CU_ASSERT_PTR_EQUAL(ptrarray_nth(&pa, 1), PTR1);

    ptrarray_fini(&pa);
}

/* vim: set ft=c: */
