From 648cb538b62ad3370012b731b76541ca5a765a85 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sun, 3 Jan 2010 13:33:45 +0100
Subject: [PATCH] Testsuite: added support for changing and testing an
 extension's configuration.

---
 Makefile.am                                   |   1 +
 src/lib-sieve/plugins/enotify/ext-enotify.c   |   4 +
 .../plugins/imap4flags/ext-imapflags.c        |   8 +-
 src/lib-sieve/plugins/include/ext-include.c   |   8 +-
 .../spamvirustest/ext-spamvirustest-common.c  |  10 +
 .../plugins/subaddress/ext-subaddress.c       |   9 +-
 src/lib-sieve/sieve-extensions.c              |  16 +
 src/lib-sieve/sieve-extensions.h              |   3 +
 src/testsuite/Makefile.am                     |   5 +-
 src/testsuite/cmd-test-config.c               | 343 ++++++++++++++++++
 src/testsuite/ext-testsuite.c                 |   5 +-
 src/testsuite/testsuite-common.h              |   5 +
 tests/extensions/subaddress/config.svtest     |  77 ++++
 13 files changed, 485 insertions(+), 9 deletions(-)
 create mode 100644 src/testsuite/cmd-test-config.c
 create mode 100644 tests/extensions/subaddress/config.svtest

diff --git a/Makefile.am b/Makefile.am
index 4bc6b4bd6..bfd759138 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -76,6 +76,7 @@ test_cases = \
 	tests/extensions/relational/errors.svtest \
 	tests/extensions/subaddress/basic.svtest \
 	tests/extensions/subaddress/rfc.svtest \
+	tests/extensions/subaddress/config.svtest \
 	tests/extensions/vacation/errors.svtest \
 	tests/extensions/vacation/execute.svtest \
 	tests/extensions/vacation/message.svtest \
