From 9cd8ef590d09542e8f6e511720a5149780748721 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sat, 17 Oct 2009 23:05:03 +0200
Subject: [PATCH] Test suite: added multiscript support and added some new
 tests with the new feature.

---
 Makefile.am                                   |   1 +
 TODO                                          |   1 -
 src/testsuite/Makefile.am                     |   2 +-
 src/testsuite/ext-testsuite.c                 |   2 +
 src/testsuite/testsuite-common.h              |   3 +
 src/testsuite/testsuite-script.c              |  77 +++++++-
 src/testsuite/testsuite-script.h              |   2 +
 src/testsuite/tst-test-multiscript.c          | 170 ++++++++++++++++++
 .../extensions/imap4flags/multiscript.svtest  |  13 ++
 .../imap4flags/multiscript/group-spam.sieve   |  14 ++
 .../imap4flags/multiscript/sent-store.sieve   |   7 +
 .../imap4flags/multiscript/spam.sieve         |   8 +
 12 files changed, 294 insertions(+), 6 deletions(-)
 create mode 100644 src/testsuite/tst-test-multiscript.c
 create mode 100644 tests/extensions/imap4flags/multiscript.svtest
 create mode 100644 tests/extensions/imap4flags/multiscript/group-spam.sieve
 create mode 100644 tests/extensions/imap4flags/multiscript/sent-store.sieve
 create mode 100644 tests/extensions/imap4flags/multiscript/spam.sieve

diff --git a/Makefile.am b/Makefile.am
index 93406a2c1..bcf53d756 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -76,6 +76,7 @@ test_cases = \
 	tests/extensions/imap4flags/hasflag.svtest \
 	tests/extensions/imap4flags/errors.svtest \
 	tests/extensions/imap4flags/execute.svtest \
+	tests/extensions/imap4flags/multiscript.svtest \
 	tests/extensions/imap4flags/flagstore.svtest \
 	tests/extensions/body/basic.svtest \
 	tests/extensions/body/raw.svtest \
diff --git a/TODO b/TODO
index 001517603..7c0433df7 100644
--- a/TODO
+++ b/TODO
@@ -4,7 +4,6 @@ Current activities:
 	- Implement proper :content "multipart" behavior
 	- Implement proper :content "message/rfc822" behavior
 	- Build test cases for decoding MIME encodings to UTF-8
-* Add multiscript support to the testsuite. 
 * Build a sieve tool to filter an entire existing mailbox through a Sieve 
   script:
 	- Add commandline options to fully customize execution
diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.am
index be1fb1c6a..d722fdab2 100644
--- a/src/testsuite/Makefile.am
+++ b/src/testsuite/Makefile.am
@@ -35,10 +35,10 @@ commands = \
 	cmd-test-mailbox.c \
 	cmd-test-binary.c
 
-
 tests = \
 	tst-test-script-compile.c \
 	tst-test-script-run.c \
+	tst-test-multiscript.c \
 	tst-test-error.c \
 	tst-test-result.c \
 	tst-test-result-execute.c
