You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@etch.apache.org by jd...@apache.org on 2009/04/22 19:25:51 UTC

svn commit: r767594 [27/43] - in /incubator/etch/trunk/binding-c/runtime/c: ./ ext/ ext/hashtab/ ext/lib/ inc/ lib/ project/ project/$etchstop/ project/bin/ project/etch/ project/logcli/ project/logsrv/ project/notes/ project/test/ project/test/logcli/...

Added: incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_idname.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_idname.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_idname.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_idname.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,223 @@
+/* $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_id_name.c 
+ * tests the C implementation of the etch_id_name object.
+ */
+#include "apr_time.h" /* some apr must be included first */
+#include "etchthread.h"
+#include <stdio.h>
+#include <conio.h>
+#include "cunit.h"
+#include "basic.h"
+#include "automated.h"
+#include "etch_id_name.h"
+#include "etch_global.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;
+}
+
+
+/**
+ * test_id_name
+ */
+void test_id_name(void)
+{
+    int alloc_a = 0, alloc_b = 0, result = 0;
+    etch_id_name *id_name1 = NULL, *id_name2 = NULL;
+
+    const wchar_t* nametext1 = L"abracadabra";
+    const size_t   numelts1  = wcslen(nametext1);
+    const size_t   numbytes1 = sizeof(wchar_t) * numelts1;
+
+    const wchar_t* nametext2 = L"gilgamesh";
+    const size_t   numelts2  = wcslen(nametext2);
+    const size_t   numbytes2 = sizeof(wchar_t) * numelts2;
+    
+    alloc_a  = etch_showmem(0, FALSE);
+    id_name1 = new_id_name(NULL);
+    alloc_b  = etch_showmem(0, FALSE);
+    CU_ASSERT_PTR_NULL(id_name1);
+    CU_ASSERT_EQUAL(alloc_a, alloc_b); 
+
+    id_name1 = new_id_name(nametext1);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(id_name1);
+    CU_ASSERT_TRUE(is_good_id_name(id_name1));
+
+    id_name2 = new_id_name(nametext1);
+    CU_ASSERT_TRUE(is_equal_id_names(id_name1, id_name2));
+    result = memcmp(id_name1->name, id_name2->name, id_name1->namebytelen);
+    CU_ASSERT_EQUAL(result,0);
+
+    destroy_id_name(id_name1);
+    destroy_id_name(id_name2);
+
+    alloc_a = etch_showmem(0, FALSE);
+    CU_ASSERT_EQUAL(alloc_a, alloc_b); 
+
+    id_name1 = new_id_name(nametext1);
+    id_name2 = new_id_name(nametext2);
+
+    CU_ASSERT_FALSE(is_equal_id_names(id_name1, id_name2));
+
+    destroy_id_name(id_name1);
+    destroy_id_name(id_name2);
+
+    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_id_name_hashfunc
+ * Unit test id_name_hashfunc
+ */
+void test_id_name_hashfunc(void)
+{
+    int alloc_a = 0, alloc_b = 0, result = 0;
+    unsigned hash1 = 0, hash2 = 0;
+    etch_id_name *id_name1 = NULL, *id_name2 = NULL;
+
+    const wchar_t* nametext1 = L"abracadabra";
+    const size_t   numelts1  = wcslen(nametext1);
+    const size_t   numbytes1 = sizeof(wchar_t) * ( numelts1 + 1 );
+
+    const wchar_t* nametext2 = L"gilgamesh";
+    const size_t   numelts2  = wcslen(nametext2);
+    const size_t   numbytes2 = sizeof(wchar_t) * ( numelts2 + 1 );
+ 
+    hash1 = compute_id_name_id_from_widename(nametext1);
+    hash2 = compute_id_name_id_from_widename(nametext2);
+
+    CU_ASSERT_NOT_EQUAL(hash1, hash2);
+
+    id_name1 = new_id_name(nametext1);
+    id_name2 = new_id_name(nametext2);
+    CU_ASSERT_EQUAL(id_name1->namebytelen, numbytes1);
+    CU_ASSERT_EQUAL(id_name2->namebytelen, numbytes2);
+    CU_ASSERT_EQUAL(id_name1->id, hash1);
+    CU_ASSERT_EQUAL(id_name2->id, hash2);
+
+    destroy_id_name(id_name1);
+    destroy_id_name(id_name2);
+
+    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 */       
+}
+
+
+/**
+ * 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_id_name", init_suite, clean_suite);
+    CU_set_output_filename("../test_id_name");
+
+    CU_add_test(pSuite, "test etch_id_name constructors/destructors", test_id_name);  
+    CU_add_test(pSuite, "test etch_id_name hashing", test_id_name_hashfunc);  
+
+    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(); 
+}
\ No newline at end of file

Added: incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_message.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_message.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_message.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_message.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,1380 @@
+/* $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_message.c -- test etch_message object
+ */
+
+#include "apr_time.h" /* some apr must be included first */
+
+#include <tchar.h>
+#include <stdio.h>
+#include <conio.h>
+
+#include "cunit.h"
+#include "basic.h"
+#include "automated.h"
+
+#include "etch_connection.h"
+#include "etch_global.h"
+#include "etch_encoding.h"
+#include "etchthread.h"
+#include "etchlog.h"
+#include "etch_message.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";
+
+
+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;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - 
+ * local declarations 
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */   
+
+/* - - - - - - - - - - - - - - - - - - - - - - 
+ * local setup and teardown
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+int this_setup()
+{
+    etch_apr_mempool = g_apr_mempool;
+    return 0;
+}
+
+int this_teardown()
+{    
+    return 0;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - 
+ * individual setup and teardown
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+int this_test_setup()
+{
+    int result = -1;
+
+    do
+    {
+      result = 0;
+    } while(0);
+
+    return result;
+}
+
+int this_test_teardown()
+{    
+
+    return 0;
+}
+
+int  g_bytes_allocated, g_which_exception_test, g_is_automated_test;
+wchar_t* local_excp_text = L"global text";
+
+#if(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 CLASSID_FAKETDI 0xf6
+#define CLASSID_FAKETDO 0xf7
+#endif
+
+#define OBJTYPE_FAKEVF_IMPL ETCHTYPEB_INSTANCEDATA
+#define CLASSID_FAKEVF_IMPL    0xf2
+#define OBJTYPE_FAKEVF_VTABLE  0x90
+#define OBJTYPE_FAKEVF_IMPL ETCHTYPEB_INSTANCEDATA
+#define OBJTYPE_FAKEVF ETCHTYPEB_VALUEFACTORY
+#define CLASSID_FAKEVF  0xf5
+
+#define EXCPTEST_UNCHECKED_STATICTEXT 1
+#define EXCPTEST_CHECKED_COPYTEXT     2
+#define EXCPTEST_CHECKED_STATICTEXT   3
+
+
+typedef struct testitems
+{
+    etch_type*  mt1;
+    etch_type*  mt2;
+    etch_type*  rmt;
+    etch_field* mf1;
+    etch_field* mf2;
+    etch_field* mf3;
+    etch_field* mf4;
+
+    etch_string* s1;
+    etch_string* s2;
+    etch_int32*  n1;
+    etch_int32*  n2;
+    etch_int64*  l1;
+    etch_int64*  l2;
+
+    etch_field *mf_bool_d0_1;   
+    etch_field *mf_bool_d0_2;   
+    etch_field *mf_bool_d1_1;   
+    etch_field *mf_bool_d1_2;  
+
+    etch_field *mf_int32_d0_1;  
+    etch_field *mf_int32_d0_2;  
+    etch_field *mf_int32_d1_1;  
+    etch_field *mf_int32_d1_2;  
+
+    etch_field *mf_str_d0_1;   
+    etch_field *mf_str_d0_2;   
+    etch_field *mf_str_d1_1;  
+    etch_field *mf_str_d1_2;  
+
+    etch_field *mf_message_id;
+    etch_field *mf_in_reply_to; 
+
+    etch_hashtable* items;
+
+} testitems;
+
+enum objtyp ETCHTYPE_VTABLE_FAKETDI = 0xfa;
+enum objtyp ETCHTYPE_VTABLE_FAKETDO = 0xfb; 
+enum objtyp ETCHTYPE_VTABLE_FAKEVF  = 0xfc;
+
+
+/* = = = = = = = = = = = = = = =
+ * VALUE FACTORY IMPLEMENTATION
+ * = = = = = = = = = = = = = = =
+ */
+
+/**
+ * fake_vf_impl
+ * instance data for a value factory implementation
+ */
+typedef struct fake_vf_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;
+
+    etch_field* mf_message_id;
+    etch_field* mf_in_reply_to;
+
+} fake_vf_impl;
+
+
+/**
+ * destroy_fake_vf_impl
+ * destructor for fake_vf_impl
+ */
+int destroy_fake_vf_impl(fake_vf_impl* impl)
+{
+    int result = verify_object((objmask*)impl, OBJTYPE_FAKEVF_IMPL, CLASSID_FAKEVF_IMPL, 0);
+    if (result == -1) return -1; /* object passed was not expected object */
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(impl->mf_message_id);  
+    CU_ASSERT_PTR_NOT_NULL_FATAL(impl->mf_in_reply_to); 
+
+    impl->mf_message_id ->destroy(impl->mf_message_id);
+    impl->mf_in_reply_to->destroy(impl->mf_in_reply_to);
+
+    etch_free(impl); 
+    return 0;
+}
+
+
+/**
+ * new_fake_vf_impl()
+ * constructor for TDI implementation instance data
+ */
+fake_vf_impl* new_fake_vf_impl(testitems* items)
+{
+    fake_vf_impl* data = etch_malloc(sizeof(fake_vf_impl), ETCHTYPEB_INSTANCEDATA);
+    memset(data, 0, sizeof(fake_vf_impl));
+    data->obj_type = OBJTYPE_FAKEVF_IMPL;
+    data->class_id = CLASSID_FAKEVF_IMPL;
+
+    data->mf_message_id  = items->mf_message_id->clone(items->mf_message_id);
+    data->mf_in_reply_to = items->mf_in_reply_to->clone(items->mf_in_reply_to);
+ 
+    data->destroy = destroy_fake_vf_impl;
+    return data;
+}
+
+
+/*  
+ * value factory virtual function overrides
+ */
+
+/**
+ * fvf_get_message_id() -- valuefactory.get_message_id() implementation.
+ * @return non-disposable reference to id object, or null if not found.
+ */
+etch_int64* fvf_get_message_id (etch_value_factory* vf, etch_message* msg)
+{
+    etch_int64* id = NULL;
+    fake_vf_impl* data = (fake_vf_impl*) vf->impl;
+
+    id = (etch_int64*) message_get(msg, data->mf_message_id);
+
+    /* return (etch_int64*) verifyx((objmask*) id, 0, 
+       CLASSID_PRIMITIVE_INT64, EXCPTYPE_INTERNALERR); */
+
+    return id;
+}
+
+
+/**
+ * fvf_set_message_id() -- valuefactory.set_message_id() implementation.
+ */
+int fvf_set_message_id (etch_value_factory* vf, etch_message* msg, etch_int64* id)
+{
+    fake_vf_impl* data  = (fake_vf_impl*) vf->impl;
+    etch_field* keycopy = data->mf_message_id->clone(data->mf_message_id);
+
+    const int result = message_put(msg, keycopy, (objmask*) id);  
+    return result;
+}
+
+
+/**
+ * fvf_get_in_reply_to() -- valuefactory.get_in_reply_to() implementation.
+ */
+etch_int64* fvf_get_in_reply_to (etch_value_factory* vf, etch_message* msg)
+{
+    etch_int64* id = NULL;
+    fake_vf_impl* data = (fake_vf_impl*) vf->impl;
+
+    id = (etch_int64*) message_get(msg, data->mf_in_reply_to);
+
+    return (etch_int64*) verifyx((objmask*) id, 0, CLASSID_PRIMITIVE_INT64, EXCPTYPE_INTERNALERR);
+}
+
+
+/**
+ * fvf_set_in_reply_to() -- valuefactory.set_message_id() implementation.
+ */
+int fvf_set_in_reply_to (etch_value_factory* vf, etch_message* msg, etch_int64* id)
+{
+    fake_vf_impl* data  = (fake_vf_impl*) vf->impl;
+    etch_field* keycopy = clone_field(data->mf_in_reply_to);
+
+    /* FYI this copy of the key is put to etch_message* sent message, and gets   
+     * freed in msg.destroy(), message owns memory other than vf and type 
+     */
+    return message_put(msg, keycopy, (objmask*) id);  
+}
+
+/*  
+ * value factory constructors/destructors
+ */
+
+
+/**
+ * fakevf_close() ala java test
+ */
+void fakevf_close(etch_value_factory* vf)  
+{
+    vf->destroy(vf);  
+}
+
+
+#define CLASSID_FAKE_VALUEFACTORY 200
+
+/**
+ * new_fake_value_factory()
+ * constructor for value factory implementation  
+ */
+etch_value_factory* new_fake_value_factory(testitems* items)  
+{
+    etch_value_factory* fakevf = NULL;
+    i_value_factory* vtab = NULL;
+    const int VTABSIZE = sizeof(i_value_factory);
+    const unsigned short CLASS_ID = CLASSID_FAKE_VALUEFACTORY;
+    int result = 0;
+   
+    fakevf = new_value_factory(0);
+   
+    vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+    if(!vtab)  
+    {    
+        vtab = new_vtable(fakevf->vtab, VTABSIZE, CLASS_ID);
+
+        /* override four i_value_factory methods */
+        vtab->get_message_id  = fvf_get_message_id;
+        vtab->set_message_id  = fvf_set_message_id;
+        vtab->get_in_reply_to = fvf_get_in_reply_to;
+        vtab->set_in_reply_to = fvf_set_in_reply_to;      
+
+        vtab->vtab = fakevf->vtab;  /* chain vtabs */
+        cache_insert(vtab->hashkey, vtab, 0);  
+    } 
+
+    CU_ASSERT_EQUAL_FATAL(vtab->class_id, CLASS_ID);
+
+    fakevf->vtab = vtab;  /* set override vtab */
+
+    fakevf->impl = (objmask*) new_fake_vf_impl(items); /* create vf instance data */
+
+    switch(g_which_exception_test)
+    {   case EXCPTEST_UNCHECKED_STATICTEXT: 
+             etch_throw((objmask*) fakevf, EXCPTYPE_NULLPTR, NULL, 0);  
+             break;   
+        case EXCPTEST_CHECKED_COPYTEXT:   
+             etch_throw((objmask*)fakevf, EXCPTYPE_CHECKED_BOGUS, L"copied text", ETCHEXCP_COPYTEXT | ETCHEXCP_FREETEXT);  
+             break; 
+        case EXCPTEST_CHECKED_STATICTEXT:   
+             etch_throw((objmask*)fakevf, EXCPTYPE_CHECKED_BOGUS, local_excp_text, ETCHEXCP_STATICTEXT);  
+             break;       
+    }
+
+    return fakevf;
+}
+
+
+#if(0)
+
+/* = = = = = = = = = =    
+ * TDI IMPLEMENTATION
+ * = = = = = = = = = =     
+ */
+
+/**
+ * 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 i_value_factory* 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;
+
+    etch_type*    tdi_type;
+    etch_message* xmessage;
+    etch_iterator iterator;  
+    etch_value_factory* vf;
+    testitems*    testdata;
+    byte started, done, ended, is_owned_message;
+
+} fake_tdi_impl;
+
+
+
+/**
+ * destroy_fake_tdi_impl
+ * memory cleanup handler for fake_tdi_impl
+ */
+int destroy_fake_tdi_impl(fake_tdi_impl* impl)
+{
+    etch_destructor destroy = NULL;
+    int result = verify_object((objmask*)impl, OBJTYPE_FAKETDI_IMPL, CLASSID_FAKETDI_IMPL, 0);
+    if (result == -1) return -1; /* object passed was not expected object */
+
+    /* type is a reference, it does not belong to the tdi. message is created  
+     * in the tdi, but ownership is assumed to transfer to the caller when  
+     * message_read returns. if this is not the case then is_owned_message 
+     * must have been set elsewhere.
+     */
+    if (impl->is_owned_message) 
+    {   /* not the default case, see comment above */
+        CU_ASSERT_PTR_NOT_NULL_FATAL(impl->xmessage);  
+        impl->xmessage->destroy(impl->xmessage);  
+    }
+ 
+    etch_free(impl); 
+    return 0;
+}
+
+
+/**
+ * new_fake_tdi_impl()
+ * constructor for TDI implementation instance data
+ */
+fake_tdi_impl* new_fake_tdi_impl(etch_value_factory* vf, etch_type* static_type, testitems* testdata)
+{
+    fake_tdi_impl* data = (fake_tdi_impl*) 
+        new_object(sizeof(fake_tdi_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDI_IMPL);
+
+    /* value factory and type are references, memory not owned by tdi */
+    data->tdi_type = static_type;     
+    data->vf = vf;  
+    data->testdata = testdata;
+
+    data->destroy = destroy_fake_tdi_impl;        
+    return data;
+}
+
+
+/**
+ * faketdi_start_message() overrides tdi_start_message()
+ */
+etch_message* faketdi_start_message(tagged_data_input* tdi) 
+{
+    int result = 0;
+    fake_tdi_impl* tdidata = NULL;
+    etch_message* newmessage = NULL;
+    etch_value_factory* vf = NULL;
+    const int READONLY = TRUE, DEFSIZE = 0, DEFDELTA = 0;
+    
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);  
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+
+    tdidata = (fake_tdi_impl*) tdi->impl; /* validate instance data */
+    result  = verify_object((objmask*)tdidata, OBJTYPE_FAKETDI_IMPL, 0, 0);
+    CU_ASSERT_EQUAL_FATAL(result,0);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdidata->vf);
+
+    result = tdidata->destroy(NULL); /* ensure we can call into instance destructor */
+    CU_ASSERT_EQUAL(result,-1);
+
+    CU_ASSERT_EQUAL(tdidata->started,FALSE);
+    CU_ASSERT_EQUAL(tdidata->done,FALSE);
+    CU_ASSERT_EQUAL(tdidata->ended,FALSE);
+
+    tdidata->started = TRUE;
+
+    /* create a message (struct) to receive the elements read. a message owns all  
+     * its memory except the value factory and type (note that the vf might need 
+     * to be refcounted for that reason, to ensure that if the service shuts down 
+     * while a message is in the pipeline, a vf is not destroyed prior to a message
+     * which references it). the type is passed through to the underlying struct. 
+     * a struct owns all its memory except type, so it is assigned a reference to 
+     * the tdi's type, which is an object global to the service. ownership of the 
+     * struct, and by extension the messsage, memory, is assumed to be passed to 
+     * the caller when this function returns. if instead it was desired that the 
+     * tdi own the message and hence the struct, an indicator of such and an 
+     * associated message dtor call, would need to be coded in the tdi and tdi 
+     * destructor implementation.
+     */
+    newmessage = new_message (tdidata->tdi_type, 0, tdidata->vf);  
+
+    tdidata->xmessage = newmessage;
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdidata->xmessage);
+    
+    set_iterator(&tdidata->iterator, /* establish iterator on the input data */
+        tdidata->testdata->items, &tdidata->testdata->items->iterable);
+
+    /* while an empty dataset is valid in the general case, 
+     * this test assumes that input is non-empty */
+    result = tdidata->iterator.has_next(&tdidata->iterator); 
+    CU_ASSERT_EQUAL(result,TRUE);
+
+    return newmessage;
+}
+
+ 
+/**
+ * faketdi_read_struct_element() overrides tdi_read_struct_element()
+ */
+int faketdi_read_struct_element(tagged_data_input* tdi, etch_struct_element* out_se)   
+{
+    int result = 0;
+    fake_tdi_impl* data = NULL;
+    etch_iterator* iterator = NULL;
+    etch_destructor destroy = NULL;    
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);  
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(out_se);
+
+    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);
+    iterator = &data->iterator;
+
+	if (iterator->has_next(iterator)) 
+	{   
+        CU_ASSERT_EQUAL_FATAL(result,0);
+        CU_ASSERT_PTR_NOT_NULL_FATAL(iterator->current_key);
+        CU_ASSERT_PTR_NOT_NULL_FATAL(iterator->current_value);
+
+    	out_se->key   = (etch_field*) iterator->current_key;
+        out_se->value = (objmask*)    iterator->current_value;
+
+        iterator->next(iterator);
+		return TRUE;
+	}
+	
+	data->done = TRUE;
+	return FALSE;
+}
+
+
+/**
+ * faketdi_end_message() overrides tdi_end_struct()
+ */
+etch_message* faketdi_end_message(tagged_data_input* tdi, etch_message* msg) 
+{
+    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_FATAL(data->xmessage, msg); 
+
+    CU_ASSERT_EQUAL(data->started,TRUE);
+    CU_ASSERT_EQUAL(data->done,TRUE);
+    CU_ASSERT_EQUAL(data->ended,FALSE);
+    			
+	data->ended = TRUE;
+    return msg;
+}
+
+
+/**
+ * faketdi_close() ala 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);  
+}
+
+
+/**
+ * new_fake_tdi()
+ * constructor for TDI implementation  
+ */
+tagged_data_input* new_fake_tdi(etch_type* static_type, etch_value_factory* vfobj, testitems* data)  
+{
+    tagged_data_input* faketdi = NULL;
+    i_tagged_data_input* vtab  = NULL;
+    const unsigned short CLASS_ID = CLASSID_FAKETDI;
+    CU_ASSERT_EQUAL_FATAL(is_good_type(static_type),TRUE);
+   
+    faketdi = new_tagged_data_input();
+
+    vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+    if(!vtab)  
+    {    
+        vtab = new_vtable(faketdi->vtab, sizeof(i_tagged_data_input), CLASS_ID);
+
+        /* override three i_tagged_data_input methods */
+        vtab->start_message       = faketdi_start_message;  
+        vtab->end_message         = faketdi_end_message;    
+        vtab->read_struct_element = faketdi_read_struct_element;
+
+        vtab->vtab = faketdi->vtab;      /* chain parent vtab to override vtab */
+        cache_insert(vtab->hashkey, vtab, FALSE);
+    } 
+
+    CU_ASSERT_EQUAL_FATAL(vtab->class_id, CLASS_ID);
+
+    faketdi->vtab = vtab;  /* set override vtab */
+
+    faketdi->impl = (objmask*) 
+        new_fake_tdi_impl(vfobj, static_type, data); /* create 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;
+}
+
+
+/* = = = = = = = = = = 
+ * TDO IMPLEMENTATION
+ * = = = = = = = = = = 
+ */
+
+/**
+ * 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, closed;
+    etch_message* xmessage;  /* reference */
+    etch_hashtable* fakeout; /* fake output sink */  
+
+} fake_tdo_impl;
+
+
+/**
+ * destroy_fake_tdo_impl
+ * memory cleanup handler for fake_tdo_impl
+ * we do not destroy the message and underlying struct, this is a reference. 
+ */
+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 */
+
+    /* destroy the fake output sink, in this case a map. we do not want the map to
+     * destroy its content because the message owns that content, and we did not
+     * in this case give the output sink copies of that content. 
+    */
+    impl->fakeout->destroy(impl->fakeout); 
+
+    etch_free(impl);    
+    return 0;
+}
+
+
+/**
+ * new_fake_tdo_impl()
+ * constructor for TDO implementation instance data
+ */
+fake_tdo_impl* new_fake_tdo_impl(etch_message* msg)
+{
+    fake_tdo_impl* data = (fake_tdo_impl*) 
+        new_object(sizeof(fake_tdo_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDO_IMPL);
+
+    data->xmessage = msg; /* reference, not owned */
+
+    data->destroy = destroy_fake_tdo_impl;    
+    return data;
+}
+
+
+/**
+ * faketdo_start_message() overrides tdo_start_message()
+ */
+etch_message* faketdo_start_message(tagged_data_output* tdo, etch_message* msg)  
+{
+    int result = 0;
+    fake_tdo_impl* data = NULL;
+    etch_destructor destructor = 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, (void**) &destructor);
+    CU_ASSERT_EQUAL_FATAL(result,0);
+    
+    result = data->destroy(NULL); /* ensure we can call instance destructor */
+    CU_ASSERT_EQUAL(result,-1);
+
+    CU_ASSERT_EQUAL(data->started,FALSE);
+    CU_ASSERT_EQUAL(data->ended,FALSE);
+
+    CU_ASSERT_EQUAL_FATAL(msg, data->xmessage);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(data->fakeout); 
+
+    data->started = TRUE;    
+    return msg;
+}
+
+
+/**
+ * faketdo_write_struct_element() overrides tdo_write_struct_element()
+ */
+int faketdo_write_struct_element(tagged_data_output* tdo, etch_field* key, objmask* val)  
+{
+    int result = 0;
+    fake_tdo_impl*  data  = NULL;
+    etch_hashtable* fakeout = NULL;
+  	etch_hashitem   hashbucket;
+    etch_hashitem*  myentry = &hashbucket;
+  
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);  /* validate parameters */
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(val);
+
+    result = is_good_field(key);
+    CU_ASSERT_EQUAL_FATAL(result,TRUE);
+
+    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);
+
+    fakeout = data->fakeout;
+    CU_ASSERT_PTR_NOT_NULL_FATAL(fakeout);  
+    CU_ASSERT_PTR_NOT_NULL_FATAL(fakeout->realtable);  
+
+    CU_ASSERT_EQUAL(data->started,TRUE);
+    CU_ASSERT_EQUAL(data->ended, FALSE);
+    CU_ASSERT_EQUAL(data->closed,FALSE);
+
+    /* do the write to the fake output */ 
+    result = fakeout->vtab->insert(fakeout->realtable, key, HASHSIZE_FIELD, val, 0, 0, 0);
+    CU_ASSERT_EQUAL_FATAL(result,0);
+
+    /* verify the write by accessing the item just written */
+    result = fakeout->vtab->find(fakeout->realtable, key, HASHSIZE_FIELD, 0, &myentry);
+    CU_ASSERT_EQUAL_FATAL(result,0);
+    CU_ASSERT_EQUAL_FATAL(myentry->key, (void*)key);
+    CU_ASSERT_EQUAL_FATAL(myentry->value, val);
+    
+	return 0;
+}
+
+
+/**
+ * faketdo_end_message() overrides tdo_end_message()
+ */
+etch_message* faketdo_end_message(tagged_data_output* tdo, etch_message* msg)
+{
+    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->xmessage); 
+ 
+    CU_ASSERT_EQUAL(data->started,TRUE);
+    CU_ASSERT_EQUAL(data->ended,FALSE);
+    CU_ASSERT_EQUAL(data->closed,FALSE);
+    CU_ASSERT_EQUAL(data->xmessage, msg);
+			
+	data->ended = TRUE;
+    return msg;
+}
+
+
+/**
+ * faketdo_close() ala java test
+ */
+void faketdo_close(tagged_data_output* tdo)  
+{
+    tdo->destroy(tdo); 
+}
+
+
+/**
+ * new_fake_tdo()
+ * constructor for TDO implementation  
+ */
+tagged_data_output* new_fake_tdo(etch_message* msg)  
+{
+    tagged_data_output* faketdo = NULL;
+    i_tagged_data_output*  vtab = NULL;
+    fake_tdo_impl* impl = NULL;
+    const unsigned short CLASS_ID = CLASSID_FAKETDO;
+    CU_ASSERT_PTR_NOT_NULL_FATAL(msg);
+
+    faketdo = new_tagged_data_output();     
+
+    vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+    if(!vtab)  
+    {   
+        vtab = new_vtable(faketdo->vtab, sizeof(i_tagged_data_output), CLASS_ID);
+
+        /* override three i_tagged_data_output methods */
+        vtab->start_message        = faketdo_start_message;
+        vtab->end_message          = faketdo_end_message;
+        vtab->write_struct_element = faketdo_write_struct_element;
+
+        vtab->vtab = faketdo->vtab;       /* chain parent vtab */
+    
+        cache_insert(vtab->hashkey, vtab, FALSE);
+    } 
+
+    CU_ASSERT_EQUAL_FATAL(vtab->class_id, CLASS_ID);
+
+    faketdo->vtab = vtab;  /* set override vtab */
+
+    impl = new_fake_tdo_impl(msg);  /* construct TDO instance data */
+    CU_ASSERT_PTR_NOT_NULL_FATAL(impl); 
+            
+    impl->fakeout = new_hashtable(16); /* instantiate fake output target */
+    CU_ASSERT_PTR_NOT_NULL_FATAL(impl->fakeout);
+    impl->fakeout->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+
+    faketdo->impl = (objmask*) impl;
+
+    return faketdo; 
+}
+
+#endif /* #if(0) */
+
+
+/* = = = = = = = = = = 
+ * TEST DATA  
+ * = = = = = = = = = = 
+ */
+
+/* 
+ * testdata_clear_handler()
+ * memory callback on testdata clear
+ */
+int testdata_clear_handler (etch_field* key, objmask* value)  
+{
+    return TRUE; /* indicate free handled */
+}
+
+/* 
+ * new_testitems()
+ * instantiate and return types and fields used in tests
+ */
+testitems* new_testitems()
+{
+    testitems* items = etch_malloc(sizeof(struct testitems), 0);
+    items->mt1 = new_type(L"mt1");
+    items->mt2 = new_type(L"mt2");
+    items->rmt = new_type(L"rmt");
+    items->mf1 = new_field(L"x");
+    items->mf2 = new_field(L"y");
+    items->mf3 = new_field(L"s1");
+    items->mf4 = new_field(L"s2");
+
+    items->s1 = new_string(L"string1", ETCH_ENCODING_UTF16);
+    items->s2 = new_string(L"string2", ETCH_ENCODING_UTF16);
+    items->n1 = new_int32(1);
+    items->n2 = new_int32(2);
+    items->l1 = new_int64(12345);
+    items->l2 = new_int64(54321);
+
+    items->mf_bool_d0_1  = new_field(L"f1");
+    items->mf_bool_d0_2  = new_field(L"f2");
+    items->mf_bool_d1_1  = new_field(L"f3");
+    items->mf_bool_d1_2  = new_field(L"f4");
+
+    items->mf_int32_d0_1 = new_field(L"f5");
+    items->mf_int32_d0_2 = new_field(L"f6");
+    items->mf_int32_d1_1 = new_field(L"f7");
+    items->mf_int32_d1_2 = new_field(L"f8");
+
+    items->mf_str_d0_1   = new_field(L"f9");
+    items->mf_str_d0_2   = new_field(L"fa");
+    items->mf_str_d1_1   = new_field(L"fb");
+    items->mf_str_d1_2   = new_field(L"fc");
+
+    items->mf_message_id  = new_field(L"_messageId"); 
+    items->mf_in_reply_to = new_field(L"_inReplyTo"); 
+ 
+    etchtype_put_validator(items->mt1, clone_field(items->mf_message_id), (objmask*) etchvtor_int64_get(0));
+    etchtype_put_validator(items->mt1, clone_field(items->mf1), (objmask*) etchvtor_int32_get(0));
+    etchtype_put_validator(items->mt1, clone_field(items->mf2), (objmask*) etchvtor_int32_get(0));
+    etchtype_put_validator(items->mt1, clone_field(items->mf3), (objmask*) etchvtor_string_get(0));
+    etchtype_put_validator(items->mt1, clone_field(items->mf4), (objmask*) etchvtor_string_get(0));
+
+    etchtype_put_validator(items->rmt, clone_field(items->mf_message_id), (objmask*) etchvtor_int64_get(0));
+    etchtype_put_validator(items->rmt, clone_field(items->mf_in_reply_to),(objmask*) etchvtor_int64_get(0));
+
+    #if(0)
+    etchtype_put_validator(items->mt1, items->mf_bool_d0_1->clone(items->mf_bool_d0_1), (objmask*) etchvtor_boolean_get(0));
+    etchtype_put_validator(items->mt1, items->mf_bool_d0_2->clone(items->mf_bool_d0_2), (objmask*) etchvtor_boolean_get(0));
+
+    etchtype_put_validator(items->mt1, items->mf_bool_d1_1->clone(items->mf_bool_d1_1), (objmask*) etchvtor_boolean_get(1));
+    etchtype_put_validator(items->mt1, items->mf_bool_d1_2->clone(items->mf_bool_d1_2), (objmask*) etchvtor_boolean_get(1));
+
+    etchtype_put_validator(items->mt1, items->mf_int32_d0_1->clone(items->mf_int32_d0_1), (objmask*) etchvtor_int32_get(0));
+    etchtype_put_validator(items->mt1, items->mf_int32_d0_2->clone(items->mf_int32_d0_2), (objmask*) etchvtor_int32_get(0));
+
+    etchtype_put_validator(items->mt1, items->mf_int32_d1_1->clone(items->mf_int32_d1_1), (objmask*) etchvtor_int32_get(1));
+    etchtype_put_validator(items->mt1, items->mf_int32_d1_2->clone(items->mf_int32_d1_2), (objmask*) etchvtor_int32_get(1));
+
+    etchtype_put_validator(items->mt1, items->mf_str_d0_1->clone(items->mf_str_d0_1), (objmask*) etchvtor_string_get(0));
+    etchtype_put_validator(items->mt1, items->mf_str_d0_2->clone(items->mf_str_d0_2), (objmask*) etchvtor_string_get(0));
+
+    etchtype_put_validator(items->mt1, items->mf_str_d1_1->clone(items->mf_str_d1_1), (objmask*) etchvtor_string_get(1));
+    etchtype_put_validator(items->mt1, items->mf_str_d1_2->clone(items->mf_str_d1_2), (objmask*) etchvtor_string_get(1));
+    #endif
+
+    items->items = new_hashtable(0);
+    items->items->is_readonly_keys   = FALSE;
+    items->items->is_readonly_values = FALSE; 
+    items->items->is_tracked_memory  = TRUE;
+    items->items->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+    items->items->freehook = testdata_clear_handler;
+    return items;
+}
+
+/* 
+ * destroy_testitems()
+ * free memory for test data
+ */
+void destroy_testitems(testitems* items)
+{
+    items->mt1->destroy(items->mt1);
+    items->mt2->destroy(items->mt2);
+    items->rmt->destroy(items->rmt);
+    items->mf1->destroy(items->mf1);
+    items->mf2->destroy(items->mf2); 
+    items->mf3->destroy(items->mf3);
+    items->mf4->destroy(items->mf4);
+
+    items->s1->destroy(items->s1); 
+    items->s2->destroy(items->s2); 
+    items->n1->destroy(items->n1); 
+    items->n2->destroy(items->n2); 
+    items->l1->destroy(items->l1); 
+    items->l2->destroy(items->l2); 
+
+    items->mf_message_id->destroy (items->mf_message_id); 
+    items->mf_in_reply_to->destroy(items->mf_in_reply_to);
+
+    items->mf_bool_d0_1->destroy(items->mf_bool_d0_1);   
+    items->mf_bool_d0_2->destroy(items->mf_bool_d0_2);   
+    items->mf_bool_d1_1->destroy(items->mf_bool_d1_1);  
+    items->mf_bool_d1_2->destroy(items->mf_bool_d1_2);   
+
+    items->mf_int32_d0_1->destroy(items->mf_int32_d0_1); 
+    items->mf_int32_d0_2->destroy(items->mf_int32_d0_2);  
+    items->mf_int32_d1_1->destroy(items->mf_int32_d1_1);  
+    items->mf_int32_d1_2->destroy(items->mf_int32_d1_2);  
+
+    items->mf_str_d0_1->destroy(items->mf_str_d0_1);    
+    items->mf_str_d0_2->destroy(items->mf_str_d0_2);    
+    items->mf_str_d1_1->destroy(items->mf_str_d1_1);    
+    items->mf_str_d1_2->destroy(items->mf_str_d1_2);   
+
+    etchvtor_clear_cache(); /* free all cached validators */
+
+    destroy_hashtable(items->items, FALSE, FALSE);
+
+    etch_free(items);   
+}
+
+
+/* 
+ * compare_lists()
+ * compares specified message content with content of some other map.
+ * returns boolean indicating equal or not.
+ */
+int compare_lists(etch_message* msg, etch_hashtable* otherdata)  
+{
+    int thiscount = 0, fakecount = 0, result = 0, i = 0, eqcount = 0;
+    etch_structvalue* svx = NULL;
+    etch_iterator  iterator;
+  	etch_hashitem  hashbucket;
+    etch_hashitem* sventry = &hashbucket;
+
+    svx = msg->sv;
+    CU_ASSERT_PTR_NOT_NULL_FATAL(svx);  
+    CU_ASSERT_PTR_NOT_NULL_FATAL(svx->items);
+
+    thiscount = svx->items->vtab->count(svx->items->realtable,0,0); 
+    fakecount = otherdata ->vtab->count(otherdata->realtable,0,0); 
+    CU_ASSERT_EQUAL(fakecount, thiscount);
+    if (fakecount != thiscount) return FALSE;
+
+    set_iterator(&iterator, otherdata, &otherdata->iterable);
+
+    while(iterator.vtab->has_next(&iterator) && result == 0)
+    {
+        result = svx->items->vtab->findh
+            (svx->items->realtable, ((objmask*)iterator.current_key)->hashkey, 
+                otherdata, &sventry);
+        CU_ASSERT_EQUAL(iterator.current_value, sventry->value);
+
+        iterator.vtab->next(&iterator);
+    }
+
+    return TRUE;
+}
+
+
+/* 
+ * hashtable_clear_handler()
+ * override callback from hashtable during clear()
+ */
+int hashtable_clear_handler (void* key, void* value)  
+{
+    /* prior to calling clear() on any hashtable htab, set: 
+     *    htab.callback = hashtable_clear_handler; 
+     * and the hashtable will call back here for each item in the table,
+     * replacing any such handler installed. return FALSE to have the hashtable 
+     * proceed as it would with a handler. save off the existing handler and call 
+     * it, to proceed as if you had not replaced the handler. return TRUE to  
+     * indicate you handled the free and the hashtable should not attempt to free 
+     * memory for key and/or value, if in fact it would have done so otherwise. 
+     * note that structs always set such a callback on their backing store
+     * in order to free key and value allocations.
+     */
+    return FALSE;
+}
+
+
+/* = = = = = = = = = = 
+ * TESTS   
+ * = = = = = = = = = = 
+ */
+
+/* 
+ * run_iterator_test()
+ * iterate over test collection, inserting pairs to specified message.
+ * iterate over message, veriying that content matches test collection.
+ */
+int run_iterator_test(etch_message* msg, testitems* data)
+{
+    etch_iterator* iterator  = NULL;
+    int msgcount = 0, testcount = 0, result = 0;
+
+    iterator = new_iterator(data->items, &data->items->iterable);
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(iterator);
+    CU_ASSERT_EQUAL(iterator->ordinal,1);
+
+    while(iterator->has_next(iterator))
+    {
+        testcount++;
+
+        result = message_put(msg, iterator->current_key, iterator->current_value);
+
+        CU_ASSERT_EQUAL(result,0);
+        
+        iterator->next(iterator);
+    }
+    
+    set_iterator(iterator, msg->sv->items, &msg->sv->items->iterable);
+    CU_ASSERT_EQUAL(iterator->ordinal,1);
+
+    while(iterator->has_next(iterator))
+    {
+        msgcount++;
+        iterator->next(iterator);
+    }
+
+    CU_ASSERT_EQUAL(testcount, msgcount);
+    iterator->destroy(iterator);
+    return msgcount;
+}
+
+
+/* 
+ * iterator_test()
+ */
+void iterator_test(void)
+{
+    int result = 0;
+    testitems* data = NULL;
+    etch_hashtable* map = NULL;
+    etch_message*   msg = NULL;
+    etch_value_factory* vf = NULL; 
+
+    etch_string *sval1 = NULL, *sval2 = NULL;
+    etch_int32  *ival1 = NULL, *ival2 = NULL;
+    etch_field  *skey1 = NULL, *skey2 = NULL;
+    etch_field  *ikey1 = NULL, *ikey2 = NULL;
+
+    data = new_testitems();
+    map  = data->items;
+
+    /* a message owns all its memory except the value factory and type, so we'll
+     * make copies of all the testdata keys and values to be inserted into it */
+    ikey1 = data->mf1->clone(data->mf1); ikey2 = data->mf2->clone(data->mf2);
+    skey1 = data->mf3->clone(data->mf3); skey2 = data->mf4->clone(data->mf4);
+
+    sval1 = data->s1->clone(data->s1);  sval2 = data->s2->clone(data->s2);
+    ival1 = data->n1->clone(data->n1);  ival2 = data->n2->clone(data->n2);
+
+    /* insert cloned fields and values to the test input collection */
+    map->vtab->inserth(map->realtable, ikey1, ival1, map, 0);
+    map->vtab->inserth(map->realtable, ikey2, ival2, map, 0);
+    map->vtab->inserth(map->realtable, skey1, sval1, map, 0);
+    map->vtab->inserth(map->realtable, skey2, sval2, map, 0);
+   
+    vf = new_fake_value_factory(data);
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(vf);
+    CU_ASSERT_FALSE_FATAL(is_exception(vf));
+
+    /* a message owns its memory except vf and type, so we give it global type */
+    msg = new_message (data->mt1, 0, vf);  
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(msg);
+    CU_ASSERT_FALSE_FATAL(is_exception(msg));
+    
+    result = run_iterator_test(msg, data);
+    CU_ASSERT_EQUAL(result,4);  /* 4 items in message */
+
+    msg->destroy(msg);
+    vf ->destroy(vf); 
+  
+    destroy_testitems(data);
+
+    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 */   
+}
+
+/* 
+ * reply_test
+ * test message.reply 
+ */
+void reply_test(void)
+{
+    int result = 0;
+    etch_message* msg = NULL; 
+    etch_message* newmsg = NULL;
+    etch_type* replytype = NULL;
+    etch_value_factory* vf = NULL;
+    etch_int64* id_original = NULL;
+    etch_int64* id_replied_to = NULL;
+    int64 val_origid = 0, val_replyid = 0;
+    testitems* data = new_testitems();
+
+    id_original = data->l1;
+
+    vf = new_fake_value_factory(data);
+    
+    msg = new_message (data->mt1, 0, vf);  /* message owns neither arg */
+
+    result = message_set_id(msg, id_original->clone(id_original));  
+    CU_ASSERT_EQUAL_FATAL(result,0);
+
+    replytype = data->rmt;    /* again, a message does not own a type */ 
+
+    newmsg = message_reply (msg, replytype);
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(newmsg);
+    CU_ASSERT_FALSE_FATAL(is_exception(newmsg));
+
+    result = is_equal_types(data->rmt, newmsg->sv->struct_type);
+    CU_ASSERT_TRUE(result);
+    CU_ASSERT_EQUAL(vf, newmsg->vf);
+
+    id_replied_to = message_get_in_reply_to(newmsg);
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(id_replied_to);
+    CU_ASSERT_FALSE_FATAL(is_exception(id_replied_to));
+
+    val_origid  = id_original->value;
+    val_replyid = id_replied_to->value;   
+    CU_ASSERT_EQUAL(val_origid, val_replyid);
+
+    newmsg->destroy(newmsg);
+    msg->destroy(msg);  
+    vf->destroy(vf);   
+    destroy_testitems(data);
+
+    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_exception_test
+ * execute an individual exception test 
+ */
+void run_exception_test(const int whichtest)
+{
+    int result = 0;
+    tagged_data_input* tdi = NULL;
+    etch_message* msg = NULL;
+    etchexception* excp = NULL;
+    etch_type* static_type = NULL;
+    etch_value_factory* vf = NULL;
+    testitems* data = new_testitems();
+
+    /* global marker asks components to throw exceptions */
+    g_which_exception_test = whichtest; 
+    static_type = new_type(L"type1");
+    vf = new_fake_value_factory(data);
+
+    #if(0)
+    /* create a bogus TDI inheriting from tagged data input */
+    tdi = new_fake_tdi(static_type, vf, NULL);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+
+    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 on tdi\n", tdi->result->exception->excptext);          
+            #endif   
+        }
+    }
+
+    msg = message_read(NULL); /* pass NULL for TDI */
+    CU_ASSERT_PTR_NOT_NULL_FATAL(msg);
+    CU_ASSERT_TRUE(is_exception(msg));
+    excp = get_exception(msg);
+    CU_ASSERT_EQUAL(excp->excptype, EXCPTYPE_ILLEGALARG);
+    #if IS_DEBUG_CONSOLE 
+    wprintf(L"\ncaught %s exception on message\n", msg->result->exception->excptext);          
+    #endif   
+
+    /* free TDI */
+    faketdi_close(tdi);
+
+    /* free struct, it is just a shell with no content other than the exception */
+    msg->destroy(msg);
+
+    #endif
+
+    vf->destroy(vf);
+
+    destroy_testitems(data);
+    destroy_type(static_type);
+    g_which_exception_test = 0;
+
+    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 */   
+}
+
+
+/**
+ * exception_test
+ * all exception tests   
+ */
+void exception_test(void)
+{
+    run_exception_test(EXCPTEST_UNCHECKED_STATICTEXT);
+    run_exception_test(EXCPTEST_CHECKED_COPYTEXT);
+    run_exception_test(EXCPTEST_CHECKED_STATICTEXT);
+    g_which_exception_test = 0;
+}
+
+
+
+/**
+ * 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_message", init_suite, clean_suite);
+    CU_set_output_filename("../test_message");
+    etch_watch_id = 0; 
+
+    CU_add_test(pSuite, "test iterator over message", iterator_test);  
+    CU_add_test(pSuite, "test msg exceptions", exception_test); 
+    CU_add_test(pSuite, "message reply test", reply_test); 
+
+    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(); 
+}
+

Added: incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_structvalue.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_structvalue.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_structvalue.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/test/message/test_structvalue.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,1157 @@
+/* $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_structvalue.c -- test etch_structvalue object
+ */
+
+#include "apr_time.h" /* some apr must be included first */
+#include <tchar.h>
+#include <stdio.h>
+#include <conio.h>
+
+#include "cunit.h"
+#include "basic.h"
+#include "automated.h"
+
+#include "etch_connection.h"
+#include "etch_global.h"
+#include "etch_encoding.h"
+#include "etchthread.h"
+#include "etchlog.h"
+#include "etch_structval.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";
+
+
+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;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - 
+ * local declarations 
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */   
+
+/* - - - - - - - - - - - - - - - - - - - - - - 
+ * local setup and teardown
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+int this_setup()
+{
+    etch_apr_mempool = g_apr_mempool;
+    return 0;
+}
+
+int this_teardown()
+{    
+    return 0;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - 
+ * individual setup and teardown
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+int this_test_setup()
+{
+    int result = -1;
+
+    do {
+        result = 0;
+
+    } while(0);
+
+    return result;
+}
+
+int this_test_teardown()
+{    
+
+    return 0;
+}
+
+
+etch_type *mt1, *mt2;
+
+etch_field *mf_bool_d0_1;   
+etch_field *mf_bool_d0_2;   
+etch_field *mf_bool_d1_1;   
+etch_field *mf_bool_d1_2;  
+
+etch_field *mf_int32_d0_1;  
+etch_field *mf_int32_d0_2;  
+etch_field *mf_int32_d1_1;  
+etch_field *mf_int32_d1_2;  
+
+etch_field *mf_str_d0_1;   
+etch_field *mf_str_d0_2;   
+etch_field *mf_str_d1_1;  
+etch_field *mf_str_d1_2;  
+
+etch_hashtable* testdata;  
+
+int  g_bytes_allocated, g_which_exception_test, g_is_automated_test;
+wchar_t* local_excp_text = L"global text";
+
+#if(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 CLASSID_FAKETDI 0xf6
+#define CLASSID_FAKETDO 0xf7
+#endif
+
+#define EXCPTEST_UNCHECKED_STATICTEXT 1
+#define EXCPTEST_CHECKED_COPYTEXT     2
+#define EXCPTEST_CHECKED_STATICTEXT   3
+
+
+#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 i_tagged_data_input* 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;
+
+    etch_type* tdi_type;
+    byte started, done, ended, is_owned_struct;
+    etch_structvalue* xstruct;
+    etch_iterator iterator;  
+
+} 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 i_tagged_data_output* 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, closed;
+    etch_structvalue* xstruct;
+    etch_hashtable* fakeout; /* fake output stream */  
+
+} fake_tdo_impl;
+
+
+/**
+ * fake_tdi_impl_destroy_handler
+ * memory cleanup handler for fake_tdi_impl
+ */
+int destroy_fake_tdi_impl(fake_tdi_impl* impl)
+{
+    etch_destructor destroy = NULL;
+    int result = verify_object((objmask*)impl, OBJTYPE_FAKETDI_IMPL, CLASSID_FAKETDI_IMPL, NULL);
+    if (result == -1) return -1; /* object passed was not expected object */
+
+    /* type is a refrence, it does  not belong to the tdi. struct is created  
+     * in the tdi, but ownership is assumed to transfer to the caller when  
+     * structvalue_read returns. if this is not the case then is_owned_struct 
+     * must have been set elsewhere
+     */
+    if (impl->is_owned_struct) 
+    {   /* not the default case, see comment above */
+        CU_ASSERT_PTR_NOT_NULL_FATAL(impl->xstruct);  
+        impl->xstruct->destroy(impl->xstruct);  
+    }
+ 
+    etch_free(impl); 
+   
+    return 0;
+}
+
+
+/**
+ * fake_tdo_impl_destroy_handler
+ * 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 */
+
+    /* destroy the fake output receptor, in this case a hashtable. we don't want the
+     * hashtable to destroy its content because its content is references to someone
+     * else's content, in this case the host struct value. 
+    */
+    destroy_hashtable(impl->fakeout, FALSE, FALSE); 
+
+    /* we do not destroy the struct value, this is merely a reference */ 
+    /* impl->xstruct->vtab->destroy(impl->xstruct); */
+
+    etch_free(impl);    
+    return 0;
+}
+
+
+/**
+ * new_fake_tdi_impl()
+ * constructor for TDI implementation instance data
+ */
+fake_tdi_impl* new_fake_tdi_impl(etch_type* static_type)
+{
+   fake_tdi_impl* data = (fake_tdi_impl*) 
+        new_object(sizeof(fake_tdi_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDI_IMPL);
+
+   /* assign type - this is a reference, it is not our memory */
+    data->tdi_type = static_type;       
+
+    /* set destructor */
+    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(etch_structvalue* sv)
+{
+    fake_tdo_impl* data = (fake_tdo_impl*) 
+         new_object(sizeof(fake_tdo_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDO_IMPL);
+
+    data->xstruct  = sv;
+
+    /* set destructor */
+    data->destroy = destroy_fake_tdo_impl;    
+    return data;
+}
+
+
+enum objtyp ETCHTYPE_VTABLE_FAKETDI = 0xf0;
+enum objtyp ETCHTYPE_VTABLE_FAKETDO = 0xf1;
+
+
+/**
+ * faketdi_start_struct() overrides tdi_start_struct()
+ */
+etch_structvalue* faketdi_start_struct(tagged_data_input* tdi) 
+{
+    int result = 0;
+    fake_tdi_impl* data = NULL;
+    etch_structvalue* newstructval = NULL;
+    const int READONLY = TRUE, DEFSIZE = 0, DEFDELTA = 0;
+    
+    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 into 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;
+
+    /* create a struct to receive the elements read. a struct owns all its memory
+     * so we give it a copy of the tdi's type. ownership of the struct is assumed
+     * to be passed to the caller when this function returns. if instead it was
+     * desired that the tdi own the struct, data.is_owned_struct would have to
+     * be set subsequently elsewhere. 
+     * note that sv no longer owns type, so we no longer clone the type
+     */
+    newstructval = new_structvalue (data->tdi_type, 0);  
+
+    data->xstruct = newstructval;
+    CU_ASSERT_PTR_NOT_NULL_FATAL(data->xstruct);
+
+    /* establish iterator on the fake input data */
+    set_iterator(&data->iterator, testdata, &testdata->iterable);
+
+    /* while an empty dataset is valid in the general case, 
+     * this test assumes that input is non-empty */
+    result = data->iterator.vtab->has_next(&data->iterator); 
+    CU_ASSERT_EQUAL(result,TRUE);
+
+    return data->xstruct;
+}
+
+
+/**
+ * faketdo_start_struct() overrides tdo_start_struct()
+ */
+int faketdo_start_struct(tagged_data_output* tdo, etch_structvalue* structval)  
+{
+    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 instance data destructor */
+    CU_ASSERT_EQUAL(result,-1);
+
+    CU_ASSERT_EQUAL(data->started,FALSE);
+    CU_ASSERT_EQUAL(data->ended,FALSE);
+
+    CU_ASSERT_EQUAL_FATAL(structval, data->xstruct);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(data->fakeout); 
+
+    data->started = TRUE;    
+    return 0;
+}
+
+
+
+/**
+ * faketdo_write_struct_element() overrides tdo_write_struct_element()
+ */
+int faketdo_write_struct_element(tagged_data_output* tdo, etch_field* key, objmask* val)  
+{
+    int result = 0;
+    fake_tdo_impl*  data  = NULL;
+    etch_hashtable* fakeout = NULL;
+  	etch_hashitem   hashbucket;
+    etch_hashitem*  myentry = &hashbucket;
+  
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);  /* validate parameters */
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(val);
+
+    result = is_good_field(key);
+    CU_ASSERT_EQUAL_FATAL(result,TRUE);
+
+    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);
+
+    fakeout = data->fakeout;
+    CU_ASSERT_PTR_NOT_NULL_FATAL(fakeout);  
+    CU_ASSERT_PTR_NOT_NULL_FATAL(fakeout->realtable);  
+
+    CU_ASSERT_EQUAL(data->started,TRUE);
+    CU_ASSERT_EQUAL(data->ended, FALSE);
+    CU_ASSERT_EQUAL(data->closed,FALSE);
+
+    /* do the write to the fake output */ 
+    result = fakeout->vtab->insert(fakeout->realtable, key, HASHSIZE_FIELD, val, 0, 0, 0);
+    CU_ASSERT_EQUAL_FATAL(result,0);
+
+    /* verify the write by accessing the item just written */
+    result = fakeout->vtab->find(fakeout->realtable, key, HASHSIZE_FIELD, 0, &myentry);
+    CU_ASSERT_EQUAL_FATAL(result,0);
+    CU_ASSERT_EQUAL_FATAL(myentry->key, (void*)key);
+    CU_ASSERT_EQUAL_FATAL(myentry->value, val);
+    
+	return 0;
+}
+
+ 
+/**
+ * faketdi_read_struct_element() overrides tdi_read_struct_element()
+ */
+int faketdi_read_struct_element(tagged_data_input* tdi, etch_struct_element* out_se)   
+{
+    int result = 0;
+    fake_tdi_impl* data = NULL;
+    etch_iterator* iterator = NULL;
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);  
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(out_se);
+
+    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);
+    iterator = &data->iterator;
+
+	if (iterator->has_next(iterator)) 
+	{   
+        CU_ASSERT_EQUAL_FATAL(result,0);
+        CU_ASSERT_PTR_NOT_NULL_FATAL(iterator->current_key);
+        CU_ASSERT_PTR_NOT_NULL_FATAL(iterator->current_value);
+
+    	out_se->key   = (etch_field*) iterator->current_key;
+        out_se->value = (objmask*)    iterator->current_value;
+
+        iterator->next(iterator);
+		return TRUE;
+	}
+	
+	data->done = TRUE;
+	return FALSE;
+}
+
+
+/**
+ * faketdi_end_struct() overrides tdi_end_struct()
+ */
+int faketdi_end_struct(tagged_data_input* tdi, etch_structvalue* sv)   
+{
+    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->xstruct); 
+ 
+    CU_ASSERT_EQUAL(data->started,TRUE);
+    CU_ASSERT_EQUAL(data->done,TRUE);
+    CU_ASSERT_EQUAL(data->ended,FALSE);
+    CU_ASSERT_EQUAL(data->xstruct,sv);
+			
+	data->ended = TRUE;
+    return 0;
+}
+
+
+/**
+ * faketdo_end_struct() overrides tdo_end_struct()
+ */
+int faketdo_end_struct(tagged_data_output* tdo, struct etch_structvalue* sv) 
+{
+    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->xstruct); 
+ 
+    CU_ASSERT_EQUAL(data->started,TRUE);
+    CU_ASSERT_EQUAL(data->ended,FALSE);
+    CU_ASSERT_EQUAL(data->closed,FALSE);
+    CU_ASSERT_EQUAL(data->xstruct, sv);
+			
+	data->ended = TRUE;
+    return 0;
+}
+
+
+/**
+ * faketdi_close() ala 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);  
+}
+
+
+/**
+ * faketdo_close() ala java test
+ */
+void faketdo_close(tagged_data_output* tdo)  
+{
+    tdo->destroy(tdo);  
+}
+
+
+/**
+ * new_fake_tdi()
+ * constructor for TDI implementation  
+ */
+tagged_data_input* new_fake_tdi(etch_type* static_type)  
+{
+    tagged_data_input* faketdi = NULL;
+    i_tagged_data_input* vtab  = NULL;
+    const unsigned short CLASS_ID = CLASSID_FAKETDI;
+    CU_ASSERT_EQUAL_FATAL(is_good_type(static_type),TRUE);
+   
+    faketdi = new_tagged_data_input();
+
+    vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+    if(!vtab)  
+    {    
+        vtab = new_vtable(faketdi->vtab, sizeof(i_tagged_data_input), CLASS_ID);
+
+        /* override three i_tagged_data_input methods */
+        vtab->start_struct = faketdi_start_struct;  
+        vtab->end_struct   = faketdi_end_struct;    
+        vtab->read_struct_element = faketdi_read_struct_element;
+
+        vtab->vtab = faketdi->vtab;      /* chain parent vtab to override vtab */
+        cache_insert(vtab->hashkey, vtab, FALSE);
+    } 
+
+    CU_ASSERT_EQUAL_FATAL(vtab->class_id, CLASS_ID);
+
+    faketdi->vtab = vtab;  /* set override vtab */
+
+    faketdi->impl = (objmask*) 
+        new_fake_tdi_impl(static_type); /* create 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(etch_structvalue* sv)  
+{
+    tagged_data_output* faketdo = NULL;
+    i_tagged_data_output*  vtab = NULL;
+    fake_tdo_impl* impl = NULL;
+    const unsigned short CLASS_ID = CLASSID_FAKETDO;
+    CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+    faketdo = new_tagged_data_output();     
+
+    vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+    if(!vtab)  
+    {   
+        vtab = new_vtable(faketdo->vtab, sizeof(i_tagged_data_output), CLASS_ID);
+
+        /* override three i_tagged_data_output methods */
+        vtab->start_struct = faketdo_start_struct;
+        vtab->end_struct   = faketdo_end_struct;
+        vtab->write_struct_element = faketdo_write_struct_element;
+
+        vtab->vtab = faketdo->vtab;  /* chain parent vtab to override vtab */
+    
+        cache_insert(vtab->hashkey, vtab, FALSE);
+    } 
+
+    CU_ASSERT_EQUAL_FATAL(vtab->class_id, CLASS_ID);
+
+    faketdo->vtab = vtab;         /* set override vtab */
+
+    impl = new_fake_tdo_impl(sv); /* set TDO instance data */
+    CU_ASSERT_PTR_NOT_NULL_FATAL(impl); 
+                  
+    impl->fakeout = new_hashtable(16); 
+    CU_ASSERT_PTR_NOT_NULL_FATAL(impl->fakeout);
+    impl->fakeout->is_readonly_keys  = impl->fakeout->is_readonly_values = FALSE;
+    impl->fakeout->is_tracked_memory = TRUE;
+    impl->fakeout->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+
+    faketdo->impl = (objmask*) impl;
+
+    return faketdo;
+}
+
+#endif /* #if(0) */
+
+
+/* 
+ * load_testdata_string()
+ * load testdata map with some ETCH_STRING objects
+ */
+int load_testdata_string()   
+{
+    int i = 0, numitems = 4, result = 0;
+    wchar_t* testval    = NULL;
+    etch_field* newkey  = NULL;
+    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 };
+    const size_t bytelen = (wcslen(str0) + 1) * sizeof(wchar_t);
+
+    for(; i < numitems; i++)
+    {
+        newkey  = new_field(strings[i]);      /* testdata map CAN free keys */
+        newobj  = new_string(strings[i], ETCH_ENCODING_UTF16);
+        result  = testdata->vtab->inserth(testdata->realtable, newkey, newobj, 0, 0);
+    }
+
+    return numitems;
+}
+
+
+/* 
+ * load_testdata_int()
+ * load testdata array with some ETCH_INT objects
+ */
+int load_testdata_int()   
+{
+    const int numitems = 4;
+    int i = 0, result = 0;
+    etch_field* newkey = NULL;
+    etch_int32* newobj = NULL;
+    wchar_t* str0 = L"fld1", *str1 = L"fld2", *str2 = L"fld3", *str3 = L"fld4";
+    wchar_t* keys[4] = { str0, str1, str2, str3 };
+    int ints[4] = { 1, 2, 3, 4 };
+
+    for(; i < numitems; i++)
+    {
+        newkey = new_field(keys[i]);
+        newobj = new_int32(ints[i]);  /* testdata table can free both keys and values */
+        result = testdata->vtab->inserth(testdata->realtable, newkey, newobj, 0, 0);
+    }
+
+    return numitems;
+}
+
+
+/* 
+ * testdata_clear_handler()
+ * memory callback on testdata clear
+ */
+int testdata_clear_handler (etch_field* key, objmask* value)  
+{
+    key->destroy(key);
+    value->destroy(value);
+    return TRUE;
+}
+
+
+/* 
+ * new_testdata()
+ * create testdata map and load it up with data objects
+ */
+int new_testdata(const int datatype)   
+{
+    int count = 0;
+    #if IS_DEBUG_CONSOLE
+    etch_iterator iterator;
+    #endif
+    g_which_exception_test = 0;
+    testdata = new_hashtable(0); 
+    testdata->is_readonly_keys   = FALSE;
+    testdata->is_readonly_values = FALSE; 
+    testdata->is_tracked_memory  = TRUE;
+    testdata->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+    testdata->freehook = testdata_clear_handler;
+ 
+    switch(datatype)
+    { case 1: count = load_testdata_int();    break;
+      case 2: count = load_testdata_string(); break;
+      default: return -1;
+    }
+
+    #if IS_DEBUG_CONSOLE
+    printf("\n");
+    #pragma warning (disable:4313)
+    set_iterator(&iterator, testdata, &testdata->iterable);
+
+    while(iterator.vtab->has_next(&iterator))
+    {
+        printf("testdata %08x %08x\n", iterator.current_key, iterator.current_value);
+        iterator.vtab->next(&iterator);
+    }
+    printf("\n");
+    #endif
+
+    mt1 = new_type(L"one"); 
+    mt2 = new_type(L"two");
+
+    mf_bool_d0_1  = new_field(L"f1");
+    mf_bool_d0_2  = new_field(L"f2");
+    mf_bool_d1_1  = new_field(L"f3");
+    mf_bool_d1_2  = new_field(L"f4");
+
+    mf_int32_d0_1 = new_field(L"f5");
+    mf_int32_d0_2 = new_field(L"f6");
+    mf_int32_d1_1 = new_field(L"f7");
+    mf_int32_d1_2 = new_field(L"f8");
+
+    mf_str_d0_1   = new_field(L"f9");
+    mf_str_d0_2   = new_field(L"fa");
+    mf_str_d1_1   = new_field(L"fb");
+    mf_str_d1_2   = new_field(L"fc");
+
+    etchtype_put_validator(mt1, mf_bool_d0_1->clone(mf_bool_d0_1), (objmask*) etchvtor_boolean_get(0));
+    etchtype_put_validator(mt1, mf_bool_d0_2->clone(mf_bool_d0_2), (objmask*) etchvtor_boolean_get(0));
+
+    etchtype_put_validator(mt1, mf_bool_d1_1->clone(mf_bool_d1_1), (objmask*) etchvtor_boolean_get(1));
+    etchtype_put_validator(mt1, mf_bool_d1_2->clone(mf_bool_d1_2), (objmask*) etchvtor_boolean_get(1));
+
+    etchtype_put_validator(mt1, mf_int32_d0_1->clone(mf_int32_d0_1), (objmask*) etchvtor_int32_get(0));
+    etchtype_put_validator(mt1, mf_int32_d0_2->clone(mf_int32_d0_2), (objmask*) etchvtor_int32_get(0));
+
+    etchtype_put_validator(mt1, mf_int32_d1_1->clone(mf_int32_d1_1), (objmask*) etchvtor_int32_get(1));
+    etchtype_put_validator(mt1, mf_int32_d1_2->clone(mf_int32_d1_2), (objmask*) etchvtor_int32_get(1));
+
+    etchtype_put_validator(mt1, mf_str_d0_1->clone(mf_str_d0_1), (objmask*) etchvtor_string_get(0));
+    etchtype_put_validator(mt1, mf_str_d0_2->clone(mf_str_d0_2), (objmask*) etchvtor_string_get(0));
+
+    etchtype_put_validator(mt1, mf_str_d1_1->clone(mf_str_d1_1), (objmask*) etchvtor_string_get(1));
+    etchtype_put_validator(mt1, mf_str_d1_2->clone(mf_str_d1_2), (objmask*) etchvtor_string_get(1));
+ 
+    return count;
+}
+
+/* 
+ * destroy_testdata()
+ * destroy testdata map and content
+ */
+void destroy_testdata()  
+{
+    destroy_hashtable(testdata, TRUE, TRUE);
+
+    destroy_type(mt1);  
+    destroy_type(mt2);  
+
+    destroy_field(mf_bool_d0_1);
+    destroy_field(mf_bool_d0_2);
+    destroy_field(mf_bool_d1_1);
+    destroy_field(mf_bool_d1_2);
+    destroy_field(mf_int32_d0_1);
+    destroy_field(mf_int32_d0_2);
+    destroy_field(mf_int32_d1_1);
+    destroy_field(mf_int32_d1_2);
+    destroy_field(mf_str_d0_1);
+    destroy_field(mf_str_d0_2);
+    destroy_field(mf_str_d1_1);
+    destroy_field(mf_str_d1_2);
+
+    etchvtor_clear_cache(); /* free all cached validators */
+}
+
+
+/* 
+ * compare_lists()
+ * compares testdata content with specified struct content.
+ * returns boolean indicating equal or not.
+ */
+int compare_lists(etch_structvalue* svx)  
+{
+    int thiscount = 0, testcount = 0, result = 0, i = 0, eqcount = 0;
+    etch_iterator  iterator;
+  	etch_hashitem  hashbucket;
+    etch_hashitem* sventry = &hashbucket;
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(svx);  
+    CU_ASSERT_PTR_NOT_NULL_FATAL(svx->items);
+
+    thiscount = svx->items->vtab->count(svx->items->realtable,0,0); 
+    testcount = testdata->vtab->count(testdata->realtable,0,0); 
+    CU_ASSERT_EQUAL(testcount, thiscount);
+    if (testcount != thiscount) return FALSE;
+
+    set_iterator(&iterator, testdata, &testdata->iterable);
+
+    while(iterator.has_next(&iterator) && result == 0)
+    {
+        result = svx->items->vtab->findh
+            (svx->items->realtable, ((objmask*)iterator.current_key)->hashkey, testdata, &sventry);
+
+        CU_ASSERT_EQUAL(iterator.current_value, sventry->value);
+
+        iterator.next(&iterator);
+    }
+
+    return TRUE;
+}
+
+
+/* 
+ * hashtable_clear_handler()
+ * override callback from hashtable during clear()
+ */
+int hashtable_clear_handler (void* key, void* value)  
+{
+    /* prior to calling clear() on any hashtable htab, set: 
+     *    htab.callback = hashtable_clear_handler; 
+     * and the hashtable will call back here for each item in the table,
+     * replacing any such handler installed. return FALSE to have the hashtable 
+     * proceed as it would with a handler. save off the existing handler and call 
+     * it, to proceed as if you had not replaced the handler. return TRUE to  
+     * indicate you handled the free and the hashtable should not attempt to free 
+     * memory for key and/or value, if in fact it would have done so otherwise. 
+     * note that structvalue always sets such a callback on its backing store
+     * in order to free key and value allocations.
+     */
+    return FALSE;
+}
+
+/* 
+ * hashtable_do_nada_handler()
+ * override callback from hashtable during clear()
+ * this callback does nothing and returns TRUE, so no content memory is freed.
+ */
+int hashtable_do_nada_handler (void* key, void* value)  
+{
+    return TRUE;
+}
+
+
+/* 
+ * run_iterator_test
+ * test struct value iterator
+ */
+void run_iterator_test(void)
+{
+    int result = 0, count = 0;
+    etch_iterator iterator;
+    etch_int32* int_obj_1 = NULL;
+    etch_int32* int_obj_2 = NULL;
+    etch_nativearray* int_array_1 = NULL;
+    etch_nativearray* int_array_2 = NULL;
+    etch_structvalue* sv = NULL;
+    etch_field* fldkey   = NULL;
+
+    new_testdata(1); /* instantiate types and fields we use here */
+
+    /* memory for field keys and object values is given to the struct to manage. 
+     * once a key and value are "put" to the struct, we give up responsibility
+     * for freeing them. therefore when we put an object to the struct, we clone 
+     * a type to use as the key. note that when we assign the struct a type, 
+     * however, we do not relinquish ownership of the memory for that etch_type.
+     * this is because types are global to the service in the binding, 
+     * and are freed only at service shutdown.
+     */
+ 
+    int_obj_1 = new_int32(123);  
+    int_obj_2 = new_int32(234);  
+
+    int_array_1 = new_nativearray(CLASSID_ARRAY_INT32, sizeof(int), 1, 3, 0, 0);
+    int_array_2 = new_nativearray(CLASSID_ARRAY_INT32, sizeof(int), 1, 5, 0, 0);
+
+    sv = new_structvalue(mt1, 0);   
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(sv); 
+    result = is_exception(sv);
+    CU_ASSERT_EQUAL(result, FALSE); 
+    CU_ASSERT_PTR_NOT_NULL_FATAL(sv->items);
+
+    /* if structvalue has ETCH_STRUCTVAL_VALIDATE_ON_PUT compiled,
+     * these structvalue.put()s will be validated. */     
+
+    fldkey = clone_field(mf_int32_d0_1);
+    result = structvalue_put(sv, fldkey, (objmask*) int_obj_1);
+    CU_ASSERT_EQUAL(result, 0);
+
+    set_iterator(&iterator, sv->items, &sv->items->iterable);
+    CU_ASSERT_EQUAL(iterator.has_next(&iterator), TRUE);
+
+    fldkey = clone_field(mf_int32_d0_2); 
+    result = structvalue_put(sv, fldkey, (objmask*) int_obj_2);
+    CU_ASSERT_EQUAL(result, 0);
+
+    set_iterator(&iterator, sv->items, &sv->items->iterable);
+    CU_ASSERT_TRUE(iterator.has_next(&iterator));
+
+    iterator.next(&iterator);
+    CU_ASSERT_TRUE(iterator.has_next(&iterator));
+
+    iterator.next(&iterator);
+    CU_ASSERT_FALSE(iterator.has_next(&iterator));
+
+    fldkey = clone_field(mf_int32_d1_1); 
+    result = structvalue_put(sv, fldkey, (objmask*) int_array_1);
+    CU_ASSERT_EQUAL(result, 0);
+
+    fldkey = clone_field(mf_int32_d1_2); 
+    result = structvalue_put(sv, fldkey, (objmask*) int_array_2);
+    CU_ASSERT_EQUAL(result, 0);
+
+    set_iterator(&iterator, sv->items, &sv->items->iterable);
+
+    while(iterator.has_next(&iterator))
+    { count++;
+      iterator.next(&iterator);
+    }
+    CU_ASSERT_EQUAL(count,4);
+
+
+    /* free memory for struct and its instance data and content */
+    sv->destroy(sv); 
+    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_exception_test
+ *  
+ */
+void run_exception_test(const int whichtest)
+{
+    int result = 0;
+    tagged_data_input* tdi = NULL;
+    etch_structvalue*  sv  = NULL;
+    etchexception* excp    = NULL;
+    etch_type* static_type = NULL;
+    /* global marker asks components to throw exceptions */
+    g_which_exception_test = whichtest; 
+    static_type = new_type(L"type1");
+
+    #if(0)
+
+    /* create a bogus TDI inheriting from tagged data input */
+    tdi = new_fake_tdi(static_type);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+
+    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 on tdi\n", tdiobj->result->exception->excptext);          
+            #endif   
+        }
+    }
+
+    sv = structvalue_read(NULL); /* pass NULL for TDI */
+    CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+    CU_ASSERT_TRUE(is_exception(tdi));
+    excp = get_exception(sv);
+    CU_ASSERT_EQUAL(excp->excptype, EXCPTYPE_ILLEGALARG);
+    #if IS_DEBUG_CONSOLE 
+    wprintf(L"\ncaught %s exception on sv\n", svobj->result->exception->excptext);          
+    #endif   
+
+    /* free TDI */
+    faketdi_close(tdi);
+
+    /* free struct, it is just a shell with no content other than the exception */
+    sv->destroy(sv);
+
+    #endif
+
+    /* destroy the type allocated above.
+     * in most tests we would NOT do this since the struct would own it after we
+     * created the struct. however we passed a null tdi parameter above to the 
+     * method which would have created the struct. the type which would have
+     * been assigned to the struct was in the tdi that we did not pass. The tdi
+     * will not destroy its type. however note that in the real world this 
+     * situation would not occur. all types would be global, and we will always
+     * create a copy of the type for the struct. see faketdi_start_struct() for
+     * an example, note that it clones the type for the new struct.  
+     */
+    static_type->destroy(static_type);
+
+    /* destroy testdata list and content */
+    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_iterator_over_hashtable
+ */
+void test_iterator_over_hashtable(void)
+{
+    etch_iterator* iterator = NULL; 
+    int testcount = 0, thiscount = 0;
+    new_testdata(1);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(testdata);
+    testcount = testdata->vtab->count(testdata->realtable, 0, 0);
+    CU_ASSERT_NOT_EQUAL(testcount, 0);
+
+    iterator = new_iterator(testdata, &testdata->iterable);
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(iterator);
+    CU_ASSERT_EQUAL_FATAL(iterator->ordinal,1);
+    thiscount = 1;
+
+    while(iterator->vtab->has_next(iterator))
+          thiscount += (iterator->vtab->next(iterator) == 0);  
+        
+    CU_ASSERT_EQUAL(testcount, thiscount);
+
+    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 */ 
+}
+
+
+/* 
+ * 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); 
+}
+
+
+/**
+ * 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_structvalue", init_suite, clean_suite);
+    CU_set_output_filename("../test_structvalue");
+    etch_watch_id = 0; 
+
+    CU_add_test(pSuite, "test iterator over hashtable", test_iterator_over_hashtable);
+    CU_add_test(pSuite, "test structvalue iterator", run_iterator_test);
+    CU_add_test(pSuite, "test sv exceptions", exception_test_2);
+
+    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(); 
+}
+