From 616b274f9112d008cd5d2e5363405e56d38b5696 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Mon, 28 Jul 2008 16:29:12 +0200
Subject: [PATCH] Testsuite: started support for error validation.

---
 src/testsuite/Makefile.am               |   3 +-
 src/testsuite/cmd-test-fail.c           |   2 +-
 src/testsuite/cmd-test-set.c            |   2 +-
 src/testsuite/ext-testsuite.c           |   5 +-
 src/testsuite/tests/errors.svtest       |  66 ++++++
 src/testsuite/tests/header-errors.sieve |  44 ++++
 src/testsuite/testsuite-common.h        |   4 +-
 src/testsuite/tst-test-compile.c        |   2 +-
 src/testsuite/tst-test-error.c          | 272 ++++++++++++++++++++++++
 9 files changed, 394 insertions(+), 6 deletions(-)
 create mode 100644 src/testsuite/tests/errors.svtest
 create mode 100644 src/testsuite/tests/header-errors.sieve
 create mode 100644 src/testsuite/tst-test-error.c

diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.am
index f34458adb..db2cf88cd 100644
--- a/src/testsuite/Makefile.am
+++ b/src/testsuite/Makefile.am
@@ -43,7 +43,8 @@ commands = \
 	cmd-test-set.c
 
 tests = \
-	tst-test-compile.c
+	tst-test-compile.c \
+	tst-test-error.c
 
 testsuite_SOURCES = \
 	namespaces.c \
diff --git a/src/testsuite/cmd-test-fail.c b/src/testsuite/cmd-test-fail.c
index 9903b908b..11dd0c9ff 100644
--- a/src/testsuite/cmd-test-fail.c
+++ b/src/testsuite/cmd-test-fail.c
@@ -13,7 +13,7 @@
  * Test_fail command
  *
  * Syntax:   
- *   test <reason: string>
+ *   test_fail <reason: string>
  */
 
 static bool cmd_test_fail_validate
diff --git a/src/testsuite/cmd-test-set.c b/src/testsuite/cmd-test-set.c
index aded4ca2a..a6abbcb8d 100644
--- a/src/testsuite/cmd-test-set.c
+++ b/src/testsuite/cmd-test-set.c
@@ -23,7 +23,7 @@
  * Test_set command 
  * 
  * Syntax
- *   redirect <address: string>
+ *   test_set <testsuite object (member): string> <value: string>
  */
 
 static bool cmd_test_set_validate
diff --git a/src/testsuite/ext-testsuite.c b/src/testsuite/ext-testsuite.c
index cf20cd066..dc8fb5a7a 100644
--- a/src/testsuite/ext-testsuite.c
+++ b/src/testsuite/ext-testsuite.c
@@ -59,6 +59,7 @@ extern const struct sieve_command cmd_test_set;
 /* Tests */
 
 extern const struct sieve_command tst_test_compile;
+extern const struct sieve_command tst_test_error;
 
 /* Operations */
 
@@ -67,7 +68,8 @@ const struct sieve_operation *testsuite_operations[] = {
 	&test_finish_operation,
 	&test_fail_operation, 
 	&test_set_operation,
-	&test_compile_operation
+	&test_compile_operation,
+	&test_error_operation
 };
 
 /* Operands */
@@ -108,6 +110,7 @@ static bool ext_testsuite_validator_load(struct sieve_validator *valdtr)
 	sieve_validator_register_command(valdtr, &cmd_test_set);
 
 	sieve_validator_register_command(valdtr, &tst_test_compile);
+	sieve_validator_register_command(valdtr, &tst_test_error);
 	
 	return testsuite_validator_context_initialize(valdtr);
 }
