changeset 272:c0c2a994da8e

Simplified dispatch command registration; changed behavior of dict_search
author Sebastien Decugis <sdecugis@nict.go.jp>
date Wed, 17 Dec 2008 15:47:05 +0900
parents 237d245fd336
children 37c6a1be18cb
files include/waaad/dictionary-api.h include/waaad/dispatch-api.h waaad/dictionary.c waaad/dispatch.c waaad/tests/testdisp.c
diffstat 5 files changed, 401 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/include/waaad/dictionary-api.h	Wed Dec 17 11:34:35 2008 +0900
+++ b/include/waaad/dictionary-api.h	Wed Dec 17 15:47:05 2008 +0900
@@ -104,12 +104,12 @@
  *   Perform a search in the dictionary. 
  *   See the object-specific sections bellow to find how to look for each objects.
  *   If the "result" parameter is NULL, the function is used to check if an object is in the dictionary.
- *   Otherwise, a reference to the object is stored in result.
+ *   Otherwise, a reference to the object is stored in result if found.
  *
  * RETURN VALUE:
- *  0      	: The object has been found in the dictionary.
+ *  0      	: The object has been found in the dictionary, or *result is NULL.
  *  EINVAL 	: A parameter is invalid.
- *  ENOENT	: No matching object has been found.
+ *  ENOENT	: No matching object has been found, and result was NULL.
  */
 int dict_search ( dict_object_type_t type, int criteria, void * what, dict_object_t **result );
 
--- a/include/waaad/dispatch-api.h	Wed Dec 17 11:34:35 2008 +0900
+++ b/include/waaad/dispatch-api.h	Wed Dec 17 15:47:05 2008 +0900
@@ -98,8 +98,7 @@
 typedef enum {
 	DISP_REG_ANY = 1,		/* The callback is called on any message. This should be only used for debug. */
 	DISP_REG_APPID,			/* The callback is called for any message with the specified application-id */
-	DISP_REG_CC,			/* The callback is called for message of the specified command-code (requests & answers). App id may be specified. */
-	DISP_REG_CC_FLAG,		/* The callback is called for message of the specified command-code and flag (request, answer, or error). App id may be specified. */
+	DISP_REG_CC,			/* The callback is called for message of the specified command-code (request or answer). App id may be specified. */
 	DISP_REG_AVP,			/* The callback is called for messages containing a specific AVP. Command-code and App id may be specified. */
 	DISP_REG_AVP_ENUMVAL		/* The callback is called for messages containing a specific AVP with a specific enumerated value. Command-code and App id may be specified. */
 } disp_reg_t;
@@ -110,7 +109,7 @@
   
   DISP_REG_ANY
   DISP_REG_AVP_ENUMVAL & DISP_REG_AVP
-  DISP_REG_CC_FLAG & DISP_REG_CC
+  DISP_REG_CC
   DISP_REG_APPID
 */
 
@@ -119,8 +118,6 @@
 	uint32_t	flags; /* 1: registering the Auth part of application; 2: registering the Acct part of application. Only used for DISP_REG_APPID */
 	dict_object_t *	app_id;
 	dict_object_t *	command;
-	uint8_t		flag_mask;
-	uint8_t		flag_val;
 	dict_object_t *	avp;
 	dict_object_t *	value;
 } disp_reg_val_t;
@@ -139,22 +136,14 @@
  *  Only the "app_id" field must be set, other fields are ignored. It points to a dictionary object of type DICT_APPLICATION.
  *
  * DISP_REG_CC.
- *  The "command" field must be defined and point to a dictionary object of type DICT_COMMAND (it may be the request or the answer object).
+ *  The "command" field must be defined and point to a dictionary object of type DICT_COMMAND.
  *  The "app_id" may be also set. In the case it is set, it restricts the callback to be called only with this command-code and app id.
  *  The other fields are ignored.
  *
- * DISP_REG_CC_FLAG.
- *  The "command" code must be set as previously. The "app_id" field may be set as previously.
- *  In addition, the flag_mask and flag_val must be set.
- *  The flag_mask specifies the bit mask of the message that must be checked.
- *  The flag_val specifies the values of the flags that must be matched.
- *  The other fields are ignored.
- *
  * DISP_REG_AVP.
  *  The "avp" field of the structure must be set and point to a dictionary object of type DICT_AVP.
  *  The "app_id" field may be set to restrict the messages matching to a specific app id.
  *  The "command" field may also be set to a valid DICT_COMMAND object.
