/* $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_binarytditdo.c 
 * test binary input and output
 */

#include "apr_time.h" /* some apr must be included first */
#include "etch_binary_tdi.h"  /* must be included second */
#include "etch_binary_tdo.h"

#include <tchar.h>
#include <stdio.h>
#include <conio.h>

#include "cunit.h"
#include "basic.h"
#include "automated.h"

#include "etch_connection.h"
#include "etch_defvalufact.h"
#include "etch_binary_tdo.h"
#include "etch_binary_tdi.h"
#include "etch_messagizer.h"
#include "etch_resources.h"
#include "etch_arrayval.h"
#include "etch_global.h"
#include "etchthread.h"
#include "etchexcp.h"
#include "etchmap.h"
#include "etchlog.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;
}


/* - - - - - - - - - - - - - - 
 * unit test support
 * - - - - - - - - - - - - - -
 */

etch_type*  mt_footype;
etch_field* mf_fookey;
int objcount, arraycount; 
unsigned short CLASSID_MY_IMPL_TP;


void this_test_setup()
{
    mt_footype = new_static_type(L"footype");
    mf_fookey  = new_static_field(L"foofield");
}

void this_test_teardown()
{
    destroy_static_type(mt_footype);
    destroy_static_field(mf_fookey);
}


signed char* get_test_bytes(signed char minval, int n)
{
    int i = 0;
    signed char* vals = malloc(n * sizeof(char));
    while(n--) vals[i++] = minval++;
    return vals;
}


short* get_test_shorts(short minval, int n)
{
    int i = 0;
    short* vals = malloc(n * sizeof(short));
    while(n--) vals[i++] = minval++;
    return vals;
}


int* get_test_ints(int* out)
{
    int n = 65536+2+2, k = 65536+2, i = 0;
    int minval = ETCHTYPE_MIN_INT16 - 1;
    int*  vals = malloc(n * sizeof(int));
    while(k--) vals[i++] = minval++;
    vals[i++] = ETCHTYPE_MIN_INT32;
    vals[i++] = ETCHTYPE_MAX_INT32;
    *out = n;
    return vals;
}


int64* get_test_longs(int* out)
{
    int n = 65536+2+6, k = 65536+2, i = 0;
    int minval  = ETCHTYPE_MIN_INT16 - 1;
    int64* vals = malloc(n * sizeof(int64));
    while(k--) vals[i++] = minval++;
    vals[i++] = ETCHTYPE_MIN_INT32;
    vals[i++] = ETCHTYPE_MAX_INT32;
    vals[i++] = (int64) ETCHTYPE_MIN_INT32 - 1;
    vals[i++] = (int64) ETCHTYPE_MAX_INT32 + 1;
    vals[i++] = ETCHTYPE_MIN_INT64;
    vals[i++] = ETCHTYPE_MAX_INT64;
    *out = n;
    return vals;
}


/* indicate if test messages should contain exceptions */
#define IS_TESTING_EXCEPTIONS TRUE


/**
 * do_perftest
 * measure performance of messagizer.transport_message()
 */