diff --git a/src/testsuite/tests/errors.svtest b/src/testsuite/tests/errors.svtest
new file mode 100644
index 000000000..107585bc2
--- /dev/null
+++ b/src/testsuite/tests/errors.svtest
@@ -0,0 +1,66 @@
+require "vnd.dovecot.testsuite";
+
+require "relational";
+require "comparator-i;ascii-numeric";
+
+test "Header errors" {
+	if test_compile "header-errors.sieve" {
+		test_fail "compile should have failed.";
+	}
+
+	if not test_error :count "eq" :comparator "i;ascii-numeric" "11" {
+		test_fail "wrong number of errors reported";
+	}
+
+	if not test_error :index 1 :matches 
+		"unknown * ':isnot' for * header test *" {
+		test_fail "error 1 is invalid";
+	}
+
+	if not test_error :index 2 :matches
+		":comparator * requires one string argument, * number *" {
+		test_fail "error 2 is invalid";
+	}
+
+	if not test_error :index 3 :matches
+		"unknown tagged argument ':all' * header test *" {
+		test_fail "error 3 is invalid";
+	}
+
+	if not test_error :index 4 :matches
+		"*header test * string list * 2 (key list), * number *" {
+		test_fail "error 4 is invalid";
+	}
+
+	if not test_error :index 5 :matches 
+		"*header test expects * string list * 1 (header names), but * number *" {
+		test_fail "error 5 is invalid";
+	}
+
+	if not test_error :index 6 :matches 
+		"* unexpected tagged argument ':tag' while *" {
+		test_fail "error 6 is invalid";
+	}
+
+	if not test_error :index 7 :matches 
+		"* header test requires 2 *, but 1 *" {
+		test_fail "error 7 is invalid";
+	}
+
+	if not test_error :index 8 :matches 
+		"* header test requires 2 *, but 0 *" {
+		test_fail "error 8 is invalid";
+	}
+
+	if not test_error :index 9 :matches 
+		"*unknown * ':hufter' * header test *" {
+		test_fail "error 9 is invalid";
+	}
+
+	if not test_error :index 10 :matches 
+		"*header test accepts no sub-tests, but tests * anyway" {
+		test_fail "error 10 is invalid";
+	}
+}	
+
+
diff --git a/src/testsuite/tests/header-errors.sieve b/src/testsuite/tests/header-errors.sieve
new file mode 100644
index 000000000..ac6108ba1
--- /dev/null
+++ b/src/testsuite/tests/header-errors.sieve
@@ -0,0 +1,44 @@
+require "comparator-i;ascii-numeric";
+
+if header :isnot :comparator "i;ascii-casemap" "From" "nico" {
+	discard;
+}
+
+if header :is :comparator 45 "From" "nico" {
+	discard;
+}
+
+if header :all :comparator "i;ascii-numeric" {
+  	keep;
+}
+
+if header :is :comparator "i;ascii-numeric" "From" 45 {
+	discard;
+}
+
+if header :is :comparator "i;ascii-numeric" 45 "nico" {
+	discard;
+}
+
+if header :is :comparator "i;ascii-numeric" "From" :tag {
+	discard;
+}
+
+if header :is :comparator "i;ascii-numeric" "From" {
+	discard;
+}
+
+if header :is :comparator "i;ascii-numeric" {
+	discard;
+}
+
+if header :is :comparator "i;ascii-casemap" "frop" ["frop", "frop"] {
+	discard;
+}
+
+if header :hufter :is :comparator "i;ascii-casemap" "frop" ["frop", "frop"] {
+        discard;
+}
+
+if header "frop" "frop" true {
+}
diff --git a/src/testsuite/testsuite-common.h b/src/testsuite/testsuite-common.h
index dd8587b01..9568a0ba1 100644
--- a/src/testsuite/testsuite-common.h
+++ b/src/testsuite/testsuite-common.h
@@ -43,7 +43,8 @@ enum testsuite_operation_code {
 	TESTSUITE_OPERATION_TEST_FINISH,
 	TESTSUITE_OPERATION_TEST_FAIL,
 	TESTSUITE_OPERATION_TEST_SET,
-	TESTSUITE_OPERATION_TEST_COMPILE
+	TESTSUITE_OPERATION_TEST_COMPILE,
+	TESTSUITE_OPERATION_TEST_ERROR,
 };
 
 extern const struct sieve_operation test_operation;
