/* $Id$ 
 * 
 * Licensed to the Apache Software Foundation (ASF) under one or more 
 * contributor license agreements. See the NOTICE file distributed with  
 * this work for additional information regarding copyright ownership. 
 * The ASF licenses this file to you under the Apache License, Version  
 * 2.0 (the "License"); you may not use this file except in compliance  
 * with the License. You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */ 

/*
 * test_arrayvalue.c -- test etch_arrayvalue object
 */
#include "apr_time.h" /* some apr must be included first */
#include "etchthread.h"
#include <tchar.h>
#include <stdio.h>
#include <conio.h>

#include "cunit.h"
#include "basic.h"
#include "automated.h"

#include "etch_global.h"
#include "etch_tagdata_inp.h"
#include "etch_arrayval.h"
#include "etchexcp.h"


int apr_setup(void);
int apr_teardown(void);
int this_setup();
int this_teardown();
apr_pool_t* g_apr_mempool;
const char* pooltag = "etchpool";


/* - - - - - - - - - - - - - - 
 * unit test infrastructure
 * - - - - - - - - - - - - - -
 */

int init_suite(void)
{
    apr_setup();
    etch_runtime_init(TRUE);
    return this_setup();
}

int clean_suite(void)
{
    this_teardown();
    etch_runtime_cleanup(0,0); /* free memtable and cache etc */
    apr_teardown();
    return 0;
}

int g_is_automated_test, g_bytes_allocated;

#define IS_DEBUG_CONSOLE FALSE

/*
 * apr_setup()
 * establish apache portable runtime environment
 */
int apr_setup(void)
{
    int result = apr_initialize();
    if (result == 0)
    {   result = etch_apr_init();
        g_apr_mempool = etch_apr_mempool;
    }
    if (g_apr_mempool)
        apr_pool_tag(g_apr_mempool, pooltag);
    else result = -1;
    return result;
}

/*
 * apr_teardown()
 * free apache portable runtime environment
 */
int apr_teardown(void)
{
    if (g_apr_mempool)
        apr_pool_destroy(g_apr_mempool);
    g_apr_mempool = NULL;
    apr_terminate();
    return 0;
}

int this_setup()
{
    etch_apr_mempool = g_apr_mempool;
    return 0;
}

int this_teardown()
{    
    return 0;
}

#define OBJTYPE_FAKETDI_IMPL ETCHTYPEB_INSTANCEDATA
#define CLASSID_FAKETDI_IMPL 0xf0
#define OBJTYPE_FAKETDO_IMPL ETCHTYPEB_INSTANCEDATA
#define CLASSID_FAKETDO_IMPL 0xf1
#define OBJTYPE_FAKETDI_VTABLE 0x88
#define OBJTYPE_FAKETDO_VTABLE 0x89

#define EXCPTEST_UNCHECKED_STATICTEXT 1
#define EXCPTEST_CHECKED_COPYTEXT     2
#define EXCPTEST_CHECKED_STATICTEXT   3

#define IS_DEBUG_CONSOLE FALSE  /* TRUE to display test diagnostics */

etch_arraylist* testdata;
int g_bytes_allocated, g_which_exception_test, g_is_automated_test;
wchar_t* local_excp_text = L"global text";


#if(0)

/**
 * fake_tdi_impl
 * instance data for a TDI implementation
 */
typedef struct fake_tdi_impl
{
    unsigned int    hashkey;    
    unsigned short  obj_type;   
    unsigned short  class_id;   
    struct objmask* vtab;       
    int  (*destroy)(void*);     
    void*(*clone)  (void*); 
    obj_gethashkey  get_hashkey;               
    struct objmask* parent;     
    etchresult*     result;     
    unsigned int    refcount;       
    unsigned int    length;     
    unsigned char   is_null;   
    unsigned char   is_copy;   
    unsigned char   is_static;  
    unsigned char   reserved;

    int index;
    byte started, done, ended;
    etch_arrayvalue* arrray;

} fake_tdi_impl;


/**
 * fake_tdo_impl
 * instance data for a TDO implementation
 */
typedef struct fake_tdo_impl
{
    unsigned int    hashkey;    
    unsigned short  obj_type;   
    unsigned short  class_id;   
    struct objmask* vtab;       
    int  (*destroy)(void*);     
    void*(*clone)  (void*);  
    obj_gethashkey  get_hashkey;              
    struct objmask* parent;     
    etchresult*     result;     
    unsigned int    refcount;       
    unsigned int    length;     
    unsigned char   is_null;   
    unsigned char   is_copy;   
    unsigned char   is_static;  
    unsigned char   reserved;

    byte started, ended;
    etch_arraylist* list;   
    etch_arrayvalue* arrray;

} fake_tdo_impl;


/**
 * destroy_fake_tdi_impl
 * memory cleanup handler for fake_tdi_impl
 */
int destroy_fake_tdi_impl(fake_tdi_impl* impl)
{
    int result = verify_object((objmask*)impl, OBJTYPE_FAKETDI_IMPL, CLASSID_FAKETDI_IMPL, NULL);
    if (result == -1) return -1; /* object passed was not expected object */

    if (impl->arrray)
        impl->arrray->destroy(impl->arrray); 

    etch_free(impl);    
    return 0;
}


/**
 * destroy_fake_tdo_impl
 * memory cleanup handler for fake_tdo_impl
 */
int destroy_fake_tdo_impl(fake_tdo_impl* impl)
{
    etch_destructor destroy = NULL;
    int result = verify_object((objmask*)impl, OBJTYPE_FAKETDO_IMPL, CLASSID_FAKETDO_IMPL, NULL);
    if (result == -1) return -1; /* object passed was not expected object */

    impl->list->is_readonly = TRUE;
    impl->list->destroy(impl->list);     /* destroy list, but not content */

    impl->arrray->list->is_readonly = FALSE;
    impl->arrray->destroy(impl->arrray); /* destroy arravalue and content */

    etch_free(impl);    
    return 0;
}


/**
 * new_fake_tdi_impl()
 * constructor for TDI implementation instance data
 */
