Coverage Report

Created: 2026-05-30 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/testsuite/testsuite-common.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2
 */
3
4
#include "lib.h"
5
#include "str.h"
6
#include "string.h"
7
#include "ostream.h"
8
#include "hash.h"
9
#include "safe-mkstemp.h"
10
#include "mail-storage.h"
11
#include "env-util.h"
12
#include "unlink-directory.h"
13
14
#include "mail-raw.h"
15
16
#include "sieve-common.h"
17
#include "sieve-code.h"
18
#include "sieve-message.h"
19
#include "sieve-commands.h"
20
#include "sieve-extensions.h"
21
#include "sieve-binary.h"
22
#include "sieve-validator.h"
23
#include "sieve-generator.h"
24
#include "sieve-interpreter.h"
25
#include "sieve-result.h"
26
#include "sieve-dump.h"
27
28
#include "testsuite-common.h"
29
#include "testsuite-settings.h"
30
#include "testsuite-objects.h"
31
#include "testsuite-log.h"
32
#include "testsuite-script.h"
33
#include "testsuite-binary.h"
34
#include "testsuite-result.h"
35
#include "testsuite-smtp.h"
36
37
#include <string.h>
38
#include <fcntl.h>
39
#include <unistd.h>
40
#include <time.h>
41
#include <sys/stat.h>
42
#include <sys/types.h>
43
44
/*
45
 * Global data
46
 */
47
48
struct sieve_instance *testsuite_sieve_instance = NULL;
49
char *testsuite_test_path = NULL;
50
bool testsuite_silent = FALSE;
51
52
unsigned int test_failures;
53
54
static struct sieve_interpreter *testsuite_interp = NULL;
55
56
/* Test context */
57
58
static string_t *test_name;
59
static sieve_size_t test_block_end;
60
static unsigned int test_index;
61
62
/* Extension */
63
64
const struct sieve_extension *testsuite_ext;
65
66
/*
67
 * Validator context
68
 */
69
70
bool testsuite_validator_context_initialize(struct sieve_validator *valdtr)
71
0
{
72
0
  pool_t pool = sieve_validator_pool(valdtr);
73
0
  struct testsuite_validator_context *ctx =
74
0
    p_new(pool, struct testsuite_validator_context, 1);
75
76
  /* Setup object registry */
77
0
  ctx->object_registrations =
78
0
    sieve_validator_object_registry_create(valdtr);
79
0
  testsuite_register_core_objects(ctx);
80
81
0
  sieve_validator_extension_set_context(valdtr, testsuite_ext, ctx);
82
83
0
  return TRUE;
84
0
}
85
86
struct testsuite_validator_context *
87
testsuite_validator_context_get(struct sieve_validator *valdtr)
88
0
{
89
0
  return (struct testsuite_validator_context *)
90
0
    sieve_validator_extension_get_context(valdtr, testsuite_ext);
91
0
}
92
93
/*
94
 * Generator context
95
 */
96
97
bool testsuite_generator_context_initialize(
98
  struct sieve_generator *gentr, const struct sieve_extension *this_ext)
99
0
{
100
0
  pool_t pool = sieve_generator_pool(gentr);
101
0
  struct sieve_binary_block *sblock = sieve_generator_get_block(gentr);
102
0
  struct testsuite_generator_context *ctx =
103
0
    p_new(pool, struct testsuite_generator_context, 1);
104
105
  /* Setup exit jumplist */
106
0
  ctx->exit_jumps = sieve_jumplist_create(pool, sblock);
107
108
0
  sieve_generator_extension_set_context(gentr, this_ext, ctx);
109
110
0
  return TRUE;
111
0
}
112
113
/*
114
 * Interpreter context
115
 */
116
117
static void
118
testsuite_interpreter_free(const struct sieve_extension *ext ATTR_UNUSED,
119
         struct sieve_interpreter *interp ATTR_UNUSED,
120
         void *context)
121
0
{
122
0
  struct testsuite_interpreter_context *ctx =
123
0
    (struct testsuite_interpreter_context *)context;
124
125
0
  sieve_binary_unref(&ctx->compiled_script);
126
0
}
127
128
const struct sieve_interpreter_extension testsuite_interpreter_ext = {
129
  .ext_def = &testsuite_extension,
130
  .free = testsuite_interpreter_free,
131
};
132
133
bool testsuite_interpreter_context_initialize(
134
  struct sieve_interpreter *interp, const struct sieve_extension *this_ext)
135
0
{
136
0
  pool_t pool = sieve_interpreter_pool(interp);
137
0
  struct testsuite_interpreter_context *ctx =
138
0
    p_new(pool, struct testsuite_interpreter_context, 1);
139
140
0
  sieve_interpreter_extension_register(interp, this_ext,
141
0
               &testsuite_interpreter_ext, ctx);
142
0
  return TRUE;
143
0
}
144
145
struct testsuite_interpreter_context *
146
testsuite_interpreter_context_get(struct sieve_interpreter *interp,
147
          const struct sieve_extension *this_ext)
