Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2018-2023 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it
6 : under the terms of the GNU General Public License as published
7 : by the Free Software Foundation; either version 3, or (at your
8 : option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 :
20 : /**
21 : * @file testing/testing_api_loop.c
22 : * @brief main interpreter loop for testcases
23 : * @author Christian Grothoff
24 : * @author Marcello Stanisci
25 : */
26 : #include "taler/taler_json_lib.h"
27 : #include <gnunet/gnunet_curl_lib.h>
28 : #include "taler/taler_util.h"
29 : #include "taler/taler_testing_lib.h"
30 :
31 :
32 : /**
33 : * Age restriction mask for the exchange under test.
34 : * Set during TALER_TESTING_run_with_fakebank by reading the [exchange] config.
35 : */
36 : struct TALER_AgeMask TALER_testing_age_restriction_mask;
37 :
38 :
39 : /**
40 : * The interpreter and its state
41 : */
42 : struct TALER_TESTING_Interpreter
43 : {
44 :
45 : /**
46 : * Commands the interpreter will run.
47 : */
48 : struct TALER_TESTING_Command *commands;
49 :
50 : /**
51 : * Interpreter task (if one is scheduled).
52 : */
53 : struct GNUNET_SCHEDULER_Task *task;
54 :
55 : /**
56 : * Handle for the child management.
57 : */
58 : struct GNUNET_ChildWaitHandle *cwh;
59 :
60 : /**
61 : * Main execution context for the main loop.
62 : */
63 : struct GNUNET_CURL_Context *ctx;
64 :
65 : /**
66 : * Context for running the CURL event loop.
67 : */
68 : struct GNUNET_CURL_RescheduleContext *rc;
69 :
70 : /**
71 : * Hash map mapping variable names to commands.
72 : */
73 : struct GNUNET_CONTAINER_MultiHashMap *vars;
74 :
75 : /**
76 : * Task run on timeout.
77 : */
78 : struct GNUNET_SCHEDULER_Task *timeout_task;
79 :
80 : /**
81 : * Instruction pointer. Tells #interpreter_run() which instruction to run
82 : * next. Need (signed) int because it gets -1 when rewinding the
83 : * interpreter to the first CMD.
84 : */
85 : int ip;
86 :
87 : /**
88 : * Result of the testcases, #GNUNET_OK on success
89 : */
90 : enum GNUNET_GenericReturnValue result;
91 :
92 : };
93 :
94 :
95 : const struct TALER_TESTING_Command *
96 1240 : TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
97 : const char *label)
98 : {
99 1240 : if (NULL == label)
100 : {
101 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
102 : "Attempt to lookup command for empty label\n");
103 0 : return NULL;
104 : }
105 : /* Search backwards as we most likely reference recent commands */
106 4971 : for (int i = is->ip; i >= 0; i--)
107 : {
108 4971 : const struct TALER_TESTING_Command *cmd = &is->commands[i];
109 :
110 : /* Give precedence to top-level commands. */
111 4971 : if ( (NULL != cmd->label) &&
112 4971 : (0 == strcmp (cmd->label,
113 : label)) )
114 170 : return cmd;
115 :
116 4801 : if (TALER_TESTING_cmd_is_batch (cmd))
117 : {
118 : struct TALER_TESTING_Command *batch;
119 : struct TALER_TESTING_Command *current;
120 : struct TALER_TESTING_Command *icmd;
121 : const struct TALER_TESTING_Command *match;
122 :
123 2776 : current = TALER_TESTING_cmd_batch_get_current (cmd);
124 2776 : GNUNET_assert (GNUNET_OK ==
125 : TALER_TESTING_get_trait_batch_cmds (cmd,
126 : &batch));
127 : /* We must do the loop forward, but we can find the last match */
128 2776 : match = NULL;
129 2776 : for (unsigned int j = 0;
130 33064 : NULL != (icmd = &batch[j])->label;
131 30288 : j++)
132 : {
133 31358 : if (current == icmd)
134 1070 : break; /* do not go past current command */
135 30288 : if ( (NULL != icmd->label) &&
136 30288 : (0 == strcmp (icmd->label,
137 : label)) )
138 1087 : match = icmd;
139 : }
140 2776 : if (NULL != match)
141 1070 : return match;
142 : }
143 : }
144 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
145 : "Command not found: %s\n",
146 : label);
147 0 : return NULL;
148 : }
149 :
150 :
151 : const struct TALER_TESTING_Command *
152 1719 : TALER_TESTING_interpreter_get_command (struct TALER_TESTING_Interpreter *is,
153 : const char *name)
154 : {
155 : const struct TALER_TESTING_Command *cmd;
156 : struct GNUNET_HashCode h_name;
157 :
158 1719 : GNUNET_CRYPTO_hash (name,
159 : strlen (name),
160 : &h_name);
161 1719 : cmd = GNUNET_CONTAINER_multihashmap_get (is->vars,
162 : &h_name);
163 1719 : if (NULL == cmd)
164 34 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
165 : "Command not found by name: %s\n",
166 : name);
167 1719 : return cmd;
168 : }
169 :
170 :
171 : struct GNUNET_CURL_Context *
172 904 : TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is)
173 : {
174 904 : return is->ctx;
175 : }
176 :
177 :
178 : void
179 0 : TALER_TESTING_touch_cmd (struct TALER_TESTING_Interpreter *is)
180 : {
181 0 : is->commands[is->ip].last_req_time
182 0 : = GNUNET_TIME_absolute_get ();
183 0 : }
184 :
185 :
186 : void
187 0 : TALER_TESTING_inc_tries (struct TALER_TESTING_Interpreter *is)
188 : {
189 0 : is->commands[is->ip].num_tries++;
190 0 : }
191 :
192 :
193 : /**
194 : * Run the main interpreter loop that performs exchange operations.
195 : *
196 : * @param cls contains the `struct InterpreterState`
197 : */
198 : static void
199 : interpreter_run (void *cls);
200 :
201 :
202 : void
203 1332 : TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is)
204 : {
205 : static unsigned long long ipc;
206 : static struct GNUNET_TIME_Absolute last_report;
207 : struct TALER_TESTING_Command *cmd;
208 :
209 1332 : if (GNUNET_SYSERR == is->result)
210 0 : return; /* ignore, we already failed! */
211 1332 : if (0 > is->ip)
212 : {
213 : /* We were rewound to before the first command; there is no
214 : command to finish, just (re)start at the beginning. */
215 0 : is->ip = 0;
216 0 : is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
217 : is);
218 0 : return;
219 : }
220 1332 : cmd = &is->commands[is->ip];
221 1332 : if (TALER_TESTING_cmd_is_batch (cmd))
222 : {
223 976 : if (TALER_TESTING_cmd_batch_next (is,
224 : cmd->cls))
225 : {
226 : /* batch is done */
227 70 : cmd->finish_time = GNUNET_TIME_absolute_get ();
228 70 : is->ip++;
229 : }
230 : }
231 : else
232 : {
233 356 : cmd->finish_time = GNUNET_TIME_absolute_get ();
234 356 : is->ip++;
235 : }
236 1332 : if (0 == (ipc % 1000))
237 : {
238 21 : if (0 != ipc)
239 0 : GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
240 : "Interpreter executed 1000 instructions in %s\n",
241 : GNUNET_STRINGS_relative_time_to_string (
242 : GNUNET_TIME_absolute_get_duration (last_report),
243 : true));
244 21 : last_report = GNUNET_TIME_absolute_get ();
245 : }
246 1332 : ipc++;
247 1332 : is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
248 : is);
249 : }
250 :
251 :
252 : void
253 0 : TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is)
254 : {
255 0 : struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
256 :
257 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
258 : "Failed at command `%s'\n",
259 : cmd->label);
260 0 : while (TALER_TESTING_cmd_is_batch (cmd))
261 : {
262 0 : cmd = TALER_TESTING_cmd_batch_get_current (cmd);
263 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
264 : "Batch is at command `%s'\n",
265 : cmd->label);
266 : }
267 0 : is->result = GNUNET_SYSERR;
268 0 : GNUNET_SCHEDULER_shutdown ();
269 0 : }
270 :
271 :
272 : const char *
273 0 : TALER_TESTING_interpreter_get_current_label (
274 : struct TALER_TESTING_Interpreter *is)
275 : {
276 0 : struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
277 :
278 0 : return cmd->label;
279 : }
280 :
281 :
282 : void
283 2238 : TALER_TESTING_update_variables_ (
284 : struct TALER_TESTING_Interpreter *is,
285 : struct TALER_TESTING_Command *cmd)
286 : {
287 : struct GNUNET_HashCode h_name;
288 :
289 2238 : if (NULL == cmd->name)
290 2176 : return;
291 62 : GNUNET_CRYPTO_hash (cmd->name,
292 : strlen (cmd->name),
293 : &h_name);
294 62 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
295 : "Storing command %s under variable `%s'\n",
296 : cmd->label,
297 : cmd->name);
298 62 : (void) GNUNET_CONTAINER_multihashmap_put (
299 : is->vars,
300 : &h_name,
301 : cmd,
302 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
303 : }
304 :
305 :
306 : static void
307 1353 : interpreter_run (void *cls)
308 : {
309 1353 : struct TALER_TESTING_Interpreter *is = cls;
310 1353 : struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
311 :
312 1353 : is->task = NULL;
313 1353 : if (NULL == cmd->label)
314 : {
315 :
316 21 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317 : "Running command END\n");
318 21 : is->result = GNUNET_OK;
319 21 : GNUNET_SCHEDULER_shutdown ();
320 21 : return;
321 : }
322 :
323 1332 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
324 : "Running command `%s'\n",
325 : cmd->label);
326 : cmd->last_req_time
327 1332 : = GNUNET_TIME_absolute_get ();
328 1332 : if (0 == cmd->num_tries)
329 426 : cmd->start_time = cmd->last_req_time;
330 1332 : cmd->num_tries++;
331 1332 : TALER_TESTING_update_variables_ (is,
332 : cmd);
333 1332 : cmd->run (cmd->cls,
334 : cmd,
335 : is);
336 : }
337 :
338 :
339 : /**
340 : * Function run when the test terminates (good or bad).
341 : * Cleans up our state.
342 : *
343 : * @param cls the interpreter state.
344 : */
345 : static void
346 21 : do_shutdown (void *cls)
347 : {
348 21 : struct TALER_TESTING_Interpreter *is = cls;
349 : struct TALER_TESTING_Command *cmd;
350 : const char *label;
351 :
352 21 : label = is->commands[is->ip].label;
353 21 : if (NULL == label)
354 21 : label = "END";
355 21 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
356 : "Executing shutdown at `%s'\n",
357 : label);
358 21 : for (unsigned int j = 0;
359 447 : NULL != (cmd = &is->commands[j])->label;
360 426 : j++)
361 426 : if (NULL != cmd->cleanup)
362 426 : cmd->cleanup (cmd->cls,
363 : cmd);
364 21 : if (NULL != is->task)
365 : {
366 0 : GNUNET_SCHEDULER_cancel (is->task);
367 0 : is->task = NULL;
368 : }
369 21 : if (NULL != is->ctx)
370 : {
371 21 : GNUNET_CURL_fini (is->ctx);
372 21 : is->ctx = NULL;
373 : }
374 21 : if (NULL != is->rc)
375 : {
376 21 : GNUNET_CURL_gnunet_rc_destroy (is->rc);
377 21 : is->rc = NULL;
378 : }
379 21 : if (NULL != is->vars)
380 : {
381 21 : GNUNET_CONTAINER_multihashmap_destroy (is->vars);
382 21 : is->vars = NULL;
383 : }
384 21 : if (NULL != is->timeout_task)
385 : {
386 21 : GNUNET_SCHEDULER_cancel (is->timeout_task);
387 21 : is->timeout_task = NULL;
388 : }
389 21 : if (NULL != is->cwh)
390 : {
391 0 : GNUNET_wait_child_cancel (is->cwh);
392 0 : is->cwh = NULL;
393 : }
394 21 : GNUNET_free (is->commands);
395 21 : }
396 :
397 :
398 : /**
399 : * Function run when the test terminates (good or bad) with timeout.
400 : *
401 : * @param cls the `struct TALER_TESTING_Interpreter *`
402 : */
403 : static void
404 0 : do_timeout (void *cls)
405 : {
406 0 : struct TALER_TESTING_Interpreter *is = cls;
407 :
408 0 : is->timeout_task = NULL;
409 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
410 : "Terminating test due to timeout\n");
411 0 : GNUNET_SCHEDULER_shutdown ();
412 0 : }
413 :
414 :
415 : /**
416 : * Task triggered whenever we receive a SIGCHLD (child
417 : * process died).
418 : *
419 : * @param cls the `struct TALER_TESTING_Interpreter *`
420 : * @param type type of the process
421 : * @param code status code of the process
422 : */
423 : static void
424 220 : maint_child_death (void *cls,
425 : enum GNUNET_OS_ProcessStatusType type,
426 : long unsigned int code)
427 : {
428 220 : struct TALER_TESTING_Interpreter *is = cls;
429 220 : struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
430 : struct GNUNET_Process **processp;
431 :
432 220 : is->cwh = NULL;
433 364 : while (TALER_TESTING_cmd_is_batch (cmd))
434 144 : cmd = TALER_TESTING_cmd_batch_get_current (cmd);
435 220 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
436 : "Got SIGCHLD for `%s'.\n",
437 : cmd->label);
438 220 : if (GNUNET_OK !=
439 220 : TALER_TESTING_get_trait_process (cmd,
440 : &processp))
441 : {
442 0 : GNUNET_break (0);
443 0 : TALER_TESTING_interpreter_fail (is);
444 0 : return;
445 : }
446 220 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
447 : "Got the dead child process handle, waiting for termination ...\n");
448 220 : GNUNET_process_destroy (*processp);
449 220 : *processp = NULL;
450 220 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
451 : "... definitively terminated\n");
452 220 : switch (type)
453 : {
454 0 : case GNUNET_OS_PROCESS_UNKNOWN:
455 0 : GNUNET_break (0);
456 0 : TALER_TESTING_interpreter_fail (is);
457 0 : return;
458 0 : case GNUNET_OS_PROCESS_RUNNING:
459 0 : GNUNET_break (0);
460 0 : TALER_TESTING_interpreter_fail (is);
461 0 : return;
462 0 : case GNUNET_OS_PROCESS_STOPPED:
463 0 : GNUNET_break (0);
464 0 : TALER_TESTING_interpreter_fail (is);
465 0 : return;
466 220 : case GNUNET_OS_PROCESS_EXITED:
467 220 : if (0 != code)
468 : {
469 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
470 : "Process exited with unexpected status %u\n",
471 : (unsigned int) code);
472 0 : TALER_TESTING_interpreter_fail (is);
473 0 : return;
474 : }
475 220 : break;
476 0 : case GNUNET_OS_PROCESS_SIGNALED:
477 0 : GNUNET_break (0);
478 0 : TALER_TESTING_interpreter_fail (is);
479 0 : return;
480 : }
481 220 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482 : "Dead child, go on with next command.\n");
483 220 : TALER_TESTING_interpreter_next (is);
484 : }
485 :
486 :
487 : void
488 220 : TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is)
489 : {
490 : struct GNUNET_Process **processp;
491 220 : struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
492 :
493 364 : while (TALER_TESTING_cmd_is_batch (cmd))
494 144 : cmd = TALER_TESTING_cmd_batch_get_current (cmd);
495 220 : if (GNUNET_OK !=
496 220 : TALER_TESTING_get_trait_process (cmd,
497 : &processp))
498 : {
499 0 : GNUNET_break (0);
500 0 : TALER_TESTING_interpreter_fail (is);
501 0 : return;
502 : }
503 220 : GNUNET_assert (NULL == is->cwh);
504 : is->cwh
505 220 : = GNUNET_wait_child (*processp,
506 : &maint_child_death,
507 : is);
508 : }
509 :
510 :
511 : void
512 21 : TALER_TESTING_run2 (struct TALER_TESTING_Interpreter *is,
513 : struct TALER_TESTING_Command *commands,
514 : struct GNUNET_TIME_Relative timeout)
515 : {
516 : unsigned int i;
517 :
518 21 : if (NULL != is->timeout_task)
519 : {
520 0 : GNUNET_SCHEDULER_cancel (is->timeout_task);
521 0 : is->timeout_task = NULL;
522 : }
523 : /* get the number of commands */
524 447 : for (i = 0; NULL != commands[i].label; i++)
525 : ;
526 21 : is->commands = GNUNET_malloc_large ( (i + 1)
527 : * sizeof (struct TALER_TESTING_Command));
528 21 : GNUNET_assert (NULL != is->commands);
529 21 : GNUNET_memcpy (is->commands,
530 : commands,
531 : sizeof (struct TALER_TESTING_Command) * i);
532 21 : is->timeout_task = GNUNET_SCHEDULER_add_delayed (
533 : timeout,
534 : &do_timeout,
535 : is);
536 21 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
537 : is);
538 21 : is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
539 : is);
540 21 : }
541 :
542 :
543 : #include "valgrind.h"
544 :
545 : void
546 21 : TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,
547 : struct TALER_TESTING_Command *commands)
548 : {
549 21 : TALER_TESTING_run2 (is,
550 : commands,
551 21 : 0 == RUNNING_ON_VALGRIND
552 21 : ? GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES,
553 : 5)
554 0 : : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES,
555 : 50));
556 21 : }
557 :
558 :
559 : /**
560 : * Information used by the wrapper around the main
561 : * "run" method.
562 : */
563 : struct MainContext
564 : {
565 : /**
566 : * Main "run" method.
567 : */
568 : TALER_TESTING_Main main_cb;
569 :
570 : /**
571 : * Closure for @e main_cb.
572 : */
573 : void *main_cb_cls;
574 :
575 : /**
576 : * Interpreter state.
577 : */
578 : struct TALER_TESTING_Interpreter *is;
579 :
580 : /**
581 : * URL of the exchange.
582 : */
583 : char *exchange_url;
584 :
585 : };
586 :
587 :
588 : /**
589 : * Initialize scheduler loop and curl context for the testcase,
590 : * and responsible to run the "run" method.
591 : *
592 : * @param cls closure, typically the "run" method, the
593 : * interpreter state and a closure for "run".
594 : */
595 : static void
596 21 : main_wrapper (void *cls)
597 : {
598 21 : struct MainContext *main_ctx = cls;
599 :
600 21 : main_ctx->main_cb (main_ctx->main_cb_cls,
601 : main_ctx->is);
602 21 : }
603 :
604 :
605 : enum GNUNET_GenericReturnValue
606 21 : TALER_TESTING_loop (TALER_TESTING_Main main_cb,
607 : void *main_cb_cls)
608 : {
609 : struct TALER_TESTING_Interpreter is;
610 21 : struct MainContext main_ctx = {
611 : .main_cb = main_cb,
612 : .main_cb_cls = main_cb_cls,
613 : /* needed to init the curl ctx */
614 : .is = &is,
615 : };
616 :
617 21 : memset (&is,
618 : 0,
619 : sizeof (is));
620 21 : is.ctx = GNUNET_CURL_init (
621 : &GNUNET_CURL_gnunet_scheduler_reschedule,
622 : &is.rc);
623 21 : GNUNET_CURL_enable_async_scope_header (is.ctx,
624 : "Taler-Correlation-Id");
625 21 : GNUNET_assert (NULL != is.ctx);
626 21 : is.rc = GNUNET_CURL_gnunet_rc_create (is.ctx);
627 21 : is.vars = GNUNET_CONTAINER_multihashmap_create (1024,
628 : false);
629 : /* Blocking */
630 21 : GNUNET_SCHEDULER_run (&main_wrapper,
631 : &main_ctx);
632 21 : return is.result;
633 : }
634 :
635 :
636 : int
637 21 : TALER_TESTING_main (char *const *argv,
638 : const char *loglevel,
639 : const char *cfg_file,
640 : const char *exchange_account_section,
641 : enum TALER_TESTING_BankSystem bs,
642 : struct TALER_TESTING_Credentials *cred,
643 : TALER_TESTING_Main main_cb,
644 : void *main_cb_cls)
645 : {
646 : enum GNUNET_GenericReturnValue ret;
647 :
648 21 : unsetenv ("XDG_DATA_HOME");
649 21 : unsetenv ("XDG_CONFIG_HOME");
650 21 : GNUNET_log_setup (argv[0],
651 : loglevel,
652 : NULL);
653 21 : if (GNUNET_OK !=
654 21 : TALER_TESTING_get_credentials (cfg_file,
655 : exchange_account_section,
656 : bs,
657 : cred))
658 : {
659 0 : GNUNET_break (0);
660 0 : return 77;
661 : }
662 21 : if (GNUNET_OK !=
663 21 : TALER_TESTING_cleanup_files_cfg (NULL,
664 21 : cred->cfg))
665 : {
666 0 : GNUNET_break (0);
667 0 : return 77;
668 : }
669 : /* Initialize age restriction mask from [exchange] config */
670 21 : memset (&TALER_testing_age_restriction_mask,
671 : 0,
672 : sizeof (TALER_testing_age_restriction_mask));
673 21 : if (GNUNET_YES ==
674 21 : GNUNET_CONFIGURATION_get_value_yesno (cred->cfg,
675 : "exchange",
676 : "AGE_RESTRICTION_ENABLED"))
677 : {
678 13 : char *groups = NULL;
679 :
680 13 : if (GNUNET_OK !=
681 13 : GNUNET_CONFIGURATION_get_value_string (cred->cfg,
682 : "exchange",
683 : "AGE_GROUPS",
684 : &groups))
685 13 : groups = GNUNET_strdup ("8:10:12:14:16:18:21");
686 13 : if (GNUNET_OK !=
687 13 : TALER_parse_age_group_string (groups,
688 : &TALER_testing_age_restriction_mask))
689 : {
690 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
691 : "Invalid AGE_GROUPS in [exchange]: %s\n",
692 : groups);
693 0 : GNUNET_free (groups);
694 0 : GNUNET_break (0);
695 0 : return 77;
696 : }
697 13 : GNUNET_free (groups);
698 : }
699 21 : ret = TALER_TESTING_loop (main_cb,
700 : main_cb_cls);
701 : /* FIXME: should we free 'cred' resources here? */
702 21 : return (GNUNET_OK == ret) ? 0 : 1;
703 : }
704 :
705 :
706 : /* ************** iterate over commands ********* */
707 :
708 :
709 : void
710 26 : TALER_TESTING_iterate (struct TALER_TESTING_Interpreter *is,
711 : bool asc,
712 : TALER_TESTING_CommandIterator cb,
713 : void *cb_cls)
714 : {
715 : unsigned int start;
716 : unsigned int end;
717 : int inc;
718 :
719 26 : if (asc)
720 : {
721 26 : inc = 1;
722 26 : start = 0;
723 26 : end = is->ip;
724 : }
725 : else
726 : {
727 0 : inc = -1;
728 0 : start = is->ip;
729 0 : end = 0;
730 : }
731 370 : for (unsigned int off = start; off != end + inc; off += inc)
732 : {
733 344 : const struct TALER_TESTING_Command *cmd = &is->commands[off];
734 :
735 344 : cb (cb_cls,
736 : cmd);
737 : }
738 26 : }
739 :
740 :
741 : /* ************** special commands ********* */
742 :
743 :
744 : struct TALER_TESTING_Command
745 91 : TALER_TESTING_cmd_end (void)
746 : {
747 : static struct TALER_TESTING_Command cmd;
748 91 : cmd.label = NULL;
749 :
750 91 : return cmd;
751 : }
752 :
753 :
754 : struct TALER_TESTING_Command
755 7 : TALER_TESTING_cmd_set_var (const char *name,
756 : struct TALER_TESTING_Command cmd)
757 : {
758 7 : cmd.name = name;
759 7 : return cmd;
760 : }
761 :
762 :
763 : /**
764 : * State for a "rewind" CMD.
765 : */
766 : struct RewindIpState
767 : {
768 : /**
769 : * Instruction pointer to set into the interpreter.
770 : */
771 : const char *target_label;
772 :
773 : /**
774 : * How many times this set should take place. However, this value lives at
775 : * the calling process, and this CMD is only in charge of checking and
776 : * decremeting it.
777 : */
778 : unsigned int counter;
779 : };
780 :
781 :
782 : /**
783 : * Seek for the @a target command in @a batch (and rewind to it
784 : * if successful).
785 : *
786 : * @param is the interpreter state (for failures)
787 : * @param cmd batch to search for @a target
788 : * @param target command to search for
789 : * @return #GNUNET_OK on success, #GNUNET_NO if target was not found,
790 : * #GNUNET_SYSERR if target is in the future and we failed
791 : */
792 : static enum GNUNET_GenericReturnValue
793 0 : seek_batch (struct TALER_TESTING_Interpreter *is,
794 : const struct TALER_TESTING_Command *cmd,
795 : const struct TALER_TESTING_Command *target)
796 : {
797 : unsigned int new_ip;
798 : struct TALER_TESTING_Command *batch;
799 : struct TALER_TESTING_Command *current;
800 : struct TALER_TESTING_Command *icmd;
801 : struct TALER_TESTING_Command *match;
802 :
803 0 : current = TALER_TESTING_cmd_batch_get_current (cmd);
804 0 : GNUNET_assert (GNUNET_OK ==
805 : TALER_TESTING_get_trait_batch_cmds (cmd,
806 : &batch));
807 0 : match = NULL;
808 0 : for (new_ip = 0;
809 0 : NULL != (icmd = &batch[new_ip])->label;
810 0 : new_ip++)
811 : {
812 0 : if (current == target)
813 0 : current = NULL;
814 0 : if (icmd == target)
815 : {
816 0 : match = icmd;
817 0 : break;
818 : }
819 0 : if (TALER_TESTING_cmd_is_batch (icmd))
820 : {
821 : enum GNUNET_GenericReturnValue ret;
822 :
823 0 : ret = seek_batch (is,
824 : icmd,
825 : target);
826 0 : if (GNUNET_SYSERR == ret)
827 0 : return GNUNET_SYSERR; /* failure! */
828 0 : if (GNUNET_OK == ret)
829 : {
830 0 : match = icmd;
831 0 : break;
832 : }
833 : }
834 : }
835 0 : if (NULL == current)
836 : {
837 : /* refuse to jump forward */
838 0 : GNUNET_break (0);
839 0 : TALER_TESTING_interpreter_fail (is);
840 0 : return GNUNET_SYSERR;
841 : }
842 0 : if (NULL == match)
843 0 : return GNUNET_NO; /* not found */
844 0 : TALER_TESTING_cmd_batch_set_current (cmd,
845 : new_ip);
846 0 : return GNUNET_OK;
847 : }
848 :
849 :
850 : /**
851 : * Run the "rewind" CMD.
852 : *
853 : * @param cls closure.
854 : * @param cmd command being executed now.
855 : * @param is the interpreter state.
856 : */
857 : static void
858 0 : rewind_ip_run (void *cls,
859 : const struct TALER_TESTING_Command *cmd,
860 : struct TALER_TESTING_Interpreter *is)
861 : {
862 0 : struct RewindIpState *ris = cls;
863 : const struct TALER_TESTING_Command *target;
864 : unsigned int new_ip;
865 :
866 : (void) cmd;
867 0 : if (0 == ris->counter)
868 : {
869 0 : TALER_TESTING_interpreter_next (is);
870 0 : return;
871 : }
872 : target
873 0 : = TALER_TESTING_interpreter_lookup_command (is,
874 : ris->target_label);
875 0 : if (NULL == target)
876 : {
877 0 : GNUNET_break (0);
878 0 : TALER_TESTING_interpreter_fail (is);
879 0 : return;
880 : }
881 0 : ris->counter--;
882 0 : for (new_ip = 0;
883 0 : NULL != is->commands[new_ip].label;
884 0 : new_ip++)
885 : {
886 0 : const struct TALER_TESTING_Command *ipcmd
887 0 : = &is->commands[new_ip];
888 :
889 0 : if (ipcmd == target)
890 0 : break;
891 0 : if (TALER_TESTING_cmd_is_batch (ipcmd))
892 : {
893 0 : int ret = seek_batch (is,
894 : ipcmd,
895 : target);
896 0 : if (GNUNET_SYSERR == ret)
897 0 : return; /* failure! */
898 0 : if (GNUNET_OK == ret)
899 0 : break;
900 : }
901 : }
902 0 : if (new_ip > (unsigned int) is->ip)
903 : {
904 : /* refuse to jump forward */
905 0 : GNUNET_break (0);
906 0 : TALER_TESTING_interpreter_fail (is);
907 0 : return;
908 : }
909 0 : is->ip = new_ip - 1; /* -1 because the next function will advance by one */
910 0 : TALER_TESTING_interpreter_next (is);
911 : }
912 :
913 :
914 : struct TALER_TESTING_Command
915 0 : TALER_TESTING_cmd_rewind_ip (const char *label,
916 : const char *target_label,
917 : unsigned int counter)
918 : {
919 : struct RewindIpState *ris;
920 :
921 0 : ris = GNUNET_new (struct RewindIpState);
922 0 : ris->target_label = target_label;
923 0 : ris->counter = counter;
924 : {
925 0 : struct TALER_TESTING_Command cmd = {
926 : .cls = ris,
927 : .label = label,
928 : .run = &rewind_ip_run
929 : };
930 :
931 0 : return cmd;
932 : }
933 : }
934 :
935 :
936 : /**
937 : * State for a "authchange" CMD.
938 : */
939 : struct AuthchangeState
940 : {
941 :
942 : /**
943 : * What is the new authorization token to send?
944 : */
945 : const char *auth_token;
946 :
947 : /**
948 : * Old context, clean up on termination.
949 : */
950 : struct GNUNET_CURL_Context *old_ctx;
951 : };
952 :
953 :
954 : /**
955 : * Run the command.
956 : *
957 : * @param cls closure.
958 : * @param cmd the command to execute.
959 : * @param is the interpreter state.
960 : */
961 : static void
962 0 : authchange_run (void *cls,
963 : const struct TALER_TESTING_Command *cmd,
964 : struct TALER_TESTING_Interpreter *is)
965 : {
966 0 : struct AuthchangeState *ss = cls;
967 :
968 : (void) cmd;
969 0 : ss->old_ctx = is->ctx;
970 0 : if (NULL != is->rc)
971 : {
972 0 : GNUNET_CURL_gnunet_rc_destroy (is->rc);
973 0 : is->rc = NULL;
974 : }
975 0 : is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
976 0 : &is->rc);
977 0 : GNUNET_CURL_enable_async_scope_header (is->ctx,
978 : "Taler-Correlation-Id");
979 0 : GNUNET_assert (NULL != is->ctx);
980 0 : is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx);
981 0 : if (NULL != ss->auth_token)
982 : {
983 : char *authorization;
984 :
985 0 : GNUNET_asprintf (&authorization,
986 : "%s: %s",
987 : MHD_HTTP_HEADER_AUTHORIZATION,
988 : ss->auth_token);
989 0 : GNUNET_assert (GNUNET_OK ==
990 : GNUNET_CURL_append_header (is->ctx,
991 : authorization));
992 0 : GNUNET_free (authorization);
993 : }
994 0 : TALER_TESTING_interpreter_next (is);
995 0 : }
996 :
997 :
998 : /**
999 : * Call GNUNET_CURL_fini(). Done as a separate task to
1000 : * ensure that all of the command's cleanups have been
1001 : * executed first. See #7151.
1002 : *
1003 : * @param cls a `struct GNUNET_CURL_Context *` to clean up.
1004 : */
1005 : static void
1006 0 : deferred_cleanup_cb (void *cls)
1007 : {
1008 0 : struct GNUNET_CURL_Context *ctx = cls;
1009 :
1010 0 : GNUNET_CURL_fini (ctx);
1011 0 : }
1012 :
1013 :
1014 : /**
1015 : * Cleanup the state from a "authchange" CMD.
1016 : *
1017 : * @param cls closure.
1018 : * @param cmd the command which is being cleaned up.
1019 : */
1020 : static void
1021 0 : authchange_cleanup (void *cls,
1022 : const struct TALER_TESTING_Command *cmd)
1023 : {
1024 0 : struct AuthchangeState *ss = cls;
1025 :
1026 : (void) cmd;
1027 0 : if (NULL != ss->old_ctx)
1028 : {
1029 0 : (void) GNUNET_SCHEDULER_add_now (&deferred_cleanup_cb,
1030 0 : ss->old_ctx);
1031 0 : ss->old_ctx = NULL;
1032 : }
1033 0 : GNUNET_free (ss);
1034 0 : }
1035 :
1036 :
1037 : struct TALER_TESTING_Command
1038 0 : TALER_TESTING_cmd_set_authorization (const char *label,
1039 : const char *auth_token)
1040 : {
1041 : struct AuthchangeState *ss;
1042 :
1043 0 : ss = GNUNET_new (struct AuthchangeState);
1044 0 : ss->auth_token = auth_token;
1045 :
1046 : {
1047 0 : struct TALER_TESTING_Command cmd = {
1048 : .cls = ss,
1049 : .label = label,
1050 : .run = &authchange_run,
1051 : .cleanup = &authchange_cleanup
1052 : };
1053 :
1054 0 : return cmd;
1055 : }
1056 : }
1057 :
1058 :
1059 : /* end of testing_api_loop.c */
|