summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/testsuite/cmd-test.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/testsuite/cmd-test.c')
-rw-r--r--pigeonhole/src/testsuite/cmd-test.c208
1 files changed, 208 insertions, 0 deletions
diff --git a/pigeonhole/src/testsuite/cmd-test.c b/pigeonhole/src/testsuite/cmd-test.c
new file mode 100644
index 0000000..c817aed
--- /dev/null
+++ b/pigeonhole/src/testsuite/cmd-test.c
@@ -0,0 +1,208 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "sieve-common.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"
+
+/*
+ * Test command
+ *
+ * Syntax:
+ * test <test-name: string> <block>
+ */
+
+static bool
+cmd_test_validate(struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool
+cmd_test_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *md);
+
+const struct sieve_command_def cmd_test = {
+ .identifier = "test",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = TRUE,
+ .block_required = TRUE,
+ .validate = cmd_test_validate,
+ .generate = cmd_test_generate,
+};
+
+/*
+ * Test operations
+ */
+
+/* Test operation */
+
+static bool
+cmd_test_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+cmd_test_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_operation_def test_operation = {
+ .mnemonic = "TEST",
+ .ext_def = &testsuite_extension,
+ .code = TESTSUITE_OPERATION_TEST,
+ .dump = cmd_test_operation_dump,
+ .execute = cmd_test_operation_execute,
+};
+
+/* Test_finish operation */
+
+static int
+cmd_test_finish_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_operation_def test_finish_operation = {
+ .mnemonic = "TEST-FINISH",
+ .ext_def = &testsuite_extension,
+ .code = TESTSUITE_OPERATION_TEST_FINISH,
+ .execute = cmd_test_finish_operation_execute,
+};
+
+/*
+ * Validation
+ */
+
+static bool
+cmd_test_validate(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *arg = cmd->first_positional;
+
+ /* Check valid command placement */
+ if (!sieve_command_is_toplevel(cmd)) {
+ sieve_command_validate_error(
+ valdtr, cmd, "tests cannot be nested: test "
+ "command must be issued at top-level");
+ return FALSE;
+ }
+
+ if (!sieve_validate_positional_argument(valdtr, cmd, arg, "test-name",
+ 1, SAAT_STRING))
+ return FALSE;
+
+ return sieve_validator_argument_activate(valdtr, cmd, 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_ext);
+}
+
+static bool
+cmd_test_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd)
+{
+ struct testsuite_generator_context *genctx =
+ _get_generator_context(cgenv->gentr);
+
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &test_operation);
+
+ /* Generate arguments */
+ if (!sieve_generate_arguments(cgenv, cmd, NULL))
+ return FALSE;
+
+ /* Prepare jumplist */
+ sieve_jumplist_reset(genctx->exit_jumps);
+ sieve_jumplist_add(genctx->exit_jumps,
+ sieve_binary_emit_offset(cgenv->sblock, 0));
+
+ /* Test body */
+ if (!sieve_generate_block(cgenv, cmd->ast_node))
+ return FALSE;
+
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &test_finish_operation);
+
+ /* Resolve exit jumps to this point */
+ sieve_jumplist_resolve(genctx->exit_jumps);
+
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+cmd_test_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ sieve_size_t tst_begin;
+ sieve_offset_t tst_end_offset;
+
+ sieve_code_dumpf(denv, "TEST:");
+ sieve_code_descend(denv);
+
+ if (!sieve_opr_string_dump(denv, address, "test name"))
+ return FALSE;
+
+ sieve_code_mark(denv);
+ tst_begin = *address;
+ if (!sieve_binary_read_offset(denv->sblock, address, &tst_end_offset))
+ return FALSE;
+ sieve_code_dumpf(denv, "end: %d [%08llx]",
+ tst_end_offset,
+ (unsigned long long)tst_begin + tst_end_offset);
+
+ return TRUE;
+}
+
+/*
+ * Interpretation
+ */
+
+static int
+cmd_test_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ sieve_size_t tst_begin, tst_end;
+ sieve_offset_t tst_end_offset;
+ string_t *test_name;
+ int ret;
+
+ ret = sieve_opr_string_read(renv, address, "test name", &test_name);
+ if (ret <= 0)
+ return ret;
+
+ tst_begin = *address;
+ if (!sieve_binary_read_offset(renv->sblock, address, &tst_end_offset)) {
+ sieve_runtime_trace_error(renv, "invalid end offset");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ tst_end = tst_begin + tst_end_offset;
+
+ sieve_runtime_trace_sep(renv);
+ sieve_runtime_trace(renv, SIEVE_TRLVL_NONE,
+ "** Testsuite test start: \"%s\" (end: %08llx)",
+ str_c(test_name),
+ (unsigned long long)tst_end);
+
+ return testsuite_test_start(renv, test_name, tst_end);
+}
+
+static int
+cmd_test_finish_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ sieve_runtime_trace(renv, SIEVE_TRLVL_NONE, "** Testsuite test end");
+ sieve_runtime_trace_sep(renv);
+
+ return testsuite_test_succeed(renv, address, NULL);
+}