diff --git a/src/testsuite/ext-testsuite.c b/src/testsuite/ext-testsuite.c
index e6f07318f..42100b5b7 100644
--- a/src/testsuite/ext-testsuite.c
+++ b/src/testsuite/ext-testsuite.c
@@ -58,6 +58,7 @@ const struct sieve_operation *testsuite_operations[] = {
 	&test_set_operation,
 	&test_script_compile_operation,
 	&test_script_run_operation,
+	&test_multiscript_operation,
 	&test_error_operation,
 	&test_result_operation,
 	&test_result_execute_operation,
@@ -122,6 +123,7 @@ static bool ext_testsuite_validator_load(struct sieve_validator *valdtr)
 
 	sieve_validator_register_command(valdtr, &tst_test_script_compile);
 	sieve_validator_register_command(valdtr, &tst_test_script_run);
+	sieve_validator_register_command(valdtr, &tst_test_multiscript);
 	sieve_validator_register_command(valdtr, &tst_test_error);
 	sieve_validator_register_command(valdtr, &tst_test_result);	
 	sieve_validator_register_command(valdtr, &tst_test_result_execute);	
diff --git a/src/testsuite/testsuite-common.h b/src/testsuite/testsuite-common.h
index 2d5022be0..398b4b441 100644
--- a/src/testsuite/testsuite-common.h
+++ b/src/testsuite/testsuite-common.h
@@ -55,6 +55,7 @@ extern const struct sieve_command cmd_test_binary;
 
 extern const struct sieve_command tst_test_script_compile;
 extern const struct sieve_command tst_test_script_run;
+extern const struct sieve_command tst_test_multiscript;
 extern const struct sieve_command tst_test_error;
 extern const struct sieve_command tst_test_result;
 extern const struct sieve_command tst_test_result_execute;
@@ -70,6 +71,7 @@ enum testsuite_operation_code {
 	TESTSUITE_OPERATION_TEST_SET,
 	TESTSUITE_OPERATION_TEST_SCRIPT_COMPILE,
 	TESTSUITE_OPERATION_TEST_SCRIPT_RUN,
+	TESTSUITE_OPERATION_TEST_MULTISCRIPT,
 	TESTSUITE_OPERATION_TEST_ERROR,
 	TESTSUITE_OPERATION_TEST_RESULT,
 	TESTSUITE_OPERATION_TEST_RESULT_EXECUTE,
@@ -89,6 +91,7 @@ extern const struct sieve_operation test_fail_operation;
 extern const struct sieve_operation test_set_operation;
 extern const struct sieve_operation test_script_compile_operation;
 extern const struct sieve_operation test_script_run_operation;
+extern const struct sieve_operation test_multiscript_operation;
 extern const struct sieve_operation test_error_operation;
 extern const struct sieve_operation test_result_operation;
 extern const struct sieve_operation test_result_execute_operation;
diff --git a/src/testsuite/testsuite-script.c b/src/testsuite/testsuite-script.c
index a12ff4005..f234d2f99 100644
--- a/src/testsuite/testsuite-script.c
+++ b/src/testsuite/testsuite-script.c
@@ -35,13 +35,11 @@ void testsuite_script_deinit(void)
 	}
 }
 