148
0
{
149
0
  struct testsuite_interpreter_context *ctx =
150
0
    sieve_interpreter_extension_get_context(interp, this_ext);
151
152
0
  return ctx;
153
0
}
154
155
/*
156
 * Test context
157
 */
158
159
static void testsuite_test_context_init(void)
160
0
{
161
0
  test_name = str_new(default_pool, 128);
162
0
  test_block_end = 0;
163
0
  test_index = 0;
164
0
  test_failures = 0;
165
0
}
166
167
int testsuite_test_start(const struct sieve_runtime_env *renv,
168
       string_t *name, sieve_size_t block_end)
169
0
{
170
0
  if (test_block_end != 0) {
171
0
    sieve_runtime_trace_error(renv, "already inside test block");
172
0
    return SIEVE_EXEC_BIN_CORRUPT;
173
0
  }
174
0
  str_truncate(test_name, 0);
175
0
  str_append_str(test_name, name);
176
177
0
  test_block_end = block_end;
178
0
  test_index++;
179
180
0
  return SIEVE_EXEC_OK;
181
0
}
182
183
int testsuite_test_fail(const struct sieve_runtime_env *renv,
184
      string_t *reason)
185
0
{
186
0
  return testsuite_test_fail_cstr(renv, str_c(reason));
187
0
}
188
189
int testsuite_test_failf(const struct sieve_runtime_env *renv,
190
       const char *fmt, ...)
191
0
{
192
0
  va_list args;
193
0
  int ret;
194
195
0
  va_start(args, fmt);
196
0
  ret = testsuite_test_fail_cstr(renv, t_strdup_vprintf(fmt, args));
197
0
  va_end(args);
198
199
0
  return ret;
200
0
}
201
202
int testsuite_test_fail_cstr(const struct sieve_runtime_env *renv,
203
           const char *reason)
204
0
{
205
0
  sieve_size_t end = test_block_end;
206
207
0
  if (!testsuite_silent) {
208
0
    if (str_len(test_name) == 0) {
209
0
      if (reason == NULL || *reason == '\0')
210
0
        printf("%2d: Test FAILED\n", test_index);
211
0
      else {
212
0
        printf("%2d: Test FAILED: %s\n",
213
0
               test_index, reason);
214
0
      }
215
0
    } else {
216
0
      if (reason == NULL || *reason == '\0') {
217
0
        printf("%2d: Test '%s' FAILED\n",
218
0
               test_index, str_c(test_name));
219
0
      } else {
220
0
        printf("%2d: Test '%s' FAILED: %s\n",
221
0
               test_index, str_c(test_name), reason);
222
0
      }
223
0
    }
224
0
  }
225
226
0
  test_failures++;
227
228
0
  if (end == 0)
229
0
    return SIEVE_EXEC_FAILURE;
230
0
  if (renv->interp != testsuite_interp) {
231
0
    sieve_interpreter_interrupt(renv->interp);
232
0
    return SIEVE_EXEC_OK;
233
0
  }
234
235
0
  str_truncate(test_name, 0);
236
0
  test_block_end = 0;
237
238
0
  return sieve_interpreter_program_jump_to(renv->interp, end, TRUE);
239
0
}
240
241
void testsuite_testcase_fail(const char *reason)
242
0
{
243
0
  if (!testsuite_silent) {
244
0
    if (reason == NULL || *reason == '\0')
245
0
      printf("XX: Test CASE FAILED\n");
246
0
    else
247
0
      printf("XX: Test CASE FAILED: %s\n", reason);
248
0
  }
249
0
  test_failures++;
250
0
}
251
252
int testsuite_test_succeed(const struct sieve_runtime_env *renv,
253
         sieve_size_t *address, string_t *reason)
