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