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 [32/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/transport/test_messagizer.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/test/transport/test_messagizer.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/test/transport/test_messagizer.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/test/transport/test_messagizer.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,1857 @@
+/* $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_messagizer.c
+ */
+
+#include "apr_time.h" /* some apr must be included first */
+#include "etch_messagizer.h"
+#include "etch_defvalufact.h"
+
+#include <tchar.h>
+#include <stdio.h>
+#include <conio.h>
+
+#include "cunit.h"
+#include "basic.h"
+#include "automated.h"
+
+#include "etchthread.h"
+#include "etch_global.h"
+#include "etchmap.h"
+#include "etchlog.h"
+#include "etch_syncobj.h"
+#include "etchexcp.h"
+
+int apr_setup(void);
+int apr_teardown(void);
+int this_setup();
+int this_teardown();
+apr_pool_t* g_apr_mempool;
+const char* pooltag = "etchpool";
+
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+int init_suite(void)
+{
+ apr_setup();
+ etch_runtime_init(TRUE);
+ return this_setup();
+}
+
+int clean_suite(void)
+{
+ this_teardown();
+ etch_runtime_cleanup(0,0); /* free memtable and cache etc */
+ apr_teardown();
+ return 0;
+}
+
+int g_is_automated_test, g_bytes_allocated;
+
+#define IS_DEBUG_CONSOLE FALSE
+
+/*
+ * apr_setup()
+ * establish apache portable runtime environment
+ */
+int apr_setup(void)
+{
+ int result = apr_initialize();
+ if (result == 0)
+ { result = etch_apr_init();
+ g_apr_mempool = etch_apr_mempool;
+ }
+ if (g_apr_mempool)
+ apr_pool_tag(g_apr_mempool, pooltag);
+ else result = -1;
+ return result;
+}
+
+/*
+ * apr_teardown()
+ * free apache portable runtime environment
+ */
+int apr_teardown(void)
+{
+ if (g_apr_mempool)
+ apr_pool_destroy(g_apr_mempool);
+ g_apr_mempool = NULL;
+ apr_terminate();
+ return 0;
+}
+
+int this_setup()
+{
+ etch_apr_mempool = g_apr_mempool;
+ return 0;
+}
+
+int this_teardown()
+{
+ return 0;
+}
+
+
+/* - - - - - - - - - - - - - -
+ * unit test support
+ * - - - - - - - - - - - - - -
+ */
+typedef struct test_value_factory test_value_factory;
+
+etch_messagizer* g_my_messagizer;
+i_transportpacket* g_my_transportpacket;
+i_sessionmessage* g_my_sessionmessage;
+etch_flexbuffer* g_flexbuffer;
+etch_resources* g_my_resources;
+vf_idname_map* g_type_map;
+class_to_type_map* g_class_to_type_map;
+etch_who* g_who;
+default_value_factory* g_my_vf;
+test_value_factory* g_my_test_vf;
+int g_which_valuefactory;
+
+unsigned short CLASSID_MY_VF;
+unsigned short CLASSID_MY_VF_VTAB;
+unsigned short CLASSID_MY_VF_IMPL;
+unsigned short CLASSID_MY_IMPL_TP;
+unsigned short CLASSID_MY_IMPL_SM;
+#define OBJTYPE_MY_IMPL_TP 0x5170
+#define OBJTYPE_MY_IMPL_SM 0x5171
+
+#define is_my_impl_tp(x) (x && ((objmask*)x)->obj_type == OBJTYPE_MY_IMPL_TP)
+#define is_my_impl_sm(x) (x && ((objmask*)x)->obj_type == OBJTYPE_MY_IMPL_SM)
+
+#define THISTEST_HEADERSIZE 8
+#define TAGDATA_VERSION 3
+#define TYPECODE_EOD_MARK (-127)
+#define THISTEST_WHO_VALUE 0x5151
+#define WHICHVF_TESTVF 1
+#define WHICHVF_MYVF 2
+#define FAKEID_TYPE_ADD 1
+#define FAKEID_TYPE_ADD_RESULT 2
+#define FAKEID_FIELD_X 3
+#define FAKEID_FIELD_Y 4
+#define FAKEID_FIELD_RESULT 5
+
+default_value_factory* get_current_valuefactory()
+{
+ if (g_my_test_vf) return (default_value_factory*) g_my_test_vf;
+ if (g_my_vf) return g_my_vf;
+ return NULL;
+}
+
+typedef enum etch_what
+{ WHAT_NONE,
+ TRANSPORT_PACKET, TRANSPORT_QUERY, TRANSPORT_CONTROL, TRANSPORT_NOTIFY,
+ SESSION_MESSAGE, SESSION_QUERY, SESSION_CONTROL, SESSION_NOTIFY
+} etch_what;
+
+
+int is_equal_who(etch_who* who1, etch_who* who2)
+{
+ int n1 = 0, n2 = 0;
+ if (!who1 || !who2) return FALSE;
+ if (who1->class_id != CLASSID_WHO || who2->class_id != CLASSID_WHO) return FALSE;
+ if (!who1->value || !who2->value) return FALSE;
+ if (!is_etch_int32(who1->value) || !is_etch_int32(who2->value)) return FALSE;
+ n1 = ((etch_int32*)who1->value)->value;
+ n2 = ((etch_int32*)who2->value)->value;
+ return n1 == n2;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * 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 */
+
+ etch_who* recipient; /* not owned */
+ etch_what what;
+ size_t bufcount;
+ char* buf; /* owned */
+ etch_object* query; /* owned */
+ etch_object* query_result; /* owned */
+ etch_object* control; /* owned */
+ etch_object* value; /* owned */
+ etch_object* eventx; /* owned */
+
+} my_impl_transportpacket;
+
+
+/**
+ * destroy_my_impl_transportpacket()
+ * my_impl_transportpacket destructor
+ */
+int destroy_my_impl_transportpacket(my_impl_transportpacket* thisx)
+{
+ assert(is_my_impl_tp(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);
+
+ if (thisx->buf)
+ etch_free(thisx->buf);
+
+ if (thisx->query)
+ thisx->query->destroy(thisx->query);
+
+ if (thisx->query_result)
+ thisx->query_result->destroy(thisx->query_result);
+
+ if (thisx->control)
+ thisx->control->destroy(thisx->control);
+
+ if (thisx->value)
+ thisx->value->destroy(thisx->value);
+
+ if (thisx->eventx)
+ thisx->eventx->destroy(thisx->eventx);
+ }
+
+ 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 (my_impl_transportpacket* mytp, etch_who* whoto, etch_flexbuffer* fbuf)
+{
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ mytp->what = TRANSPORT_PACKET;
+ mytp->recipient = whoto;
+ /* retrieve the packet data. don't skip over header (0 is skip bytes) */
+ mytp->buf = etch_flexbuf_get_allfrom(fbuf, 0, &mytp->bufcount);
+ return 0;
+}
+
+
+/**
+ * my_transport_control()
+ * my_impl_transportpacket::itransport::transport_control
+ */
+int my_transport_control (my_impl_transportpacket* mytp, etch_object* control, etch_object* value)
+{
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ mytp->what = TRANSPORT_CONTROL;
+ mytp->control = control;
+ mytp->value = value;
+ return 0;
+}
+
+
+/**
+ * my_transport_notify()
+ * my_impl_transportpacket::itransport::transport_notify
+ */
+int my_transport_notify (my_impl_transportpacket* mytp, etch_object* evt)
+{
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ mytp->what = TRANSPORT_NOTIFY;
+ mytp->eventx = evt;
+ return 0;
+}
+
+
+/**
+ * my_transport_query()
+ * my_impl_transportpacket::itransport::transport_query
+ */
+objmask* my_transport_query (my_impl_transportpacket* mytp, etch_object* query)
+{
+ etch_object* resultobj = NULL;
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ resultobj = mytp->query_result; /* set artificially in test */
+ mytp->what = TRANSPORT_QUERY;
+ mytp->query = query;
+ mytp->query_result = NULL;
+ return (objmask*) resultobj; /* caller owns */
+}
+
+
+/**
+ * my_transport_get_session()
+ * my_impl_transportpacket::itransport::get_session
+ */
+i_sessionpacket* my_transport_get_session(my_impl_transportpacket* mytp)
+{
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ return mytp->session;
+}
+
+
+/**
+ * my_transport_set_session()
+ * my_impl_transportpacket::itransport::set_session
+ */
+void my_transport_set_session(my_impl_transportpacket* mytp, i_sessionpacket* session)
+{
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ CU_ASSERT_FATAL(is_etch_sessionpacket(session));
+ 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;
+ assert(is_etch_transportpkt(itp));
+
+ mytp = itp->thisx;
+ assert(is_my_impl_tp(mytp));
+
+ 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 = get_dynamic_classid_unique(&CLASSID_MY_IMPL_TP);
+
+ my_impl_transportpacket* mytp = (my_impl_transportpacket*) new_object
+ (sizeof(my_impl_transportpacket), OBJTYPE_MY_IMPL_TP, 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);
+
+ /* default header_size() will return this value so no need to override */
+ itp->header_size = THISTEST_HEADERSIZE;
+
+ /* 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;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * my_impl_sessionmessage (i_sessionmessage implementation)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * my_impl_sessionmessage
+ * test object implementing i_sessionmessage
+ */
+typedef struct my_impl_sessionmessage
+{
+ 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_sessionmessage 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_sessionmessage
+ * in this case, contains a pointer to the wrapper object, in this case a
+ * my_impl_sessionmessage*. 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_sessionmessage* ism; /* owned */
+ objdtor destroy_sessionmessage; /* i_sessionmessage original destructor */
+ etch_session_message session_message; /* session_message() method */
+
+ etch_what what;
+ etch_who* sender; /* not owned */
+ etch_message* msg; /* not owned */
+ int is_msg_handled;
+ etch_object* query; /* owned */
+ etch_object* query_result; /* owned */
+ etch_object* control; /* owned */
+ etch_object* value; /* owned */
+ etch_object* eventx; /* owned */
+
+ i_sessionpacket* session;
+
+} my_impl_sessionmessage;
+
+
+
+/**
+ * destroy_my_impl_sessionmessage()
+ * my_impl_sessionmessage destructor
+ */
+int destroy_my_impl_sessionmessage(my_impl_sessionmessage* thisx)
+{
+ if (thisx->refcount > 0 && --thisx->refcount > 0) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ { /* invoke original i_sessionmessage destructor */
+ if (thisx->ism && thisx->destroy_sessionmessage)
+ thisx->destroy_sessionmessage(thisx->ism);
+
+ /* these are objects which would be destroyed in the binding
+ * by the last method to touch them */
+ if (thisx->msg)
+ thisx->msg->destroy(thisx->msg);
+
+ if (thisx->query)
+ thisx->query->destroy(thisx->query);
+
+ if (thisx->query_result)
+ thisx->query_result->destroy(thisx->query_result);
+
+ if (thisx->control)
+ thisx->control->destroy(thisx->control);
+
+ if (thisx->value)
+ thisx->value->destroy(thisx->value);
+
+ if (thisx->eventx)
+ thisx->eventx->destroy(thisx->eventx);
+ }
+
+ return destroy_objectex((objmask*) thisx);
+}
+
+
+/**
+ * impl_session_message()
+ * my_impl_sessionmessage::ism::session_message.
+ * @param whofrom caller retains, can be null.
+ * @param msg caller abandons
+ */
+int impl_session_message (my_impl_sessionmessage* mysm, etch_who* whofrom, etch_message* msg)
+{
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ mysm->what = SESSION_MESSAGE;
+
+ /* in this emulation we are the session consuming a message. if successful,
+ * (i.e., the message is handled), the binding will eventually destroy the
+ * message (the caller relinquishes message memory), and the who object.
+ * if not successful (message not handled) the caller retains message and
+ * who memory in order to forward the message and who somewhere else
+ * (as an unwanted message). so we model that here: if the message is not
+ * handled (a manual switch in these tests), we do not save references to
+ * the messaqe and who for cleanup, because the unwanted message, containing
+ * these objects, will be cleaned up instead.
+ */
+ if (mysm->is_msg_handled)
+ {
+ mysm->msg = msg;
+ mysm->sender = whofrom;
+ return 0;
+ }
+
+ mysm->msg = NULL;
+ mysm->sender = NULL;
+ return -1;
+}
+
+
+/**
+ * my_session_control()
+ * my_impl_sessionmessage::ism::isession::session_control
+ * control and value are always abandoned by caller so mysm must clean them up.
+ */
+int my_session_control (my_impl_sessionmessage* mysm, etch_object* control, etch_object* value)
+{
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ mysm->what = SESSION_CONTROL;
+ mysm->control = control;
+ mysm->value = value;
+ return 0;
+}
+
+
+/**
+ * my_session_notify()
+ * my_impl_sessionmessage::ism::isession::session_notify
+ * evt is always abandoned by caller so mysm must clean it up.
+ */
+int my_session_notify (my_impl_sessionmessage* mysm, etch_object* evt)
+{
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ mysm->what = SESSION_NOTIFY;
+ mysm->eventx = evt;
+ return 0;
+}
+
+
+/**
+ * my_session_query()
+ * my_impl_sessionmessage::ism::isession::session_query
+ * query is always abandoned by caller so mysm must clean it up.
+ */
+objmask* my_session_query (my_impl_sessionmessage* mysm, etch_object* query)
+{
+ etch_object* resultobj = NULL;
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ resultobj = mysm->query_result; /* artifically set in test */
+ mysm->what = SESSION_QUERY;
+ mysm->query = query;
+ mysm->query_result = NULL;
+ return (objmask*) resultobj; /* caller owns */
+}
+
+
+/*
+ * destroy_my_sessionmessage()
+ * i_sessionmessage destructor
+ * this destructor will destroy its parent (my_impl_sessionmessage),
+ * which will in turn destroy this object.
+ */
+int destroy_my_sessionmessage(i_sessionmessage* ism)
+{
+ my_impl_sessionmessage* mysm = NULL;
+ if (NULL == ism) return -1;
+
+ mysm = ism->thisx;
+
+ mysm->destroy(mysm);
+
+ return 0;
+}
+
+
+/**
+ * new_my_impl_sessionmessage()
+ * my_impl_sessionmessage constructor
+ */
+my_impl_sessionmessage* new_my_impl_sessionmessage()
+{
+ i_sessionmessage* ism = NULL;
+ i_session* isession = NULL;
+ /* this is a model for dynamic class ID assigment */
+ unsigned short class_id = CLASSID_MY_IMPL_SM? CLASSID_MY_IMPL_SM:
+ (CLASSID_MY_IMPL_SM = get_dynamic_classid());
+
+ my_impl_sessionmessage* mysm = (my_impl_sessionmessage*) new_object
+ (sizeof(my_impl_sessionmessage), OBJTYPE_MY_IMPL_SM, class_id);
+
+ mysm->destroy = destroy_my_impl_sessionmessage;
+
+ isession = new_session_interface(mysm,
+ (etch_session_control) my_session_control,
+ (etch_session_notify) my_session_notify,
+ (etch_session_query) my_session_query);
+
+ ism = new_sessionmsg_interface(mysm, impl_session_message, isession);
+
+ /* save off i_sessionmessage destructor */
+ mysm->destroy_sessionmessage = ism->destroy;
+
+ /* custom destructor will destroy the my_impl_sessionmessage */
+ ism->destroy = destroy_my_sessionmessage;
+
+ /* g_my_sessionmessage will get set to this interface */
+ mysm->ism = ism;
+
+ return mysm;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * test value factories
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_value_factory
+ * value factory version 1
+ * this version uses inherited data coded into the object, as opposed to
+ * instantiating an impl object to contain inherited data and methods.
+ */
+typedef struct test_value_factory
+{
+ unsigned int hashkey;
+ unsigned short obj_type;
+ unsigned short class_id;
+ 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;
+
+ /* - - - - - - - - - - - - -
+ * default value factory
+ * - - - - - - - - - - - - -
+ */
+ objmask* impl;
+
+ class_to_type_map* class_to_type;
+ vf_idname_map* types;
+ etch_arraylist* mixins;
+
+ unsigned char is_own_types;
+ unsigned char is_own_class_to_type;
+
+ /* - - - - - - - - - - - - -
+ * test value factory
+ * - - - - - - - - - - - - -
+ */
+ etch_type* mt_add;
+ etch_type* mt_add_result;
+ etch_field* mf_x;
+ etch_field* mf_y;
+ etch_field* mf_result;
+
+} test_value_factory;
+
+
+/**
+ * destroy_test_value_factory()
+ * destructor for value factory version 1
+ */
+int destroy_test_value_factory(test_value_factory* vf)
+{
+ if (NULL == vf) return -1;
+ if (vf->refcount > 0)
+ if (vf->refcount != 1) /* if refcount is 1 */
+ { vf->refcount--; /* parent dtor will decrement */
+ return -1;
+ }
+
+ if (!is_etchobj_static_content(vf))
+ {
+ destroy_static_type(vf->mt_add);
+ destroy_static_type(vf->mt_add_result);
+ destroy_static_field(vf->mf_x);
+ destroy_static_field(vf->mf_y);
+ destroy_static_field(vf->mf_result);
+ }
+
+ return destroy_default_value_factory((default_value_factory*) vf);
+}
+
+
+/**
+ * new_test_valuefactory()
+ * constructor for value factory version 1 inheriting from default_value_factory
+ */
+test_value_factory* new_test_valuefactory()
+{
+ etchparentinfo* inheritlist = NULL;
+ test_value_factory* vf = NULL;
+
+ /* establish global dynamic class IDs for the custom vf.
+ * these global objects are generated by the etch compiler.
+ */
+ const unsigned short class_id_vf = CLASSID_MY_VF? CLASSID_MY_VF:
+ (CLASSID_MY_VF = get_dynamic_classid());
+
+ const unsigned short class_id_vf_vtab = CLASSID_MY_VF_VTAB? CLASSID_MY_VF_VTAB:
+ (CLASSID_MY_VF_VTAB = get_dynamic_classid());
+
+ g_type_map = new_vf_types_collection(ETCH_DEFSIZE);
+ /* since we explicitly instantiate a type map, and since we explicitly destroy
+ * the test's custom types, we want the type maps destructor to not destroy
+ * the map content. overriding the map's content clear callback is one way
+ * to do this. */
+ g_type_map->freehook = etch_noop_clear_handler;
+ g_class_to_type_map = new_class_to_type_map(ETCH_DEFSIZE);
+
+ /* instantiate the new value factory.
+ * this vf does NOT own its type maps since we supply them here.
+ * however if we wanted to abandon ownership, we could set the
+ * vf.is_own_types and vf.is_own_class_to_type flags here.
+ */
+ vf = (test_value_factory*) new_default_value_factory_a
+ (sizeof(test_value_factory), g_type_map, g_class_to_type_map);
+
+ vf->destroy = destroy_test_value_factory;
+
+ /* ensure parent type keys exist in the (one-based) inheritance list.
+ * parent class of our custom vf is default_value_factory.
+ * inheritance list is used by validators and object assignment logic.
+ */
+ inheritlist = get_vtab_inheritance_list((objmask*)vf, 2, 1, CLASSID_MY_VF_VTAB);
+ inheritlist[1].obj_type = ETCHTYPEB_VALUEFACTORY;
+ inheritlist[1].class_id = CLASSID_VALUEFACTORY; /* parent class */
+ vf->class_id = CLASSID_MY_VF; /* our class */
+
+ /* instantiate the custom vf's instance data and assign it to the vf.
+ */
+ vf->mt_add = new_static_type(L"add");
+ vf->mt_add_result = new_static_type(L"add_result");
+ vf->mf_x = new_static_field(L"x");
+ vf->mf_y = new_static_field(L"y");
+ vf->mf_result = new_static_field(L"xresult");
+
+ /* we replace generated ids with 1-byte IDs to make test data buffers easier to construct */
+ vf->mt_add->id = FAKEID_TYPE_ADD;
+ vf->mt_add_result->id = FAKEID_TYPE_ADD_RESULT;
+ vf->mf_x->id = FAKEID_FIELD_X;
+ vf->mf_y->id = FAKEID_FIELD_Y;
+ vf->mf_result->id = FAKEID_FIELD_RESULT;
+
+ vf->vtab->add_type(vf, vf->mt_add);
+ vf->vtab->add_type(vf, vf->mt_add_result);
+
+ etchtype_put_validator(vf->mt_add, clone_field(vf->mf_x),
+ (objmask*) etchvtor_int32_get(0));
+ etchtype_put_validator(vf->mt_add, clone_field(vf->mf_y),
+ (objmask*) etchvtor_int32_get(0));
+ etchtype_put_validator(vf->mt_add, clone_field(builtins._mf__message_id),
+ (objmask*) etchvtor_int64_get(0));
+
+ etchtype_put_validator(vf->mt_add_result, clone_field(vf->mf_result),
+ (objmask*) etchvtor_int32_get(0));
+ etchtype_put_validator(vf->mt_add_result, clone_field(builtins._mf__message_id),
+ (objmask*) etchvtor_int64_get(0));
+ etchtype_put_validator(vf->mt_add_result, clone_field(builtins._mf__in_reply_to),
+ (objmask*) etchvtor_int64_get(0));
+
+ g_my_test_vf = vf;
+ return g_my_test_vf;
+}
+
+
+/**
+ * my_valufactory_impl
+ * value factory version 2 instance data object
+ */
+typedef struct my_valufactory_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_type* mt_add;
+ etch_type* mt_add_result;
+ etch_field* mf_x;
+ etch_field* mf_y;
+ etch_field* mf_result;
+
+} my_valufactory_impl;
+
+
+/**
+ * destroy_my_valufactory_impl()
+ * destructor for inheriting value factory version 2 instance data
+ */
+int destroy_my_valufactory_impl(my_valufactory_impl* impl)
+{
+ if (NULL == impl) return -1;
+ if (impl->refcount > 0 && --impl->refcount > 0) return -1;
+
+ if (!is_etchobj_static_content(impl))
+ {
+ destroy_static_type(impl->mt_add);
+ destroy_static_type(impl->mt_add_result);
+ destroy_static_field(impl->mf_x);
+ destroy_static_field(impl->mf_y);
+ destroy_static_field(impl->mf_result);
+ }
+
+ return destroy_objectex((objmask*) impl);
+}
+
+
+/**
+ * new_my_valufactory_impl()
+ * constructor for inheriting value factory version 2 instance data
+ */
+my_valufactory_impl* new_my_valufactory_impl()
+{
+ unsigned short class_id = CLASSID_MY_VF_IMPL? CLASSID_MY_VF_IMPL:
+ (CLASSID_MY_VF_IMPL = get_dynamic_classid());
+
+ my_valufactory_impl* impl = (my_valufactory_impl*) new_object
+ (sizeof(my_valufactory_impl), ETCHTYPEB_VALUEFACTIMP, class_id);
+
+ impl->destroy = destroy_my_valufactory_impl;
+
+ impl->mt_add = new_static_type(L"add");
+ impl->mt_add_result = new_static_type(L"add_result");
+ impl->mf_x = new_static_field(L"x");
+ impl->mf_y = new_static_field(L"y");
+ impl->mf_result = new_static_field(L"xresult");
+
+ /* we replace generated ids with 1-byte IDs to make test data buffers easier to construct */
+ impl->mt_add->id = FAKEID_TYPE_ADD;
+ impl->mt_add_result->id = FAKEID_TYPE_ADD_RESULT;
+ impl->mf_x->id = FAKEID_FIELD_X;
+ impl->mf_y->id = FAKEID_FIELD_Y;
+ impl->mf_result->id = FAKEID_FIELD_RESULT;
+
+ etchtype_put_validator(impl->mt_add, clone_field(impl->mf_x),
+ (objmask*) etchvtor_int32_get(0));
+ etchtype_put_validator(impl->mt_add, clone_field(impl->mf_y),
+ (objmask*) etchvtor_int32_get(0));
+ etchtype_put_validator(impl->mt_add, clone_field(builtins._mf__message_id),
+ (objmask*) etchvtor_int64_get(0));
+
+ etchtype_put_validator(impl->mt_add_result, clone_field(impl->mf_result),
+ (objmask*) etchvtor_int32_get(0));
+ etchtype_put_validator(impl->mt_add_result, clone_field(builtins._mf__message_id),
+ (objmask*) etchvtor_int64_get(0));
+ etchtype_put_validator(impl->mt_add_result, clone_field(builtins._mf__in_reply_to),
+ (objmask*) etchvtor_int64_get(0));
+
+ return impl;
+}
+
+
+/**
+ * new_bogus_valuefactory()
+ * constructor for value factory version 2 inheriting from default_value_factory
+ */
+default_value_factory* new_bogus_valuefactory()
+{
+ etchparentinfo* inheritlist = NULL;
+ my_valufactory_impl* impl = NULL;
+
+ /* establish global dynamic class IDs for the custom vf.
+ * these global objects are generated by the etch compiler.
+ */
+ const unsigned short class_id_vf = CLASSID_MY_VF? CLASSID_MY_VF:
+ (CLASSID_MY_VF = get_dynamic_classid());
+
+ const unsigned short class_id_vf_vtab = CLASSID_MY_VF_VTAB? CLASSID_MY_VF_VTAB:
+ (CLASSID_MY_VF_VTAB = get_dynamic_classid());
+
+ g_type_map = new_vf_types_collection(ETCH_DEFSIZE);
+ /* since we explicitly instantiate a type map, and since we explicitly destroy
+ * the test's custom types, we want the type maps destructor to not destroy
+ * the map content. overriding the map's content clear callback is one way
+ * to do this. */
+ g_type_map->freehook = etch_noop_clear_handler;
+ g_class_to_type_map = new_class_to_type_map(ETCH_DEFSIZE);
+
+ /* instantiate the new value factory.
+ * this vf does NOT own its type maps since we supply them here.
+ * however if we wanted to abandon ownership, we could clear the
+ * vf.is_own_types and vf.is_own_class_to_type flags here.
+ */
+ g_my_vf = new_default_value_factory(g_type_map, g_class_to_type_map);
+
+ /* ensure parent type keys exist in the (one-based) inheritance list.
+ * parent class of our custom vf is default_value_factory.
+ * inheritance list is used by validators and object assignment logic.
+ */
+ inheritlist = get_vtab_inheritance_list((objmask*)g_my_vf, 2, 1, CLASSID_MY_VF_VTAB);
+ inheritlist[1].obj_type = ETCHTYPEB_VALUEFACTORY;
+ inheritlist[1].class_id = CLASSID_VALUEFACTORY; /* parent class */
+ g_my_vf->class_id = CLASSID_MY_VF; /* our class */
+
+ /* instantiate the custom vf's instance data and assign it to the vf.
+ * the impl comprises all data specific to the inheriting class, including
+ * data and methods if any. the default value factory destructor will call
+ * the destructor on the vf's impl object.
+ */
+ impl = new_my_valufactory_impl();
+ g_my_vf->impl = (objmask*) impl;
+
+ g_my_vf->vtab->add_type(g_my_vf, impl->mt_add);
+ g_my_vf->vtab->add_type(g_my_vf, impl->mt_add_result);
+
+ return g_my_vf;
+}
+
+
+/**
+ * get_vftype()
+ * return a type depending on which version of value factory is instantiated
+ */
+etch_type* get_vftype(const int typeid)
+{
+ etch_type* type = NULL;
+
+ if (g_my_vf)
+ {
+ my_valufactory_impl* impl = (my_valufactory_impl*) g_my_vf->impl;
+
+ if (typeid == impl->mt_add->id)
+ type = impl->mt_add;
+ else
+ if (typeid == impl->mt_add_result->id)
+ type = impl->mt_add_result;
+ }
+ else
+ if (g_my_test_vf)
+ {
+ if (typeid == g_my_test_vf->mt_add->id)
+ type = g_my_test_vf->mt_add;
+ else
+ if (typeid == g_my_test_vf->mt_add_result->id)
+ type = g_my_test_vf->mt_add_result;
+ }
+
+ return type;
+}
+
+
+/* - - - - - - - - - - - - - - - - -
+ * test messagizer
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_bogus_messagizer()
+ */
+etch_messagizer* new_bogus_messagizer(i_transportpacket* transport)
+{
+ g_my_messagizer = new_messagizer(transport, L"foo:?Messagizer.format=binary", g_my_resources);
+ return g_my_messagizer;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * individual test data setup and teardown
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * setup_this_test()
+ */
+int setup_this_test(const int which_valuefactory)
+{
+ my_impl_transportpacket* mytp_impl = NULL;
+ my_impl_sessionmessage* mysm_impl = NULL;
+ default_value_factory* thisvf = NULL;
+ g_which_valuefactory = which_valuefactory;
+
+ /* two versions of value factory using different inheritance models */
+ if (which_valuefactory == WHICHVF_TESTVF) /* vf version 1 inline inheritance */
+ { thisvf = (default_value_factory*) new_test_valuefactory();
+ set_etchobj_static_all(g_my_test_vf); /* so resources will not destroy */
+ }
+ else
+ { thisvf = new_bogus_valuefactory(); /* vf version 2 impl object inheritance */
+ set_etchobj_static_all(g_my_vf); /* so resources will not destroy */
+ }
+
+ g_my_resources = new_etch_resources(ETCH_DEFSIZE);
+ etch_resources_add(g_my_resources, ETCH_RESXKEY_MSGIZER_VALUFACT, (objmask*) thisvf);
+
+ /* we instantiate a wrapper x which implements and instantiates i_transportpacket.
+ * the instantiation of i_transportpacket will contain a pointer to x.
+ * our global reference g_my_transportpacket is a pointer to the interface.
+ * the purpose of this excercise is that, in the real binding we can pass
+ * around the interface, whose methods can be then invoked without knowing
+ * anything about the wrapper. when we want to reference the wrapper x,
+ * it is (my_impl_transportpacket) g_my_transportpacket->thisx.
+ */
+ mytp_impl = new_my_impl_transportpacket();
+ g_my_transportpacket = mytp_impl->ixp;
+
+ /* we instantiate a wrapper y which implements and instantiates i_sessionmessage.
+ * the instantiation of i_sessionmessage will contain a pointer to y.
+ * our global reference g_my_sessionmessage is a pointer to the interface.
+ * the purpose of this excercise is that, in the real binding we can pass
+ * around the interface, whose methods can be then invoked without knowing
+ * anything about the wrapper. when we want to reference the wrapper x,
+ * it is (my_impl_sessionmessage*) g_my_sessionmessage->thisx.
+ */
+ mysm_impl = new_my_impl_sessionmessage();
+ g_my_sessionmessage = mysm_impl->ism;
+
+ g_who = new_who(new_int32(THISTEST_WHO_VALUE), TRUE);
+
+ /* finally instantiate the test messagizer */
+ new_bogus_messagizer(g_my_transportpacket);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_my_messagizer);
+ etch_msgizer_set_session(g_my_messagizer, g_my_sessionmessage);
+
+ return 0;
+}
+
+
+/**
+ * teardown_this_test()
+ */
+int teardown_this_test()
+{
+ g_my_messagizer->destroy(g_my_messagizer);
+
+ g_my_transportpacket->destroy(g_my_transportpacket);
+
+ g_my_sessionmessage->destroy(g_my_sessionmessage);
+
+ g_my_resources->destroy(g_my_resources);
+
+ if (g_my_vf)
+ { clear_etchobj_static_all(g_my_vf);
+ g_my_vf->destroy(g_my_vf);
+ }
+ else /* can only instantiate one or the other */
+ if (g_my_test_vf)
+ { clear_etchobj_static_all(g_my_test_vf);
+ g_my_test_vf->destroy(g_my_test_vf);
+ }
+
+ g_who->destroy(g_who);
+
+ g_type_map->destroy(g_type_map); // ************************************************
+ g_class_to_type_map->destroy(g_class_to_type_map);
+
+ if (g_flexbuffer)
+ g_flexbuffer->destroy(g_flexbuffer);
+
+ g_my_transportpacket = NULL;
+ g_my_sessionmessage = NULL;
+ g_class_to_type_map = NULL;
+ g_my_messagizer = NULL;
+ g_my_resources = NULL;
+ g_flexbuffer = NULL;
+ g_my_test_vf = NULL;
+ g_type_map = NULL;
+ g_my_vf = NULL;
+ g_who = NULL;
+
+ etchvf_free_builtins();
+
+ return 0;
+}
+
+
+/* - - - - - - - - - - - - - -
+ * unit tests
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_transportpacket_constructor()
+ */
+void test_transportpacket_constructor(void)
+{
+ my_impl_transportpacket* mytp_impl = new_my_impl_transportpacket();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mytp_impl);
+
+ /* the custom interface object destructors are coded to destroy their
+ * implementing objects, so we do that here to verify this functionality.
+ */
+ do
+ { i_transportpacket* itp = mytp_impl->ixp;
+ itp->destroy(itp);
+
+ } while(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 */
+}
+
+
+/**
+ * test_sessionmessage_constructor()
+ */
+void test_sessionmessage_constructor(void)
+{
+ my_impl_sessionmessage* mysm_impl = new_my_impl_sessionmessage();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mysm_impl);
+
+ /* the custom interface object destructors are coded to destroy their
+ * implementing objects, so we do that here to verify this functionality.
+ */
+ do
+ { i_sessionmessage* ism = mysm_impl->ism;
+ ism->destroy(ism);
+
+ } while(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 */
+}
+
+
+/**
+ * test_messagizer_constructor()
+ */
+void test_messagizer_constructor(void)
+{
+ etch_messagizer* mzr = NULL;
+ i_transportpacket* itp = NULL;
+ etch_resources* resxmap = NULL;
+ default_value_factory* vf = NULL;
+ my_impl_transportpacket* mytp_impl = NULL;
+
+ vf = new_default_value_factory(NULL, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(vf);
+ set_etchobj_static_all(vf); /* so resources will not destroy */
+
+ resxmap = new_etch_resources(ETCH_DEFSIZE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(resxmap);
+ etch_resources_add(resxmap, ETCH_RESXKEY_MSGIZER_VALUFACT, (objmask*) vf);
+
+ mytp_impl = new_my_impl_transportpacket();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mytp_impl);
+
+ itp = mytp_impl->ixp;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(itp);
+
+ /* messagizer does not own i_transportpacket* itp */
+ mzr = new_messagizer(itp, L"foo:?Messagizer.format=binary", resxmap);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mzr);
+
+ mzr->destroy(mzr);
+
+ /* i_transportpacket.destroy() will destroy my_impl_transportpacket */
+ itp->destroy(itp);
+
+ resxmap->destroy(resxmap);
+ clear_etchobj_static_all(vf); /* so we can destroy it now */
+ vf->destroy(vf);
+ etchvf_free_builtins();
+
+ 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_testsetup_teardown_a
+ */
+void test_testsetup_teardown_a(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+ teardown_this_test();
+
+ 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_testsetup_teardown_b
+ */
+void test_testsetup_teardown_b(void)
+{
+ setup_this_test(WHICHVF_MYVF);
+ teardown_this_test();
+
+ 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_packet_1
+ * mimics the transport messagizing a packet and delivering the message to the session
+ */
+void test_packet_1(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ etch_type* msgtype = NULL;
+ etch_flexbuffer* fbuf = NULL;
+ const int THISTEST_BUFSIZE = 4;
+ char* buf = etch_malloc(THISTEST_BUFSIZE, ETCHTYPEB_BYTES);
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+
+ /* load up the packet buffer with test data */
+ buf[0] = TAGDATA_VERSION; buf[1] = FAKEID_TYPE_ADD; buf[2] = 0; buf[3] = TYPECODE_EOD_MARK;
+ g_flexbuffer = new_flexbuffer_from(buf, THISTEST_BUFSIZE, THISTEST_BUFSIZE, 0);
+
+ my_session->is_msg_handled = TRUE;
+
+ /* messagize the packet and deliver the message to the session */
+ result = g_my_messagizer->session_packet (g_my_messagizer, g_who, g_flexbuffer);
+ CU_ASSERT_EQUAL(result, 0);
+ if (0 != result) break;
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_MESSAGE);
+ result = is_equal_who(my_session->sender, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+ CU_ASSERT_EQUAL(message_size(my_session->msg), 0);
+ CU_ASSERT_PTR_NULL(my_session->eventx);
+ /* assert that message type is "add" (since we buffered FAKEID_TYPE_ADD above) */
+ msgtype = message_type(my_session->msg);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msgtype);
+ CU_ASSERT_EQUAL(msgtype->id, FAKEID_TYPE_ADD);
+
+ } while(0);
+
+ teardown_this_test();
+
+ 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_packet_2
+ * mimics the transport messagizing a packet and delivering the message to the session
+ * which rejects the message and forwards the message as rejected.
+ */
+void test_packet_2(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ etch_type* msgtype = NULL;
+ etch_flexbuffer* fbuf = NULL;
+ const int THISTEST_BUFSIZE = 4;
+ etch_message* thismessage = NULL;
+ etch_unwanted_message* uwmsg = NULL;
+ char* buf = etch_malloc(THISTEST_BUFSIZE, ETCHTYPEB_BYTES);
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+
+ /* load up the packet buffer with test data */
+ buf[0] = TAGDATA_VERSION; buf[1] = FAKEID_TYPE_ADD; buf[2] = 0; buf[3] = TYPECODE_EOD_MARK;
+ g_flexbuffer = new_flexbuffer_from(buf, THISTEST_BUFSIZE, THISTEST_BUFSIZE, 0);
+
+ /* for the purposes of this test we manually specify that the message was not handled.
+ * when this is the case, the message and who will be wrapped up in an "unwanted message"
+ * which is forwarded to the session as an event. when this is the case, memory for the
+ * message and who is not cleaned up by the session in the normal manner, but is instead
+ * owned by the unwanted message, and destroyed with that object.
+ */
+ my_session->is_msg_handled = FALSE;
+
+ /* messagize the packet and deliver the message to the session */
+ result = g_my_messagizer->session_packet (g_my_messagizer, g_who, g_flexbuffer);
+ CU_ASSERT_EQUAL(result, 0);
+ if (0 != result) break;
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_NOTIFY);
+ CU_ASSERT_EQUAL(message_size(my_session->msg), 0);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->eventx);
+
+ uwmsg = (etch_unwanted_message*) my_session->eventx;
+ CU_ASSERT_EQUAL_FATAL(uwmsg->class_id, CLASSID_EVENT_UNWANTMSG);
+
+ /* find the message and who in the "unwanted message" wrapper */
+ result = is_equal_who(uwmsg->whofrom, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ thismessage = uwmsg->message;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thismessage);
+
+ /* assert that message type is "add" (since we buffered FAKEID_TYPE_ADD above) */
+ msgtype = message_type(thismessage);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msgtype);
+ CU_ASSERT_EQUAL(msgtype->id, FAKEID_TYPE_ADD);
+
+ } while(0);
+
+ teardown_this_test();
+
+ 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_packet_3
+ * mimics the transport messagizing a packet and delivering the message to the session
+ */
+void test_packet_3(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ etch_type* msgtype = NULL;
+ etch_flexbuffer* fbuf = NULL;
+ const int THISTEST_BUFSIZE = 4;
+ etch_message* thismessage = NULL;
+ etch_unwanted_message* uwmsg = NULL;
+ char* buf = etch_malloc(THISTEST_BUFSIZE, ETCHTYPEB_BYTES);
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+
+ /* load up the packet buffer with test data */
+ buf[0] = TAGDATA_VERSION;
+ buf[1] = FAKEID_TYPE_ADD_RESULT;
+ buf[2] = 0;
+ buf[3] = TYPECODE_EOD_MARK;
+
+ g_flexbuffer = new_flexbuffer_from(buf, THISTEST_BUFSIZE, THISTEST_BUFSIZE, 0);
+
+ /* for the purposes of this test we manually specify that the message was handled */
+ my_session->is_msg_handled = TRUE;
+
+ /* messagize the packet and deliver the message to the session */
+ result = g_my_messagizer->session_packet (g_my_messagizer, g_who, g_flexbuffer);
+ CU_ASSERT_EQUAL(result, 0);
+ if (0 != result) break;
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_MESSAGE);
+ CU_ASSERT_EQUAL(message_size(my_session->msg), 0);
+
+ /* assert that message type is "add_result" (since we buffered FAKEID_TYPE_ADD_RESULT above) */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->msg);
+ msgtype = message_type(my_session->msg);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msgtype);
+ CU_ASSERT_EQUAL(msgtype->id, FAKEID_TYPE_ADD_RESULT);
+
+ } while(0);
+
+ teardown_this_test();
+
+ 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_message_1
+ * mimics the session buffering a message and delivering it to the transport
+ */
+void test_message_1(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ etch_type* msgtype = NULL;
+ etch_value_factory* vf = NULL;
+ const int EXPECTED_BUFSIZE = 4;
+ etch_message* thismessage = NULL;
+
+ /* g_my_transportpacket is the i_transportpacket interface
+ * my_impl_transportpacket is the implementing test class */
+ my_impl_transportpacket* my_transport = g_my_transportpacket->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+
+ vf = (etch_value_factory*) get_current_valuefactory();
+ msgtype = get_vftype(FAKEID_TYPE_ADD);
+
+ g_my_transportpacket->header_size = 0; /* test no header */
+
+ thismessage = new_message(msgtype, ETCH_DEFSIZE, vf);
+
+ /* buffer up the message and deliver to transport */
+ result = g_my_messagizer->transport_message (g_my_messagizer, g_who, thismessage);
+ CU_ASSERT_EQUAL(result, 0);
+ if (0 != result) break;
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_PACKET);
+ result = is_equal_who(my_transport->recipient, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->buf);
+ CU_ASSERT_EQUAL(my_transport->bufcount, EXPECTED_BUFSIZE);
+
+ /* check that packet buffer contains expected serialized message */
+ CU_ASSERT_EQUAL(my_transport->buf[0], TAGDATA_VERSION);
+ CU_ASSERT_EQUAL(my_transport->buf[1], FAKEID_TYPE_ADD);
+ CU_ASSERT_EQUAL(my_transport->buf[2], 0);
+ CU_ASSERT_EQUAL(my_transport->buf[3], TYPECODE_EOD_MARK);
+
+ } while(0);
+
+ teardown_this_test();
+
+ 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_message_2
+ * mimics the session buffering a message and delivering it to the transport
+ */
+void test_message_2(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0, i = 0, errs = 0;
+ etch_type* msgtype = NULL;
+ etch_value_factory* vf = NULL;
+ etch_message* thismessage = NULL;
+ const int TEST_HEADER_SIZE = 8, EXPECTED_BUFSIZE = 4 + TEST_HEADER_SIZE;
+
+ /* g_my_transportpacket is the i_transportpacket interface
+ * my_impl_transportpacket is the implementing test class */
+ my_impl_transportpacket* my_transport = g_my_transportpacket->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+
+ vf = (etch_value_factory*) get_current_valuefactory();
+ msgtype = get_vftype(FAKEID_TYPE_ADD_RESULT);
+
+ g_my_transportpacket->header_size = TEST_HEADER_SIZE; /* test with 8-byte header */
+
+ thismessage = new_message(msgtype, ETCH_DEFSIZE, vf);
+
+ /* buffer up the message and deliver to transport */
+ result = g_my_messagizer->transport_message(g_my_messagizer, g_who, thismessage);
+ CU_ASSERT_EQUAL(result, 0);
+ if (0 != result) break;
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_PACKET);
+ result = is_equal_who(my_transport->recipient, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->buf);
+ CU_ASSERT_EQUAL(my_transport->bufcount, EXPECTED_BUFSIZE);
+
+ /* check that packet buffer contains expected serialized message */
+ for(; i < TEST_HEADER_SIZE; i++) if (my_transport->buf[i]) errs++;
+ CU_ASSERT_EQUAL(errs, 0);
+ CU_ASSERT_EQUAL(my_transport->buf[8], TAGDATA_VERSION);
+ CU_ASSERT_EQUAL(my_transport->buf[9], FAKEID_TYPE_ADD_RESULT);
+ CU_ASSERT_EQUAL(my_transport->buf[10],0);
+ CU_ASSERT_EQUAL(my_transport->buf[11],TYPECODE_EOD_MARK);
+
+ } while(0);
+
+ teardown_this_test();
+
+ 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_session_control
+ * test the session control notification plumbing
+ */
+void test_session_control(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ const int MY_CONTROL_CLASSID = 0x5200, MY_VALUE_CLASSID = 0x5201;
+ etch_object* mycontrolobj = new_etch_object(MY_CONTROL_CLASSID, NULL);
+ etch_object* myvalueobj = new_etch_object(MY_VALUE_CLASSID, NULL);
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+ CU_ASSERT_EQUAL(my_session->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_session->control);
+ CU_ASSERT_PTR_NULL(my_session->value);
+
+ /* we relinquish memory for mycontrolobj and myvalueobj here.
+ * the session_control terminal destination must destroy them, which here
+ * is handled by our session object destructor when we teardown_this_test() */
+ g_my_messagizer->session_control(g_my_messagizer, mycontrolobj, myvalueobj);
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_CONTROL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->control);
+ CU_ASSERT_EQUAL(my_session->control->class_id, MY_CONTROL_CLASSID);
+ CU_ASSERT_EQUAL(my_session->value->class_id, MY_VALUE_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+ 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_session_notify
+ * test the session notify notification plumbing
+ */
+void test_session_notify(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ const int MY_EVENT_CLASSID = 0x5202;
+ etch_object* myeventobj = new_etch_object(MY_EVENT_CLASSID, NULL);
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+ CU_ASSERT_EQUAL(my_session->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_session->eventx);
+
+ /* we relinquish memory for myeventobj here.
+ * the session_control terminal destination must destroy it, which here
+ * is handled by our session object destructor when we teardown_this_test() */
+ g_my_messagizer->session_notify(g_my_messagizer, myeventobj);
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_NOTIFY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->eventx);
+ CU_ASSERT_EQUAL(my_session->eventx->class_id, MY_EVENT_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+ 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_session_query
+ * test the session query notification plumbing
+ */
+void test_session_query(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ const int MY_QUERY_CLASSID = 0x5203, MY_RESULT_CLASSID = 0x5204;
+ etch_object* myqueryobj = new_etch_object(MY_QUERY_CLASSID, NULL);
+ etch_object* myresultobj = new_etch_object(MY_RESULT_CLASSID, NULL);
+ etch_object* queryresult = NULL;
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+ CU_ASSERT_EQUAL(my_session->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_session->query);
+
+ /* we relinquish myresultobj here. see comments following */
+ my_session->query_result = myresultobj;
+
+ /* we relinquish memory for myqueryobj here and assume queryresult.
+ * the session_control terminal destination must destroy it, which here
+ * is handled by our session object destructor when we teardown_this_test() */
+ queryresult = g_my_messagizer->session_query (g_my_messagizer, myqueryobj);
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_QUERY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->query);
+ CU_ASSERT_EQUAL(my_session->query->class_id, MY_QUERY_CLASSID);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(queryresult);
+ CU_ASSERT_EQUAL(queryresult->class_id, MY_RESULT_CLASSID);
+ queryresult->destroy(queryresult);
+
+ } while(0);
+
+ teardown_this_test();
+
+ 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_transport_control
+ * test the transport control notification plumbing
+ */
+void test_transport_control(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ my_impl_transportpacket* my_transport = NULL;
+ const int MY_CONTROL_CLASSID = 0x5200, MY_VALUE_CLASSID = 0x5201;
+ etch_object* mycontrolobj = new_etch_object(MY_CONTROL_CLASSID, NULL);
+ etch_object* myvalueobj = new_etch_object(MY_VALUE_CLASSID, NULL);
+
+ /* g_my_transportpacket is the i_transportpacket interface
+ * my_impl_transportpacket is the implementing test class
+ */
+ g_my_messagizer->transport = g_my_transportpacket;
+
+ my_transport = g_my_transportpacket->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+ CU_ASSERT_EQUAL(my_transport->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport->control);
+ CU_ASSERT_PTR_NULL(my_transport->value);
+
+ /* we relinquish memory for mycontrolobj and myvalueobj here.
+ * the transport_control terminal destination must destroy them, which here
+ * is handled by our transport object destructor when we teardown_this_test()
+ */
+ g_my_messagizer->transport_control (g_my_messagizer, mycontrolobj, myvalueobj);
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_CONTROL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->control);
+ CU_ASSERT_EQUAL(my_transport->control->class_id, MY_CONTROL_CLASSID);
+ CU_ASSERT_EQUAL(my_transport->value->class_id, MY_VALUE_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+ 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_transport_notify
+ * test the transport notify notification plumbing
+ */
+void test_transport_notify(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ my_impl_transportpacket* my_transport = NULL;
+ const int MY_EVENT_CLASSID = 0x5202;
+ etch_object* myeventobj = new_etch_object(MY_EVENT_CLASSID, NULL);
+
+ /* g_my_transportpacket is the i_transportpacket interface
+ * my_impl_transportpacket is the implementing test class
+ */
+ g_my_messagizer->transport = g_my_transportpacket;
+
+ my_transport = g_my_transportpacket->thisx;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+ CU_ASSERT_EQUAL(my_transport->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport->control);
+ CU_ASSERT_PTR_NULL(my_transport->value);
+
+ /* we relinquish memory for myeventobj here.
+ * the transport_control terminal destination must destroy it, which here
+ * is handled by our transport object destructor when we teardown_this_test() */
+ g_my_messagizer->transport_notify (g_my_messagizer, myeventobj);
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_NOTIFY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->eventx);
+ CU_ASSERT_EQUAL(my_transport->eventx->class_id, MY_EVENT_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+ 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_transport_query
+ * test the transport query notification plumbing
+ */
+void test_transport_query(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ my_impl_transportpacket* my_transport = NULL;
+ const int MY_QUERY_CLASSID = 0x5203, MY_RESULT_CLASSID = 0x5204;
+ etch_object* myqueryobj = new_etch_object(MY_QUERY_CLASSID, NULL);
+ etch_object* myresultobj = new_etch_object(MY_RESULT_CLASSID, NULL);
+ etch_object* queryresult = NULL;
+
+ /* g_my_transportpacket is the i_transportpacket interface
+ * my_impl_transportpacket is the implementing test class
+ */
+ g_my_messagizer->transport = g_my_transportpacket;
+
+ my_transport = g_my_transportpacket->thisx;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+ CU_ASSERT_EQUAL(my_transport->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport->control);
+ CU_ASSERT_PTR_NULL(my_transport->value);
+
+ /* we relinquish myresultobj here. see comments following */
+ my_transport->query_result = myresultobj;
+
+ /* we relinquish memory for myqueryobj here and assume queryresult.
+ * the transport_control terminal destination must destroy it, which here
+ * is handled by our transport object destructor when we teardown_this_test() */
+ queryresult = g_my_messagizer->transport_query (g_my_messagizer, myqueryobj);
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_QUERY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->query);
+ CU_ASSERT_EQUAL(my_transport->query->class_id, MY_QUERY_CLASSID);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(queryresult);
+ CU_ASSERT_EQUAL(queryresult->class_id, MY_RESULT_CLASSID);
+ queryresult->destroy(queryresult);
+
+ } while(0);
+
+ teardown_this_test();
+
+ 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 messagizer", init_suite, clean_suite);
+ CU_set_output_filename("../test_messagizer");
+ etch_watch_id = 0;
+
+ CU_add_test(pSuite, "test transportpacket impl constructor", test_transportpacket_constructor);
+ CU_add_test(pSuite, "test sessionmessage impl constructor", test_sessionmessage_constructor);
+ CU_add_test(pSuite, "test messagizer constructor", test_messagizer_constructor);
+ CU_add_test(pSuite, "test test setup and teardown v1", test_testsetup_teardown_a);
+ CU_add_test(pSuite, "test test setup and teardown v2", test_testsetup_teardown_b);
+
+ CU_add_test(pSuite, "test packet 1", test_packet_1);
+ CU_add_test(pSuite, "test packet 2", test_packet_2);
+ CU_add_test(pSuite, "test packet 3", test_packet_2);
+ CU_add_test(pSuite, "test message 1", test_message_1);
+ CU_add_test(pSuite, "test message 2", test_message_2);
+
+ CU_add_test(pSuite, "test session control",test_session_control);
+ CU_add_test(pSuite, "test session notify", test_session_notify);
+ CU_add_test(pSuite, "test session query", test_session_query);
+ CU_add_test(pSuite, "test transport control",test_transport_control);
+ CU_add_test(pSuite, "test transport notify", test_transport_notify);
+ CU_add_test(pSuite, "test transport query", test_transport_query);
+
+ 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(); printf("\n"); }
+ CU_cleanup_registry();
+ return CU_get_error();
+}
\ No newline at end of file