- *  The flags may be set also as previously.
  *  The content of the "value" field is ignored.
  *
  * DISP_REG_AVP_ENUMVAL.
@@ -163,12 +152,11 @@
  *
  * Here is a sumary of the fields: ( M : must be set; m : may be set, or NULL/0 to ignore; 0 : ignored )
  *
- *  field:     app_id    command   flag_mask    avp    value
- * APPID :       M          0          0         0       0
- * CC    :       m          M          0         0       0
- * CC_FLA:       m          M          M         0       0
- * AVP   :       m          m          m         M       0
- * AVP_EN:       m          m          m         M       M
+ *  field:     app_id    command     avp    value
+ * APPID :       M          0         0       0
+ * CC    :       m          M         0       0
+ * AVP   :       m          m         M       0
+ * AVP_EN:       m          m         M       M
  */
  
 
--- a/waaad/dictionary.c	Wed Dec 17 11:34:35 2008 +0900
+++ b/waaad/dictionary.c	Wed Dec 17 15:47:05 2008 +0900
@@ -583,6 +583,8 @@
 
 /* Functions used to search for objects in the lists, according to some criteria */
 
+/* On a general note, if result is not NULL, ENOENT is not returned but *result is NULL. */
+
 /* The following macros assume that "what", "ret", "result" (variables), and "end" (label) exist
 in the local context where they are called. They are meant to be called only from the functions that follow. */
 
@@ -602,18 +604,21 @@
 	char * __str = (char *) str;						\
 	int __cmp;								\
 	uti_list_t * __li;							\
+	ret = 0;								\
 	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
 		__cmp = strcmp(__str, _O(__li->next->o)->data. datafield );	\
 		if (__cmp == 0) {						\
 			if (result)						\
 				*result = _O(__li->next->o);			\
-			ret = 0;						\
 			goto end;						\
 		}								\
 		if ((isindex) && (__cmp < 0))					\
 			break;							\
 	}									\
-	ret = ENOENT;								\
+	if (result)								\
+		*result = NULL;							\
+	else									\
+		ret = ENOENT;							\
 }
 /* For search of octetstrings in lists (not \0 terminated). */
 #define SEARCH_ocstring( ostr, length, sentinel, osdatafield, isindex ) {	\
@@ -621,6 +626,7 @@
 	int __cmp;								\
 	size_t __len;								\
 	uti_list_t * __li;							\
+	ret = 0;								\
 	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
 		__len = _O(__li->next->o)->data. osdatafield .len;		\
 		if ( __len > (length) )						\
@@ -635,43 +641,48 @@
 		if (__cmp == 0) {						\
 			if (result)						\
 				*result = _O(__li->next->o);			\
-			ret = 0;						\
 			goto end;						\
 		}								\
 		if ((isindex) && (__cmp < 0))					\
 			break;							\
 	}									\
-	ret = ENOENT;								\
+	if (result)								\
+		*result = NULL;							\
+	else									\
+		ret = ENOENT;							\
 }
 /* For search of AVP name in rule lists. */
 #define SEARCH_ruleavpname( str, sentinel ) {					\
 	char * __str = (char *) str;						\
 	int __cmp;								\
 	uti_list_t * __li;							\
+	ret = 0;								\
 	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
 		__cmp = strcmp(__str, 						\
 		  _O(_O(__li->next->o)->data.rule.rule_avp)->data.avp.avp_name);\
 		if (__cmp == 0) {						\
 			if (result)						\
 				*result = _O(__li->next->o);			\
-			ret = 0;						\
 			goto end;						\
 		}								\
 		if (__cmp < 0)							\
 			break;							\
 	}									\
-	ret = ENOENT;								\
+	if (result)								\
+		*result = NULL;							\
+	else									\
+		ret = ENOENT;							\
 }
 
 /* For search of scalars in lists. isindex= 1 if the value is the ordering key of the list */
 #define SEARCH_scalar( value, sentinel, datafield, isindex, defaultobj ) {	\
 	int __cmp;								\
 	uti_list_t * __li;							\