fake_tdi_impl* new_fake_tdi_impl()
{
    fake_tdi_impl* data = (fake_tdi_impl*) new_object
        (sizeof(fake_tdi_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDI_IMPL);

    data->destroy = destroy_fake_tdi_impl;
    
    return data;
}


/**
 * new_fake_tdo_impl()
 * constructor for TDO implementation instance data
 */
fake_tdo_impl* new_fake_tdo_impl()
{
    fake_tdo_impl* data = (fake_tdo_impl*) new_object
        (sizeof(fake_tdo_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDO_IMPL);
 
    data->destroy = destroy_fake_tdo_impl;
    
    return data;
}


enum etch_classid CLASSID_VTABLE_FAKETDI = CLASSID_DYNAMIC_START + 0x0;  
enum etch_classid CLASSID_VTABLE_FAKETDO = CLASSID_DYNAMIC_START + 0x1;


/**
 * faketdi_start_array() overrides tdi_start_array()
 * Starts reading an array from the stream.
 * @return the array that we are reading.
 * @throws IOException if there is a problem with the stream.
 */
etch_arrayvalue* faketdi_start_array(tagged_data_input* tdi)
{
    int result = 0;
    fake_tdi_impl* data = NULL;
    etch_arrayvalue* newarrayval = NULL;
    const int DIM_ZERO = 0, READONLY = TRUE, DEFSIZE = 0, DEFDELTA = 0;
    byte fake_typecode = 0;
    etch_type* fake_structtype = NULL;
    
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);

    data = (fake_tdi_impl*) tdi->impl; /* validate instance data */
    result = verify_object((objmask*)data, OBJTYPE_FAKETDI_IMPL, 0, 0);
    CU_ASSERT_EQUAL_FATAL(result,0);

    result = data->destroy(NULL); /* ensure we can call instance data destructor */
    CU_ASSERT_EQUAL(result,-1);

    CU_ASSERT_EQUAL(data->started,FALSE);
    CU_ASSERT_EQUAL(data->done,FALSE);
    CU_ASSERT_EQUAL(data->ended,FALSE);

    data->started = TRUE;
    data->index = 0;

    /* create instance array */
    newarrayval = new_arrayvalue 
        (fake_typecode, fake_structtype, DIM_ZERO, DEFSIZE, DEFDELTA, READONLY); 

    data->arrray = newarrayval;
    CU_ASSERT_PTR_NOT_NULL_FATAL(data->arrray);

    return data->arrray;
}


/**
 * faketdo_start_array() overrides tdo_start_array()
 */
etch_arrayvalue* faketdo_start_array(tagged_data_output* tdo, etch_arrayvalue* thisp)
{
    int result = 0;
    fake_tdo_impl* data = NULL;
  
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);

    data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
    result = verify_object((objmask*)data, OBJTYPE_FAKETDO_IMPL, 0, 0);
    CU_ASSERT_EQUAL_FATAL(result,0);

    result = data->destroy(NULL); /* ensure we can call into instance data destructor */
    CU_ASSERT_EQUAL(result,-1);

    CU_ASSERT_EQUAL(data->started,FALSE);
    CU_ASSERT_EQUAL(data->ended,FALSE);

    data->arrray = thisp;
    data->list = new_arrayvalue_arraylist();
    CU_ASSERT_PTR_NOT_NULL_FATAL(data->arrray);
    CU_ASSERT_PTR_NOT_NULL_FATAL(data->list);

    data->started = TRUE;
    return data->arrray;
}


/**
 * faketdi_read_array_element() overrides tdi_read_array_element()
 */
int faketdi_read_array_element(tagged_data_input* tdi, ETCH_ARRAY_ELEMENT** out_ae)   
{
    int result = 0;
    fake_tdi_impl* data = NULL;
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);

    data = (fake_tdi_impl*) tdi->impl; /* validate instance data */
    result = verify_object((objmask*)data, OBJTYPE_FAKETDI_IMPL, 0, 0);
    CU_ASSERT_EQUAL_FATAL(result,0);

    CU_ASSERT_EQUAL(data->started,TRUE);
    CU_ASSERT_EQUAL(data->done,FALSE);
    CU_ASSERT_EQUAL(data->ended,FALSE);

	if (data->index < (int)testdata->count)
	{   
		*out_ae = arraylist_get(testdata, data->index++);
		return TRUE;
	}
	
	data->done = TRUE;
	return FALSE;
}


/**
 * faketdo_write_array_element() overrides tdi_read_array_element()
 */
int faketdo_write_array_element(tagged_data_output* tdo, objmask* newitem)   
{
    int result = 0;
    fake_tdo_impl* data = NULL;
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);

    data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
    result = verify_object((objmask*)data, OBJTYPE_FAKETDO_IMPL, 0, 0);
    CU_ASSERT_EQUAL_FATAL(result,0);
    CU_ASSERT_PTR_NOT_NULL_FATAL(data->list);  

    CU_ASSERT_EQUAL(data->started,TRUE);
    CU_ASSERT_EQUAL(data->ended,FALSE);
   
	result = arraylist_add(data->list, newitem);
	return 0;
}


/**
 * faketdi_end_array() overrides tdi_end_array()
 * Ends an array that we are reading.
 * @param array the array that we read.
 * @throws IOException if there is a problem with the stream.
 */
int faketdi_end_array(tagged_data_input* tdi, etch_arrayvalue* av)
{
    int result = 0;
    fake_tdi_impl* data = NULL;
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);

    data = (fake_tdi_impl*) tdi->impl; /* validate instance data */
    result = verify_object((objmask*)data, OBJTYPE_FAKETDI_IMPL, 0, 0);
    CU_ASSERT_EQUAL_FATAL(result,0);
    CU_ASSERT_PTR_NOT_NULL_FATAL(data->arrray); 
 
    CU_ASSERT_EQUAL(data->started,TRUE);
    CU_ASSERT_EQUAL(data->done,TRUE);
    CU_ASSERT_EQUAL(data->ended,FALSE);
    CU_ASSERT_EQUAL(data->arrray,av);
			
	data->ended = TRUE;
    return 0;
}


/**
 * faketdo_end_array() overrides tdo_end_array()
 */
int faketdo_end_array(tagged_data_output* tdo, etch_arrayvalue* av)
{
    int result = 0;
    fake_tdo_impl* data = NULL;
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);

    data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
    result = verify_object((objmask*)data, OBJTYPE_FAKETDO_IMPL, 0, 0);
    CU_ASSERT_EQUAL_FATAL(result,0);
    CU_ASSERT_PTR_NOT_NULL_FATAL(data->list); 
    CU_ASSERT_PTR_NOT_NULL_FATAL(data->arrray); 
 
    CU_ASSERT_EQUAL(data->started,TRUE);
    CU_ASSERT_EQUAL(data->ended,FALSE);
    CU_ASSERT_EQUAL(data->arrray,av);
			
	data->ended = TRUE;
    return 0;
}


/**
 * faketdi_close() port from java test
 * also tested here is that we can call base methods on a derived object. we invoke 
 * the TDI destructor here, which is an overide of the etchobject destructor.
 * the TDI destructor walks the vtable chain to its parent, and invokes the parent
 * destructor to destroy etchobject content such as any exception, and finally
 * the object itself.
 */
void faketdi_close(tagged_data_input* tdi)  
{
    tdi->destroy(tdi); /* destroy object */
}


/**
 * faketdo_close() port from java test
 */
void faketdo_close(tagged_data_output* tdo)  
{
    tdo->destroy(tdo); /* destroy object */
}


/**
 * new_fake_tdi()
 * constructor for TDI implementation  
 */
tagged_data_input* new_fake_tdi()
{
    tagged_data_input* faketdi = NULL;
    i_tagged_data_input*  vtab = NULL;
    const unsigned short CLASS_ID = CLASSID_VTABLE_FAKETDI;

    faketdi = new_tagged_data_input();
   
    vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);

    if(!vtab)  
    {   
        vtab = new_vtable(NULL, sizeof(i_tagged_data_input), CLASS_ID);
   
        /* override three i_tagged_data_input methods */
        vtab->start_array = faketdi_start_array;
        vtab->end_array   = faketdi_end_array;
        vtab->read_array_element = faketdi_read_array_element;

        vtab->vtab = faketdi->vtab; /* chain parent vtable to override vtab */

        cache_insert(vtab->hashkey, vtab, FALSE);
    } 

    CU_ASSERT_EQUAL_FATAL(vtab->class_id, CLASSID_VTABLE_FAKETDI);

    faketdi->vtab = vtab;  /* set override vtab */

    faketdi->impl = (objmask*) new_fake_tdi_impl(); /* instantiate tdi instance data */

    switch(g_which_exception_test)
    {   case EXCPTEST_UNCHECKED_STATICTEXT: 
             etch_throw((objmask*)faketdi, EXCPTYPE_NULLPTR, NULL, 0);  
             break;   
        case EXCPTEST_CHECKED_COPYTEXT:   
             etch_throw((objmask*)faketdi, EXCPTYPE_CHECKED_BOGUS, L"copied text", ETCHEXCP_COPYTEXT | ETCHEXCP_FREETEXT);  
             break; 
        case EXCPTEST_CHECKED_STATICTEXT:   
             etch_throw((objmask*)faketdi, EXCPTYPE_CHECKED_BOGUS, local_excp_text, ETCHEXCP_STATICTEXT);  
             break;       
    }

    return faketdi;
}