254
0
{
255
0
  sieve_size_t end = test_block_end;
256
0
  int ret;
257
258
0
  if (!testsuite_silent) {
259
0
    if (str_len(test_name) == 0) {
260
0
      if (reason == NULL || str_len(reason) == 0)
261
0
        printf("%2d: Test SUCCEEDED\n", test_index);
262
0
      else {
263
0
        printf("%2d: Test SUCCEEDED: %s\n",
264
0
               test_index, str_c(reason));
265
0
      }
266
0
    } else {
267
0
      if (reason == NULL || str_len(reason) == 0) {
268
0
        printf("%2d: Test '%s' SUCCEEDED\n",
269
0
               test_index, str_c(test_name));
270
0
      } else {
271
0
        printf("%2d: Test '%s' SUCCEEDED: %s\n",
272
0
               test_index, str_c(test_name),
273
0
               str_c(reason));
274
0
      }
275
0
    }
276
0
  }
277
278
0
  str_truncate(test_name, 0);
279
0
  test_block_end = 0;
280
281
0
  if (*address > end) {
282
0
    sieve_runtime_trace_error(
283
0
      renv, "invalid test block end offset");
284
0
    return SIEVE_EXEC_BIN_CORRUPT;
285
0
  } else if (*address < end) {
286
0
    ret = sieve_interpreter_program_jump_to(
287
0
      renv->interp, end, FALSE);
288
0
    if (ret <= 0)
289
0
      return ret;
290
0
  }
291
292
0
  return SIEVE_EXEC_OK;
293
0
}
294
295
static void testsuite_test_context_deinit(void)
296
0
{
297
0
  str_free(&test_name);
298
0
}
299
300
bool testsuite_testcase_result(bool expect_failure)
301
0
{
302
0
  if (expect_failure) {
303
0
    if (test_failures < test_index) {
304
0
      if (!testsuite_silent) {
305
0
        printf("\nFAIL: Only %d of %d tests failed "
306
0
               "(all expected to fail).\n\n",
307
0
               test_failures, test_index);
308
0
      }
309
0
      return FALSE;
310
0
    }
311
312
0
    if (!testsuite_silent) {
313
0
      printf("\nPASS: %d tests failed "
314
0
             "(expected to fail).\n\n",
315
0
             (test_index == 0 ? 1 : test_index));
316
0
    }
317
0
    return TRUE;
318
0
  }
319
320
0
  if (test_failures > 0) {
321
0
    if (!testsuite_silent) {
322
0
      printf("\nFAIL: %d of %d tests failed.\n\n",
323
0
             test_failures, test_index);
324
0
    }
325
0
    return FALSE;
326
0
  }
327
328
0
  if (!testsuite_silent)
329
0
    printf("\nPASS: %d tests succeeded.\n\n", test_index);
330
0
  return TRUE;
331
0
}
332
333
/*
334
 * Testsuite temporary directory
335
 */
336
337
static char *testsuite_tmp_dir;
338
339
static void testsuite_tmp_dir_init(const char *tmp_path)
340
0
{
341
0
  if (tmp_path == NULL)
342
0
    tmp_path = "/tmp";
343
344
0
  string_t *dir = t_str_new(256);
345
0
  str_append(dir, tmp_path);
346
0
  str_append_c(dir, '/');
347
0
  str_append(dir, "sieve-testsuite");
348
0
  str_append_c(dir, '-');
349
350
0
  if (safe_mkstemp_dir_pid(dir, 0700) < 0)
351
0
    i_fatal("safe_mkstemp_dir(%s) failed: %m", str_c(dir));
352
0
  testsuite_tmp_dir = i_strdup(str_c(dir));
353
0
}
354
355
void testsuite_tmp_dir_deinit(void)
356
0
{
357
0
  if (testsuite_tmp_dir == NULL)
358
0
    return;
359
360
0
  const char *error = NULL;
361
0
  if (unlink_directory(testsuite_tmp_dir,
362
0
           UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0)
363
0
    i_warning("failed to remove temporary directory '%s': %s.",
364
0
        testsuite_tmp_dir, error);
365
366
0
  i_free(testsuite_tmp_dir);
367
0
}
368
369
const char *testsuite_tmp_dir_get(void)
370
0
{
371
0
  return testsuite_tmp_dir;
372
0
}
373
374
/*
375
 * Main testsuite init/run/deinit
376
 */
377
378
void testsuite_init(struct sieve_instance *svinst, const char *test_path,
379
        const char *wdir_path, bool log_stdout)
380
0
{
381
0
  int ret;
382
383
0
  testsuite_sieve_instance = svinst;
384
385
0
  testsuite_test_context_init();
386
0
  testsuite_log_init(log_stdout);
387
0
  testsuite_tmp_dir_init(wdir_path);
388
389
0
  testsuite_script_init();
390
0
  testsuite_binary_init();
391
0
  testsuite_smtp_init();
392
393
0
  ret = sieve_extension_register(svinst, &testsuite_extension, TRUE,
394
0
               &testsuite_ext);
395
0
  i_assert(ret == 0);
396
397
0
  testsuite_test_path = i_strdup(test_path);
398
0
}
399
400
int testsuite_run(struct sieve_binary *sbin,
401
      struct sieve_error_handler *ehandler)
402
0
{
403
0
  struct sieve_result *result;
404
0
  int ret = 0;
405
406
  /* Create the interpreter */
407
0
  testsuite_interp = sieve_interpreter_create(
408
0
    sbin, NULL, &testsuite_execute_env, ehandler);
409
0
  if (testsuite_interp == NULL)
410
0
    return SIEVE_EXEC_BIN_CORRUPT;
411
412
  /* Run the interpreter */
413
0
  result = testsuite_result_get();
414
0
  ret = sieve_interpreter_run(testsuite_interp, result);
415
416
  /* Free the interpreter */
417
0
  sieve_interpreter_free(&testsuite_interp);
418
419
0
  return ret;
420
0
}
421
422
void testsuite_deinit(void)
423
0
{
424
0
  i_free(testsuite_test_path);
425
426
0
  testsuite_smtp_deinit();
427
0
  testsuite_binary_deinit();
428
0
  testsuite_script_deinit();
429
430
0
  testsuite_tmp_dir_deinit();
431
0
  testsuite_log_deinit();
432
0
  testsuite_test_context_deinit();
433
0
}