void do_perftest(char* name, const int iter, etch_messagizer* messagizer, 
    etch_message* msg, const int n)
{
    int64 t0, t1;
    double dtime, drate;
    int i = 0, result = 0;
    etch_who* whofrom = NULL;

    /* ensure message will not be destroyed by transport_message 
     * since we invoke transport_message multiple times here */
    unsigned char save_msg_staticflag = msg->is_static;
    set_etchobj_static_all(msg); 
    printf("\n");

    t0 = etch_system_nanotime();

    for(; i < n; i++)
        result = messagizer->transport_message(messagizer, whofrom, msg);

    t1 = etch_system_nanotime();

    dtime = (t1 - t0) / (double) 1000000000;
    drate = (double) n / dtime;

    printf("%s iteration %d time %7.3f count %d rate %7.3f\n", 
        name, iter, dtime, n, drate);

    msg->is_static = save_msg_staticflag;
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 * my_impl_transportpacket (i_transportpacket implementation) 
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 */

/**
 * my_impl_transportpacket
 * test object implementing i_transportpacket
 */
typedef struct my_impl_transportpacket
{
    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;

    /* i_transportpacket interface and methods, plus original destructor
     * which becomes replaced with a custom destructor to destroy this
     * object. this is the model for destroying an interface wrapper object
     * when we do not save and pass around a pointer to the wrapper, but rather
     * a pointer to the interface. the interface in question, i_transportpacket
     * in this case, contains a pointer to the wrapper object, in this case a
     * my_impl_transportpacket*. when the interface is instantiated, its original 
     * destructor is saved, and is replaced with a destructor which invokes
     * the wrapper's destructor. the wrapper destructor must then know to 
     * invoke the interface's original destructor when destroying the interface.
     */
    i_transportpacket* ixp;           /* owned */ 
    objdtor destroy_transportpacket;  /* i_transportpacket original destructor */
    etch_transport_packet transport_packet;        /* transport_packet() */
    etch_transport_packet_headersize  header_size; /* header_size() */

    i_sessionpacket* session;         /* not owned */

} my_impl_transportpacket;


/**
 * destroy_my_impl_transportpacket()
 * my_impl_transportpacket destructor
 */
int destroy_my_impl_transportpacket(my_impl_transportpacket* thisx)
{
    if (thisx->refcount > 0 && --thisx->refcount > 0) return -1;  

    if (!is_etchobj_static_content(thisx))
    {       /* invoke original i_transportpacket destructor */
        if (thisx->ixp && thisx->destroy_transportpacket)   
            thisx->destroy_transportpacket(thisx->ixp);
    }

   return destroy_objectex((objmask*) thisx);
}


/**
 * impl_transport_packet()
 * my_impl_transportpacket::transport_packet
 * @param whoto caller retains, can be null
 * @param fbuf caller retains
 */
int impl_transport_packet (i_transportpacket* itp, etch_who* whoto, etch_flexbuffer* fbuf)
{
    return 0;
}


/**
 * my_transport_control()
 * my_impl_transportpacket::itransport::transport_control 
 */
int my_transport_control (i_transportpacket* itp, etch_object* control, etch_object* value)
{
    return 0;
}


/**
 * my_transport_notify()
 * my_impl_transportpacket::itransport::transport_notify 
 */
int my_transport_notify (i_transportpacket* itp, etch_object* evt)
{
    return 0;
}


/**
 * my_transport_query()
 * my_impl_transportpacket::itransport::transport_query 
 */
objmask* my_transport_query (i_transportpacket* itp, etch_object* query) 
{
    return NULL;
}


/**
 * my_transport_get_session()
 * my_impl_transportpacket::itransport::get_session 
 */
i_sessionpacket* my_transport_get_session(i_transportpacket* itp)
{
    my_impl_transportpacket* mytp = itp->thisx; 
    return mytp->session;
}


/**
 * my_transport_set_session()
 * my_impl_transportpacket::itransport::set_session
 */
void my_transport_set_session(i_transportpacket* itp, i_sessionpacket* session)
{   
    my_impl_transportpacket* mytp = itp->thisx; 
    mytp->session = session;
}


/*
 * destroy_my_transportpacket()
 * i_transportpacket destructor
 * this destructor will destroy its parent (my_impl_transportpacket), 
 * which will in turn destroy this object.
 */
int destroy_my_transportpacket(i_transportpacket* itp)
{
    my_impl_transportpacket* mytp = NULL;
    if (NULL == itp) return -1;

    mytp = itp->thisx;  

    mytp->destroy(mytp);

    return 0;
}


/**
 * new_my_impl_transportpacket()
 * my_impl_transportpacket constructor
 */
my_impl_transportpacket* new_my_impl_transportpacket()
{
    i_transportpacket* itp  = NULL;
    i_transport* itransport = NULL;
    /* this is a model for dynamic class ID assigment */
    unsigned short class_id = CLASSID_MY_IMPL_TP? CLASSID_MY_IMPL_TP: 
        (CLASSID_MY_IMPL_TP = get_dynamic_classid());

    my_impl_transportpacket* mytp = (my_impl_transportpacket*) new_object
      (sizeof(my_impl_transportpacket), ETCHTYPEB_TRANSPORTPKT, class_id);

    mytp->destroy = destroy_my_impl_transportpacket;

    itransport = new_transport_interface_ex(mytp,
        (etch_transport_control)     my_transport_control, 
        (etch_transport_notify)      my_transport_notify, 
        (etch_transport_query)       my_transport_query,
        (etch_transport_get_session) my_transport_get_session, 
        (etch_transport_set_session) my_transport_set_session);

    itp = new_transportpkt_interface(mytp, impl_transport_packet, itransport);

    /* save off i_transportpacket destructor */
    mytp->destroy_transportpacket = itp->destroy;

    /* replace i_transportpacket destructor with one which will destroy this object */
    itp->destroy = destroy_my_transportpacket;

    /* g_my_transportpacket will get set to this interface */
    mytp->ixp = itp;  

    return mytp;
}



/* - - - - - - - - - - - - - - 
 * unit tests
 * - - - - - - - - - - - - - -
 */

/**
 * test_constructor_0
 * verify that memory is freed as expected
 */
void test_constructor_0(void)
{
    binary_tagged_data_output* tdo = new_binary_tagdata_output(NULL, NULL);

    tdo->destroy(tdo);

    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


/**
 * test_constructor_1
 * verify that memory is freed as expected
 */
void test_constructor_1(void)
{
    default_value_factory *vf = new_default_value_factory(NULL, NULL);
    binary_tagged_data_output* tdo = new_binary_tagdata_output((etch_value_factory*) vf, NULL);

    tdo->destroy(tdo);
    vf->destroy(vf);

    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


/**
 * test_constructor_2
 * verify that memory is freed as expected
 */
void test_constructor_2(void)
{
    default_value_factory *vf = new_default_value_factory(NULL, NULL);
    etch_flexbuffer* fbuf = new_flexbuffer(0);

    /* since we pass in vf and buf, we retain ownership */
    binary_tagged_data_output* tdo = new_binary_tagdata_output((etch_value_factory*)vf, fbuf);

    fbuf->destroy(fbuf);
    vf->destroy(vf);
    tdo->destroy(tdo);

    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


/**
 * test_check_value()
 */
void test_check_value(void)
{
    int  i = 0, result = 0;
    signed char typecode = 0;
    binary_tagged_data_output* tdo = new_binary_tagdata_output(NULL, NULL);

    do  /* check byte values */
    {   signed char *testbytes = get_test_bytes(ETCHTYPE_MIN_BYTE, 256), c = 0;
        etch_byte* byteobj = new_byte(0);

        for(i = 0; i < 256; i++)
        {   
            byteobj->value = c = testbytes[i];
            typecode = etchtagdata_check_value((etch_object*) byteobj);

            if (is_inrange_tiny(c)) 
               { CU_ASSERT_EQUAL(typecode, c);  }
            else
               { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTE); }
        }

        free(testbytes);
        byteobj->destroy(byteobj);

    } while(0);

    do  /* check short values */
    {   short *testshorts = get_test_shorts(ETCHTYPE_MIN_INT16, 65536), n = 0;
        etch_int16* shortobj = new_int16(0);

        for(i = 0; i < 65536; i++)
        {                             
            shortobj->value = n = testshorts[i];
            typecode = etchtagdata_check_value((etch_object*) shortobj);

            if (is_inrange_tiny(n)) 
               { CU_ASSERT_EQUAL(typecode, (signed char) n);  }
            else
            if (is_inrange_byte(n))
               { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTE); }
            else
               { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_SHORT); }
        }

        free(testshorts);
        shortobj->destroy(shortobj);

    } while(0);

    do  /* check int values */
    {   int intcount = 0, *testints = get_test_ints(&intcount), n = 0;
        etch_int32* intobj = new_int32(0);

        for(i = 0; i < intcount; i++)
        {                             
            intobj->value = n = testints[i];
            typecode = etchtagdata_check_value((etch_object*) intobj);

            if (is_inrange_tiny(n)) 
               { CU_ASSERT_EQUAL(typecode, (signed char) n);  }
            else
            if (is_inrange_byte(n))
               { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTE); }
            else
            if (is_inrange_int16(n))
               { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_SHORT); }
            else
               { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_INT); }
        }

        free(testints);
        intobj->destroy(intobj);

    } while(0);

    do  /* check long values */
    {   int   longcount = 0;
        int64 *testlongs = get_test_longs(&longcount), n = 0;
        etch_int64* longobj = new_int64(0);

        for(i = 0; i < longcount; i++)
        {                             
            longobj->value = n = testlongs[i];
            typecode = etchtagdata_check_value((etch_object*) longobj);

            if (is_inrange_tiny(n)) 
               { CU_ASSERT_EQUAL(typecode, (signed char) n);  }
            else
            if (is_inrange_byte(n))
               { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTE); }
            else
            if (is_inrange_int16(n))
               { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_SHORT); }
            else
            if (is_inrange_int32(n))
               { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_INT); }
            else
               { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_LONG); }
        }

        free(testlongs);
        longobj->destroy(longobj);

    } while(0);

    typecode = etchtagdata_check_value(NULL);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_NULL);

    do {  /* check boolean values */
    etch_boolean* boolobj = new_boolean(FALSE);
    typecode = etchtagdata_check_value((etch_object*)boolobj);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE);
    boolobj->value = TRUE;
    typecode = etchtagdata_check_value((etch_object*)boolobj);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE);
    boolobj->destroy(boolobj);
    } while(0);

    do {  /* check ieee values */
    etch_float* floatobj = new_float((float) 3.14159);
    etch_double* doubobj = new_double(3.14159);
    typecode = etchtagdata_check_value((etch_object*)floatobj);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_FLOAT);
    typecode = etchtagdata_check_value((etch_object*)doubobj);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_DOUBLE);
    floatobj->destroy(floatobj);
    doubobj->destroy(doubobj);
    } while(0);

    do {  /* check bytes */
    etch_nativearray* bytearray = new_nativearray(CLASSID_ARRAY_BYTE, sizeof(byte), 1, 4, 0, 0);  
    etch_arrayvalue*  arrayvalu = new_arrayvalue(ETCH_XTRNL_TYPECODE_BYTES, NULL, 1, 0, 0, 0, 0);
    arrayvalu->content_obj_type = ETCHTYPEB_BYTE; 
    typecode = etchtagdata_check_value((etch_object*)bytearray);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTES);
    typecode = etchtagdata_check_value((etch_object*)arrayvalu);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTES);
    bytearray->destroy(bytearray);
    arrayvalu->destroy(arrayvalu);
    } while(0);

    do {  /* check string */
    etch_string* strempty = new_string(L"",  ETCH_ENCODING_UTF16);
    etch_string* strother = new_string(L"x", ETCH_ENCODING_UTF16);
    typecode = etchtagdata_check_value((etch_object*)strempty);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_EMPTY_STRING);
    typecode = etchtagdata_check_value((etch_object*)strother);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_STRING);
    strempty->destroy(strempty);
    strother->destroy(strother);
    } while(0);

    do {  /* check custom */
    etch_type* newtype = new_type(L"unknown");
    etch_date* newdate = new_date();
    etch_structvalue* sv = new_structvalue(newtype, 0);
    typecode = etchtagdata_check_value((etch_object*)sv);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_CUSTOM); 
    typecode = etchtagdata_check_value((etch_object*)newdate);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_CUSTOM); 
    newdate->destroy(newdate);
    newtype->destroy(newtype);
    sv->destroy(sv);
    } while(0);

    do {  /* check end of data marker */
    objmask* eodobj = etchtagdata_new_eodmarker(FALSE);  
    typecode = etchtagdata_check_value((etch_object*)eodobj);
    CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_NONE); 
    eodobj->destroy(eodobj);
    } while(0);

    tdo->destroy(tdo);

    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


/**
 * message_to_bytes()
 * return the encoded bytes for a message.
 * caller relinquishes msg, assumes returned bytes.
 */