/**
 * new_fake_tdo()
 * constructor for TDO implementation  
 */
tagged_data_output* new_fake_tdo()
{
    tagged_data_output* faketdo = NULL;
    i_tagged_data_output*  vtab = NULL;
    const unsigned short CLASS_ID = CLASSID_VTABLE_FAKETDO;

    faketdo = new_tagged_data_output();
     
    vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);

    if(!vtab)  
    {   vtab = new_vtable(NULL, sizeof(i_tagged_data_input), CLASS_ID);
     
        /* override three i_tagged_data_output methods */
        vtab->start_array = faketdo_start_array;
        vtab->end_array   = faketdo_end_array;
        vtab->write_array_element = faketdo_write_array_element;

        vtab->vtab = faketdo->vtab; /* chain parent vtab to override vtab */
    
        cache_insert(vtab->hashkey, vtab, FALSE);
    } 

    CU_ASSERT_EQUAL_FATAL(vtab->class_id, CLASSID_VTABLE_FAKETDO);

    faketdo->vtab = vtab; /* set override vtab */

    faketdo->impl = (objmask*) new_fake_tdo_impl(); /* instantiate tdo instance data */

    return faketdo;
}

#endif /* if(0) */


/* 
 * load_testdata_string()
 * load testdata array with some ETCH_STRING objects
 */
int load_testdata_string()
{
    int i = 0, numitems = 4;
    etch_string* newobj = NULL;
    wchar_t* str0 = L"now ", *str1 = L"is  ", *str2 = L"the ", *str3 = L"time";
    wchar_t* strings[4] = { str0, str1, str2, str3 };

    for(; i < numitems; i++)
    {
        /* new_etch_string copies parameter string */        
        newobj = new_string(strings[i], ETCH_ENCODING_UTF16);  
        arraylist_add(testdata, newobj);
    }

    return numitems;
}


/* 
 * load_testdata_int()
 * load testdata array with some ETCH_INT objects
 */
int load_testdata_int()
{
    int i = 0, numitems = 4;
    etch_int32* newobj = NULL;
    int ints[4] = { 1, 2, 3, 4 };

    for(; i < numitems; i++)
    {
        newobj = new_int32(ints[i]);
        arraylist_add(testdata, newobj);
    }

    return numitems;
}


/* 
 * new_testdata()
 * create testdata array and load it up with data objects
 */
int new_testdata(const int datatype)
{
    int count = 0;
    g_which_exception_test = 0;
    testdata = new_arraylist(0,0);  
    testdata->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
 
    switch(datatype)
    { case 1: count = load_testdata_int();    break;
      case 2: count = load_testdata_string(); break;
      default: return -1;
    }
  
    return count;
}


/* 
 * destroy_testdata()
 * destroy testdata array and content
 */
void destroy_testdata()
{
    arraylist_destroy(testdata, TRUE);
}


/* 
 * compare_lists()
 * compares testdata list with the arrayvalue's list
 * returns boolean indicating equal or not
 */
int compare_lists(etch_arrayvalue* av)
{
    int testcount = 0, result = 0, i = 0, eqcount = 0;
    CU_ASSERT_PTR_NOT_NULL_FATAL(av);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(av->list);

    testcount = av->list->count; 
    CU_ASSERT_EQUAL(testcount, testdata->count);
    if (testcount != testdata->count) return FALSE;

    for(; i < (const int) testcount; i++)     
        if (arraylist_get(testdata, i) == arraylist_get(av->list, i))
            eqcount++;  

    CU_ASSERT_EQUAL(testcount, eqcount);     
    return testcount == eqcount;
}


#if(0)

/* 
 * run_read_test
 * create an array value to read current test data
 * and verify that content matches test data
 */
void run_read_test()
{
    int result = 0;
    tagged_data_input* tdi = NULL;
    etch_arrayvalue* av = NULL;

    tdi = new_fake_tdi();
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
    CU_ASSERT_FALSE_FATAL(is_exception(tdi));
    CU_ASSERT_EQUAL_FATAL(get_result(tdi),0);

    /* create array value, reading from test values */
    av = arrayvalue_read(tdi);

    result = compare_lists(av);
    CU_ASSERT_EQUAL(result, TRUE);

    /* clean up TDI */
    faketdi_close(tdi);

    /* destroy testdata list and content */
    testdata->destroy(testdata);

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); /* verify all memory freed */
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */   
}


/* 
 * run_write_test
 * create an array value to write current test data
 * and verify that content matches test data
 */
void run_write_test()
{
    int result = 0, i = 0;
    etch_arrayvalue* arrayval = NULL;
    tagged_data_output* tdo = NULL;
    const int DIM_ZERO = 0, NOT_READONLY = FALSE, DEFSIZE = 0, DEFDELTA = 0;
    etch_type* fake_structtype = NULL;
    byte  fake_typecode = 0;
    void* thisitem = NULL;
                                         
    arrayval = new_arrayvalue /* create array value instance */
        (fake_typecode, fake_structtype, DIM_ZERO, DEFSIZE, DEFDELTA, NOT_READONLY);

    CU_ASSERT_PTR_NOT_NULL_FATAL(arrayval);
    CU_ASSERT_FALSE_FATAL(is_exception(arrayval));
    CU_ASSERT_EQUAL_FATAL(get_result(arrayval),0);

    for(; i < (const int) testdata->count; i++)
    {     
        thisitem = arraylist_get(testdata, i);
        result = arrayvalue_add(arrayval, (ETCH_ARRAY_ELEMENT*) thisitem);
        CU_ASSERT_EQUAL(result,0);
    }

    tdo = new_fake_tdo();

    result = arrayvalue_write(arrayval, tdo);
    CU_ASSERT_EQUAL(result,0);

    faketdo_close(tdo);

    /* destroy testdata list but not content */
    arraylist_destroy(testdata, FALSE);  

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  /* verify all memory freed */
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */   
}

#endif /* #if(0) */


/* 
 * run_exception_test
 *  
 */
void run_exception_test(const int whichtest)
{
    int result = 0;
    /* global marker asks components to throw exceptions */
    tagged_data_input* tdi = NULL;
    etch_arrayvalue* av = NULL;
    etchexception* excp = NULL;
    g_which_exception_test = whichtest; 

    #if(0)
    /* create a bogus TDI inheriting from tagged data input */
    tdi = new_fake_tdi();
    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);

    do 
    {   switch(whichtest)
        {   case EXCPTEST_UNCHECKED_STATICTEXT:
            case EXCPTEST_CHECKED_COPYTEXT:
            case EXCPTEST_CHECKED_STATICTEXT:
            {   CU_ASSERT_TRUE_FATAL(is_exception(tdi));
                #if IS_DEBUG_CONSOLE 
                wprintf(L"\ncaught %s exception\n", tdi->result->exception->excptext);          
                #endif   
                break;          
            }
        }
        if (is_exception(tdi)) break;

        /* create array value, reading from test values */
        av = arrayvalue_read(tdi);
        /* if av is NULL we would throw an exception here */

        result = compare_lists(av);
        CU_ASSERT_EQUAL(result, TRUE); 

    } while(0);  

    /* clean up TDI */
    faketdi_close(tdi);

    #endif

    /* destroy testdata list and content */
    testdata->destroy(testdata);

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); /* verify all memory freed */
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */   
}


#if(0)

/* 
 * read_test_integer
 */
void read_test_integer(void)
{
    int itemcount = 0;
    if ((itemcount = new_testdata(1)) > 0)
        run_read_test(); 
}


/* 
 * write_test_integer
 */
