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