byte* message_to_bytes(default_value_factory* vf, etch_message* msg, size_t* out)
{
    int result = 0;
    size_t bufcount = 0;
    signed char* msgbytes = NULL;

    etch_flexbuffer* fbuf = new_flexbuffer(ETCH_DEFSIZE);
    binary_tagged_data_output* tdo = new_binary_tagdata_output(msg->vf, fbuf);

    if (0 == tdo->vtab->write_message((tagged_data_output*) tdo, msg, fbuf))
    {
        etch_flexbuf_set_index(fbuf, 0);
        msgbytes = etch_flexbuf_get_all(fbuf, &bufcount); 
    }
                                                                        
    fbuf->destroy(fbuf);
    tdo->destroy(tdo);
    msg->destroy(msg);
             
    if (out) *out = bufcount;
    return msgbytes;
}


/**
 * bytes_to_message()
 * return the message constructed from the supplied bytes.
 * caller relinquishes bytes, assumes returned message.
 */
etch_message* bytes_to_message(default_value_factory* vf, byte* bytes, const size_t bytecount)   
{           
    etch_flexbuffer* fbuf = new_flexbuffer_from(bytes, bytecount, bytecount, 0);

    binary_tagged_data_input* tdi = new_binary_tagdata_input((etch_value_factory*) vf);

    etch_message* msg = bintdi_read_message((tagged_data_input*) tdi, fbuf);

    tdi->destroy(tdi);
    fbuf->destroy(fbuf);  /* flexbuf destroys bytes */
    return msg;
}


/**
 * is_equal_objects()
 * currently we don't have a convenient way to determine if objects are equal,
 * primarily because the default hashkey is generated from object address, and
 * we have to date omitted the equality function from out object definition.
 * it remains to be seen whether we need a general equality test, in practice
 * we may only need it for certain high level objects. so we'll wing it for now.
 */
int is_equal_objects(objmask* a, objmask* b)
{
    if ((a->obj_type == b->obj_type) && (a->class_id == b->class_id)) 
        return TRUE;

    /* todo: tests for array type and content comparison here */

    return FALSE;
}


/*
 * show_value() - debug display for do_tdo_tdi_test()
 */
void show_value(objmask* obj)
{
    char x[2] = {0,0};

    #if IS_DEBUG_CONSOLE

    if (is_etch_byte(obj))  
    {   x[0] = ((etch_byte*)obj)->value;
        printf("  >>> '%s'\n", x);  
    }
    else 
    if (is_etch_arrayvalue(obj))
    {   etch_arrayvalue* av = (etch_arrayvalue*) obj;
        const int dim = av->dim;
        const int count = av->list->count;
        printf("$$$ array dim %d count %d\n", dim, count);  
    }
    fflush(stdout);

    #endif
}


/*
 * do_tdo_tdi_test()
*/
objmask* do_tdo_tdi_test(default_value_factory* vf, objmask* thisobj, etch_validator* vtor)
{
    etch_message* thismsg = NULL, *resultmsg = NULL;
    etch_hashitem x, *thisitem = &x; 
    objmask* returnobj = NULL;
    byte*  msgbytes  = NULL;
    size_t bytecount = 0;
    int    result = 0;
    memset(thisitem, 0, sizeof(thisitem));
    show_value(thisobj); 

    etchtype_clear_validator(mt_footype, mf_fookey);
    result = etchtype_put_validator (mt_footype, clone_field(mf_fookey), (objmask*) vtor);
    CU_ASSERT_EQUAL(result, 0);
    /* protect caller's object so that temporary owners in this test can't destroy it */
    set_etchobj_static_all(thisobj); 

    thismsg = new_message(mt_footype, ETCH_DEFSIZE, (etch_value_factory*) vf);

    result  = message_put(thismsg, clone_field(mf_fookey), thisobj);
    CU_ASSERT_EQUAL_FATAL(result, 0);

    #if IS_TESTING_EXCEPTIONS
    etch_throw(thismsg, EXCPTYPE_ETCHRUNTIME, L"test message exception", ETCHEXCP_COPYTEXT); 
    #endif

    /* serialize thismsg. relinquish thismsg, assume msgbytes */
    msgbytes = message_to_bytes(vf, thismsg, &bytecount); 
    CU_ASSERT_PTR_NOT_NULL(msgbytes);
    CU_ASSERT_NOT_EQUAL(bytecount, 0);

    /* deserialize msgbytes. relinquish msgbytes, assume resultmsg */
    resultmsg = bytes_to_message(vf, msgbytes, bytecount);  
    CU_ASSERT_PTR_NOT_NULL_FATAL(resultmsg);

    result = structvalue_is_type(resultmsg->sv, mt_footype);
    CU_ASSERT_EQUAL(result, TRUE);

    result = message_size(resultmsg);
    CU_ASSERT_EQUAL(result, 1);

    result = etchmap_map_find(resultmsg->sv->items, (objmask*) mf_fookey, &thisitem);
    CU_ASSERT_EQUAL(result, 0);

    /* we verified that the deserialized message contains caller's reconstituted object.
     * we want to return that object to caller so that it can be compared with the
     * original object; however before we return, we are going to destroy the message, 
     * and since the message owns its content, it will destroy the object we intend to
     * return, so we remove the object from the message before we destroy the message.
     * this call frees all hashtable memory associated with this key/value pair except
     * except for the value object itself, including the key.
     */
    if (result == 0)
        returnobj = message_remove(resultmsg, mf_fookey);

    resultmsg->destroy(resultmsg);
    clear_etchobj_static_all(thisobj); /* unprotect caller's object protected above */  
    return returnobj;
}


/**
 * do_recursive_test()
 * recurse input object creating message from all content from scalar up to top level
 * enclosing array. serialize and deserialize each message and verify that deserialized
 * object matches original.
 */
void do_recursive_test(default_value_factory* vf, objmask* obj, etch_validator* vtor)
{
    objmask* thisobj = obj, *reconstituted_obj = NULL; 

    if (is_etch_arrayvalue(thisobj)) 
    {
        etch_iterator iterator;        
        arrayvalue_set_iterator((etch_arrayvalue*) thisobj, &iterator); 

        while(iterator.has_next(&iterator))
        {
            objmask* subobj = iterator.current_value;

            do_recursive_test(vf, subobj, vtor->element_validator(vtor)); 

            iterator.next(&iterator);
        }
    }
    /* printf("\n(%05d. type %d class %d)\n", ++objcount, obj->obj_type, obj->class_id); fflush(stdout); */
   
    reconstituted_obj = do_tdo_tdi_test(vf, thisobj, vtor);  

    CU_ASSERT_PTR_NOT_NULL_FATAL(reconstituted_obj);
    CU_ASSERT_EQUAL(is_equal_objects(thisobj, reconstituted_obj), TRUE);     
    reconstituted_obj->destroy(reconstituted_obj);
}


/**
 * test_byte_array_out_in()
 * test serialization and subsequent deserialization of a byte array
 */
void test_byte_array_out_in(void)
{
    const int NUMDIMS = 2;
    char  x[2][3] = { {'a','b','c', }, {'d','e','f', }, };

    default_value_factory* vf = new_default_value_factory(NULL, NULL);

    etch_nativearray* natarray  = new_nativearray_from(x, CLASSID_ARRAY_BYTE, sizeof(byte), NUMDIMS, 3, 2, 0);
    etch_arrayvalue*  arrayval  = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_BYTES, NULL, 2, 0, FALSE);
    etch_arrayvalue*  reconstituted_arrayval = NULL;
    etch_validator*   validator = etchvtor_byte_get(NUMDIMS);

    this_test_setup();
    vf->vtab->add_type(vf, mt_footype); /* the type we are testing with */
    objcount = arraycount = 0; /* initialize global counters */

    /* test serialization and deserialization of the array object as a whole. 
     * we are returned an object which is the reconstituted array after it has
     * been serialized and subsequently deserialized */
    reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test
        (vf, (objmask*) arrayval, validator);

    CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);

    /* recursively test serialization and deserialization of all the components 
     * of the array object, including the array itself, subarrays, and scalars,
     * as per the java version of this test. this really ensures that we have 
     * all our memory management ducks in a row. */

    do_recursive_test (vf, (objmask*) arrayval, validator);

    arrayval->destroy(arrayval);
    if (reconstituted_arrayval)
        reconstituted_arrayval->destroy(reconstituted_arrayval);
   
    vf->destroy(vf);
    this_test_teardown();
    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


/**
 * test_bool_array_out_in()
 * test serialization and subsequent deserialization of a boolean array
 */
