Coverage Report

Created: 2026-07-16 07:09

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