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