diff --git a/src/lib-sieve/plugins/enotify/ext-enotify.c b/src/lib-sieve/plugins/enotify/ext-enotify.c
index 39cafb78c..4d5f44348 100644
--- a/src/lib-sieve/plugins/enotify/ext-enotify.c
+++ b/src/lib-sieve/plugins/enotify/ext-enotify.c
@@ -61,6 +61,10 @@ static bool ext_enotify_load(const struct sieve_extension *ext, void **context)
 {
 	struct ext_enotify_context *ectx;
 
+	if ( *context != NULL ) {
+		ext_enotify_unload(ext);
+	}	
+
 	ectx = i_new(struct ext_enotify_context, 1);
 	ectx->var_ext = sieve_ext_variables_get_extension(ext->svinst);
 	*context = (void *) ectx;
diff --git a/src/lib-sieve/plugins/imap4flags/ext-imapflags.c b/src/lib-sieve/plugins/imap4flags/ext-imapflags.c
index b66311c8d..8c356f03f 100644
--- a/src/lib-sieve/plugins/imap4flags/ext-imapflags.c
+++ b/src/lib-sieve/plugins/imap4flags/ext-imapflags.c
@@ -91,9 +91,11 @@ const struct sieve_extension_def imapflags_extension = {
 static bool ext_imapflags_load
 (const struct sieve_extension *ext, void **context)
 {
-	/* Make sure real extension is registered, it is needed by the binary */
-	*context = (void *)	
-		sieve_extension_require(ext->svinst, &imap4flags_extension);
+	if ( *context == NULL ) {	
+		/* Make sure real extension is registered, it is needed by the binary */
+		*context = (void *)	
+			sieve_extension_require(ext->svinst, &imap4flags_extension);
+	}
 
 	return TRUE;
 }
diff --git a/src/lib-sieve/plugins/include/ext-include.c b/src/lib-sieve/plugins/include/ext-include.c
index 7b409879a..d0f050d8a 100644
--- a/src/lib-sieve/plugins/include/ext-include.c
+++ b/src/lib-sieve/plugins/include/ext-include.c
@@ -84,7 +84,13 @@ const struct sieve_extension_def include_extension = {
 static bool ext_include_load
 (const struct sieve_extension *ext, void **context)
 {
-	struct ext_include_context *ctx = i_new(struct ext_include_context, 1);
+	struct ext_include_context *ctx;
+
+	if ( *context != NULL ) {
+		ctx = (struct ext_include_context *) ext->context;
+	} else {
+		ctx =  i_new(struct ext_include_context, 1);
+	}
 
 	/* Extension dependencies */	
 	ctx->var_ext = sieve_ext_variables_get_extension(ext->svinst);
diff --git a/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c b/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c
index fbbc30818..8ae761a76 100644
--- a/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c
+++ b/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c
@@ -237,6 +237,10 @@ bool ext_spamvirustest_load(const struct sieve_extension *ext, void **context)
 	const char *ext_name;
 	const char *error;
 
+	if ( *context != NULL ) {
+		ext_spamvirustest_unload(ext);
+	}
+
 	/* FIXME: 
 	 *   Prevent loading of both spamtest and spamtestplus: let these share 
 	 *   contexts.
@@ -326,6 +330,12 @@ bool ext_spamvirustest_load(const struct sieve_extension *ext, void **context)
 	return TRUE;
 }
 
+void ext_spamvirustest_unload(const struct sieve_extension *ext)
+{
+	/* FIXME */
+}
+
+
 /*
  * Score extraction
  */
diff --git a/src/lib-sieve/plugins/subaddress/ext-subaddress.c b/src/lib-sieve/plugins/subaddress/ext-subaddress.c
index 0444945eb..edaa1f6cf 100644
--- a/src/lib-sieve/plugins/subaddress/ext-subaddress.c
+++ b/src/lib-sieve/plugins/subaddress/ext-subaddress.c
@@ -70,8 +70,13 @@ static bool ext_subaddress_load
 (const struct sieve_extension *ext, void **context)
 {
 	struct ext_subaddress_config *config;
-	const char *delim = sieve_setting_get
-		(ext->svinst, "recipient_delimiter");
+	const char *delim;
+
+	if ( *context != NULL ) {
+		ext_subaddress_unload(ext);
+	}
+
+	delim = sieve_setting_get(ext->svinst, "recipient_delimiter");
 
 	if ( delim == NULL )
 		delim = SUBADDRESS_DEFAULT_DELIM;
diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c
index 4596ecf0e..fcc8add38 100644
--- a/src/lib-sieve/sieve-extensions.c
+++ b/src/lib-sieve/sieve-extensions.c
@@ -281,6 +281,22 @@ static bool _sieve_extension_load(struct sieve_extension *ext)
 	return TRUE;
 }
 
+bool sieve_extension_reload(const struct sieve_extension *ext)
+{
+	struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg;
+	struct sieve_extension *mod_ext;
+	int ext_id = ext->id;
+	
+	/* Let's not just cast the 'const' away */
+	if ( ext_id > 0 && ext_id < (int) array_count(&ext_reg->extensions) ) {
+		mod_ext = array_idx_modifiable(&ext_reg->extensions, ext_id);
+
+		return _sieve_extension_load(mod_ext);
+	}
+
+	return FALSE;
+}
+
 static struct sieve_extension *_sieve_extension_register
 (struct sieve_instance *svinst, const struct sieve_extension_def *extdef, 
 	bool load, bool required)
diff --git a/src/lib-sieve/sieve-extensions.h b/src/lib-sieve/sieve-extensions.h
index df645ed15..d639de7bd 100644
--- a/src/lib-sieve/sieve-extensions.h
+++ b/src/lib-sieve/sieve-extensions.h
@@ -116,7 +116,10 @@ const struct sieve_extension *sieve_extension_register
 		bool load);
 const struct sieve_extension *sieve_extension_require
 	(struct sieve_instance *svinst, const struct sieve_extension_def *extension);
+bool sieve_extension_reload(const struct sieve_extension *ext);
+
 int sieve_extensions_get_count(struct sieve_instance *svinst);
+
 const struct sieve_extension *sieve_extension_get_by_id
 	(struct sieve_instance *svinst, unsigned int ext_id);
 const struct sieve_extension *sieve_extension_get_by_name
diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.am
index e1f9ebd09..6a0ef860d 100644
--- a/src/testsuite/Makefile.am
+++ b/src/testsuite/Makefile.am
@@ -11,8 +11,8 @@ testsuite_LDFLAGS = -export-dynamic
 libs = \
 	$(top_srcdir)/src/lib-sieve/libdovecot-sieve.la \
 	$(top_srcdir)/src/lib-sieve-tool/libsieve-tool.la \
-	$(LIBDOVECOT) \
-	$(LIBDOVECOT_STORAGE)
+	$(LIBDOVECOT_STORAGE) \
+	$(LIBDOVECOT)
 
 testsuite_LDADD = $(libs)
 testsuite_DEPENDENCIES = $(libs)
@@ -20,6 +20,7 @@ testsuite_DEPENDENCIES = $(libs)
 commands = \
 	cmd-test.c \
 	cmd-test-fail.c \
+	cmd-test-config.c \
 	cmd-test-set.c \
 	cmd-test-result-reset.c \
 	cmd-test-result-print.c \
diff --git a/src/testsuite/cmd-test-config.c b/src/testsuite/cmd-test-config.c
new file mode 100644
index 000000000..62e595c22
--- /dev/null
+++ b/src/testsuite/cmd-test-config.c
@@ -0,0 +1,343 @@
+/* Copyright (c) 2002-2010 Dovecot Sieve authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-code.h"
+#include "sieve-binary.h"
+#include "sieve-dump.h"
+
+#include "testsuite-common.h"
+#include "testsuite-settings.h"
+
+/*
+ * Test_config command
+ *
+ * Syntax:   
+ *   test_config (
+ *     :set <setting: string> <value: string> / 
+ *     :reload [<extension: string>] )
+ */
+
+static bool cmd_test_config_registered
+	(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+		struct sieve_command_registration *cmd_reg);
+static bool cmd_test_config_generate
+	(const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+const struct sieve_command_def cmd_test_config = { 
+	"test_config", 
+	SCT_COMMAND, 
+	0, 0, FALSE, FALSE,
+	cmd_test_config_registered, 
+	NULL, NULL,
+	cmd_test_config_generate, 
+	NULL 
+};
+
+/* 
+ * Operations
+ */ 
+ 
+/* Test_message_set operation */
+
+static bool cmd_test_config_set_operation_dump
+	(const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_test_config_set_operation_execute
+	(const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def test_config_set_operation = { 
+	"TEST_CONFIG_SET",
+	&testsuite_extension, 
+	TESTSUITE_OPERATION_TEST_CONFIG_SET,
+	cmd_test_config_set_operation_dump, 
+	cmd_test_config_set_operation_execute 
+};
+
+/* Test_message_mailbox operation */
+
+static bool cmd_test_config_reload_operation_dump
+	(const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_test_config_reload_operation_execute
+	(const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def test_config_reload_operation = { 
+	"TEST_CONFIG_RELOAD",
+	&testsuite_extension, 
+	TESTSUITE_OPERATION_TEST_CONFIG_RELOAD,
+	cmd_test_config_reload_operation_dump, 
+	cmd_test_config_reload_operation_execute 
+};
+
+/*
+ * Compiler context data
+ */
+
+enum cmd_test_config_action {
+	CONFIG_ACTION_SET,
+	CONFIG_ACTION_RELOAD,
+	CONFIG_ACTION_LAST
+};
+
+const struct sieve_operation_def *test_config_operations[] = {
+	&test_config_set_operation,
+	&test_config_reload_operation
+};
+ 
+struct cmd_test_config_data {
+	enum cmd_test_config_action action;
+};
+
+/* 
+ * Command tags 
+ */
+ 
+static bool tag_action_is_instance_of
+	(struct sieve_validator *valdtr, struct sieve_command *cmd, 
+		const struct sieve_extension *ext, const char *identifier, void **data);
+static bool tag_action_validate
+	(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, 
+		struct sieve_command *cmd);
+static bool tag_action_generate
+	(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, 
+		struct sieve_command *cmd);
+
+static const struct sieve_argument_def config_action_tag = { 
+	"CONFIG_ACTION",
+	tag_action_is_instance_of,
+	tag_action_validate, 
+	NULL,	NULL,
+	tag_action_generate 
+};
+
+static bool tag_action_is_instance_of
+(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd, 
+	const struct sieve_extension *ext ATTR_UNUSED, const char *identifier, 
+	void **data)
+{
+	enum cmd_test_config_action action = CONFIG_ACTION_LAST; 
+	struct cmd_test_config_data *ctx_data;
+
+	if ( strcmp(identifier, "set") == 0 )
+		action = CONFIG_ACTION_SET;
+	else if ( strcmp(identifier, "reload") == 0 )
+		action = CONFIG_ACTION_RELOAD;
+	else 
+		return FALSE;
+
+	if ( data != NULL ) {
+		ctx_data = p_new
+			(sieve_command_pool(cmd), struct cmd_test_config_data, 1);
+		ctx_data->action = action;
+		*data = (void *) ctx_data;
+  }		
+		
+	return TRUE;
+}
+
+static bool cmd_test_config_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+	struct sieve_command_registration *cmd_reg) 
+{
+	/* Register our tags */
+	sieve_validator_register_tag
+		(valdtr, cmd_reg, ext, &config_action_tag, 0); 	
+
+	return TRUE;
+}
+
+static bool tag_action_validate
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, 
+	struct sieve_command *cmd)
+{
+	struct sieve_ast_argument *tag = *arg;
+	struct cmd_test_config_data *ctx_data = 
+		(struct cmd_test_config_data *) (*arg)->argument->data;
+
+	*arg = sieve_ast_argument_next(*arg);
+			
+	switch ( ctx_data->action ) {
+	case CONFIG_ACTION_SET:
+		/* Check syntax:
+		 *   :set <setting: string> <value: string>
+		 */
+		if ( !sieve_validate_tag_parameter
+			(valdtr, cmd, tag, *arg, "setting", 1, SAAT_STRING, TRUE) ) {
+			return FALSE;
+		}
+
+		tag->parameters = *arg;
+		*arg = sieve_ast_argument_next(*arg);
+
+		if ( !sieve_validate_tag_parameter
+			(valdtr, cmd, tag, *arg, "value", 2, SAAT_STRING, TRUE) ) {
+			return FALSE;
+		}
+
+		/* Detach parameters */
+		*arg = sieve_ast_arguments_detach(tag->parameters,2);
+		break;
+	case CONFIG_ACTION_RELOAD:
+		/* Check syntax:
+		 *   :reload <extension: string>
+		 */
+		if ( !sieve_validate_tag_parameter
+			(valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING, TRUE) ) {
+			return FALSE;
+		}
+
+		/* Detach parameter */
+		tag->parameters = *arg;
+		*arg = sieve_ast_arguments_detach(*arg,1);
+		break;
+	default:
+		i_unreached();
+	}
+			
+	return TRUE;
+}
+
+static bool tag_action_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, 
+	struct sieve_command *cmd)
+{
+	struct sieve_ast_argument *param = arg->parameters;
+	struct cmd_test_config_data *ctx_data = 
+		(struct cmd_test_config_data *) arg->argument->data;
+			
+	i_assert(ctx_data->action < CONFIG_ACTION_LAST);
+
+	sieve_operation_emit
+		(cgenv->sbin, cmd->ext, test_config_operations[ctx_data->action]);
+
+	while ( param != NULL ) {
+		if ( !sieve_generate_argument(cgenv, param, cmd) )
+			return FALSE;
+
+		param = sieve_ast_argument_next(param);
+	}
+
+	return TRUE;
+}
+
+/* 
+ * Code generation 
+ */
+
+static bool cmd_test_config_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{	  	
+ 	/* Generate arguments */
+	if ( !sieve_generate_arguments(cgenv, cmd, NULL) )
+		return FALSE;
+
+	return TRUE;
+}
+
+/* 
+ * Code dump
+ */
+ 
+static bool cmd_test_config_set_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	sieve_code_dumpf(denv, "TEST_CONFIG_SET:");
+	
+	sieve_code_descend(denv);
+	
+	return sieve_opr_string_dump(denv, address, "setting") &&
+		sieve_opr_string_dump(denv, address, "value");
+}
+
+static bool cmd_test_config_reload_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	sieve_code_dumpf(denv, "TEST_CONFIG_RELOAD:");
+	
+	sieve_code_descend(denv);
+
+	return 
+		sieve_opr_string_dump(denv, address, "extension");
+}
+
+/*
+ * Intepretation
+ */
+ 
+static int cmd_test_config_set_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+	string_t *setting;
+	string_t *value;
+
+	/* 
+	 * Read operands 
+	 */
+
+	/* Settings */
+
+	if ( !sieve_opr_string_read(renv, address, &setting) ) {
+		sieve_runtime_trace_error(renv, "invalid setting operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+
+	/* Value */
+
+	if ( !sieve_opr_string_read(renv, address, &value) ) {
+		sieve_runtime_trace_error(renv, "invalid value operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+
+	/*
+	 * Perform operation
+	 */
+		
+	sieve_runtime_trace(renv, "TEST_CONFIG_SET %s = '%s'", 
+		str_c(setting), str_c(value));
+
+	testsuite_setting_set(str_c(setting), str_c(value));
+
+	return SIEVE_EXEC_OK;
+}
+
+static int cmd_test_config_reload_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+	const struct sieve_extension *ext;
+	string_t *extension;
+
+	/* 
+	 * Read operands 
+	 */
+
+	/* Extension */
+
+	if ( !sieve_opr_string_read(renv, address, &extension) ) {
+		sieve_runtime_trace_error(renv, "invalid extension operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+
+	/*
+	 * Perform operation
+	 */
+		
+	sieve_runtime_trace(renv, "TEST_CONFIG_RELOAD [%s]", str_c(extension));
+
+	ext = sieve_extension_get_by_name(renv->svinst, str_c(extension));
+	if ( ext == NULL ) {
+		testsuite_test_failf("unknown extension '%s'", str_c(extension));
+		return SIEVE_EXEC_OK;
+	}	
+
+	sieve_extension_reload(ext);
+	return SIEVE_EXEC_OK;
+}
+
+
+
+
+
diff --git a/src/testsuite/ext-testsuite.c b/src/testsuite/ext-testsuite.c
index a9dd0cd22..73e75169b 100644
--- a/src/testsuite/ext-testsuite.c
+++ b/src/testsuite/ext-testsuite.c
@@ -54,7 +54,9 @@
 const struct sieve_operation_def *testsuite_operations[] = { 
 	&test_operation, 
 	&test_finish_operation,
-	&test_fail_operation, 
+	&test_fail_operation,
+	&test_config_set_operation,
+	&test_config_reload_operation,
 	&test_set_operation,
 	&test_script_compile_operation,
 	&test_script_run_operation,
@@ -115,6 +117,7 @@ static bool ext_testsuite_validator_load
 {
 	sieve_validator_register_command(valdtr, ext, &cmd_test);
 	sieve_validator_register_command(valdtr, ext, &cmd_test_fail);
+	sieve_validator_register_command(valdtr, ext, &cmd_test_config);
 	sieve_validator_register_command(valdtr, ext, &cmd_test_set);
 	sieve_validator_register_command(valdtr, ext, &cmd_test_result_print);
 	sieve_validator_register_command(valdtr, ext, &cmd_test_result_reset);
diff --git a/src/testsuite/testsuite-common.h b/src/testsuite/testsuite-common.h
index 7d59931ef..9f3556a31 100644
--- a/src/testsuite/testsuite-common.h
+++ b/src/testsuite/testsuite-common.h
@@ -49,6 +49,7 @@ bool testsuite_generator_context_initialize
 
 extern const struct sieve_command_def cmd_test;
 extern const struct sieve_command_def cmd_test_fail;
+extern const struct sieve_command_def cmd_test_config;
 extern const struct sieve_command_def cmd_test_set;
 extern const struct sieve_command_def cmd_test_result_reset;
 extern const struct sieve_command_def cmd_test_result_print;
@@ -75,6 +76,8 @@ enum testsuite_operation_code {
 	TESTSUITE_OPERATION_TEST,
 	TESTSUITE_OPERATION_TEST_FINISH,
 	TESTSUITE_OPERATION_TEST_FAIL,
+	TESTSUITE_OPERATION_TEST_CONFIG_SET,
+	TESTSUITE_OPERATION_TEST_CONFIG_RELOAD,
 	TESTSUITE_OPERATION_TEST_SET,
 	TESTSUITE_OPERATION_TEST_SCRIPT_COMPILE,
 	TESTSUITE_OPERATION_TEST_SCRIPT_RUN,
@@ -95,6 +98,8 @@ enum testsuite_operation_code {
 extern const struct sieve_operation_def test_operation;
 extern const struct sieve_operation_def test_finish_operation;
 extern const struct sieve_operation_def test_fail_operation;
+extern const struct sieve_operation_def test_config_set_operation;
+extern const struct sieve_operation_def test_config_reload_operation;
 extern const struct sieve_operation_def test_set_operation;
 extern const struct sieve_operation_def test_script_compile_operation;
 extern const struct sieve_operation_def test_script_run_operation;
diff --git a/tests/extensions/subaddress/config.svtest b/tests/extensions/subaddress/config.svtest
new file mode 100644
index 000000000..aa5c647af
--- /dev/null
+++ b/tests/extensions/subaddress/config.svtest
@@ -0,0 +1,77 @@
+require "vnd.dovecot.testsuite";
+require "subaddress";
+require "envelope";
+
+test_set "message" text:
+From: stephan+sieve@renane-it.nl
+To: test-failed@example.com
+Subject: subaddress test
+
+Test!
+.
+;
+
+test_set "envelope.to" "friep++frop@dovecot.org";
+test_set "envelope.from" "list--request@lists.dovecot.org";
+
+test "Delimiter default" {
+    if not address :is :user "from" "stephan" {
+        test_fail "wrong user part extracted";
+    }
+
+    if not address :is :detail "from" "sieve" {
+        test_fail "wrong detail part extracted";
+    }
+}
+
+test "Delimiter \"-\"" {
+	test_config :set "recipient_delimiter" "-";
+	test_config :reload "subaddress";
+
+    if not address :is :user "to" "test" {
+        test_fail "wrong user part extracted";
+    }
+
+    if not address :is :detail "to" "failed" {
+        test_fail "wrong detail part extracted";
+    }
+}
+
+test "Delimiter \"++\"" {
+	test_config :set "recipient_delimiter" "++";
+	test_config :reload "subaddress";
+
+    if not envelope :is :user "to" "friep" {
+        test_fail "wrong user part extracted";
+    }
+
+    if not envelope :is :detail "to" "frop" {
+        test_fail "wrong detail part extracted";
+    }
+}
+
+test "Delimiter \"--\"" {
+	test_config :set "recipient_delimiter" "--";
+	test_config :reload "subaddress";
+
+    if not envelope :is :user "from" "list" {
+        test_fail "wrong user part extracted";
+    }
+
+    if not envelope :is :detail "from" "request" {
+        test_fail "wrong detail part extracted";
+    }
+}
+
+test "Delimiter \"+-\"" {
+	test_config :set "recipient_delimiter" "+-";
+	test_config :reload "subaddress";
+
+    if envelope :is :user "from" "list" {
+        test_fail "user part extracted";
+    }
+
+    if envelope :is :detail "from" "request" {
+        test_fail "detail part extracted";
+    }
+}
-- 
GitLab