void test_bool_array_out_in(void)
{
    const int NUMDIMS = 2;
    boolean x[2][3] = { { TRUE, FALSE, TRUE, }, { FALSE, TRUE, FALSE, }, };

    default_value_factory* vf = new_default_value_factory(NULL, NULL);

    etch_nativearray* natarray  = new_nativearray_from(x, CLASSID_ARRAY_BOOL, sizeof(boolean), NUMDIMS, 3, 2, 0);
    etch_arrayvalue*  arrayval  = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE, NULL, 2, 0, FALSE);
    etch_arrayvalue*  reconstituted_arrayval = NULL;
    etch_validator*   validator = etchvtor_boolean_get(NUMDIMS);

    this_test_setup();
    vf->vtab->add_type(vf, mt_footype); /* the type we are testing with */
    objcount = arraycount = 0; /* initialize global counters */

    /* test serialization and deserialization of the array object as a whole. 
     * we are returned an object which is the reconstituted array after it has
     * been serialized and subsequently deserialized */
    reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (objmask*) arrayval, validator);

    CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);

    /* recursively test serialization and deserialization of all the components 
     * of the array object, including the array itself, subarrays, and scalars,
     * as per the java version of this test. this really ensures that we have 
     * all our memory management ducks in a row. */

    do_recursive_test (vf, (objmask*) arrayval, validator);

    arrayval->destroy(arrayval);
    if (reconstituted_arrayval)
        reconstituted_arrayval->destroy(reconstituted_arrayval);
   
    vf->destroy(vf);
    this_test_teardown();
    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


/**
 * test_short_array_out_in()
 * test serialization and subsequent deserialization of a short integer array
 */
void test_short_array_out_in(void)
{
    const int NUMDIMS = 2;
    short x[2][3] = { { 0, 1, 2, }, { -1, -32768, 32767, }, };

    default_value_factory* vf = new_default_value_factory(NULL, NULL);

    etch_nativearray* natarray  = new_nativearray_from(x, CLASSID_ARRAY_INT16, sizeof(short), NUMDIMS, 3, 2, 0);
    etch_arrayvalue*  arrayval  = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_SHORT, NULL, 2, 0, FALSE);
    etch_arrayvalue*  reconstituted_arrayval = NULL;
    etch_validator*   validator = etchvtor_int16_get(NUMDIMS);

    this_test_setup();
    vf->vtab->add_type(vf, mt_footype); /* the type we are testing with */
    objcount = arraycount = 0; /* initialize global counters */

    /* test serialization and deserialization of the array object as a whole. 
     * we are returned an object which is the reconstituted array after it has
     * been serialized and subsequently deserialized */
    reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (objmask*) arrayval, validator);

    CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);

    /* recursively test serialization and deserialization of all the components 
     * of the array object, including the array itself, subarrays, and scalars,
     * as per the java version of this test. this really ensures that we have 
     * all our memory management ducks in a row. */

    do_recursive_test (vf, (objmask*) arrayval, validator);

    arrayval->destroy(arrayval);
    if (reconstituted_arrayval)
        reconstituted_arrayval->destroy(reconstituted_arrayval);
   
    vf->destroy(vf);
    this_test_teardown();
    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


/**
 * test_int_array_out_in()
 * test serialization and subsequent deserialization of an int array
 */
void test_int_array_out_in(void)
{
    const int NUMDIMS = 2;
    int x[2][3] = { { 0, 1, 2, }, { -1, ETCHTYPE_MIN_INT32, ETCHTYPE_MAX_INT32, }, };

    default_value_factory* vf = new_default_value_factory(NULL, NULL);

    etch_nativearray* natarray  = new_nativearray_from(x, CLASSID_ARRAY_INT32, sizeof(int), NUMDIMS, 3, 2, 0);
    etch_arrayvalue*  arrayval  = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_INT, NULL, 2, 0, FALSE);
    etch_arrayvalue*  reconstituted_arrayval = NULL;
    etch_validator*   validator = etchvtor_int32_get(NUMDIMS);

    this_test_setup();
    vf->vtab->add_type(vf, mt_footype); /* the type we are testing with */
    objcount = arraycount = 0; /* initialize global counters */

    /* test serialization and deserialization of the array object as a whole. 
     * we are returned an object which is the reconstituted array after it has
     * been serialized and subsequently deserialized */
    reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (objmask*) arrayval, validator);

    CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);

    /* recursively test serialization and deserialization of all the components 
     * of the array object, including the array itself, subarrays, and scalars,
     * as per the java version of this test. this really ensures that we have 
     * all our memory management ducks in a row. */

    do_recursive_test (vf, (objmask*) arrayval, validator);

    arrayval->destroy(arrayval);
    if (reconstituted_arrayval)
        reconstituted_arrayval->destroy(reconstituted_arrayval);
   
    vf->destroy(vf);
    this_test_teardown();
    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


/**
 * test_long_array_out_in()
 * test serialization and subsequent deserialization of a long array
 */
void test_long_array_out_in(void)
{
    const int NUMDIMS = 2;
    int64 x[2][3] = { { 0, 1, 2, }, { -1, ETCHTYPE_MIN_INT64, ETCHTYPE_MAX_INT64, }, };

    default_value_factory* vf = new_default_value_factory(NULL, NULL);

    etch_nativearray* natarray  = new_nativearray_from(x, CLASSID_ARRAY_INT64, sizeof(int64), NUMDIMS, 3, 2, 0);
    etch_arrayvalue*  arrayval  = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_LONG, NULL, 2, 0, FALSE);
    etch_arrayvalue*  reconstituted_arrayval = NULL;
    etch_validator*   validator = etchvtor_int64_get(NUMDIMS);

    this_test_setup();
    vf->vtab->add_type(vf, mt_footype); /* the type we are testing with */
    objcount = arraycount = 0; /* initialize global counters */

    /* test serialization and deserialization of the array object as a whole. 
     * we are returned an object which is the reconstituted array after it has
     * been serialized and subsequently deserialized */
    reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (objmask*) arrayval, validator);

    CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);

    /* recursively test serialization and deserialization of all the components 
     * of the array object, including the array itself, subarrays, and scalars,
     * as per the java version of this test. this really ensures that we have 
     * all our memory management ducks in a row. */

    do_recursive_test (vf, (objmask*) arrayval, validator);

    arrayval->destroy(arrayval);
    if (reconstituted_arrayval)
        reconstituted_arrayval->destroy(reconstituted_arrayval);
   
    vf->destroy(vf);
    this_test_teardown();
    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


/**
 * test_float_array_out_in()
 * test serialization and subsequent deserialization of a float array
 */
void test_float_array_out_in(void)
{
    const int NUMDIMS = 2;
    float x[2][3] = { { 0.0, 1.0, 2.0, }, { -1.0, ETCHTYPE_MIN_FLOAT, ETCHTYPE_MAX_FLOAT, }, };

    default_value_factory* vf = new_default_value_factory(NULL, NULL);

    etch_nativearray* natarray  = new_nativearray_from(x, CLASSID_ARRAY_FLOAT, sizeof(float), NUMDIMS, 3, 2, 0);
    etch_arrayvalue*  arrayval  = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_FLOAT, NULL, 2, 0, FALSE);
    etch_arrayvalue*  reconstituted_arrayval = NULL;
    etch_validator*   validator = etchvtor_float_get(NUMDIMS);

    this_test_setup();
    vf->vtab->add_type(vf, mt_footype); /* the type we are testing with */
    objcount = arraycount = 0; /* initialize global counters */

    /* test serialization and deserialization of the array object as a whole. 
     * we are returned an object which is the reconstituted array after it has
     * been serialized and subsequently deserialized */
    reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (objmask*) arrayval, validator);

    CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);

    /* recursively test serialization and deserialization of all the components 
     * of the array object, including the array itself, subarrays, and scalars,
     * as per the java version of this test. this really ensures that we have 
     * all our memory management ducks in a row. */

    do_recursive_test (vf, (objmask*) arrayval, validator);

    arrayval->destroy(arrayval);
    if (reconstituted_arrayval)
        reconstituted_arrayval->destroy(reconstituted_arrayval);
   
    vf->destroy(vf);
    this_test_teardown();
    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


/**
 * test_double_array_out_in()
 * test serialization and subsequent deserialization of a double array
 */