void write_test_integer(void)
{
    int itemcount = 0;
    if ((itemcount = new_testdata(1)) > 0)
        run_write_test(); 
}


/* 
 * read_test_string
 */
void read_test_string(void)
{
    int itemcount = 0;
    if ((itemcount = new_testdata(2)) > 0)
        run_read_test(); 
}


/* 
 * write_test_string
 */
void write_test_string(void)
{
    int itemcount = 0;
    if ((itemcount = new_testdata(2)) > 0)
        run_write_test(); 
}

#endif /* #if(0) */


/* 
 * exception_test_1
 */
void exception_test_1(void)
{
    int itemcount = 0;
    if ((itemcount = new_testdata(1)) > 0)
        run_exception_test(EXCPTEST_UNCHECKED_STATICTEXT); 
}


/* 
 * exception_test_2
 */
void exception_test_2(void)
{
    int itemcount = 0;
    if ((itemcount = new_testdata(1)) > 0)
        run_exception_test(EXCPTEST_CHECKED_COPYTEXT); 
}


/* 
 * exception_test_3
 */
void exception_test_3(void)
{
    int itemcount = 0;
    if ((itemcount = new_testdata(1)) > 0)
        run_exception_test(EXCPTEST_CHECKED_STATICTEXT); 
}


/* 
 * test_iterator_over_arraylist
 */
