From 52d25c526fb9d58d9de312a79dd8165669079a9f Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sun, 25 May 2008 12:25:47 +0200
Subject: [PATCH] Testsuite: implemented testsuite object interface.

---
 src/lib-sieve/sieve-code.h                    |   2 +-
 src/lib-sieve/sieve-extensions-private.h      |   2 +
 src/testsuite/Makefile.am                     |   2 +-
 .../{cmd-test-message.c => cmd-test-set.c}    |  91 ++++--
 src/testsuite/ext-testsuite.c                 |  35 ++-
 src/testsuite/tests/testsuite.sieve           |   4 +-
 src/testsuite/testsuite-common.c              | 284 +++++++++++++++++-
 src/testsuite/testsuite-common.h              |  49 +++
 8 files changed, 420 insertions(+), 49 deletions(-)
 rename src/testsuite/{cmd-test-message.c => cmd-test-set.c} (51%)

diff --git a/src/lib-sieve/sieve-code.h b/src/lib-sieve/sieve-code.h
index 33be0bc5c..8d9900070 100644
--- a/src/lib-sieve/sieve-code.h
+++ b/src/lib-sieve/sieve-code.h
@@ -36,7 +36,7 @@ struct sieve_operand_class {
 struct sieve_operand {
 	const char *name;
 	
-	struct sieve_extension *extension;
+	const struct sieve_extension *extension;
 	unsigned int code;
 	
 	const struct sieve_operand_class *class;
diff --git a/src/lib-sieve/sieve-extensions-private.h b/src/lib-sieve/sieve-extensions-private.h
index 0131c2ce3..249492c1e 100644
--- a/src/lib-sieve/sieve-extensions-private.h
+++ b/src/lib-sieve/sieve-extensions-private.h
@@ -21,6 +21,8 @@ static inline const void *_sieve_extension_get_object
 	}
 	return NULL;
 }