void test_double_array_out_in(void)
{
    const int NUMDIMS = 2;
    double x[2][3] = { { 0.0, 1.0, 2.0, }, { -1.0, ETCHTYPE_MIN_DOUBLE, ETCHTYPE_MAX_DOUBLE, }, };

    default_value_factory* vf = new_default_value_factory(NULL, NULL);

    etch_nativearray* natarray  = new_nativearray_from(x, CLASSID_ARRAY_DOUBLE, sizeof(double), NUMDIMS, 3, 2, 0);
    etch_arrayvalue*  arrayval  = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_DOUBLE, NULL, 2, 0, FALSE);
    etch_arrayvalue*  reconstituted_arrayval = NULL;
    etch_validator*   validator = etchvtor_double_get(NUMDIMS);

    this_test_setup();
    vf->vtab->add_type(vf, mt_footype); /* the type we are testing with */
    objcount = arraycount = 0; /* initialize global counters */

    /* test serialization and deserialization of the array object as a whole. 
     * we are returned an object which is the reconstituted array after it has
     * been serialized and subsequently deserialized */
    reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (objmask*) arrayval, validator);

    CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);

    /* recursively test serialization and deserialization of all the components 
     * of the array object, including the array itself, subarrays, and scalars,
     * as per the java version of this test. this really ensures that we have 
     * all our memory management ducks in a row. */

    do_recursive_test (vf, (objmask*) arrayval, validator);

    arrayval->destroy(arrayval);
    if (reconstituted_arrayval)
        reconstituted_arrayval->destroy(reconstituted_arrayval);
   
    vf->destroy(vf);
    this_test_teardown();
    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


etch_string* ns(int n) 
{ 
    wchar_t buf[32]; swprintf(buf, 31, L"%d", (size_t) n);
    return new_string(buf, ETCH_ENCODING_UTF16);
}


/**
 * test_string_array_out_in()
 * test serialization and subsequent deserialization of a string array
 */
void test_string_array_out_in(void)
{
    const int NUMDIMS = 2;
    etch_string* x[2][3] = { { ns(0), ns(1), ns(2), }, { ns(3), ns(4), ns(5), }, };

    default_value_factory* vf = new_default_value_factory(NULL, NULL);

    etch_nativearray* natarray  = new_nativearray_from(x, CLASSID_ARRAY_STRING, sizeof(void*), NUMDIMS, 3, 2, 0);
    etch_arrayvalue*  arrayval  = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_STRING, NULL, 2, 0, FALSE);
    etch_arrayvalue*  reconstituted_arrayval = NULL;
    etch_validator*   validator = etchvtor_string_get(NUMDIMS);

    this_test_setup();
    vf->vtab->add_type(vf, mt_footype); /* the type we are testing with */
    objcount = arraycount = 0; /* initialize global counters */

    /* test serialization and deserialization of the array object as a whole. 
     * we are returned an object which is the reconstituted array after it has
     * been serialized and subsequently deserialized */
    reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (objmask*) arrayval, validator);

    CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);

    /* recursively test serialization and deserialization of all the components 
     * of the array object, including the array itself, subarrays, and scalars,
     * as per the java version of this test. this really ensures that we have 
     * all our memory management ducks in a row. */

    do_recursive_test (vf, (objmask*) arrayval, validator);

    arrayval->destroy(arrayval);
    if (reconstituted_arrayval)
        reconstituted_arrayval->destroy(reconstituted_arrayval);
   
    vf->destroy(vf);
    do 
    { int i, j;
      for(i = 0; i < 2; i++)
      for(j = 0; j < 3; j++)
          destroy_string(x[i][j]); 
    } while(0);
    this_test_teardown();
    etchvf_free_builtins(); 

    g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE);  
    CU_ASSERT_EQUAL(g_bytes_allocated, 0);  
    memtable_clear();  /* start fresh for next test */
}


/**
 * test_add_perf()
 * test performance of add(x,y) message serialization and delivery
 */
void test_add_perf(void)
{
    default_value_factory* vf = new_default_value_factory(NULL, NULL);

    int result = 0;
    etch_who* whofrom = NULL;
    etch_message* msg = NULL;
    etch_messagizer* messagizer = NULL;
    i_transportpacket* my_packetsource = NULL;
    my_impl_transportpacket* my_impl_packetsource = NULL;
    etch_resources* resx = new_etch_resources(ETCH_RESOURCES_DEFSIZE);

    etch_type*  addtype = new_type(L"add");
    etch_field* xfield  = new_field(L"x");
    etch_field* yfield  = new_field(L"y");

    etchtype_put_validator(addtype, clone_field(xfield), (objmask*) etchvtor_int32_get(0));
    etchtype_put_validator(addtype, clone_field(yfield), (objmask*) etchvtor_int32_get(0));
    etchtype_put_validator(addtype, clone_field(builtins._mf__message_id), (objmask*) etchvtor_int64_get(0));

    msg = new_message(addtype, ETCH_DEFSIZE, (etch_value_factory*) vf);
    CU_ASSERT_PTR_NOT_NULL_FATAL(msg);
    message_put(msg, clone_field(xfield), (objmask*) new_int32(1));  /* relinquish all content */
    message_put(msg, clone_field(yfield), (objmask*) new_int32(2));
    message_put(msg, clone_field(builtins._mf__message_id), (objmask*) new_int64(0x0123456789abcdef));   

    /* we relinquish vf to the resources destructor here */
    etch_resources_add(resx, ETCH_RESXKEY_MSGIZER_VALUFACT, (objmask*) vf); 

    /* instantiate object which implements i_transportpacket and extract its interface.
     * the interface's destructor is coded to destroy its instantiating object. */
    my_impl_packetsource = new_my_impl_transportpacket();
    CU_ASSERT_PTR_NOT_NULL_FATAL(my_impl_packetsource);
    my_packetsource = my_impl_packetsource->ixp; /* extract interface */
    CU_ASSERT_PTR_NOT_NULL_FATAL(my_packetsource); 

    messagizer = new_messagizer(my_packetsource, L"foo:?Messagizer.format=binary", resx);
    CU_ASSERT_PTR_NOT_NULL_FATAL(messagizer);  

    /* we need to continue to use the message in this test, so since the message's
     * destructor will be called by messagizer.transport_message(), mark the message
     * static such that it is protected from destruction.
     */
    set_etchobj_static_all(msg); 

    /* we ordinarily relinquish msg on success, retain it on failure;  
     * however here we have retained it regardless, since we marked it  
     * static above, thus disabling its destructor. 
     */
    result = messagizer->transport_message(messagizer, whofrom, msg);
    CU_ASSERT_EQUAL(result, 0);  

    if (0 == result)
    {   int i = 0;
        const int n = 900973; /* this code is ported from the java test */

        for(i = 0; i < 3; i++)
            do_perftest("test_add_perf", i, messagizer, msg, n);
    }

    clear_etchobj_static_all(msg);  /* clear static state so we can destroy it now */
    msg->destroy(msg);

    xfield->destroy(xfield);
    yfield->destroy(yfield);
    addtype->destroy(addtype);

    resx->destroy(resx);  /* content is destroyed also (vf in this case) */
    messagizer->destroy(messagizer);
    my_packetsource->destroy(my_packetsource); /* destroys my_impl_transportpacket also */
   
    etchvf_free_builtins(); 

    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 */
}


/**
 * get_bytearray()
 * support for test_value_to_bytes().
 * return an etch_nativearray wrapping the supplied static byte* array.
 */
etch_nativearray* get_bytearray(byte* bytes, const int bytecount)
{
    etch_nativearray* na = new_nativearray_from(bytes, CLASSID_ARRAY_BYTE, sizeof(byte), 1, bytecount, 0, 0);  
    na->content_obj_type = ETCHTYPEB_BYTES;
    na->content_class_id = CLASSID_NONE; /* unwrapped */
    return na;
}

 
 /**
 * assert_value_to_bytes()
 * support for test_value_to_bytes() -- serialize value object, and assert 
 * that the serialized bytes match the supplied expected byte array.
 */
