Coverage Report

Created: 2026-05-30 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/testsuite/cmd-test.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2
 */
3
4
#include "sieve-common.h"
5
#include "sieve-commands.h"
6
#include "sieve-validator.h"
7
#include "sieve-generator.h"
8
#include "sieve-interpreter.h"
9
#include "sieve-code.h"
10
#include "sieve-binary.h"
11
#include "sieve-dump.h"
12
13
#include "testsuite-common.h"
14
15
/*
16
 * Test command
17
 *
18
 * Syntax:
19
 *   test <test-name: string> <block>
20
 */
21
22
static bool
23
cmd_test_validate(struct sieve_validator *valdtr, struct sieve_command *cmd);
24
static bool
25
cmd_test_generate(const struct sieve_codegen_env *cgenv,
26
      struct sieve_command *md);
27
28
const struct sieve_command_def cmd_test = {
29
  .identifier = "test",
30
  .type = SCT_COMMAND,
31
  .positional_args = 1,
32
  .subtests = 0,
33
  .block_allowed = TRUE,
34
  .block_required = TRUE,
35
  .validate = cmd_test_validate,
36
  .generate = cmd_test_generate,
37
};
38
39
/*
40
 * Test operations
41
 */
42
43
/* Test operation */
44
45
static bool
46
cmd_test_operation_dump(const struct sieve_dumptime_env *denv,
47
      sieve_size_t *address);
48
static int
49
cmd_test_operation_execute(const struct sieve_runtime_env *renv,
50
         sieve_size_t *address);
51
52
const struct sieve_operation_def test_operation = {
53
  .mnemonic = "TEST",
54
  .ext_def = &testsuite_extension,
55
  .code = TESTSUITE_OPERATION_TEST,
56
  .dump = cmd_test_operation_dump,
57
  .execute = cmd_test_operation_execute,
58
};
59
60
/* Test_finish operation */
61
62
static int
63
cmd_test_finish_operation_execute(const struct sieve_runtime_env *renv,
64
          sieve_size_t *address);
65
66
const struct sieve_operation_def test_finish_operation = {
67
  .mnemonic = "TEST-FINISH",
68
  .ext_def = &testsuite_extension,
69
  .code = TESTSUITE_OPERATION_TEST_FINISH,
70
  .execute = cmd_test_finish_operation_execute,
71
};
72
73
/*
74
 * Validation
75
 */
76
77
static bool
78
cmd_test_validate(struct sieve_validator *valdtr ATTR_UNUSED,
79
      struct sieve_command *cmd)
80
0
{
81
0
  struct sieve_ast_argument *arg = cmd->first_positional;
82
83
  /* Check valid command placement */
84
0
  if (!sieve_command_is_toplevel(cmd)) {
85
0
    sieve_command_validate_error(
86
0
      valdtr, cmd, "tests cannot be nested: test "
87
0
      "command must be issued at top-level");
88
0
    return FALSE;
89
0
  }
90
91
0
  if (!sieve_validate_positional_argument(valdtr, cmd, arg, "test-name",
92
0
            1, SAAT_STRING))
93
0
    return FALSE;
94
95
0
  return sieve_validator_argument_activate(valdtr, cmd, arg, FALSE);
96
0
}
97
98
/*
99
 * Code generation
100
 */
101
102
static inline struct testsuite_generator_context *
103
_get_generator_context(struct sieve_generator *gentr)
104
0
{
105
0
  return (struct testsuite_generator_context *)
106
0
    sieve_generator_extension_get_context(gentr, testsuite_ext);
107
0
}
108
109
static bool
110
cmd_test_generate(const struct sieve_codegen_env *cgenv,
111
      struct sieve_command *cmd)
112
0
{
113
0
  struct testsuite_generator_context *genctx =
114
0
    _get_generator_context(cgenv->gentr);
115
116
0
  sieve_operation_emit(cgenv->sblock, cmd->ext, &test_operation);
117
118
  /* Generate arguments */
119
0
  if (!sieve_generate_arguments(cgenv, cmd, NULL))
120
0
    return FALSE;
121
122
  /* Prepare jumplist */
123
0
  sieve_jumplist_reset(genctx->exit_jumps);
124
0
  sieve_jumplist_add(genctx->exit_jumps,
125
0
    sieve_binary_emit_offset(cgenv->sblock, 0));
126
127
  /* Test body */
128
0
  if (!sieve_generate_block(cgenv, cmd->ast_node))
129
0
    return FALSE;
130
131
0
  sieve_operation_emit(cgenv->sblock, cmd->ext, &test_finish_operation);
132
133
  /* Resolve exit jumps to this point */
134
0
  sieve_jumplist_resolve(genctx->exit_jumps);
135
136
0
  return TRUE;
137
0
}
138
139
/*
140
 * Code dump
141
 */
142
143
static bool
144
cmd_test_operation_dump(const struct sieve_dumptime_env *denv,
145
      sieve_size_t *address)
146
0
{
147
0
  sieve_size_t tst_begin;
148
0
  sieve_offset_t tst_end_offset;
149
150
0
  sieve_code_dumpf(denv, "TEST:");
151
0
  sieve_code_descend(denv);
152
153
0
  if (!sieve_opr_string_dump(denv, address, "test name"))
154
0
    return FALSE;
155
156
0
  sieve_code_mark(denv);
157
0
  tst_begin = *address;
158
0
  if (!sieve_binary_read_offset(denv->sblock, address, &tst_end_offset))
159
0
    return FALSE;
160
0
  sieve_code_dumpf(denv, "end: %d [%08llx]",
161
0
       tst_end_offset,
162
0
       (unsigned long long)tst_begin + tst_end_offset);
163
164
0
  return TRUE;
165
0
}
166
167
/*
168
 * Interpretation
169
 */
170
171
static int
172
cmd_test_operation_execute(const struct sieve_runtime_env *renv,
173
         sieve_size_t *address)
174
0
{
175
0
  sieve_size_t tst_begin, tst_end;
176
0
  sieve_offset_t tst_end_offset;
177
0
  string_t *test_name;
178
0
  int ret;
179
180
0
  ret = sieve_opr_string_read(renv, address, "test name", &test_name);
181
0
  if (ret <= 0)
182
0
    return ret;
183
184
0
  tst_begin = *address;
185
0
  if (!sieve_binary_read_offset(renv->sblock, address, &tst_end_offset)) {
186
0
    sieve_runtime_trace_error(renv, "invalid end offset");
187
0
    return SIEVE_EXEC_BIN_CORRUPT;
188
0
  }
189
0
  tst_end = tst_begin + tst_end_offset;
190
191
0
  sieve_runtime_trace_sep(renv);
192
0
  sieve_runtime_trace(renv, SIEVE_TRLVL_NONE,
193
0
          "** Testsuite test start: \"%s\" (end: %08llx)",
194
0
          str_c(test_name),
195
0
          (unsigned long long)tst_end);
196
197
0
  return testsuite_test_start(renv, test_name, tst_end);
198
0
}
199
200
static int
201
cmd_test_finish_operation_execute(const struct sieve_runtime_env *renv,
202
          sieve_size_t *address)
203
0
{
204
0
  sieve_runtime_trace(renv, SIEVE_TRLVL_NONE, "** Testsuite test end");
205
0
  sieve_runtime_trace_sep(renv);
206
207
  return testsuite_test_succeed(renv, address, NULL);
208
0
}