@@ -51,6 +52,7 @@ extern const struct sieve_operation test_finish_operation;
 extern const struct sieve_operation test_fail_operation;
 extern const struct sieve_operation test_set_operation;
 extern const struct sieve_operation test_compile_operation;
+extern const struct sieve_operation test_error_operation;
 
 /* Testsuite operands */
 
diff --git a/src/testsuite/tst-test-compile.c b/src/testsuite/tst-test-compile.c
index b5a82e52f..49a1e5266 100644
--- a/src/testsuite/tst-test-compile.c
+++ b/src/testsuite/tst-test-compile.c
@@ -16,7 +16,7 @@
  * Test_compile command
  *
  * Syntax:   
- *   test <reason: string>
+ *   test_compile <scriptpath: string>
  */
 
 static bool tst_test_compile_validate
diff --git a/src/testsuite/tst-test-error.c b/src/testsuite/tst-test-error.c
new file mode 100644
index 000000000..77c4524f4
--- /dev/null
+++ b/src/testsuite/tst-test-error.c
@@ -0,0 +1,272 @@
+#include "sieve-common.h"
+#include "sieve-error.h"
+#include "sieve-script.h"
+#include "sieve-commands.h"
+#include "sieve-commands-private.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.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-match.h"
+
+#include "testsuite-common.h"
+
+/*
+ * Test_error command
+ *
+ * Syntax:   
+ *   test [MATCH-TYPE] [COMPARATOR] [:index number] <key-list: string-list>
+ */
+
+static bool tst_test_error_registered
+    (struct sieve_validator *validator, struct sieve_command_registration *cmd_reg);
+static bool tst_test_error_validate
+	(struct sieve_validator *validator, struct sieve_command_context *cmd);
+static bool tst_test_error_generate
+	(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx);
+
+const struct sieve_command tst_test_error = { 
+	"test_error", 
+	SCT_TEST, 
+	1, 0, FALSE, FALSE,
+	tst_test_error_registered, 
+	NULL,
+	tst_test_error_validate, 
+	tst_test_error_generate, 
+	NULL 
+};
+
+/* Test_error operation */
+
+static bool tst_test_error_operation_dump
+	(const struct sieve_operation *op,
+		const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int tst_test_error_operation_execute
+	(const struct sieve_operation *op, 
+		const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation test_error_operation = { 
+	"TEST_ERROR",
+	&testsuite_extension, 
+	TESTSUITE_OPERATION_TEST_ERROR,
+	tst_test_error_operation_dump, 
+	tst_test_error_operation_execute 
+};
+
+/*
+ * Tagged arguments
+ */ 
+
+/* NOTE: This will be merged with the date-index extension when it is 
+ * implemented.
+ */
+
+static bool tst_test_error_validate_index_tag
+	(struct sieve_validator *validator, struct sieve_ast_argument **arg,
+		struct sieve_command_context *cmd);
+
+static const struct sieve_argument test_error_index_tag = {
+    "index",
+    NULL, NULL,
+    tst_test_error_validate_index_tag,
+    NULL, NULL
+};
+
+enum tst_test_error_optional {
+	OPT_INDEX = SIEVE_MATCH_OPT_LAST,
+};
+
+
+/*
+ * Argument implementation
+ */
+
+static bool tst_test_error_validate_index_tag
+(struct sieve_validator *validator, struct sieve_ast_argument **arg,
+	struct sieve_command_context *cmd)
+{
+	struct sieve_ast_argument *tag = *arg;
+
+	/* Detach the tag itself */
+	*arg = sieve_ast_arguments_detach(*arg,1);
+
+	/* Check syntax:
+	 *   :index number
+	 */
+	if ( !sieve_validate_tag_parameter
+		(validator, cmd, tag, *arg, SAAT_NUMBER) ) {
+		return FALSE;
+	}
+
+    /* Skip parameter */
+    *arg = sieve_ast_argument_next(*arg);
+    return TRUE;
+}
+
+
+/*
+ * Command registration
+ */
+
+static bool tst_test_error_registered
+(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg)
+{
+	/* The order of these is not significant */
+	sieve_comparators_link_tag(validator, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
+	sieve_match_types_link_tags(validator, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
+
+	 sieve_validator_register_tag
+        (validator, cmd_reg, &test_error_index_tag, OPT_INDEX);
+
+	return TRUE;
+}
+
+/* 
+ * Validation 
+ */
+
+static bool tst_test_error_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, "key list", 2, SAAT_STRING_LIST) ) {
+        return FALSE;
+    }
+
+    if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+        return FALSE;
+
+    /* Validate the key argument to a specified match type */
+    return sieve_match_type_validate(valdtr, tst, arg);
+}
+
+/* 
+ * 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_error_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command_context *tst)
+{
+	sieve_operation_emit_code(cgenv->sbin, &test_error_operation);
+
+	/* Generate arguments */
+	return sieve_generate_arguments(cgenv, tst, NULL);
+}
+
+/* 
+ * Code dump
+ */
+ 
+static bool tst_test_error_operation_dump
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	int opt_code = 0;
+
+	sieve_code_dumpf(denv, "TEST_ERROR:");
+	sieve_code_descend(denv);
+
+	/* Handle any optional arguments */
+	do {
+		if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
+			return FALSE;
+
+		switch ( opt_code ) {
+		case SIEVE_MATCH_OPT_END:
+			break;
+		case OPT_INDEX:
+			if ( !sieve_opr_number_dump(denv, address) )
+				return FALSE;
+			break;
+		default:
+			return FALSE;
+		}
+	} while ( opt_code != SIEVE_MATCH_OPT_END );
+
+	return sieve_opr_stringlist_dump(denv, address);
+}
+
+/*
+ * Intepretation
+ */
+
+static int tst_test_error_operation_execute
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_runtime_env *renv, sieve_size_t *address)
+{	
+	int opt_code = 0;
+	bool result = TRUE;
+	const struct sieve_comparator *cmp = &i_octet_comparator;
+	const struct sieve_match_type *mtch = &is_match_type;
+	struct sieve_match_context *mctx;
+	struct sieve_coded_stringlist *key_list;
+	bool matched;
+	int index = 0;
+	int ret;
+
+	/*
+	 * Read operands
+	 */
+
+	/* Handle optional operands */
+	do {
+		sieve_size_t number; 
+
+		if ( (ret=sieve_match_read_optional_operands
+			(renv, address, &opt_code, &cmp, &mtch)) <= 0 )
+ 			return ret;
+
+		switch ( opt_code ) {
+		case SIEVE_MATCH_OPT_END:
+			break;
+		case OPT_INDEX:
+			if ( !sieve_opr_number_read(renv, address, &number) ) {
+				sieve_runtime_trace_error(renv, "invalid index operand");
+				return SIEVE_EXEC_BIN_CORRUPT;
+			}
+			index = (int) number;
+			break;
+		default:
+			sieve_runtime_trace_error(renv, "invalid optional operand");
+			return SIEVE_EXEC_BIN_CORRUPT;
+		}	
+	} while ( opt_code != SIEVE_MATCH_OPT_END);
+
+	/* Read key-list */
+	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
+		sieve_runtime_trace_error(renv, "invalid key-list operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+
+	/*
+	 * Perform operation
+	 */
+	
+	matched = TRUE;
+
+	/* Set test result for subsequent conditional jump */
+    if ( result ) {
+        sieve_interpreter_set_test_result(renv->interp, matched);
+        return SIEVE_EXEC_OK;
+    }
+
+    sieve_runtime_trace_error(renv, "invalid string-list item");
+    return SIEVE_EXEC_BIN_CORRUPT;
+}
+
+
+
+
-- 
GitLab