+	ret = 0;								\
 	if (  ((defaultobj) != NULL) 						\
 		   && (_O(defaultobj)->data. datafield  == value)) {		\
 		if (result)							\
 			*result = _O(defaultobj);				\
-		ret = 0;							\
 		goto end;							\
 	}									\
 	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
@@ -679,19 +690,22 @@
 		if (__cmp == 0) {						\
 			if (result)						\
 				*result = _O(__li->next->o);			\
-			ret = 0;						\
 			goto end;						\
 		}								\
 		if ((isindex) && (__cmp < 0))					\
 			break;							\
 	}									\
-	ret = ENOENT;								\
+	if (result)								\
+		*result = NULL;							\
+	else									\
+		ret = ENOENT;							\
 }
 
 /* For search of commands in lists by code and flag. R_flag_val = 0 or CMD_FLAG_REQUEST */
 #define SEARCH_codefl( value, R_flag_val) {					\
 	int __cmp;								\
 	uti_list_t * __li;							\
+	ret = 0;								\
 	for  (	  __li = &g_list_cmd_code; 					\
 		  __li->next != &g_list_cmd_code; 				\
 		  __li = __li->next) {						\
@@ -707,13 +721,15 @@
 				continue;					\
 			if (result)						\
 				*result = _O(__li->next->o);			\
-			ret = 0;						\
 			goto end;						\
 		}								\
 		if (__cmp < 0)							\
 			break;							\
 	}									\
-	ret = ENOENT;								\
+	if (result)								\
+		*result = NULL;							\
+	else									\
+		ret = ENOENT;							\
 }
 
 
--- a/waaad/dispatch.c	Wed Dec 17 11:34:35 2008 +0900
+++ b/waaad/dispatch.c	Wed Dec 17 15:47:05 2008 +0900
@@ -135,6 +135,8 @@
 }
 		
 