int assert_value_to_bytes(objmask* value, etch_nativearray* expected_bytes, etch_value_factory* vf)
{
    byte* buf = NULL;
    int  bytecount = 0, i = 0, result = 0, errs = 0;
    etch_flexbuffer* fbuf = new_flexbuffer(ETCH_DEFSIZE);
    etch_validator*  vtor = etchvtor_object_get(0);

    binary_tagged_data_output* tdo = new_binary_tagdata_output(vf, fbuf);

    /* serialize the specified value object to the buffer fbuf */
    result = bintdo_write_value(tdo, vtor, value);
    CU_ASSERT_EQUAL(result, 0);
    if (0 != result) errs++;

    if (0 == result)
    {
        fbuf->index = 0; /* get the serialized bytes to a new buffer */
        buf = etch_flexbuf_get_all(fbuf, (size_t*) &bytecount); 

        CU_ASSERT_EQUAL(bytecount, expected_bytes->bytecount);
        if (bytecount != expected_bytes->bytecount) errs++;

        if (bytecount == expected_bytes->bytecount)
        {   /* verify that serialized bytes match expected bytes  */
            for(i = 0; i < bytecount; i++)
            {   signed char a = buf[i], b = 0;
                expected_bytes->get1(expected_bytes, &b, i);
                result = ((int) a == (int) b);
                CU_ASSERT_EQUAL(result, TRUE);
                if (!result) 
                    errs++;
            }
        }
    }

    /* free memory */
    if (value) value->destroy(value);
    expected_bytes->destroy(expected_bytes);
    tdo->destroy(tdo);
    fbuf->destroy(fbuf);
    etch_free(buf);

    return errs? -1: 0;
}


/**
 * test_single_value_to_bytes()
 * test that various value objects serialized by the tdo match hard-coded expected byte arrays
 */