-bool testsuite_script_compile(const char *script_path)
+static struct sieve_binary *_testsuite_script_compile(const char *script_path)
 {
 	struct sieve_binary *sbin;
 	const char *sieve_dir;
 
-	testsuite_log_clear_messages();
-
 	/* Initialize environment */
 	sieve_dir = strrchr(script_path, '/');
 	if ( sieve_dir == NULL )
@@ -53,8 +51,19 @@ bool testsuite_script_compile(const char *script_path)
 	env_put(t_strconcat("SIEVE_DIR=", sieve_dir, "included", NULL));
 	env_put(t_strconcat("SIEVE_GLOBAL_DIR=", sieve_dir, "included-global", NULL));
 	
-
 	if ( (sbin = sieve_compile(script_path, NULL, testsuite_log_ehandler)) == NULL )
+		return NULL;
+
+	return sbin;
+}
+
+bool testsuite_script_compile(const char *script_path)
+{
+	struct sieve_binary *sbin;
+
+	testsuite_log_clear_messages();
+
+	if ( (sbin=_testsuite_script_compile(script_path)) == NULL )
 		return FALSE;
 
 	if ( _testsuite_compiled_script != NULL ) {
@@ -125,3 +134,63 @@ void testsuite_script_set_binary(struct sieve_binary *sbin)
 	sieve_binary_ref(sbin);
 }
 
+/*
+ * Multiscript
+ */
+
+bool testsuite_script_multiscript
+(const struct sieve_runtime_env *renv, ARRAY_TYPE (const_string) *scriptfiles)
+{
+	struct sieve_script_env scriptenv;
+	struct sieve_multiscript *mscript;
+	struct sieve_result *result;
+	const char *const *scripts;
+	unsigned int count, i;
+	bool more = TRUE;
+	int ret;
+
+	testsuite_log_clear_messages();
+
+	/* Compose script execution environment */
+	memset(&scriptenv, 0, sizeof(scriptenv));
+	scriptenv.default_mailbox = "INBOX";
+	scriptenv.namespaces = NULL;
+	scriptenv.username = "user";
+	scriptenv.hostname = "host.example.com";
+	scriptenv.postmaster_address = "postmaster@example.com";
+	scriptenv.smtp_open = NULL;
+	scriptenv.smtp_close = NULL;
+	scriptenv.duplicate_mark = NULL;
+	scriptenv.duplicate_check = NULL;
+	
+	result = testsuite_result_get();
+
+	/* Start execution */
+
+	mscript = sieve_multiscript_start_execute(renv->msgdata, &scriptenv);
+
+	/* Execute scripts before main script */
+
+	scripts = array_get(scriptfiles, &count);
+
+	for ( i = 0; i < count && more; i++ ) {
+		struct sieve_binary *sbin = NULL;
+		const char *script_path = scripts[i];
+		bool final = ( i == count - 1 );
+
+		/* Open */
+	
+		if ( (sbin=_testsuite_script_compile(script_path)) == NULL )
+			break;
+
+		/* Execute */
+
+		more = sieve_multiscript_run(mscript, sbin, testsuite_log_ehandler, final);
+
+		sieve_close(&sbin);
+	}
+
+	ret = sieve_multiscript_finish(&mscript, testsuite_log_ehandler, NULL);
+	
+	return ( ret > 0 );
+}
diff --git a/src/testsuite/testsuite-script.h b/src/testsuite/testsuite-script.h
index 7ce8992d0..2c720c2b7 100644
--- a/src/testsuite/testsuite-script.h
+++ b/src/testsuite/testsuite-script.h
@@ -11,6 +11,8 @@ void testsuite_script_deinit(void);
 
 bool testsuite_script_compile(const char *script_path);
 bool testsuite_script_run(const struct sieve_runtime_env *renv);
+bool testsuite_script_multiscript
+(const struct sieve_runtime_env *renv, ARRAY_TYPE (const_string) *scriptfiles);
 
 struct sieve_binary *testsuite_script_get_binary(void);
 void testsuite_script_set_binary(struct sieve_binary *sbin);
diff --git a/src/testsuite/tst-test-multiscript.c b/src/testsuite/tst-test-multiscript.c
new file mode 100644
index 000000000..e01315968
--- /dev/null
+++ b/src/testsuite/tst-test-multiscript.c
@@ -0,0 +1,170 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-script.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 "sieve.h"
+
+#include "testsuite-common.h"
+#include "testsuite-script.h"
+
+/*
+ * Test_multiscript command
+ *
+ * Syntax:   
+ *   test_multiscript <scripts: string-list>
+ */
+
+static bool tst_test_multiscript_validate
+	(struct sieve_validator *validator, struct sieve_command_context *cmd);
+static bool tst_test_multiscript_generate
+	(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx);
+
+const struct sieve_command tst_test_multiscript = { 
+	"test_multiscript", 
+	SCT_TEST, 
+	1, 0, FALSE, FALSE,
+	NULL, NULL,
+	tst_test_multiscript_validate, 
+	tst_test_multiscript_generate, 
+	NULL 
+};
+
+/* 
+ * Operation 
+ */
+
+static bool tst_test_multiscript_operation_dump
+	(const struct sieve_operation *op,
+		const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int tst_test_multiscript_operation_execute
+	(const struct sieve_operation *op, 
+		const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation test_multiscript_operation = { 
+	"TEST_MULTISCRIPT",
+	&testsuite_extension, 
+	TESTSUITE_OPERATION_TEST_MULTISCRIPT,
+	tst_test_multiscript_operation_dump, 
+	tst_test_multiscript_operation_execute 
+};
+
+/* 
+ * Validation 
+ */
+
+static bool tst_test_multiscript_validate
+(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command_context *tst) 
+{
+	struct sieve_ast_argument *arg = tst->first_positional;
+	
+	if ( !sieve_validate_positional_argument
+		(valdtr, tst, arg, "scripts", 1, SAAT_STRING_LIST) ) {
+		return FALSE;
+	}
+	
+	return sieve_validator_argument_activate(valdtr, tst, arg, FALSE);
+}
+
+/* 
+ * Code generation 
+ */
+
+static inline struct testsuite_generator_context *
+	_get_generator_context(struct sieve_generator *gentr)
+{
+	return (struct testsuite_generator_context *) 
+		sieve_generator_extension_get_context(gentr, &testsuite_extension);
+}
+
+static bool tst_test_multiscript_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command_context *tst)
+{
+	sieve_operation_emit_code(cgenv->sbin, &test_multiscript_operation);
+
+	/* Generate arguments */
+	return sieve_generate_arguments(cgenv, tst, NULL);
+}
+
+/* 
+ * Code dump
+ */
+ 
+static bool tst_test_multiscript_operation_dump
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	sieve_code_dumpf(denv, "TEST_MULTISCRIPT:");
+	sieve_code_descend(denv);
+
+	if ( !sieve_opr_stringlist_dump(denv, address, "scripts") ) 
+		return FALSE;
+
+	return TRUE;
+}
+
+/*
+ * Intepretation
+ */
+
+static int tst_test_multiscript_operation_execute
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+	struct sieve_coded_stringlist *scripts_list;
+	string_t *script_name;
+	const char *script_path;
+	ARRAY_TYPE (const_string) scriptfiles;
+	bool result = TRUE;
+
+	/*
+	 * Read operands
+	 */
+
+  if ( (scripts_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
+		sieve_runtime_trace_error(renv, "invalid scripts operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+
+	/*
+	 * Perform operation
+	 */
+
+	sieve_runtime_trace(renv, "TEST MULTISCRIPT");
+
+	t_array_init(&scriptfiles, 16);
+
+	script_path = sieve_script_dirpath(renv->script);
+	if ( script_path == NULL ) 
+		return SIEVE_EXEC_FAILURE;
+
+	script_name = NULL;
+	while ( result && 
+		(result=sieve_coded_stringlist_next_item(scripts_list, &script_name))
+		&& script_name != NULL ) {	
+
+		const char *path = 
+			t_strconcat(script_path, "/", str_c(script_name), NULL);
+
+		/* Attempt script compile */
+		array_append(&scriptfiles, &path, 1);	
+	}
+
+	result = result && testsuite_script_multiscript(renv, &scriptfiles);
+
+	/* Set result */
+	sieve_interpreter_set_test_result(renv->interp, result);
+
+	return SIEVE_EXEC_OK;
+}
+
+
+
+
diff --git a/tests/extensions/imap4flags/multiscript.svtest b/tests/extensions/imap4flags/multiscript.svtest
new file mode 100644
index 000000000..0a0418cb1
--- /dev/null
+++ b/tests/extensions/imap4flags/multiscript.svtest
@@ -0,0 +1,13 @@
+require "vnd.dovecot.testsuite";
+
+test "Segfault Trigger 1" {
+
+	if not test_multiscript [
+		"multiscript/group-spam.sieve", 
+		"multiscript/spam.sieve", 
+		"multiscript/sent-store.sieve"]
+	{
+		test_fail "failed multiscript execution";	
+	}
+}
+
diff --git a/tests/extensions/imap4flags/multiscript/group-spam.sieve b/tests/extensions/imap4flags/multiscript/group-spam.sieve
new file mode 100644
index 000000000..700d33de5
--- /dev/null
+++ b/tests/extensions/imap4flags/multiscript/group-spam.sieve
@@ -0,0 +1,14 @@
+require ["fileinto", "variables", "envelope"];
+
+if header :contains "X-Group-Mail" ["Yes", "YES", "1"] {
+  if header :contains "X-Spam-Flag" ["Yes", "YES", "1"] {
+        if envelope :matches :localpart "to" "*" {
+		fileinto "group/${1}/SPAM"; stop;
+	}
+  }
+  if address :is ["To"] "sales@florist.ru" {
+  	fileinto "group/info/Orders";
+  }
+  stop;
+}
+keep;
diff --git a/tests/extensions/imap4flags/multiscript/sent-store.sieve b/tests/extensions/imap4flags/multiscript/sent-store.sieve
new file mode 100644
index 000000000..e5e3906df
--- /dev/null
+++ b/tests/extensions/imap4flags/multiscript/sent-store.sieve
@@ -0,0 +1,7 @@
+require ["imap4flags"];
+
+if header :contains "X-Set-Seen" ["Yes", "YES", "1"] {
+  	setflag "\\Seen";
+}
+
+keep;
diff --git a/tests/extensions/imap4flags/multiscript/spam.sieve b/tests/extensions/imap4flags/multiscript/spam.sieve
new file mode 100644
index 000000000..9e1b6c349
--- /dev/null
+++ b/tests/extensions/imap4flags/multiscript/spam.sieve
@@ -0,0 +1,8 @@
+require ["fileinto"];
+
+if header :contains "X-Spam-Flag" ["Yes", "YES", "1"] {
+  fileinto "SPAM";
+}
+keep;
+
+
-- 
GitLab