+/**************************************************************************************/
+
 /* Dispatch thread */
 static void * disp_th(void * arg)
 {
@@ -241,11 +243,6 @@
 					if ((hdl->how == DISP_REG_AVP_ENUMVAL) && (hdl->when.value != avp_constant))
 						continue;
 					
-					if ((hdl->when.flag_mask != 0) && (
-						( hdl->when.flag_mask & hdl->when.flag_val ) != ( hdl->when.flag_mask & hdr->msg_flags )
-							))
-						continue;
-					
 					if ((hdl->when.command != NULL) && (hdl->when.command != msg_cmd))
 						continue;
 					
@@ -267,11 +264,6 @@
 
 			ASSERT( hdl->when.command == msg_cmd );
 
-			if ((hdl->how == DISP_REG_CC_FLAG) && (hdl->when.flag_mask != 0) && (
-				( hdl->when.flag_mask & hdl->when.flag_val ) != ( hdl->when.flag_mask & hdr->msg_flags )
-					))
-				continue;
-
 			if ((hdl->when.app_id != NULL) && (hdl->when.app_id != msg_app))
 				continue;
 
@@ -375,7 +367,6 @@
 			break;
 			
 		case DISP_REG_CC:
-		case DISP_REG_CC_FLAG:
 			CHECK_FCT( dict_disp_cb(DICT_COMMAND, ((disp_reg_val_t *)when)->command, &where) );
 			break;
 			
--- a/waaad/tests/testdisp.c	Wed Dec 17 11:34:35 2008 +0900
+++ b/waaad/tests/testdisp.c	Wed Dec 17 15:47:05 2008 +0900
@@ -37,16 +37,377 @@
 
 /* Test for the dispatch module */
 
+/* vendor id */
+#define T_VID	735671
+/* app ids */
+#define T_AID1	735674	/* Vendor, auth + acct */
+#define T_AID2	735677	/* auth only */
+#define T_AID3	735675	/* auth + acct */
+/* command ids */
+#define T_CID1	735679
+#define T_CID2	735680
+/* AVP ids */
+#define T_AVID1	735681
+#define T_AVID2	735682
+/* Enum val Ids */
+#define T_EV1	735688
+#define T_EV2	735689
+
+static pthread_mutex_t test_mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  test_cnd = PTHREAD_COND_INITIALIZER;
+static uint32_t cb_called = 0;
+
+#define DEF_CB_DISP( NR, RET )						\
+static int disp_cb ## NR ( msg_t * msg, msg_avp_t * avp, int handled )	\
+{									\
+	CHECK( 0, pthread_mutex_lock(&test_mtx) );			\
+	cb_called |= 1<< NR ;						\
+	CHECK( 0, pthread_cond_signal(&test_cnd) );			\
+	CHECK( 0, pthread_mutex_unlock(&test_mtx) );			\
+	return RET;							\
+}
+/* The callbacks to be called by the dispatch thread */
+DEF_CB_DISP( 0, DISP_CBRET_CONTINUE );
+DEF_CB_DISP( 1, DISP_CBRET_HANDLED_CONTINUE );
+DEF_CB_DISP( 2, DISP_CBRET_HANDLED_CONTINUE );
+DEF_CB_DISP( 3, DISP_CBRET_HANDLED_CONTINUE );
+DEF_CB_DISP( 4, DISP_CBRET_CONTINUE );
+DEF_CB_DISP( 5, DISP_CBRET_CONTINUE );
+DEF_CB_DISP( 6, DISP_CBRET_CONTINUE );
+DEF_CB_DISP( 7, DISP_CBRET_CONTINUE );
+DEF_CB_DISP( 8, DISP_CBRET_CONTINUE );
+DEF_CB_DISP( 9, DISP_CBRET_CONTINUE );
+DEF_CB_DISP(10, DISP_CBRET_HANDLED_STOP );
+
+/* Create a new message to pass to dispatch module */
+static void dispatch_new_message(
+		int appid, 
+		dict_object_t * cmd, 
+		dict_object_t * avp1,
+		int val1, 
+		dict_object_t * avp2, /* or NULL */
+		int val2 )
+{
+	msg_t * msg = NULL;
+	msg_avp_t * avp = NULL;
+	msg_data_t * msgdata = NULL;
+	avp_value_t value = { .i32 = val1 };
+	
+	/* Create the message */
+	CHECK( 0, msg_new ( cmd, MSGFL_ALLOW_ETEID, &msg ) );
+	CHECK( 0, msg_data ( msg, &msgdata ) );	
+	msgdata->msg_appl = appid;
+	
+	/* Add AVP1 */
+	CHECK( 0, msg_avp_new ( avp1, 0, &avp ) );
+	CHECK( 0, msg_avp_setvalue ( avp, &value ) );
+	CHECK( 0, msg_avp_add ( msg, MSG_BRW_FIRST_CHILD, avp ) );
+	
+	/* Add AVP2 */
+	if (avp2) {
+		CHECK( 0, msg_avp_new ( avp2, 0, &avp ) );
+		value.i32 = val2;
+		CHECK( 0, msg_avp_setvalue ( avp, &value ) );
+		CHECK( 0, msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp ) );
+	}
+	
+	/* reset cb_called */
+	cb_called = 0;
+	
+	/* Push to the queue for dispatch module */
+	CHECK( 0, meq_post( g_meq_local, msg ) );
+	
+	return;
+}
+
+static void wait_for_val( uint32_t yes, uint32_t no )
+{
+	/* reset the timer */
+	alarm(2);
+	
+	CHECK( 0, pthread_mutex_lock(&test_mtx) );
+
+	do {
+		/* Check if any cb from the "no" mask was called */
+		CHECK( 0, cb_called & no );
+
+		/* Check if all cb from "yes" mask were called */
+		if ( (yes & cb_called) == yes )
+			break;
+		
+		#if 0
+		fprintf(stderr,
+				"%s:%-4d: log ( cb_called == %d )\n",
+				__FILE__,
+				__LINE__,
+				cb_called);
+		#endif
+		
+		CHECK( 0, pthread_cond_wait( &test_cnd, &test_mtx ) );
+	} while (1);
+	
+	CHECK( 0, pthread_mutex_unlock(&test_mtx) );
+}
 
 
 /* Main test routine */
 int main(int argc, char *argv[])
 {
+	disp_cb_hdl_t * hdl1 = NULL;
+	disp_cb_hdl_t * hdl2 = NULL;
+	disp_cb_hdl_t * hdl3 = NULL;
+	
+	dict_object_t * vendor = NULL;
+	dict_object_t * app1 = NULL;
+	dict_object_t * app2 = NULL;
+	dict_object_t * app3 = NULL;
+	dict_object_t * cmd1r = NULL;
+	dict_object_t * cmd1a = NULL;
+	dict_object_t * cmd2 = NULL;
+	dict_object_t * avp1 = NULL;
+	dict_object_t * avp2 = NULL;
+	dict_object_t * enu1 = NULL;
+	dict_object_t * enu2 = NULL;
+	
 	
 	/* First, initialize the daemon modules */
 	INIT_WAAAD();
 	CONFIG_WAAAD();
 	
+	/* Create the dictionary objects */
+	{
+		{
+			dict_vendor_data_t vendor_data    = { T_VID,  "Vendor for test" };
+			CHECK( 0, dict_new ( DICT_VENDOR, &vendor_data , NULL, &vendor ) );
+		}
+		{
+			dict_application_data_t app_data1 = { T_AID1, "Application 1 for test" };
+			dict_application_data_t app_data2 = { T_AID2, "Application 2 for test" };
+			dict_application_data_t app_data3 = { T_AID3, "Application 3 for test" };
+			CHECK( 0, dict_new ( DICT_APPLICATION, &app_data1 , vendor, &app1 ) );
+			CHECK( 0, dict_new ( DICT_APPLICATION, &app_data2 , NULL,   &app2 ) );
+			CHECK( 0, dict_new ( DICT_APPLICATION, &app_data3 , NULL,   &app3 ) );
+		}
+		{
+			dict_cmd_data_t	cmd_data1_r = { 
+				T_CID1, 
+				"Command 1R for test", 
+				CMD_FLAG_PROXIABLE | CMD_FLAG_REQUEST, 
+				CMD_FLAG_PROXIABLE | CMD_FLAG_REQUEST 
+				};
+			dict_cmd_data_t	cmd_data1_a = { 
+				T_CID1, 
+				"Command 1A for test", 
+				CMD_FLAG_PROXIABLE | CMD_FLAG_REQUEST, 
+				CMD_FLAG_PROXIABLE 
+				};
+			dict_cmd_data_t	cmd_data2 = { 
+				T_CID2, 
+				"Command 2 for test", 
+				CMD_FLAG_PROXIABLE | CMD_FLAG_REQUEST, 
+				CMD_FLAG_PROXIABLE 
+				};
+			CHECK( 0, dict_new ( DICT_COMMAND, &cmd_data1_r , NULL,   &cmd1r ) );
+			CHECK( 0, dict_new ( DICT_COMMAND, &cmd_data1_a , NULL,   &cmd1a ) );
+			CHECK( 0, dict_new ( DICT_COMMAND, &cmd_data2   , NULL,   &cmd2 ) );
+		}
+		{
+			dict_object_t * type = NULL;
+			dict_type_data_t tdata = {
+				AVP_TYPE_INTEGER32,
+				"Type for test",
+				NULL,
+				NULL
+			};
+			dict_type_enum_data_t te1data = {
+				"Enum val 1 for test",
+				{ .i32 = T_EV1 }
+				};
+			dict_type_enum_data_t te2data = {
+				"Enum val 2 for test",
+				{ .i32 = T_EV2 }
+				};
+			dict_avp_data_t avp1_data = {
+				T_AVID1,
+				T_VID,
+				"AVP 1 for test",
+				AVP_FLAG_VENDOR,
+				AVP_FLAG_VENDOR,
+				AVP_TYPE_INTEGER32
+				};
+			dict_avp_data_t avp2_data = {
+				T_AVID2,
+				0,
+				"AVP 2 for test",
+				0,
+				0,
+				AVP_TYPE_INTEGER32
+				};
+			CHECK( 0, dict_new ( DICT_TYPE, &tdata , app1, &type ) );
+			CHECK( 0, dict_new ( DICT_TYPE_ENUM, &te1data , type, &enu1 ) );
+			CHECK( 0, dict_new ( DICT_TYPE_ENUM, &te2data , type, &enu2 ) );
+			CHECK( 0, dict_new ( DICT_AVP, &avp1_data , type, &avp1 ) );
+			CHECK( 0, dict_new ( DICT_AVP, &avp2_data , type, &avp2 ) );
+		}
+	}
+	
+	/* Test registration of callbacks */
+	{
+		disp_cb_hdl_t * hdl = NULL;
+		disp_reg_val_t  regdata;
+		
+		CHECK( 0, disp_register( disp_cb0, DISP_REG_ANY, NULL, &hdl ) );
+		CHECK( 0, disp_unregister( hdl ) );
+		CHECK( 0, disp_register( disp_cb0, DISP_REG_ANY, NULL, &hdl ) );
+		
+		memset( &regdata, 0, sizeof(disp_reg_val_t) );
+		regdata.flags = 0x3;
+		regdata.app_id = app1;
+		CHECK( 0, disp_register( disp_cb1, DISP_REG_APPID, &regdata, &hdl1 ) );
+		
+		memset( &regdata, 0, sizeof(disp_reg_val_t) );
+		regdata.flags = 0x1;
+		regdata.app_id = app2;
+		CHECK( 0, disp_register( disp_cb2, DISP_REG_APPID, &regdata, &hdl2 ) );
+		
+		memset( &regdata, 0, sizeof(disp_reg_val_t) );
+		regdata.flags = 0x3;
+		regdata.app_id = app3;
+		CHECK( 0, disp_register( disp_cb3, DISP_REG_APPID, &regdata, &hdl3 ) );
+		
+		/* Now check the configuration of waaad is created as expected */
+		CHECK( 5, 		g_conf->supported_apps_nb );
+		CHECK( T_AID1, 		g_conf->supported_apps_list[0].a );
+		CHECK( T_VID,  		g_conf->supported_apps_list[0].v );
+		CHECK( APP_TYPE_AUTH, 	g_conf->supported_apps_list[0].t );
+		CHECK( T_AID1, 		g_conf->supported_apps_list[1].a );
+		CHECK( T_VID,  		g_conf->supported_apps_list[1].v );
+		CHECK( APP_TYPE_ACCT, 	g_conf->supported_apps_list[1].t );
+		CHECK( T_AID3, 		g_conf->supported_apps_list[2].a );
+		CHECK( 0,  		g_conf->supported_apps_list[2].v );
+		CHECK( APP_TYPE_AUTH, 	g_conf->supported_apps_list[2].t );
+		CHECK( T_AID3, 		g_conf->supported_apps_list[3].a );
+		CHECK( 0,  		g_conf->supported_apps_list[3].v );
+		CHECK( APP_TYPE_ACCT, 	g_conf->supported_apps_list[3].t );
+		CHECK( T_AID2, 		g_conf->supported_apps_list[4].a );
+		CHECK( 0,  		g_conf->supported_apps_list[4].v );
+		CHECK( APP_TYPE_AUTH, 	g_conf->supported_apps_list[4].t );
+		
+		/* Register other callbacks with different criteria */
+		memset( &regdata, 0, sizeof(disp_reg_val_t) );
+		regdata.command = cmd2;
+		CHECK( 0, disp_register( disp_cb4, DISP_REG_CC, &regdata, NULL ) );
+		
+		memset( &regdata, 0, sizeof(disp_reg_val_t) );
+		regdata.app_id = app2;
+		regdata.command = cmd1r;
+		CHECK( 0, disp_register( disp_cb5, DISP_REG_CC, &regdata, NULL ) );
+		
+		memset( &regdata, 0, sizeof(disp_reg_val_t) );
+		regdata.app_id = app2;
+		regdata.command = cmd1r;
+		regdata.flag_mask = CMD_FLAG_REQUEST;
+		regdata.flag_val = CMD_FLAG_REQUEST;
+		CHECK( 0, disp_register( disp_cb6, DISP_REG_CC_FLAG, &regdata, NULL ) );
+		
+		memset( &regdata, 0, sizeof(disp_reg_val_t) );
+		regdata.app_id = app2;
+		regdata.command = cmd1r;
+		regdata.avp = avp1;
+		CHECK( 0, disp_register( disp_cb7, DISP_REG_AVP, &regdata, NULL ) );
+		
+		memset( &regdata, 0, sizeof(disp_reg_val_t) );
+		regdata.command = cmd1a;
+		regdata.avp = avp1;
+		regdata.value = enu1;
+		CHECK( 0, disp_register( disp_cb8, DISP_REG_AVP_ENUMVAL, &regdata, NULL ) );
+		
+		memset( &regdata, 0, sizeof(disp_reg_val_t) );
+		regdata.value = enu2;
+		CHECK( EINVAL, disp_register( disp_cb9, DISP_REG_AVP_ENUMVAL, &regdata, NULL ) );
+		
+		memset( &regdata, 0, sizeof(disp_reg_val_t) );
+		regdata.avp = avp1;
+		regdata.value = enu2;
+		CHECK( 0, disp_register( disp_cb9, DISP_REG_AVP_ENUMVAL, &regdata, NULL ) );
+		
+		memset( &regdata, 0, sizeof(disp_reg_val_t) );
+		regdata.app_id = app3;
+		regdata.command = cmd1a;
+		regdata.avp = avp2;
+		regdata.value = enu2;
+		CHECK( 0, disp_register( disp_cb10, DISP_REG_AVP_ENUMVAL, &regdata, NULL ) );
+		
+	}
+	
+	{
+		struct {
+			int appid;
+			dict_object_t * cmd;
+			dict_object_t * avp1;
+			int val1;
+			dict_object_t * avp2;
+			int val2;
+			int cb[11]; /* 0: may, 1: must, -1: must not */
+		} test_matrix[] = {
+		/*	appid	cmd	avp1	val1	avp2	val2	cb0,cb1,cb2,cb3,cb4,cb5,cb6,cb7,cb8,cb9,cb10 */
+		 {	0,	cmd1r,	avp1,	0,	NULL,	0,	{ 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }}
+		,{	0,	cmd1r,	avp1,	T_EV1,	avp1,	0,	{ 1, -1, -1, -1, -1, -1, -1, -1,  1, -1, -1 }}
+		,{	0,	cmd1r,	avp1,	T_EV1,	avp1,	T_EV2,	{ 1, -1, -1, -1, -1, -1, -1, -1,  1,  1, -1 }}
+		,{	0,	cmd1r,	avp1,	T_EV2,	avp1,	T_EV1,	{ 1, -1, -1, -1, -1, -1, -1, -1,  1,  1, -1 }}
+		};
+		int i;
+		
+		for (i=0; i< sizeof(test_matrix) / sizeof(test_matrix[0]); i++) {
+			uint32_t mask_yes = 0;
+			uint32_t mask_no = 0;
+			int j;
+		
+			for (j=0; j < 11; j++) {
+				if (test_matrix[i].cb[j]) {
+					if (test_matrix[i].cb[j] == 1) {
+						mask_yes |= 1 << j;
+					} else {
+						mask_no |= 1 << j;
+					}
+				}
+			}
+			
+			#if 0
+			fprintf(stderr,
+					"%s:%-4d: Starting next test, line %d, yes: %x, no: %x\n",
+					__FILE__,
+					__LINE__,
+					i, mask_yes, mask_no);
+			#endif
+			
+			dispatch_new_message(
+					test_matrix[i].appid,
+					test_matrix[i].cmd,
+					test_matrix[i].avp1,
+					test_matrix[i].val1,
+					test_matrix[i].avp2,
+					test_matrix[i].val2
+					);
+			
+			wait_for_val( mask_yes, mask_no );
+			
+			#if 0
+			fprintf(stderr,
+					"%s:%-4d: Test line %d passed\n",
+					__FILE__,
+					__LINE__,
+					i);
+			#endif
+		}
+	}
+	
+	/* Test de-registration */
+	{
+		CHECK( 0, disp_unregister( hdl1 ) );
+		CHECK( 0, disp_unregister( hdl2 ) );
+		CHECK( 0, disp_unregister( hdl3 ) );
+	}
 	
 	/* That's all for the tests yet */
 	PASSTEST();
"Welcome to our mercurial repository"