void test_single_value_to_bytes(void)
{
    int  result = 0;
    etch_value_factory* vf = (etch_value_factory*) new_default_value_factory(NULL, NULL);

    do  /* NULL */
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_NULL }; 
        result = assert_value_to_bytes(NULL, get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do  /* boolean primitive objects */
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE }; 
        result = assert_value_to_bytes((objmask*) new_boolean(FALSE), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do 
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE }; 
        result = assert_value_to_bytes((objmask*) new_boolean(TRUE), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do  /* some tiny integers */
    {   signed char a[] = { 0 }; 
        result = assert_value_to_bytes((objmask*) new_byte(0), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do   
    {   signed char a[] = { 1 }; 
        result = assert_value_to_bytes((objmask*) new_byte(1), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do   
    {   signed char a[] = { -1 }; 
        result = assert_value_to_bytes((objmask*) new_byte(-1), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do  /* non-tinyint byte */
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_BYTE, -100 }; 
        result = assert_value_to_bytes((objmask*) new_byte(-100), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do  /* some short integers */
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_SHORT, 39, 16 }; 
        result = assert_value_to_bytes((objmask*) new_int16(10000), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do   
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_SHORT, -40, -16 }; 
        result = assert_value_to_bytes((objmask*) new_int16(-10000), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do  /* 32-bit integers */
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_INT, 59, -102, -54, 0 }; 
        result = assert_value_to_bytes((objmask*) new_int32(1000000000), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do   
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_INT, -60, 101, 54, 0 }; 
        result = assert_value_to_bytes((objmask*) new_int32(-1000000000), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do  /* long integers */
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_LONG, 13, -32, -74, -77, -89, 100, 0, 0 }; 
        result = assert_value_to_bytes((objmask*) new_int64(1000000000000000000), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do   
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_LONG, -14, 31, 73, 76, 88, -100, 0, 0 }; 
        result = assert_value_to_bytes((objmask*) new_int64(-1000000000000000000), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do  /* float */
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_FLOAT, 70, 64, -28, 0 }; 
        result = assert_value_to_bytes((objmask*) new_float(12345.0), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    do  /* double */
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_DOUBLE, 64, -42, -24, 0, 0, 0, 0, 0 }; 
        result = assert_value_to_bytes((objmask*) new_double(23456.0), get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);
    } while(0);

    vf->destroy(vf);

    etchvf_free_builtins(); 

    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_boolean_array_value_to_bytes()
 * test that various value objects serialized by the tdo match hard-coded expected byte arrays
 */
void test_boolean_array_value_to_bytes(void)
{
    int  result = 0;
    etch_value_factory* vf = (etch_value_factory*) new_default_value_factory(NULL, NULL);

    do  /* boolean[] */
    {   const int NUMDIMS1 = 1, ITEMCOUNT = 2;

        signed char a[] =
        { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
          ETCH_XTRNL_TYPECODE_BOOLEAN_CONTENT, /* array content type */
          NUMDIMS1,  /* dimensions count */
          ITEMCOUNT, /* count of content items following */
          ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE,   /* item[0] */
          ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE,  /* item[1] */
          ETCH_XTRNL_TYPECODE_NONE /* eod mark */
        }; 

        etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_BOOLEAN_CONTENT, 
            NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);

        arrayvalue_add(av, (etch_object*) new_boolean(TRUE));
        arrayvalue_add(av, (etch_object*) new_boolean(FALSE));
 
        result = assert_value_to_bytes((objmask*) av, get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    vf->destroy(vf);

    etchvf_free_builtins(); 

    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_byte_array_value_to_bytes()
 * test that various value objects serialized by the tdo match hard-coded expected byte arrays
 */
void test_byte_array_value_to_bytes(void)
{
    int  result = 0;
    etch_value_factory* vf = (etch_value_factory*) new_default_value_factory(NULL, NULL);

    do  /* byte[] */
    {   const int NUMDIMS1 = 1, ITEMCOUNT = 3;
        signed char content[] = { 1, 2, 3 };

        signed char a[] =  /* byte vector is a special case, not an array type */
        { ETCH_XTRNL_TYPECODE_BYTES, /* array content type */
          ITEMCOUNT,  /* count of items following */
          1,   /* item[0] */
          2,   /* item[1] */
          3    /* item[2] */
        }; 

        etch_nativearray* expected = get_bytearray(a, sizeof(a));

        /* memory management notes: here we create a nativearray na from static
         * content. new_nativearray_from() defaults nativearray.is_content_owned   
         * to false, so when the nativearray na is destroyed, it will not attempt 
         * to free memory for the static content bytes. we then create an arrayvalue 
         * from nativearray na, specifying FALSE for parameter 6, is_readonly. the  
         * arrayvalue destructor will as a result destroy the content nativearray, 
         * which will in turn *not* destroy its content bytearray. finally, we   
         * relinquish the arrayvalue to assert_value_to_bytes(), which calls the 
         * arrayvalue destructor, causing the above to occur.
         */
        etch_nativearray* na = new_nativearray_from(content, CLASSID_ARRAY_BYTE, sizeof(byte), 1, ITEMCOUNT, 0, 0); 
        etch_arrayvalue*  av = new_arrayvalue_from(na, ETCH_XTRNL_TYPECODE_BYTE, NULL, ITEMCOUNT, 0, FALSE);
      
        result = assert_value_to_bytes((objmask*) av, expected, vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    do  /* byte[][] */
    {   
        #define NUMDIMS2   2
        #define ITEMCOUNT2 2
        #define ITEMCOUNT3 3
        signed char content[NUMDIMS2][ITEMCOUNT3] = { { 1, 2, 3, }, { 4, 5, 6, }, };

        signed char a[] =    /* array[2] of byte vectors */
        { ETCH_XTRNL_TYPECODE_ARRAY, /* value type array */    
          ETCH_XTRNL_TYPECODE_BYTE,  /* array content type */  
          NUMDIMS2,                  /* dimensions count */
          ITEMCOUNT2,                /* item count in dim[1] */
	      ETCH_XTRNL_TYPECODE_BYTES, /* byte vector follows */
          ITEMCOUNT3,                /* count of items following */
          1,                         /* item[0][0] */
          2,                         /* item[0][1] */
          3,                         /* item[0][2] */          
	      ETCH_XTRNL_TYPECODE_BYTES, /* byte vector follows */
          ITEMCOUNT3,                /* count of items following */
          4,                         /* item[1][0] */
          5,                         /* item[1][1] */
          6,                         /* item[1][2] */             
          ETCH_XTRNL_TYPECODE_NONE   /* eod mark */
        }; 

        etch_nativearray* expected = get_bytearray(a, sizeof(a));

        /* memory management notes: here we create a nativearray na from static
         * content. new_nativearray_from() defaults nativearray.is_content_owned   
         * to false, so when the nativearray na is destroyed, it will not attempt 
         * to free memory for the static content bytes. we then create an arrayvalue 
         * from nativearray na, specifying FALSE for parameter 6, is_readonly. the  
         * arrayvalue destructor will as a result destroy the content nativearray, 
         * which will in turn *not* destroy its content bytearray. finally, we   
         * relinquish the arrayvalue to assert_value_to_bytes(), which calls the 
         * arrayvalue destructor, causing the above to occur.
         */
        etch_nativearray* na = new_nativearray_from(content, CLASSID_ARRAY_BYTE, 
            sizeof(byte), NUMDIMS2, ITEMCOUNT3, ITEMCOUNT2, 0); 

        /* note here that the arrayvalue content type (ETCH_XTRNL_TYPECODE_BYTE)
         * is the content type serialized, so it must be the same as a[1].
         * the serialized type of arrays of byte vectors is BYTE, as defined by the 
         * tagged data spec, and not BYTES, which one might expect
         */
        etch_arrayvalue*  av = new_arrayvalue_from(na, ETCH_XTRNL_TYPECODE_BYTE, 
            NULL, ETCH_DEFSIZE, 0, FALSE);
      
        result = assert_value_to_bytes((objmask*) av, expected, vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    vf->destroy(vf);

    etchvf_free_builtins(); 

    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_short_array_value_to_bytes()
 * test that various value objects serialized by the tdo match hard-coded expected byte arrays
 */
void test_short_array_value_to_bytes(void)
{
    int  result = 0;
    etch_value_factory* vf = (etch_value_factory*) new_default_value_factory(NULL, NULL);

    do  /* short[] */
    {   const int NUMDIMS1 = 1, ITEMCOUNT = 3;

        signed char a[] =
        { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
          ETCH_XTRNL_TYPECODE_SHORT, /* array content type */
          NUMDIMS1,  /* dimensions count */
          ITEMCOUNT, /* count of content items following */
          1,   /* item[0] */
          2,   /* item[1] */
          3,   /* item[2] */
          ETCH_XTRNL_TYPECODE_NONE  /* eod mark */
        }; 

        etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_SHORT, 
            NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);

        arrayvalue_add(av, (etch_object*) new_int16(1));
        arrayvalue_add(av, (etch_object*) new_int16(2));
        arrayvalue_add(av, (etch_object*) new_int16(3));
 
        result = assert_value_to_bytes((objmask*) av, get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    vf->destroy(vf);

    etchvf_free_builtins(); 

    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_int_array_value_to_bytes()
 * test that various value objects serialized by the tdo match hard-coded expected byte arrays
 */
void test_int_array_value_to_bytes(void)
{
    int  result = 0;
    etch_value_factory* vf = (etch_value_factory*) new_default_value_factory(NULL, NULL);

    do  /* short[] */
    {   const int NUMDIMS1 = 1, ITEMCOUNT = 3;

        signed char a[] =
        { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
          ETCH_XTRNL_TYPECODE_INT,   /* array content type */
          NUMDIMS1,  /* dimensions count */
          ITEMCOUNT, /* count of content items following */
          1,   /* item[0] */
          2,   /* item[1] */
          3,   /* item[2] */
          ETCH_XTRNL_TYPECODE_NONE  /* eod mark */
        }; 

        etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_INT, 
            NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);

        arrayvalue_add(av, (etch_object*) new_int32(1));
        arrayvalue_add(av, (etch_object*) new_int32(2));
        arrayvalue_add(av, (etch_object*) new_int32(3));
 
        result = assert_value_to_bytes((objmask*) av, get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    vf->destroy(vf);

    etchvf_free_builtins(); 

    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_long_array_value_to_bytes()
 * test that various value objects serialized by the tdo match hard-coded expected byte arrays
 */
void test_long_array_value_to_bytes(void)
{
    int  result = 0;
    etch_value_factory* vf = (etch_value_factory*) new_default_value_factory(NULL, NULL);

    do  /* short[] */
    {   const int NUMDIMS1 = 1, ITEMCOUNT = 3;

        signed char a[] =
        { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
          ETCH_XTRNL_TYPECODE_LONG,  /* array content type */
          NUMDIMS1,  /* dimensions count */
          ITEMCOUNT, /* count of content items following */
          1,   /* item[0] */
          2,   /* item[1] */
          3,   /* item[2] */
          ETCH_XTRNL_TYPECODE_NONE  /* eod mark */
        }; 

        etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_LONG, 
            NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);

        arrayvalue_add(av, (etch_object*) new_int64(1));
        arrayvalue_add(av, (etch_object*) new_int64(2));
        arrayvalue_add(av, (etch_object*) new_int64(3));
 
        result = assert_value_to_bytes((objmask*) av, get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    vf->destroy(vf);

    etchvf_free_builtins(); 

    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_float_array_value_to_bytes()
 * test that various value objects serialized by the tdo match hard-coded expected byte arrays
 */
void test_float_array_value_to_bytes(void)
{
    int  result = 0;
    etch_value_factory* vf = (etch_value_factory*) new_default_value_factory(NULL, NULL);

    do  /* short[] */
    {   const int NUMDIMS1 = 1, ITEMCOUNT = 3;

        signed char a[] =
        { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
          ETCH_XTRNL_TYPECODE_FLOAT, /* array content type */
          NUMDIMS1,  /* dimensions count */
          ITEMCOUNT, /* count of content items following */
          ETCH_XTRNL_TYPECODE_FLOAT, 63,-128, 0, 0,   /* item[0] */
          ETCH_XTRNL_TYPECODE_FLOAT, 64,   0, 0, 0,   /* item[1] */
          ETCH_XTRNL_TYPECODE_FLOAT, 64,  64, 0, 0,   /* item[2] */
          ETCH_XTRNL_TYPECODE_NONE  /* eod mark */
        }; 

        etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_FLOAT, 
            NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);

        arrayvalue_add(av, (etch_object*) new_float(1.0));
        arrayvalue_add(av, (etch_object*) new_float(2.0));
        arrayvalue_add(av, (etch_object*) new_float(3.0));
 
        result = assert_value_to_bytes((objmask*) av, get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    vf->destroy(vf);

    etchvf_free_builtins(); 

    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_double_array_value_to_bytes()
 * test that various value objects serialized by the tdo match hard-coded expected byte arrays
 */
void test_double_array_value_to_bytes(void)
{
    int  result = 0;
    etch_value_factory* vf = (etch_value_factory*) new_default_value_factory(NULL, NULL);

    do  /* short[] */
    {   const int NUMDIMS1 = 1, ITEMCOUNT = 3;

        signed char a[] =
        { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
          ETCH_XTRNL_TYPECODE_DOUBLE, /* array content type */
          NUMDIMS1,  /* dimensions count */
          ITEMCOUNT, /* count of content items following */
          ETCH_XTRNL_TYPECODE_DOUBLE, 63,-16, 0, 0,  0, 0, 0, 0,  /* item[0] */
          ETCH_XTRNL_TYPECODE_DOUBLE, 64,  0, 0, 0,  0, 0, 0, 0,  /* item[1] */
          ETCH_XTRNL_TYPECODE_DOUBLE, 64,  8, 0, 0,  0, 0, 0, 0,  /* item[2] */
          ETCH_XTRNL_TYPECODE_NONE  /* eod mark */
        }; 

        etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_DOUBLE, 
            NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);

        arrayvalue_add(av, (etch_object*) new_double(1.0));
        arrayvalue_add(av, (etch_object*) new_double(2.0));
        arrayvalue_add(av, (etch_object*) new_double(3.0));
 
        result = assert_value_to_bytes((objmask*) av, get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    vf->destroy(vf);

    etchvf_free_builtins(); 

    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_string_values_to_bytes()
 * test that various value objects serialized by the tdo match hard-coded expected byte arrays
 */
void test_string_values_to_bytes(void)
{
    int  result = 0;
    etch_value_factory* vf = (etch_value_factory*) new_default_value_factory(NULL, NULL);

    do  /* empty string */
    {   signed char a[] = { ETCH_XTRNL_TYPECODE_EMPTY_STRING }; 

        result = assert_value_to_bytes((objmask*) new_string(L"", ETCH_ENCODING_UTF16), 
            get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    do  /* string */
    {   /* note here that our test object is a unicode string, while our expected byte 
         * array encodes an array of 8-bit characters. this is because we carry all
         * strings internally as unicode, but the tdo converts strings to the configured 
         * wire encoding format, which is utf-8 at this writing. 
         */ 
        const int STRLEN = 3;
        signed char a[] = { ETCH_XTRNL_TYPECODE_STRING, STRLEN, 97, 98, 99 }; 

        result = assert_value_to_bytes((objmask*) new_string(L"abc", ETCH_ENCODING_UTF16), 
            get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    do  /* string[] */
    {   const int NUMDIMS1 = 1, ITEMCOUNT = 3, STRLEN1 = 1;

        signed char a[] =
        { ETCH_XTRNL_TYPECODE_ARRAY,  /* array type */
          ETCH_XTRNL_TYPECODE_STRING, /* array content type */
          NUMDIMS1,  /* dimensions count */
          ITEMCOUNT, /* count of content items following */
          ETCH_XTRNL_TYPECODE_STRING, STRLEN1, 97,  /* item[0] */
          ETCH_XTRNL_TYPECODE_EMPTY_STRING,         /* item[1] */
          ETCH_XTRNL_TYPECODE_STRING, STRLEN1, 99,  /* item[2] */
          ETCH_XTRNL_TYPECODE_NONE    /* eod mark */
        }; 

        etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_STRING, 
            NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);

        arrayvalue_add(av, (etch_object*) new_string(L"a", ETCH_ENCODING_UTF16));
        arrayvalue_add(av, (etch_object*) new_string(L"",  ETCH_ENCODING_UTF16));
        arrayvalue_add(av, (etch_object*) new_string(L"c", ETCH_ENCODING_UTF16));
 
        result = assert_value_to_bytes((objmask*) av, get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    vf->destroy(vf);

    etchvf_free_builtins(); 

    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_date_values_to_bytes()
 * test that various value objects serialized by the tdo match hard-coded expected byte arrays
 */
void test_date_values_to_bytes(void)
{
    int  result = 0;
    etch_value_factory* vf = (etch_value_factory*) new_default_value_factory(NULL, NULL);

    do  /* date */
    {   const int64 BOGUSDATEVAL = 1234567890;

        signed char a[] = 
        { ETCH_XTRNL_TYPECODE_CUSTOM,   /* indicates struct follows */
          ETCH_XTRNL_TYPECODE_INT, 43, 57, 107, -52, /* _mt__etch_datetime type id */
          1,                            /* struct member count */
          ETCH_XTRNL_TYPECODE_INT, 102, 0,  26,  64, /* "dateTime" field id */
          ETCH_XTRNL_TYPECODE_INT, 73, -106, 2, -46, /* serialized 1234567890 */
          ETCH_XTRNL_TYPECODE_NONE      /* eod mark */
        }; 

        etch_date* testdate = new_date();
        testdate->value = (time_t) BOGUSDATEVAL;

        result = assert_value_to_bytes((objmask*) testdate, get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    do  /* date[] */
    {   const int NUMDIMS1 = 1, ITEMCOUNT1 = 1; 
        const int64 BOGUSDATEVAL1 = 1234567890;
        const int64 BOGUSDATEVAL2 = 2345678901;

        signed char a[] =
        { ETCH_XTRNL_TYPECODE_ARRAY,  /* array type */
          ETCH_XTRNL_TYPECODE_CUSTOM, /* array content type */
          ETCH_XTRNL_TYPECODE_INT, 43, 57, 107, -52, /* "custom struct type" _mt__etch_datetime */
          NUMDIMS1,   /* array dimensions */
          ITEMCOUNT2, /* count of array content items following */  

          ETCH_XTRNL_TYPECODE_CUSTOM, /* start array member[0] */
          ETCH_XTRNL_TYPECODE_INT, 43, 57, 107, -52, /* struct type _mt__etch_datetime */
          ITEMCOUNT1,                 /* struct item count */
          ETCH_XTRNL_TYPECODE_INT, 102, 0,  26,  64, /* "dateTime" field id */
          ETCH_XTRNL_TYPECODE_INT, 73, -106, 2, -46, /* serialized 1234567890 */     
          ETCH_XTRNL_TYPECODE_NONE,   /* eod mark for struct - end of array member[0] */

          ETCH_XTRNL_TYPECODE_CUSTOM, /* start array member[1] */
          ETCH_XTRNL_TYPECODE_INT, 43, 57, 107, -52, /* struct type _mt__etch_datetime */
          ITEMCOUNT1,                 /* struct item count */
          ETCH_XTRNL_TYPECODE_INT, 102, 0,  26,  64, /* "dateTime" field id */
          ETCH_XTRNL_TYPECODE_LONG,  0, 0, 0, 0,  -117, -48, 56, 53, /* serialized 2345678901 */     
          ETCH_XTRNL_TYPECODE_NONE,   /* eod mark for struct - end of array member[1] */

          ETCH_XTRNL_TYPECODE_NONE,   /* eod mark for array */
        }; 

        etch_arrayvalue* av = NULL;
        etch_type* my_custom_struct_type = builtins._mt__etch_datetime;

        etch_date* date1 = new_date();
        etch_date* date2 = new_date();
        date1->value = BOGUSDATEVAL1;
        date2->value = BOGUSDATEVAL2;

        av = new_arrayvalue(ETCH_XTRNL_TYPECODE_CUSTOM, 
             my_custom_struct_type, NUMDIMS1, ITEMCOUNT2, 0, FALSE, FALSE);

        arrayvalue_add(av, (etch_object*) date1);
        arrayvalue_add(av, (etch_object*) date2);
 
        result = assert_value_to_bytes((objmask*) av, get_bytearray(a, sizeof(a)), vf);
        CU_ASSERT_EQUAL(result,0);

    } while(0);

    vf->destroy(vf);

    etchvf_free_builtins(); 

    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 ps = NULL;
    g_is_automated_test = argc > 1 && 0 != wcscmp(argv[1], L"-a");
    if (0 != CU_initialize_registry()) return 0;
    CU_set_output_filename("../test_bintditdo");
    ps = CU_add_suite("bintditdo test suite", init_suite, clean_suite);
    etch_watch_id = 0;

    CU_add_test(ps, "test constructor 0", test_constructor_0);
    CU_add_test(ps, "test constructor 1", test_constructor_1);
    CU_add_test(ps, "test constructor 2", test_constructor_2);
    CU_add_test(ps, "test check value",   test_check_value);
     
    CU_add_test(ps, "test byte[][] out and back in",   test_byte_array_out_in);
    CU_add_test(ps, "test bool[][] out and back in",   test_bool_array_out_in);
    CU_add_test(ps, "test short[][] out and back in",  test_short_array_out_in);

    CU_add_test(ps, "test int[][] out and back in",    test_int_array_out_in);
    CU_add_test(ps, "test long[][] out and back in",   test_long_array_out_in);
    CU_add_test(ps, "test float[][] out and back in",  test_float_array_out_in);
    CU_add_test(ps, "test double[][] out and back in", test_double_array_out_in);
    CU_add_test(ps, "test string[][] out and back in", test_string_array_out_in);
    CU_add_test(ps, "test add perf", test_add_perf);

    CU_add_test(ps, "test single values to bytes", test_single_value_to_bytes);
    CU_add_test(ps, "test bool array to bytes",    test_boolean_array_value_to_bytes);
    CU_add_test(ps, "test byte arrays to bytes",   test_byte_array_value_to_bytes);
    CU_add_test(ps, "test short array to bytes",   test_short_array_value_to_bytes);
    CU_add_test(ps, "test int array to bytes",     test_int_array_value_to_bytes);
    CU_add_test(ps, "test long array to bytes",    test_long_array_value_to_bytes);
    CU_add_test(ps, "test float array to bytes",   test_float_array_value_to_bytes);
    CU_add_test(ps, "test double array to bytes",  test_double_array_value_to_bytes);
    CU_add_test(ps, "test string values to bytes", test_string_values_to_bytes);
    CU_add_test(ps, "test date values to bytes",   test_date_values_to_bytes);

    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();
}