void test_iterator_over_arraylist(void)
{
    etch_iterator* iterator = NULL; 
    int testcount = 0;
    new_testdata(1);
    CU_ASSERT_PTR_NOT_NULL_FATAL(testdata);
    CU_ASSERT_NOT_EQUAL(testdata->count, 0);

    iterator = new_iterator(testdata, &testdata->iterable);

    CU_ASSERT_PTR_NOT_NULL_FATAL(iterator);
    CU_ASSERT_EQUAL_FATAL(iterator->ordinal,1);
    testcount = 1;

    while(iterator->vtab->has_next(iterator))
          testcount += (iterator->vtab->next(iterator) == 0);  
        
    CU_ASSERT_EQUAL(testcount, testdata->count);

    destroy_iterator(iterator);
    destroy_testdata();

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  /* verify all memory freed */
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_create_from_1
 * create arrayvalue from single-dimensioned byte nativearray and verify
 */
void test_create_from_1(void)
{
    etch_arrayvalue* av = NULL;
    etch_byte* thisitem = NULL;
    int   i = 0, result = 0;
    byte  fake_typecode = 0xff;
    char  x[4] = {'a','b','c','d'};
    const int numdimensions = 1, itemcount = 4;

    etch_nativearray* a = new_nativearray 
        (CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, itemcount, 0, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type = ETCHTYPEB_BYTE; /* array content is type byte */
    a->content_class_id = CLASSID_NONE;   /* array content is unwrapped (default) */

    for(i = 0; i < itemcount; i++) /* populate native array from test data */
        CU_ASSERT_EQUAL(0, result = a->put1(a, &x[i], i)); 

    /* populate arrayvalue from native array */
    av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
    CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);

    /* assert that arrayvalue content matches test data */
    for(i=0; i < itemcount; i++)
    {     
        thisitem = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
        CU_ASSERT_EQUAL(thisitem->obj_type, ETCHTYPEB_PRIMITIVE);
        CU_ASSERT_EQUAL(thisitem->class_id, CLASSID_PRIMITIVE_BYTE);
        CU_ASSERT_EQUAL(thisitem->value, x[i]);
    }

    av->destroy(av);  /* destroy array value and content */

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0); /* assert all memory freed */ 
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_create_from_2
 * create arrayvalue from single-dimensioned int nativearray and verify
 */
void test_create_from_2(void)
{
    etch_arrayvalue* av  = NULL;
    etch_int32* thisitem = NULL;
    int   i = 0, result  = 0;
    byte  fake_typecode  = 0xff;
    int   n[4] = {10,11,12,13};
    const int numdimensions = 1, itemcount = 4;

    etch_nativearray* a = new_nativearray 
        (CLASSID_ARRAY_INT32, sizeof(int), numdimensions, itemcount, 0, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type = ETCHTYPEB_INT32; /* array content is type int */
    a->content_class_id = CLASSID_NONE;   /* array content is unwrapped (default) */

    for(i = 0; i < itemcount; i++) /* populate native array from test data */
        CU_ASSERT_EQUAL(0, result = a->put1(a, &n[i], i));
 
    /* populate arrayvalue from native array */
    av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
    CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);

    /* assert that arrayvalue content matches test data */
    for(i=0; i < itemcount; i++)
    {     
        thisitem = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
        CU_ASSERT_EQUAL(thisitem->obj_type, ETCHTYPEB_PRIMITIVE);
        CU_ASSERT_EQUAL(thisitem->class_id, CLASSID_PRIMITIVE_INT32);
        CU_ASSERT_EQUAL(thisitem->value, n[i]);
    }

    av->destroy(av);  /* destroy array value and content */

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);   
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_create_from_3
 * create arrayvalue from single-dimensioned long long nativearray and verify
 */
void test_create_from_3(void)
{
    etch_arrayvalue* av  = NULL;
    etch_int64* thisitem = NULL;
    int   i = 0, result  = 0;
    byte  fake_typecode  = 0xff;
    int64 n[5] = {-2, -1, 0 , 1, 2};
    const int numdimensions = 1, itemcount = 5;

    etch_nativearray* a = new_nativearray 
        (CLASSID_ARRAY_INT64, sizeof(int64), numdimensions, itemcount, 0, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type = ETCHTYPEB_INT64; /* array content is type int64 */
    a->content_class_id = CLASSID_NONE;   /* array content is unwrapped (default) */

    for(i = 0; i < itemcount; i++) /* populate native array from test data */
        CU_ASSERT_EQUAL(0, result = a->put1(a, &n[i], i));
 
    /* populate arrayvalue from native array */
    av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
    CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);

    /* assert that arrayvalue content matches test data */
    for(i=0; i < itemcount; i++)
    {     
        thisitem = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
        CU_ASSERT_EQUAL(thisitem->obj_type, ETCHTYPEB_PRIMITIVE);
        CU_ASSERT_EQUAL(thisitem->class_id, CLASSID_PRIMITIVE_INT64);
        CU_ASSERT_EQUAL(thisitem->value, n[i]);
    }

    av->destroy(av);  /* destroy array value and content */

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);   
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_create_from_4
 * create arrayvalue from single-dimensioned double nativearray and verify
 */
void test_create_from_4(void)
{
    etch_arrayvalue* av   = NULL;
    etch_double* thisitem = NULL;
    int    i = 0, result  = 0;
    byte   fake_typecode  = 0xff;
    double f[3] = {-1000.0, 3.14149, 65536.0};
    const int numdimensions = 1, itemcount = 3;

    etch_nativearray* a = new_nativearray 
        (CLASSID_ARRAY_DOUBLE, sizeof(int64), numdimensions, itemcount, 0, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type = ETCHTYPEB_IEEE64; /* array content is type double */
    a->content_class_id = CLASSID_NONE;   /* array content is unwrapped (default) */

    for(i = 0; i < itemcount; i++) /* populate native array from test data */
        CU_ASSERT_EQUAL(0, result = a->put1(a, &f[i], i));
 
    /* populate arrayvalue from native array */
    av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
    CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);

    /* assert that arrayvalue content matches test data */
    for(i=0; i < itemcount; i++)
    {     
        thisitem = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
        CU_ASSERT_EQUAL(thisitem->obj_type, ETCHTYPEB_PRIMITIVE);
        CU_ASSERT_EQUAL(thisitem->class_id, CLASSID_PRIMITIVE_DOUBLE);
        CU_ASSERT_DOUBLE_EQUAL(thisitem->value, f[i], 0.01);
    }

    av->destroy(av);  /* destroy array value and content */

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);   
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_create_from_5
 * create arrayvalue from single-dimensioned string nativearray and verify
 */
void test_create_from_5(void)
{
    etch_arrayvalue* av   = NULL;
    etch_string* thisitem = NULL;
    int   i = 0, result   = 0;
    byte  fake_typecode   = 0xff;
    char* c[3] = {"hey", "it", "works!"};
    const int numdimensions = 1, itemcount = 3;

    etch_nativearray* a = new_nativearray 
        (CLASSID_ARRAY_STRING, sizeof(void*), numdimensions, itemcount, 0, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type = ETCHTYPEB_STRING; /* array content is type string */
    a->content_class_id = CLASSID_NONE;    /* array content is unwrapped (default) */

    for(i = 0; i < itemcount; i++) /* populate native array from test data */
        CU_ASSERT_EQUAL(0, result = a->put1(a, &c[i], i));
 
    /* populate arrayvalue from native array */
    av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
    CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);

    /* assert that arrayvalue content matches test data */
    for(i=0; i < itemcount; i++)
    {     
        thisitem = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
        CU_ASSERT_EQUAL(thisitem->obj_type, ETCHTYPEB_PRIMITIVE);
        CU_ASSERT_EQUAL(thisitem->class_id, CLASSID_STRING);
        CU_ASSERT_NSTRING_EQUAL(thisitem->v.value, c[i], strlen(c[i]));
    }

    av->destroy(av);  /* destroy array value and content */

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);   
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_create_from_10
 * create arrayvalue from two-dimensioned byte nativearray and verify
 */
void test_create_from_10(void)
{
    etch_arrayvalue* av = NULL;
    etch_arrayvalue* subav = NULL;
    etch_byte*       thisitem = NULL;
    int   i = 0, j = 0, result = 0;
    byte  fake_typecode = 0xff;
    char  x[2][4] = {  {'a','b','c','d',},  {'e', 'f', 'g', 'h',},  };
    const int numdimensions = 2, dim0count = 4, dim1count = 2;

    etch_nativearray* a = new_nativearray 
        (CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, dim0count, dim1count, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type = ETCHTYPEB_BYTE; /* array content is type byte */
    a->content_class_id = CLASSID_NONE;   /* array content is unwrapped (default) */

    for(i = 0; i < dim1count; i++)
        for(j = 0; j < dim0count; j++) /* populate native array from test data */
            CU_ASSERT_EQUAL(0, result = a->put2(a, &x[i][j], i, j)); 

    /* populate arrayvalue from native array. we tell the arrayvalue to take  
     * ownership of the native array. result is a 2-dimensional arrayvalue,
     * which is represented as an arrayvalue of arrayvalues
     */
    av = new_arrayvalue_from(a, fake_typecode, NULL, 32, 0, FALSE);
    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
    CU_ASSERT_EQUAL(av->dim, numdimensions); 
    CU_ASSERT_EQUAL(arrayvalue_count(av), dim1count); 

    /* assert that arrayvalue content matches test data */
    for(i = 0; i < dim1count; i++)
    {
        subav = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(subav); 
        CU_ASSERT_EQUAL(subav->obj_type, ETCHTYPEB_ARRAYVAL);
        CU_ASSERT_EQUAL(arrayvalue_count(subav), dim0count);

        for(j = 0; j < dim0count; j++)
        {     
            byte expected_value = x[i][j];
            thisitem = arrayvalue_get(subav, j);
            CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
            CU_ASSERT_EQUAL(thisitem->obj_type, ETCHTYPEB_PRIMITIVE);
            CU_ASSERT_EQUAL(thisitem->class_id, CLASSID_PRIMITIVE_BYTE);
            CU_ASSERT_EQUAL(thisitem->value, expected_value);
        }
    }

    /* destroy array value and content. memory includes the sub-arrayvalues,
     * the native array from above, and the sub-native arrays */
    av->destroy(av);  

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0); /* assert all memory freed */ 
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_create_from_11
 * create arrayvalue from two-dimensioned int16 nativearray and verify
 */
void test_create_from_11(void)
{
    etch_arrayvalue* av = NULL;
    etch_arrayvalue* subav = NULL;
    etch_int16*      thisitem = NULL;
    int   i = 0, j = 0, result = 0;
    byte  fake_typecode = 0xff;
    short n[2][4] = { { 100,101,102,103, },  { 0xeffc,0xeffd,0xeffe,0xefff },  };
    const int numdimensions = 2, dim0count = 4, dim1count = 2;

    etch_nativearray* a = new_nativearray 
        (CLASSID_ARRAY_INT16, sizeof(short), numdimensions, dim0count, dim1count, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type = ETCHTYPEB_INT16; /* array content is type short int */
    a->content_class_id = CLASSID_NONE;   /* array content is unwrapped (default) */

    for(i = 0; i < dim1count; i++)
        for(j = 0; j < dim0count; j++) /* populate native array from test data */
            CU_ASSERT_EQUAL(0, result = a->put2(a, &n[i][j], i, j)); 

    /* populate arrayvalue from native array. we tell the arrayvalue to take  
     * ownership of the native array. result is a 2-dimensional arrayvalue,
     * which is represented as an arrayvalue of arrayvalues
     */
    av = new_arrayvalue_from(a, fake_typecode, NULL, 32, 0, FALSE);
    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
    CU_ASSERT_EQUAL(av->dim, numdimensions); 
    CU_ASSERT_EQUAL(arrayvalue_count(av), dim1count); 

    /* assert that arrayvalue content matches test data */
    for(i = 0; i < dim1count; i++)
    {
        subav = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(subav); 
        CU_ASSERT_EQUAL(subav->obj_type, ETCHTYPEB_ARRAYVAL);
        CU_ASSERT_EQUAL(arrayvalue_count(subav), dim0count);

        for(j = 0; j < dim0count; j++)
        {     
            short expected_value = n[i][j];
            thisitem = arrayvalue_get(subav, j);
            CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
            CU_ASSERT_EQUAL(thisitem->obj_type, ETCHTYPEB_PRIMITIVE);
            CU_ASSERT_EQUAL(thisitem->class_id, CLASSID_PRIMITIVE_INT16);
            CU_ASSERT_EQUAL(thisitem->value, expected_value);
        }
    }

    /* destroy array value and content. memory includes the sub-arrayvalues,
     * the native array from above, and the sub-native arrays */
    av->destroy(av);  

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0); /* assert all memory freed */ 
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_create_from_12
 * create arrayvalue from two-dimensioned string nativearray and verify
 */
void test_create_from_12(void)
{
    etch_arrayvalue* av = NULL;
    etch_arrayvalue* subav = NULL;
    etch_string*     thisitem = NULL;
    const int DEFSIZE=0, DEFDELTA=0; 
    int   i = 0, j = 0, result = 0;
    byte  fake_typecode = 0xff;

    wchar_t* x[6][4] 
      = { { L"I think that I", L"shall never see,", L"A poem as lovely", L"as a tree.", }, 
          { L"She knows not what", L"the curse may be,", L"And so she weaveth", L"steadily,", }, 
          { L"And little other", L"care hath she,", L"The Lady of Shalott", L".", }, 
          { L"And therefore I have sailed", L"the seas, and come", L"To the holy city", L"of Byzantium.", },
          { L"Once out of nature", L"I shall never take", L"My bodily form", L" from any natural thing,", }, 
          { L"But such a form", L"as Grecian goldsmiths make", L"Of hammered gold ", L"and gold enameling", }, 
       };

    const int numdimensions = 2, dim0count = 4, dim1count = 6;

    #if(0) /* create empty native array and populate it item by item */

    etch_nativearray* a = new_nativearray (CLASSID_ARRAY_STRING, 
        sizeof(void*), numdimensions, dim0count, dim1count, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type = ETCHTYPEB_STRING; /* array content is type string */
    a->content_class_id = CLASSID_NONE;   /* array content is unwrapped (default) */

    for(i = 0; i < dim1count; i++)
    for(j = 0; j < dim0count; j++)  /* populate native array from test data */
        CU_ASSERT_EQUAL(0, result = a->put2(a, &x[i][j], i, j)); 

    #else /* create native array from static array */

    etch_nativearray* a = new_nativearray_from(&x, CLASSID_ARRAY_STRING, 
        sizeof(void*), numdimensions, dim0count, dim1count, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type = ETCHTYPEB_STRING; /* array content is type string */
    a->content_class_id = CLASSID_NONE;     /* array content is unwrapped (default) */

    #endif /* end create native array

    for(i = 0; i < dim1count; i++) /* verify array content is as expected */
    for(j = 0; j < dim0count; j++)       
    {   wchar_t* thisx = 0, *thatx = x[i][j];          
        result = a->get2(a, &thisx, i, j); 
        CU_ASSERT_EQUAL(result, 0); 
        CU_ASSERT_PTR_NOT_NULL_FATAL(thisx); 
        result = wcscmp(thisx, thatx);
        CU_ASSERT_EQUAL(result,0); 
    }   

    /* populate arrayvalue from native array. we tell the arrayvalue to take  
     * ownership of the native array. result is a 2-dimensional arrayvalue,
     * which is represented as an arrayvalue of arrayvalues
     */
    av = new_arrayvalue_from(a, fake_typecode, NULL, DEFSIZE, DEFDELTA, FALSE);
    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
    CU_ASSERT_EQUAL(av->dim, numdimensions); 
    CU_ASSERT_EQUAL(result = arrayvalue_count(av), dim1count); 

    /* assert that arrayvalue content matches test data */
    for(i = 0; i < dim1count; i++)
    {
        subav = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(subav); 
        CU_ASSERT_EQUAL(subav->obj_type, ETCHTYPEB_ARRAYVAL);
        CU_ASSERT_EQUAL(result = arrayvalue_count(subav), dim0count);

        for(j = 0; j < dim0count; j++)
        {     
            wchar_t* expected_value = x[i][j];
            thisitem = arrayvalue_get(subav, j);
            CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
            CU_ASSERT_EQUAL(thisitem->obj_type, ETCHTYPEB_PRIMITIVE);
            CU_ASSERT_EQUAL(thisitem->class_id, CLASSID_STRING);
            result = wcscmp(expected_value, thisitem->v.value);
            CU_ASSERT_EQUAL(result, 0);
        }
    }

    /* destroy array value and content. memory includes the sub-arrayvalues,
     * the native array from above, and the sub-native arrays */
    av->destroy(av);  

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0); /* assert all memory freed */ 
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_create_from_13
 * create arrayvalue from two-dimensioned object nativearray and verify
 */
void test_create_from_13(void)
{
    etch_nativearray* a = NULL;
    etch_arrayvalue* av = NULL;
    etch_arrayvalue* subav = NULL;
    etch_object* thisitem = NULL;
    const int DEFSIZE=0, DEFDELTA=0; 
    int   i = 0, j = 0, result = 0, count = 0;
    byte  fake_typecode = 0xff;

    etch_object* x[2][3] = { { NULL, NULL, NULL, }, { NULL, NULL, NULL, }, };

    const int numdimensions = 2, dim0count = 3, dim1count = 2;

    for(i = 0; i < dim1count; i++)  /* populate array of objects */
    for(j = 0; j < dim0count; j++)
    {   etch_object* thisobj = new_etch_object(0, etch_malloc(4,0));
        thisobj->is_value_object = FALSE;  /* indicates etch_free value */
        thisobj->hashkey = ++count;     /* usurp hashkey for this test */
        x[i][j] = thisobj;
    }

    /* create an etch_nativearray from the above array of objects. 
     * native array owns its data and so since the data are etch objects
     * the native array will destroy them at such time as arrayvalue
     * destroys the native array, the objects likewise destroying their
     * own content (that content being the etch_malloc'ed memory, above).
     */
    a = new_nativearray_from(&x, CLASSID_ARRAY_OBJECT, 
        sizeof(void*), numdimensions, dim0count, dim1count, 0);  

    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type = ETCHTYPEB_ETCHOBJECT; /* array content is type object */
    a->content_class_id = CLASSID_OBJECT;       /* array content is wrapped */

    for(i = 0; i < dim1count; i++)   /* assert that native array content */
    for(j = 0; j < dim0count; j++)   /* when read back, is as expected */    
    {   etch_object* thisx = 0, *thatx = x[i][j];          
        result = a->get2(a, &thisx, i, j); 
        CU_ASSERT_EQUAL(result, 0); 
        CU_ASSERT_PTR_NOT_NULL_FATAL(thisx); 
        CU_ASSERT_PTR_EQUAL_FATAL(thisx, thatx);  
    }   

    /* populate arrayvalue from native array. we tell the arrayvalue to take  
     * ownership of the native array. result is a 2-dimensional arrayvalue,
     * which is represented as an arrayvalue of arrayvalues
     */
    av = new_arrayvalue_from(a, fake_typecode, NULL, DEFSIZE, DEFDELTA, FALSE);

    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
    CU_ASSERT_EQUAL(av->dim, numdimensions); 
    CU_ASSERT_EQUAL(result = arrayvalue_count(av), dim1count); 

    /* assert that arrayvalue content matches test data */
    for(i = 0; i < dim1count; i++)
    {
        subav = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(subav); 
        CU_ASSERT_EQUAL(subav->obj_type, ETCHTYPEB_ARRAYVAL);
        CU_ASSERT_EQUAL(result = arrayvalue_count(subav), dim0count);

        for(j = 0; j < dim0count; j++)
        {     
            etch_object* expected_object = x[i][j];
            thisitem = arrayvalue_get(subav, j);
            CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
            CU_ASSERT_EQUAL_FATAL(thisitem->obj_type, ETCHTYPEB_ETCHOBJECT);
            // TODO this is CLASSID_ANY (zero) not CLASSID_OBJECT - verify that this is as expected
            // CU_ASSERT_EQUAL_FATAL(thisitem->class_id, CLASSID_OBJECT);
            CU_ASSERT_EQUAL(thisitem->hashkey, expected_object->hashkey);
        }
    }

    /* destroy array value and content. memory freed includes sub-arrayvalues,
     * the native array and test objects from above, and the sub-arrays */
    av->destroy(av);  

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0); /* assert all memory freed */ 
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_create_from_21
 * create arrayvalue from three-dimensioned byte nativearray and verify
 */
void test_create_from_21(void)
{
    etch_arrayvalue* av = NULL;
    etch_arrayvalue* subav1 = NULL;
    etch_arrayvalue* subav2 = NULL;
    etch_byte* thisitem = NULL;
    int   i = 0, j = 0, k = 0, result = 0;
    const int DEFSIZE=0, DEFDELTA=0; 
    byte  fake_typecode = 0xff;

    char x[2][3][4] 
      = { { {'a','b','c','d',},             
            {'e','f','g','h',}, 
            {'i','j','k','l',}, 
          },
          { {'m','n','o','p',},             
            {'q','r','s','t',}, 
            {'u','v','w','x',}, 
          },
        };

    const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;

    #if(0)
    etch_nativearray* a = new_nativearray   /* construct empty native array */
        (CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, dim0count, dim1count, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type = ETCHTYPEB_BYTE; /* array content is type byte */
    a->content_class_id = CLASSID_NONE;   /* array content is unwrapped (default) */

    for(i = 0; i < dim2count; i++) /* populate native array iteratively */
    for(j = 0; j < dim1count; j++) 
    for(k = 0; k < dim0count; k++) 
        CU_ASSERT_EQUAL(0, result = a->put3(a, &x[i][j][k], i, j, k)); 
    #else
    /* populate native arrary from static test data array */

    etch_nativearray* a = new_nativearray_from(&x, CLASSID_ARRAY_BYTE, 
        sizeof(byte), numdimensions, dim0count, dim1count, dim2count);  

    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 
    a->content_obj_type = ETCHTYPEB_BYTE; /* array content is type byte */
    a->content_class_id = CLASSID_NONE;   /* array content is unwrapped */

    #endif

    /* populate arrayvalue from native array. we tell the arrayvalue to take  
     * ownership of the native array. result is a 3-dimensional arrayvalue,
     * which is represented as an arrayvalue of arrayvalues of arrayvalues.
     */
    av = new_arrayvalue_from(a, fake_typecode, NULL, DEFSIZE, DEFDELTA, FALSE);

    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
    CU_ASSERT_EQUAL(av->dim, numdimensions); 
    CU_ASSERT_EQUAL(arrayvalue_count(av), dim2count); 

    /* assert that arrayvalue content matches test data */
    for(i = 0; i < dim2count; i++)
    {
        subav1 = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(subav1); 
        CU_ASSERT_EQUAL(subav1->obj_type, ETCHTYPEB_ARRAYVAL);
        CU_ASSERT_EQUAL(result = arrayvalue_count(subav1), dim1count);

        for(j = 0; j < dim1count; j++)
        {     
            subav2 = arrayvalue_get(subav1, j);
            CU_ASSERT_PTR_NOT_NULL_FATAL(subav2); 
            CU_ASSERT_EQUAL(subav2->obj_type, ETCHTYPEB_ARRAYVAL);
            CU_ASSERT_EQUAL(result = arrayvalue_count(subav2), dim0count);

            for(k = 0; k < dim0count; k++)
            {     
                byte expected_value = x[i][j][k];
                thisitem = arrayvalue_get(subav2, k);
                CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
                CU_ASSERT_EQUAL(thisitem->obj_type, ETCHTYPEB_PRIMITIVE);
                CU_ASSERT_EQUAL(thisitem->class_id, CLASSID_PRIMITIVE_BYTE);
                CU_ASSERT_EQUAL(thisitem->value, expected_value);
            }
        }
    }

    /* destroy array value and content. memory freed includes the native array 
     * constructed above, the arrayvalue, its sub-arrayvalues and their sub-
     * native arrays, and any disposable native array content. 
     */ 
    av->destroy(av);  

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0); /* assert all memory freed */ 
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_arrayvalue_to_nativearray_10
 * create create native byte[] array from arrayvalue
 */
void test_arrayvalue_to_nativearray_10(void)
{
    etch_arrayvalue* av = NULL;
    int   i = 0, result = 0;
    byte  fake_typecode = 0xff;
    char  x[4] = {'a','b','c','d'};
    const int numdimensions = 1, itemcount = 4;
    etch_nativearray* old_nativearray_dangling_ref = NULL;

    etch_nativearray* a = new_nativearray 
        (CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, itemcount, 0, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 

    a->content_obj_type  = ETCHTYPEB_BYTE; /* array content is type byte */
    a->content_class_id  = CLASSID_NONE;   /* array content is unwrapped (default) */

    for(i = 0; i < itemcount; i++) /* populate native array from test data */
        CU_ASSERT_EQUAL(0, result = a->put1(a, &x[i], i)); 

    /* populate arrayvalue from native array */
    av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
    CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);

    for(i=0; i < itemcount; i++)
    {     
        etch_byte* thisitem = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
        CU_ASSERT_EQUAL(thisitem->value, x[i]);
    }

    /* this will be a dangling pointer after we arrayvalue_to_nativearray() */
    old_nativearray_dangling_ref = av->natarray;

    /* convert new arrayvalue back to a nativearray. the embedded nativearray 
     * we created above, is destroyed in the process, and replaced with a new
     * nativearray, whose content should of course be identical to the old. 
     */
    result = arrayvalue_to_nativearray(av); 
    CU_ASSERT_PTR_NOT_NULL_FATAL (av->natarray); 
    CU_ASSERT_PTR_NOT_EQUAL_FATAL(av->natarray, old_nativearray_dangling_ref); 
    CU_ASSERT_EQUAL_FATAL(result,itemcount);  /* returns items inserted count */

    /* get items one at a time from the arrayvalue's new native array,
     * and compare to expected value from the test data above.
     */
    for(i = 0; i < itemcount; i++)  
    {
        byte c2=0, c1 = x[i];
        result = av->natarray->get1(av->natarray, &c2, i);
        CU_ASSERT_EQUAL(result,0);
        CU_ASSERT_EQUAL(c1, c2); 
    }

    av->destroy(av);  /* destroy array value and content */

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0); /* assert all memory freed */ 
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_arrayvalue_to_nativearray_20
 * create create native string[][] array from arrayvalue
 */
void test_arrayvalue_to_nativearray_20(void)
{
    etch_arrayvalue* av = NULL;
    etch_arrayvalue* subav = NULL;
    etch_string*     thisitem = NULL;
    byte  fake_typecode = 0xff;
    const int numdimensions = 2, dim0count = 4, dim1count = 6;
    const int DEFSIZE=0, DEFDELTA=0; 
    int   i = 0, j = 0, result = 0;
    etch_nativearray* old_nativearray_dangling_ref = NULL;

    wchar_t* x[6][4] 
      = { { L"I think that I", L"shall never see,", L"A poem as lovely", L"as a tree.", }, 
          { L"She knows not what", L"the curse may be,", L"And so she weaveth", L"steadily,", }, 
          { L"And little other", L"care hath she,", L"The Lady of Shalott", L".", }, 
          { L"And therefore I have sailed", L"the seas, and come", L"To the holy city", L"of Byzantium.", },
          { L"Once out of nature", L"I shall never take", L"My bodily form", L" from any natural thing,", }, 
          { L"But such a form", L"as Grecian goldsmiths make", L"Of hammered gold ", L"and gold enameling", }, 
       };

    etch_nativearray* a = new_nativearray_from(&x, CLASSID_ARRAY_STRING, 
        sizeof(void*), numdimensions, dim0count, dim1count, 0);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 
    a->content_obj_type = ETCHTYPEB_STRING; /* content is type string */
    a->content_class_id = CLASSID_NONE;     /* content is unwrapped (default) */

    /* populate arrayvalue from native array as in earlier test */
    av = new_arrayvalue_from(a, fake_typecode, NULL, DEFSIZE, DEFDELTA, FALSE);
    CU_ASSERT_PTR_NOT_NULL_FATAL(av);

    /* this will be a dangling pointer after we arrayvalue_to_nativearray() */
    old_nativearray_dangling_ref = av->natarray;

    /* convert new arrayvalue back to a nativearray. the embedded nativearray 
     * we created above, is destroyed in the process, and replaced with a new
     * nativearray, whose content should of course be identical to the old. 
     */
    result = arrayvalue_to_nativearray(av); 
    CU_ASSERT_PTR_NOT_NULL_FATAL (av->natarray); 
    CU_ASSERT_PTR_NOT_EQUAL_FATAL(av->natarray, old_nativearray_dangling_ref); 
    CU_ASSERT_EQUAL_FATAL(result, dim0count * dim1count);   
  
    /* get items one at a time from the arrayvalue's new native array,
     * and compare to expected value from the test data above.
     */
    for(i = 0; i < dim1count; i++)
    {
        subav = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(subav); 
        CU_ASSERT_EQUAL(subav->obj_type, ETCHTYPEB_ARRAYVAL);
        CU_ASSERT_EQUAL(result = arrayvalue_count(subav), dim0count);

        for(j = 0; j < dim0count; j++)
        {     
            wchar_t* expected_value = x[i][j];
            thisitem = arrayvalue_get(subav, j);
            CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
            CU_ASSERT_EQUAL(thisitem->obj_type, ETCHTYPEB_PRIMITIVE);
            CU_ASSERT_EQUAL(thisitem->class_id, CLASSID_STRING);
            result = wcscmp(expected_value, thisitem->v.value);
            CU_ASSERT_EQUAL(result, 0);
        }
    }

    av->destroy(av);  /* destroy array value and content */

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0); /* assert all memory freed */ 
    memtable_clear();  /* start fresh for next test */ 
}


/* 
 * test_arrayvalue_to_nativearray_30
 * create create native byte[][][] array from arrayvalue
 */
void test_arrayvalue_to_nativearray_30(void)
{
    etch_arrayvalue* av = NULL;
    etch_arrayvalue* subav1 = NULL;
    etch_arrayvalue* subav2 = NULL;
    etch_byte* thisitem = NULL;
    byte  fake_typecode = 0xff;
    int   i = 0, j = 0, k = 0, result = 0;
    const int DEFSIZE=0, DEFDELTA=0; 
    const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
    etch_nativearray* old_nativearray_dangling_ref = NULL;

    char x[2][3][4] 
      = { { {'a','b','c','d',},             
            {'e','f','g','h',}, 
            {'i','j','k','l',}, 
          },
          { {'m','n','o','p',},             
            {'q','r','s','t',}, 
            {'u','v','w','x',}, 
          },
        };

    etch_nativearray* a = new_nativearray_from(&x, CLASSID_ARRAY_BYTE, 
        sizeof(byte), numdimensions, dim0count, dim1count, dim2count);  

    CU_ASSERT_PTR_NOT_NULL_FATAL(a); 
    a->content_obj_type = ETCHTYPEB_BYTE; /* content is type byte */
    a->content_class_id = CLASSID_NONE;   /* content is unwrapped */

    /* populate arrayvalue from native array. we tell the arrayvalue to take  
     * ownership of the native array. result is a 3-dimensional arrayvalue,
     * which is represented as an arrayvalue of arrayvalues of arrayvalues.
     */
    av = new_arrayvalue_from(a, fake_typecode, NULL, DEFSIZE, DEFDELTA, FALSE);

    CU_ASSERT_PTR_NOT_NULL_FATAL(av);
 
    /* this will be a dangling pointer after we arrayvalue_to_nativearray() */
    old_nativearray_dangling_ref = av->natarray;

    /* convert new arrayvalue back to a nativearray. the embedded nativearray 
     * we created above, is destroyed in the process, and replaced with a new
     * nativearray, whose content should of course be identical to the old. 
     */
    result = arrayvalue_to_nativearray(av); 
    CU_ASSERT_PTR_NOT_NULL_FATAL (av->natarray); 
    CU_ASSERT_PTR_NOT_EQUAL_FATAL(av->natarray, old_nativearray_dangling_ref); 
    CU_ASSERT_EQUAL_FATAL(result, dim0count * dim1count * dim2count);   
  
    /* get items one at a time from the arrayvalue's new native array,
     * and compare to expected value from the test data above.
     */
    for(i = 0; i < dim2count; i++)
    {
        subav1 = arrayvalue_get(av, i);
        CU_ASSERT_PTR_NOT_NULL_FATAL(subav1); 
        CU_ASSERT_EQUAL(subav1->obj_type, ETCHTYPEB_ARRAYVAL);
        CU_ASSERT_EQUAL(result = arrayvalue_count(subav1), dim1count);

        for(j = 0; j < dim1count; j++)
        {     
            subav2 = arrayvalue_get(subav1, j);
            CU_ASSERT_PTR_NOT_NULL_FATAL(subav2); 
            CU_ASSERT_EQUAL(subav2->obj_type, ETCHTYPEB_ARRAYVAL);
            CU_ASSERT_EQUAL(result = arrayvalue_count(subav2), dim0count);

            for(k = 0; k < dim0count; k++)
            {     
                byte expected_value = x[i][j][k];
                thisitem = arrayvalue_get(subav2, k);
                CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem); 
                CU_ASSERT_EQUAL(thisitem->obj_type, ETCHTYPEB_PRIMITIVE);
                CU_ASSERT_EQUAL(thisitem->class_id, CLASSID_PRIMITIVE_BYTE);
                CU_ASSERT_EQUAL(thisitem->value, expected_value);
            }
        }
    }

    av->destroy(av);  /* destroy array value and content */

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0); /* assert all memory freed */ 
    memtable_clear();  /* start fresh for next test */ 
}


/**
 * main   
 */
int _tmain(int argc, _TCHAR* argv[])
{    
    char c=0;
    CU_pSuite pSuite = NULL;
    g_is_automated_test = argc > 1 && 0 != wcscmp(argv[1], L"-a");
    if (CUE_SUCCESS != CU_initialize_registry()) return 0;
    pSuite = CU_add_suite("suite_arrayvalue", init_suite, clean_suite);
    CU_set_output_filename("../test_arrayvalue");

    CU_add_test(pSuite, "test iterator over arraylist",   test_iterator_over_arraylist);
    #if(0)
    CU_add_test(pSuite, "test array value read ints",     read_test_integer);
    CU_add_test(pSuite, "test array value read strings",  read_test_string);
    CU_add_test(pSuite, "test array value write ints",    write_test_integer);
    CU_add_test(pSuite, "test array value write strings", write_test_string);
    #endif
    CU_add_test(pSuite, "test predefined exception",      exception_test_1);
    CU_add_test(pSuite, "test copy text exception",       exception_test_2);
    CU_add_test(pSuite, "test global text exception",     exception_test_3);
    CU_add_test(pSuite, "test create from byte[]",        test_create_from_1);
    CU_add_test(pSuite, "test create from int[]",         test_create_from_2);
    CU_add_test(pSuite, "test create from int64[]",       test_create_from_3);
    CU_add_test(pSuite, "test create from double[]",      test_create_from_4);
    CU_add_test(pSuite, "test create from string[]",      test_create_from_5);
    CU_add_test(pSuite, "test create from byte[][]",      test_create_from_10);
    CU_add_test(pSuite, "test create from short[][]",     test_create_from_11);
    CU_add_test(pSuite, "test create from string[][]",    test_create_from_12);
    CU_add_test(pSuite, "test create from object[][]",    test_create_from_13);
    CU_add_test(pSuite, "test create from byte[][][]",    test_create_from_21);
    CU_add_test(pSuite, "test arrayvalue to byte[]",      test_arrayvalue_to_nativearray_10);
    CU_add_test(pSuite, "test arrayvalue to string[][]",  test_arrayvalue_to_nativearray_20);
    CU_add_test(pSuite, "test arrayvalue to byte[][][]",  test_arrayvalue_to_nativearray_30);

    if (g_is_automated_test)    
        CU_automated_run_tests();    
    else
    {   CU_basic_set_mode(CU_BRM_VERBOSE);
        CU_basic_run_tests();
    }

    if (!g_is_automated_test) { printf("any key ..."); while(!c) c = _getch(); wprintf(L"\n"); }     
    CU_cleanup_registry();
    return CU_get_error(); 
}

