From 5e293402f6a34721c68709dde185b1d5578cb74f Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Tue, 27 May 2008 17:47:24 +0200
Subject: [PATCH] Testsuite: added support for test object members.

---
 src/testsuite/cmd-test-set.c        |  14 ++-
 src/testsuite/ext-testsuite.c       |   6 +-
 src/testsuite/tests/testsuite.sieve |   2 +
 src/testsuite/testsuite-common.c    |  15 ++++
 src/testsuite/testsuite-common.h    |   4 +
 src/testsuite/testsuite-objects.c   | 135 ++++++++++++++++++++++++++--
 src/testsuite/testsuite-objects.h   |   5 ++
 7 files changed, 167 insertions(+), 14 deletions(-)

diff --git a/src/testsuite/cmd-test-set.c b/src/testsuite/cmd-test-set.c
index 8dc5a9529..7506177db 100644
--- a/src/testsuite/cmd-test-set.c
+++ b/src/testsuite/cmd-test-set.c
@@ -144,10 +144,12 @@ static bool cmd_test_set_operation_execute
 {
 	const struct testsuite_object *object;
 	string_t *value;
+	int member_id;
 
 	t_push();
 	
-	if ( (object=testsuite_object_read(renv->sbin, address)) == NULL ) {
+	if ( (object=testsuite_object_read_member(renv->sbin, address, &member_id)) 
+		== NULL ) {
 		t_pop();
 		return FALSE;
 	}
@@ -157,9 +159,15 @@ static bool cmd_test_set_operation_execute
 		return FALSE;
 	}
 
-	printf(">> TEST SET %s = \"%s\"\n", object->identifier, str_c(value));
+	printf(">> TEST SET %s = \"%s\"\n", 
+		testsuite_object_member_name(object, member_id), str_c(value));
 	
-	object->set_member(-1, value);
+	if ( object->set_member == NULL ) {
+		t_pop();
+		return FALSE;
+	}
+		
+	object->set_member(member_id, value);
 	
 	t_pop();
 	
diff --git a/src/testsuite/ext-testsuite.c b/src/testsuite/ext-testsuite.c
index 2608e4d86..11100afe0 100644
--- a/src/testsuite/ext-testsuite.c
+++ b/src/testsuite/ext-testsuite.c
@@ -7,9 +7,9 @@
  * Implementation: skeleton
  * Status: under development
  * Purpose: This custom extension is used to add sieve commands that act
- *   on the test suite. This provides the ability to specify and change the input 
- *   message inside the script and to fail validation, code generation and 
- *   particularly execution based on predicates.
+ *   on the test suite. This provides the ability to specify and change the 
+ *   input message inside the script and to fail validation, code generation and 
+ *   execution based on predicates.
  */
 
 #include <stdio.h>
diff --git a/src/testsuite/tests/testsuite.sieve b/src/testsuite/tests/testsuite.sieve
index a5f350c2c..6a260b4c6 100644
--- a/src/testsuite/tests/testsuite.sieve
+++ b/src/testsuite/tests/testsuite.sieve
@@ -8,6 +8,7 @@ Subject: Frop!
 Frop!
 .
 ;
+test_set "envelope.from" "stephan@rename-it.nl";
 
 if not header :contains "from" "rename-it.nl" {
 	discard;
@@ -22,6 +23,7 @@ Subject: Friep!
 Friep!
 .
 ;
+test_set "envelope.from" "stephan@rename-it.nl";
 
 if not header :is "from" "nico@vestingbar.nl" {
     discard;
diff --git a/src/testsuite/testsuite-common.c b/src/testsuite/testsuite-common.c
index ff3e2317a..b03846b65 100644
--- a/src/testsuite/testsuite-common.c
+++ b/src/testsuite/testsuite-common.c
@@ -102,6 +102,21 @@ void testsuite_message_deinit(void)
 	mail_raw_deinit();
 }
 
+void testsuite_envelope_set_sender(const char *value)
+{
+	testsuite_msgdata.return_path = value;
+}
+
+void testsuite_envelope_set_recipient(const char *value)
+{
+	testsuite_msgdata.to_address = value;
+}
+
+void testsuite_envelope_set_auth_user(const char *value)
+{
+	testsuite_msgdata.auth_user = value;
+}
+
 /* 
  * Validator context 
  */
diff --git a/src/testsuite/testsuite-common.h b/src/testsuite/testsuite-common.h
index 0cac40c8b..d2e62e931 100644
--- a/src/testsuite/testsuite-common.h
+++ b/src/testsuite/testsuite-common.h
@@ -14,6 +14,10 @@ void testsuite_message_deinit(void);
 
 void testsuite_message_set(string_t *message);
 
+void testsuite_envelope_set_sender(const char *value);
+void testsuite_envelope_set_recipient(const char *value);
+void testsuite_envelope_set_auth_user(const char *value);
+
 /* Testsuite validator context */
 
 struct testsuite_validator_context {
diff --git a/src/testsuite/testsuite-objects.c b/src/testsuite/testsuite-objects.c
index 5ddf0fda4..d4dda7b91 100644
--- a/src/testsuite/testsuite-objects.c
+++ b/src/testsuite/testsuite-objects.c
@@ -131,10 +131,15 @@ const struct sieve_operand testsuite_object_operand = {
 };
 
 static void testsuite_object_emit
-	(struct sieve_binary *sbin, const struct testsuite_object *obj, int ext_id)
+(struct sieve_binary *sbin, const struct testsuite_object *obj, int ext_id,
+	int member_id)
 { 
 	(void) sieve_operand_emit_code(sbin, obj->operand, ext_id);	
 	(void) sieve_binary_emit_byte(sbin, obj->code);
+	
+	if ( obj->get_member_id != NULL ) {
+		(void) sieve_binary_emit_byte(sbin, (unsigned char) member_id);
+	}
 }
 
 const struct testsuite_object *testsuite_object_read
@@ -159,18 +164,55 @@ const struct testsuite_object *testsuite_object_read
 		(struct testsuite_object, intf->testsuite_objects, obj_code);
 }
 
+const struct testsuite_object *testsuite_object_read_member
+(struct sieve_binary *sbin, sieve_size_t *address, int *member_id)
+{
+	const struct testsuite_object *object;
+		
+	if ( (object = testsuite_object_read(sbin, address)) == NULL )
+		return NULL;
+		
+	*member_id = -1;
+	if ( object->get_member_id != NULL ) {
+		if ( !sieve_binary_read_code(sbin, address, member_id) ) 
+			return NULL;
+	}
+	
+	return object;
+}
+
+const char *testsuite_object_member_name
+(const struct testsuite_object *object, int member_id)
+{
+	const char *member = NULL;
+
+	if ( object->get_member_id != NULL ) {
+		if ( object->get_member_name != NULL )
+			member = object->get_member_name(member_id);
+	} else 
+		return object->identifier;
+		
+	if ( member == NULL )	
+		return t_strdup_printf("%s.%d", object->identifier, member_id);
+	
+	return t_strdup_printf("%s.%s", object->identifier, member);
+}
+
 bool testsuite_object_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
 	const struct testsuite_object *object;
+	int member_id;
 
 	sieve_code_mark(denv);
 		
-	if ( (object = testsuite_object_read(denv->sbin, address)) == NULL )
+	if ( (object = testsuite_object_read_member(denv->sbin, address, &member_id)) 
+		== NULL )
 		return FALSE;
 	
-	sieve_code_dumpf(denv, "TESTSUITE_OBJECT: %s", object->identifier);
-		
+	sieve_code_dumpf(denv, "TESTSUITE_OBJECT: %s", 
+		testsuite_object_member_name(object, member_id));
+	
 	return TRUE;
 }
 
@@ -188,6 +230,7 @@ const struct sieve_argument testsuite_object_argument =
 struct testsuite_object_argctx {
 	const struct testsuite_object *object;
 	int ext_id;
+	int member;
 };
 
 bool testsuite_object_argument_activate
@@ -196,9 +239,20 @@ bool testsuite_object_argument_activate
 {
 	const char *objname = sieve_ast_argument_strc(arg);
 	const struct testsuite_object *object;
-	int ext_id;
+	int ext_id, member_id;
+	const char *member;
 	struct testsuite_object_argctx *ctx;
 	
+	/* Parse the object specifier */
+	
+	member = strchr(objname, '.');
+	if ( member != NULL ) {
+		objname = t_strdup_until(objname, member);
+		member++;
+	}
+	
+	/* Find the object */
+	
 	object = testsuite_object_find(valdtr, objname, &ext_id);
 	if ( object == NULL ) {
 		sieve_command_validate_error(valdtr, cmd, 
@@ -206,9 +260,24 @@ bool testsuite_object_argument_activate
 		return FALSE;
 	}
 	
+	/* Find the object member */
+	
+	member_id = -1;
+	if ( member != NULL ) {
+		if ( object->get_member_id == NULL || 
+			(member_id=object->get_member_id(member)) == -1 ) {
+			sieve_command_validate_error(valdtr, cmd, 
+				"member '%s' does not exist for testsuite object '%s'", member, objname);
+			return FALSE;
+		}
+	}
+	
+	/* Assign argument context */
+	
 	ctx = p_new(sieve_command_pool(cmd), struct testsuite_object_argctx, 1);
 	ctx->object = object;
 	ctx->ext_id = ext_id;
+	ctx->member = member_id;
 	
 	arg->argument = &testsuite_object_argument;
 	arg->context = (void *) ctx;
@@ -224,7 +293,7 @@ static bool arg_testsuite_object_generate
 	struct testsuite_object_argctx *ctx = 
 		(struct testsuite_object_argctx *) arg->context;
 	
-	testsuite_object_emit(sbin, ctx->object, ctx->ext_id);
+	testsuite_object_emit(sbin, ctx->object, ctx->ext_id, ctx->member);
 		
 	return TRUE;
 }
@@ -235,18 +304,28 @@ static bool arg_testsuite_object_generate
  
 static bool tsto_message_set_member(int id, string_t *value);
 
+static int tsto_envelope_get_member_id(const char *identifier);
+static const char *tsto_envelope_get_member_name(int id);
+static bool tsto_envelope_set_member(int id, string_t *value);
+
+
 const struct testsuite_object message_testsuite_object = { 
 	"message",
 	TESTSUITE_OBJECT_MESSAGE,
 	&testsuite_object_operand,
-	NULL, tsto_message_set_member, NULL
+	NULL, NULL, 
+	tsto_message_set_member, 
+	NULL
 };
 
 const struct testsuite_object envelope_testsuite_object = { 
 	"envelope",
 	TESTSUITE_OBJECT_ENVELOPE,
 	&testsuite_object_operand,
-	NULL, NULL, NULL
+	tsto_envelope_get_member_id, 
+	tsto_envelope_get_member_name,
+	tsto_envelope_set_member, 
+	NULL
 };
 
 static bool tsto_message_set_member(int id, string_t *value) 
@@ -257,3 +336,43 @@ static bool tsto_message_set_member(int id, string_t *value)
 	
 	return TRUE;
 }
+
+static int tsto_envelope_get_member_id(const char *identifier)
+{
+	if ( strcasecmp(identifier, "from") == 0 )
+		return 0;
+	if ( strcasecmp(identifier, "to") == 0 )
+		return 1;
+	if ( strcasecmp(identifier, "auth") == 0 )
+		return 2;	
+	
+	return -1;
+}
+
+static const char *tsto_envelope_get_member_name(int id) 
+{
+	switch ( id ) {
+	case 0: return "from";
+	case 1: return "to";
+	case 2: return "auth";
+	}
+	
+	return NULL;
+}
+
+static bool tsto_envelope_set_member(int id, string_t *value)
+{
+	switch ( id ) {
+	case 0: 
+		testsuite_envelope_set_sender(str_c(value));
+		return TRUE;
+	case 1:
+		testsuite_envelope_set_recipient(str_c(value));
+		return TRUE;
+	case 2: 
+		testsuite_envelope_set_auth_user(str_c(value));
+		return TRUE;
+	}
+	
+	return FALSE;
+}
diff --git a/src/testsuite/testsuite-objects.h b/src/testsuite/testsuite-objects.h
index a8e2b97d1..6719afd18 100644
--- a/src/testsuite/testsuite-objects.h
+++ b/src/testsuite/testsuite-objects.h
@@ -19,6 +19,7 @@ struct testsuite_object {
 	const struct sieve_operand *operand;
 	
 	int (*get_member_id)(const char *identifier);
+	const char *(*get_member_name)(int id);
 	bool (*set_member)(int id, string_t *value);
 	string_t *(*get_member)(int id);
 };
@@ -43,6 +44,10 @@ bool testsuite_object_argument_activate
 
 const struct testsuite_object *testsuite_object_read
   (struct sieve_binary *sbin, sieve_size_t *address);
+const struct testsuite_object *testsuite_object_read_member
+  (struct sieve_binary *sbin, sieve_size_t *address, int *member_id);
+const char *testsuite_object_member_name
+	(const struct testsuite_object *object, int member_id);
 bool testsuite_object_dump
 	(const struct sieve_dumptime_env *denv, sieve_size_t *address);
 
-- 
GitLab