+#define sieve_extension_get_object(type, reg, code) \
+	((const type *) _sieve_extension_get_object(&(reg), code))
 
 static inline sieve_size_t _sieve_extension_emit_obj
 (struct sieve_binary *sbin, 
diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.am
index 3a5a89451..3e2ddb4e4 100644
--- a/src/testsuite/Makefile.am
+++ b/src/testsuite/Makefile.am
@@ -38,7 +38,7 @@ testsuite_LDADD = $(ldadd)
 testsuite_DEPENDENCIES = $(libs)
 
 cmds = \
-	cmd-test-message.c
+	cmd-test-set.c
 
 testsuite_SOURCES = \
 	namespaces.c \
diff --git a/src/testsuite/cmd-test-message.c b/src/testsuite/cmd-test-set.c
similarity index 51%
rename from src/testsuite/cmd-test-message.c
rename to src/testsuite/cmd-test-set.c
index 7dd35f169..925592c00 100644
--- a/src/testsuite/cmd-test-message.c
+++ b/src/testsuite/cmd-test-set.c
@@ -20,54 +20,80 @@
 
 /* Forward declarations */
 
-static bool cmd_test_message_operation_dump
+static bool cmd_test_set_operation_dump
 	(const struct sieve_operation *op,
 		const struct sieve_dumptime_env *denv, sieve_size_t *address);
-static bool cmd_test_message_operation_execute
+static bool cmd_test_set_operation_execute
 	(const struct sieve_operation *op, 
 		const struct sieve_runtime_env *renv, sieve_size_t *address);
 
-static bool cmd_test_message_validate
+static bool cmd_test_set_validate
 	(struct sieve_validator *validator, struct sieve_command_context *cmd);
-static bool cmd_test_message_generate
+static bool cmd_test_set_generate
 	(struct sieve_generator *generator,	struct sieve_command_context *ctx);
 
-/* Test command 
+/* Test_set command 
  * 
  * Syntax
  *   redirect <address: string>
  */
 
-const struct sieve_command cmd_test_message = { 
-	"test_message", 
+const struct sieve_command cmd_test_set = { 
+	"test_set", 
 	SCT_COMMAND,
-	1, 0, FALSE, FALSE, 
+	2, 0, FALSE, FALSE, 
 	NULL, NULL,
-	cmd_test_message_validate, 
-	cmd_test_message_generate, 
+	cmd_test_set_validate, 
+	cmd_test_set_generate, 
 	NULL 
 };
 
-/* Test operation */
+/* Test_set operation */
 
-const struct sieve_operation test_message_operation = { 
-	"TEST_MESSAGE",
+const struct sieve_operation test_set_operation = { 
+	"TEST_SET",
 	&testsuite_extension, 
 	0,
-	cmd_test_message_operation_dump, 
-	cmd_test_message_operation_execute 
+	cmd_test_set_operation_dump, 
+	cmd_test_set_operation_execute 
 };
 
-/* Validation */
+/* Fields */
 
-static bool cmd_test_message_validate
-	(struct sieve_validator *validator, struct sieve_command_context *cmd) 
+enum cmd_test_set_object {
+	CMD_SET_OBJ_MESSAGE,
+	CMD_SET_OBJ_ENVELOPE
+};
+
+enum cmd_test_set_envelope_field {
+	CMD_SET_ENVELOPE_SENDER,
+	CMD_SET_ENVELOPE_RECIPIENT,
+	CMD_SET_ENVELOPE_AUTH_USER
+};
+
+/* 
+ * Validation 
+ */
+ 
+static bool cmd_test_set_validate
+(struct sieve_validator *validator, struct sieve_command_context *cmd) 
 {
 	struct sieve_ast_argument *arg = cmd->first_positional;
 
-	/* Check argument */
+	/* Check arguments */
+	
 	if ( !sieve_validate_positional_argument
-		(validator, cmd, arg, "address", 1, SAAT_STRING) ) {
+		(validator, cmd, arg, "object", 1, SAAT_STRING) ) {
+		return FALSE;
+	}
+	
+	if ( !testsuite_object_argument_activate(validator, arg, cmd) )
+		return FALSE;
+	
+	arg = sieve_ast_argument_next(arg);
+	
+	if ( !sieve_validate_positional_argument
+		(validator, cmd, arg, "value", 2, SAAT_STRING) ) {
 		return FALSE;
 	}
 	
@@ -78,10 +104,10 @@ static bool cmd_test_message_validate
  * Generation
  */
  
-static bool cmd_test_message_generate
+static bool cmd_test_set_generate
 	(struct sieve_generator *generator,	struct sieve_command_context *ctx) 
 {
-	sieve_generator_emit_operation_ext(generator, &test_message_operation, 
+	sieve_generator_emit_operation_ext(generator, &test_set_operation, 
 		ext_testsuite_my_id);
 
 	/* Generate arguments */
@@ -95,14 +121,15 @@ static bool cmd_test_message_generate
  * Code dump
  */
  
-static bool cmd_test_message_operation_dump
+static bool cmd_test_set_operation_dump
 (const struct sieve_operation *op ATTR_UNUSED,
 	const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
-	sieve_code_dumpf(denv, "TEST MESSAGE:");
+	sieve_code_dumpf(denv, "TEST SET:");
 	sieve_code_descend(denv);
 
 	return 
+		testsuite_object_dump(denv, address) &&
 		sieve_opr_string_dump(denv, address);
 }
 
@@ -110,22 +137,28 @@ static bool cmd_test_message_operation_dump
  * Intepretation
  */
 
-static bool cmd_test_message_operation_execute
+static bool cmd_test_set_operation_execute
 (const struct sieve_operation *op ATTR_UNUSED,
 	const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
-	string_t *message;
+	const struct testsuite_object *object;
+	string_t *value;
 
 	t_push();
+	
+	if ( (object=testsuite_object_read(renv->sbin, address)) == NULL ) {
+		t_pop();
+		return FALSE;
+	}
 
-	if ( !sieve_opr_string_read(renv, address, &message) ) {
+	if ( !sieve_opr_string_read(renv, address, &value) ) {
 		t_pop();
 		return FALSE;
 	}
 
-	printf(">> TEST MESSAGE \"%s\"\n", str_c(message));
+	printf(">> TEST SET %s = \"%s\"\n", object->identifier, str_c(value));
 	
-	testsuite_message_set(message);
+	object->set_member(-1, value);
 	
 	t_pop();
 	
diff --git a/src/testsuite/ext-testsuite.c b/src/testsuite/ext-testsuite.c
index 5e3f717b7..2608e4d86 100644
--- a/src/testsuite/ext-testsuite.c
+++ b/src/testsuite/ext-testsuite.c
@@ -24,22 +24,33 @@
 #include "sieve-interpreter.h"
 #include "sieve-result.h"
 
+#include "testsuite-common.h"
+
 /* Forward declarations */
 
 static bool ext_testsuite_load(int ext_id);
 static bool ext_testsuite_validator_load(struct sieve_validator *validator);
 static bool ext_testsuite_binary_load(struct sieve_binary *sbin);
 
-/* Extension definitions */
+/* Commands */
 
-int ext_testsuite_my_id;
+extern const struct sieve_command cmd_test_set;
+
+/* Operands */
+
+const struct sieve_operand *testsuite_operands[] =
+    { &testsuite_object_operand };
+
+/* Operations */
 
-extern const struct sieve_operation test_message_operation;
+extern const struct sieve_operation test_set_operation;
 
 const struct sieve_operation *testsuite_operations[] =
-    { &test_message_operation };
+    { &test_set_operation };
+    
+/* Extension definitions */
 
-extern const struct sieve_command cmd_test_message;
+int ext_testsuite_my_id;
 
 const struct sieve_extension testsuite_extension = { 
 	"vnd.dovecot.testsuite", 
@@ -48,8 +59,8 @@ const struct sieve_extension testsuite_extension = {
 	NULL, NULL,
 	ext_testsuite_binary_load, 
 	NULL, 
-	SIEVE_EXT_DEFINE_OPERATION(test_message_operation),
-	SIEVE_EXT_DEFINE_NO_OPERANDS
+	SIEVE_EXT_DEFINE_OPERATION(test_set_operation),
+	SIEVE_EXT_DEFINE_OPERAND(testsuite_object_operand)
 };
 
 static bool ext_testsuite_load(int ext_id)
@@ -60,13 +71,17 @@ static bool ext_testsuite_load(int ext_id)
 }
 
 /* Load extension into validator */
+
 static bool ext_testsuite_validator_load(struct sieve_validator *validator)
 {
-	sieve_validator_register_command(validator, &cmd_test_message);
-	return TRUE;
+	sieve_validator_register_command(validator, &cmd_test_set);
+	
+	return testsuite_validator_context_initialize(validator);
 }
 
-static bool ext_testsuite_binary_load(struct sieve_binary *sbin)
+/* Load extension into binary */
+
+static bool ext_testsuite_binary_load(struct sieve_binary *sbin ATTR_UNUSED)
 {
 	return TRUE;
 }
diff --git a/src/testsuite/tests/testsuite.sieve b/src/testsuite/tests/testsuite.sieve
index 2551a6bfe..a5f350c2c 100644
--- a/src/testsuite/tests/testsuite.sieve
+++ b/src/testsuite/tests/testsuite.sieve
@@ -1,6 +1,6 @@
 require "vnd.dovecot.testsuite";
 
-test_message text:
+test_set "message" text:
 From: sirius@rename-it.nl
 To: nico@vestingbar.nl
 Subject: Frop!
@@ -14,7 +14,7 @@ if not header :contains "from" "rename-it.nl" {
 	stop;
 }
 
-test_message text:
+test_set "message" text:
 From: nico@vestingbar.nl
 To: stephan@zuiphol.nl
 Subject: Friep!
diff --git a/src/testsuite/testsuite-common.c b/src/testsuite/testsuite-common.c
index ca1eed615..bc253964f 100644
--- a/src/testsuite/testsuite-common.c
+++ b/src/testsuite/testsuite-common.c
@@ -1,23 +1,74 @@
 #include "lib.h"
 #include "string.h"
 #include "ostream.h"
+#include "hash.h"
 #include "mail-storage.h"
 
 #include "mail-raw.h"
 #include "namespaces.h"
 #include "sieve.h"
+#include "sieve-code.h"
+#include "sieve-commands.h"
+#include "sieve-extensions-private.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-dump.h"
 
 #include "testsuite-common.h"
 
-/* Message environment */
+/* 
+ * Types
+ */
+ 
+struct testsuite_object_registration {
+	int ext_id;
+	const struct testsuite_object *object;
+};
+ 
+struct testsuite_validator_context {
+	struct hash_table *object_registrations;
+};
+
+/* 
+ * Testsuite core objects
+ */
+ 
+enum testsuite_object_code {
+	TESTSUITE_OBJECT_MESSAGE,
+	TESTSUITE_OBJECT_ENVELOPE
+};
+
+const struct testsuite_object *testsuite_core_objects[] = {
+	&message_testsuite_object, &envelope_testsuite_object
+};
+
+const unsigned int testsuite_core_objects_count =
+	N_ELEMENTS(testsuite_core_objects);
+
+
+/*
+ * Global data
+ */
+ 
+/* Testsuite message environment */
 
 struct sieve_message_data testsuite_msgdata;
 
+/* 
+ * Forward declarations
+ */
+ 
+static void _testsuite_object_register
+	(pool_t pool, struct testsuite_validator_context *ctx, 
+		const struct testsuite_object *obj, int ext_id);
+
+/* 
+ * Testsuite message environment 
+ */
+
 static const char *testsuite_user;
 static struct mail_raw *_raw_message;
 
-/* Testsuite message environment */
-
 static const char *_default_message_data = 
 "From: stephan@rename-it.nl\n"
 "To: sirius@drunksnipers.com\n"
@@ -43,7 +94,7 @@ static void _testsuite_message_set(string_t *message)
 	 
 	/* Get recipient address */ 
 	(void)mail_get_first_header(mail, "Envelope-To", &recipient);
-	if ( recipient == NULL ) 
+	if ( recipient == NULL )
 		(void)mail_get_first_header(mail, "To", &recipient);
 	if ( recipient == NULL ) 
 		recipient = "recipient@example.com";
@@ -57,8 +108,7 @@ static void _testsuite_message_set(string_t *message)
 	if ( sender == NULL ) 
 		sender = "sender@example.com";
 
-	memset(&testsuite_msgdata, 0, sizeof(testsuite_msgdata));
-	testsuite_msgdata.mail = mail;
+	memset(&testsuite_msgdata, 0, sizeof(testsuite_msgdata));	testsuite_msgdata.mail = mail;
 	testsuite_msgdata.auth_user = testsuite_user;
 	testsuite_msgdata.return_path = sender;
 	testsuite_msgdata.to_address = recipient;
@@ -88,3 +138,225 @@ void testsuite_message_deinit(void)
 	mail_raw_close(_raw_message);
 	mail_raw_deinit();
 }
+
+/* 
+ * Validator context 
+ */
+
+static inline struct testsuite_validator_context *
+	get_validator_context(struct sieve_validator *valdtr)
+{
+	return (struct testsuite_validator_context *) 
+		sieve_validator_extension_get_context(valdtr, ext_testsuite_my_id);
+}
+
+bool testsuite_validator_context_initialize(struct sieve_validator *valdtr)
+{
+	unsigned int i;
+	pool_t pool = sieve_validator_pool(valdtr);
+	struct testsuite_validator_context *ctx = 
+		p_new(pool, struct testsuite_validator_context, 1);
+	
+	/* Setup comparator registry */
+	ctx->object_registrations = hash_create
+		(pool, pool, 0, str_hash, (hash_cmp_callback_t *) strcmp);
+
+	/* Register core testsuite objects */
+	for ( i = 0; i < testsuite_core_objects_count; i++ ) {
+		const struct testsuite_object *object = testsuite_core_objects[i];
+		
+		_testsuite_object_register(pool, ctx, object, ext_testsuite_my_id);
+	}
+
+	sieve_validator_extension_set_context(valdtr, ext_testsuite_my_id, ctx);
+
+	return TRUE;
+}
+
+/* 
+ * Testsuite object registry
+ */
+ 
+static void _testsuite_object_register
+(pool_t pool, struct testsuite_validator_context *ctx, 
+	const struct testsuite_object *obj, int ext_id) 
+{	
+	struct testsuite_object_registration *reg;
+	
+	reg = p_new(pool, struct testsuite_object_registration, 1);
+	reg->object = obj;
+	reg->ext_id = ext_id;
+
+	hash_insert(ctx->object_registrations, (void *) obj->identifier, (void *) reg);
+}
+ 
+void testsuite_object_register
+(struct sieve_validator *valdtr, const struct testsuite_object *obj, int ext_id) 
+{
+	pool_t pool = sieve_validator_pool(valdtr);
+	struct testsuite_validator_context *ctx = get_validator_context(valdtr);
+
+	_testsuite_object_register(pool, ctx, obj, ext_id);
+}
+
+const struct testsuite_object *testsuite_object_find
+(struct sieve_validator *valdtr, const char *identifier, int *ext_id) 
+{
+	struct testsuite_validator_context *ctx = get_validator_context(valdtr);		
+	struct testsuite_object_registration *reg =
+		(struct testsuite_object_registration *) 
+			hash_lookup(ctx->object_registrations, identifier);
+			
+	if ( reg == NULL ) return NULL;
+
+	if ( ext_id != NULL ) *ext_id = reg->ext_id;
+
+  return reg->object;
+}
+ 
+/* 
+ * Testsuite object code
+ */ 
+ 
+const struct sieve_operand_class testsuite_object_oprclass = 
+	{ "testsuite-object" };
+
+const struct testsuite_object_operand_interface testsuite_object_oprint = {
+	SIEVE_EXT_DEFINE_OBJECTS(testsuite_core_objects)
+};
+
+const struct sieve_operand testsuite_object_operand = { 
+	"testsuite-object",
+	&testsuite_extension, 
+	TESTSUITE_OPERAND_OBJECT, 
+	&testsuite_object_oprclass,
+	&testsuite_object_oprint
+};
+
+static void testsuite_object_emit
+	(struct sieve_binary *sbin, const struct testsuite_object *obj, int ext_id)
+{ 
+	(void) sieve_operand_emit_code(sbin, obj->operand, ext_id);	
+	(void) sieve_binary_emit_byte(sbin, obj->code);
+}
+
+const struct testsuite_object *testsuite_object_read
+(struct sieve_binary *sbin, sieve_size_t *address)
+{
+	const struct sieve_operand *operand;
+	const struct testsuite_object_operand_interface *intf;	
+	unsigned int obj_code; 
+
+	operand = sieve_operand_read(sbin, address);
+	if ( operand == NULL || operand->class != &testsuite_object_oprclass ) 
+		return NULL;
+	
+	intf = operand->interface;
+	if ( intf == NULL ) 
+		return NULL;
+			
+	if ( !sieve_binary_read_byte(sbin, address, &obj_code) ) 
+		return NULL;
+
+	return sieve_extension_get_object
+		(struct testsuite_object, intf->testsuite_objects, obj_code);
+}
+
+bool testsuite_object_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	const struct testsuite_object *object;
+
+	sieve_code_mark(denv);
+		
+	if ( (object = testsuite_object_read(denv->sbin, address)) == NULL )
+		return FALSE;
+	
+	sieve_code_dumpf(denv, "TESTSUITE_OBJECT: %s", object->identifier);
+		
+	return TRUE;
+}
+
+/* 
+ * Testsuite object argument
+ */
+ 
+static bool arg_testsuite_object_generate
+	(struct sieve_generator *generator, struct sieve_ast_argument *arg, 
+		struct sieve_command_context *cmd);
+
+const struct sieve_argument testsuite_object_argument = 
+	{ "testsuite-object", NULL, NULL, NULL, arg_testsuite_object_generate };
+ 
+struct testsuite_object_argctx {
+	const struct testsuite_object *object;
+	int ext_id;
+};
+
+bool testsuite_object_argument_activate
+(struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
+	struct sieve_command_context *cmd) 
+{
+	const char *objname = sieve_ast_argument_strc(arg);
+	const struct testsuite_object *object;
+	int ext_id;
+	struct testsuite_object_argctx *ctx;
+	
+	object = testsuite_object_find(valdtr, objname, &ext_id);
+	if ( object == NULL ) {
+		sieve_command_validate_error(valdtr, cmd, 
+			"unknown testsuite object '%s'", objname);
+		return FALSE;
+	}
+	
+	ctx = p_new(sieve_command_pool(cmd), struct testsuite_object_argctx, 1);
+	ctx->object = object;
+	ctx->ext_id = ext_id;
+	
+	arg->argument = &testsuite_object_argument;
+	arg->context = (void *) ctx;
+	
+	return TRUE;
+}
+
+static bool arg_testsuite_object_generate
+	(struct sieve_generator *generator, struct sieve_ast_argument *arg, 
+	struct sieve_command_context *cmd ATTR_UNUSED)
+{
+	struct sieve_binary *sbin = sieve_generator_get_binary(generator);
+	struct testsuite_object_argctx *ctx = 
+		(struct testsuite_object_argctx *) arg->context;
+	
+	testsuite_object_emit(sbin, ctx->object, ctx->ext_id);
+		
+	return TRUE;
+}
+
+/* 
+ * Testsuite core object implementation
+ */
+ 
+static bool tsto_message_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
+};
+
+const struct testsuite_object envelope_testsuite_object = { 
+	"envelope",
+	TESTSUITE_OBJECT_ENVELOPE,
+	&testsuite_object_operand,
+	NULL, NULL, NULL
+};
+
+static bool tsto_message_set_member(int id, string_t *value) 
+{
+	if ( id != -1 ) return FALSE;
+	
+	testsuite_message_set(value);
+	
+	return TRUE;
+}
diff --git a/src/testsuite/testsuite-common.h b/src/testsuite/testsuite-common.h
index 41c148eb1..2d878b315 100644
--- a/src/testsuite/testsuite-common.h
+++ b/src/testsuite/testsuite-common.h
@@ -14,4 +14,53 @@ void testsuite_message_deinit(void);
 
 void testsuite_message_set(string_t *message);
 
+/* Testsuite validator context */
+
+bool testsuite_validator_context_initialize(struct sieve_validator *valdtr);
+
+/* Testsuite operands */
+
+struct testsuite_object_operand_interface {
+	struct sieve_extension_obj_registry testsuite_objects;
+};
+
+extern const struct sieve_operand_class testsuite_object_oprclass;
+extern const struct sieve_operand testsuite_object_operand;
+
+enum testsuite_operand_code {
+	TESTSUITE_OPERAND_OBJECT
+};
+
+/* Testsuite object access */
+
+struct testsuite_object {
+	const char *identifier;
+	unsigned int code;
+	const struct sieve_operand *operand;
+	
+	int (*get_member_id)(const char *identifier);
+	bool (*set_member)(int id, string_t *value);
+	string_t *(*get_member)(int id);
+};
+
+const struct testsuite_object *testsuite_object_find
+	(struct sieve_validator *valdtr, const char *identifier, int *ext_id);
+void testsuite_object_register
+	(struct sieve_validator *valdtr, const struct testsuite_object *obj, 
+		int ext_id);
+		
+const struct testsuite_object *testsuite_object_read
+  (struct sieve_binary *sbin, sieve_size_t *address);
+bool testsuite_object_dump
+	(const struct sieve_dumptime_env *denv, sieve_size_t *address);
+	
+bool testsuite_object_argument_activate
+	(struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
+		struct sieve_command_context *cmd);
+
+/* Testsuite core objects */
+
+extern const struct testsuite_object message_testsuite_object;
+extern const struct testsuite_object envelope_testsuite_object;
+
 #endif
-- 
GitLab