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