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