exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

testing_api_loop.c (24769B)


      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/platform.h"
     27 #include "taler/taler_json_lib.h"
     28 #include <gnunet/gnunet_curl_lib.h>
     29 #include "taler/taler_extensions.h"
     30 #include "taler/taler_signatures.h"
     31 #include "taler/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 TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
     92                                           const char *label)
     93 {
     94   if (NULL == label)
     95   {
     96     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     97                 "Attempt to lookup command for empty label\n");
     98     return NULL;
     99   }
    100   /* Search backwards as we most likely reference recent commands */
    101   for (int i = is->ip; i >= 0; i--)
    102   {
    103     const struct TALER_TESTING_Command *cmd = &is->commands[i];
    104 
    105     /* Give precedence to top-level commands.  */
    106     if ( (NULL != cmd->label) &&
    107          (0 == strcmp (cmd->label,
    108                        label)) )
    109       return cmd;
    110 
    111     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       current = TALER_TESTING_cmd_batch_get_current (cmd);
    119       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       match = NULL;
    124       for (unsigned int j = 0;
    125            NULL != (icmd = &batch[j])->label;
    126            j++)
    127       {
    128         if (current == icmd)
    129           break; /* do not go past current command */
    130         if ( (NULL != icmd->label) &&
    131              (0 == strcmp (icmd->label,
    132                            label)) )
    133           match = icmd;
    134       }
    135       if (NULL != match)
    136         return match;
    137     }
    138   }
    139   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    140               "Command not found: %s\n",
    141               label);
    142   return NULL;
    143 }
    144 
    145 
    146 const struct TALER_TESTING_Command *
    147 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   GNUNET_CRYPTO_hash (name,
    154                       strlen (name),
    155                       &h_name);
    156   cmd = GNUNET_CONTAINER_multihashmap_get (is->vars,
    157                                            &h_name);
    158   if (NULL == cmd)
    159     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    160                 "Command not found by name: %s\n",
    161                 name);
    162   return cmd;
    163 }
    164 
    165 
    166 struct GNUNET_CURL_Context *
    167 TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is)
    168 {
    169   return is->ctx;
    170 }
    171 
    172 
    173 void
    174 TALER_TESTING_touch_cmd (struct TALER_TESTING_Interpreter *is)
    175 {
    176   is->commands[is->ip].last_req_time
    177     = GNUNET_TIME_absolute_get ();
    178 }
    179 
    180 
    181 void
    182 TALER_TESTING_inc_tries (struct TALER_TESTING_Interpreter *is)
    183 {
    184   is->commands[is->ip].num_tries++;
    185 }
    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 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   struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
    203 
    204   if (GNUNET_SYSERR == is->result)
    205     return; /* ignore, we already failed! */
    206   if (TALER_TESTING_cmd_is_batch (cmd))
    207   {
    208     if (TALER_TESTING_cmd_batch_next (is,
    209                                       cmd->cls))
    210     {
    211       /* batch is done */
    212       cmd->finish_time = GNUNET_TIME_absolute_get ();
    213       is->ip++;
    214     }
    215   }
    216   else
    217   {
    218     cmd->finish_time = GNUNET_TIME_absolute_get ();
    219     is->ip++;
    220   }
    221   if (0 == (ipc % 1000))
    222   {
    223     if (0 != ipc)
    224       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     last_report = GNUNET_TIME_absolute_get ();
    230   }
    231   ipc++;
    232   is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
    233                                        is);
    234 }
    235 
    236 
    237 void
    238 TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is)
    239 {
    240   struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
    241 
    242   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    243               "Failed at command `%s'\n",
    244               cmd->label);
    245   while (TALER_TESTING_cmd_is_batch (cmd))
    246   {
    247     cmd = TALER_TESTING_cmd_batch_get_current (cmd);
    248     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    249                 "Batch is at command `%s'\n",
    250                 cmd->label);
    251   }
    252   is->result = GNUNET_SYSERR;
    253   GNUNET_SCHEDULER_shutdown ();
    254 }
    255 
    256 
    257 const char *
    258 TALER_TESTING_interpreter_get_current_label (
    259   struct TALER_TESTING_Interpreter *is)
    260 {
    261   struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
    262 
    263   return cmd->label;
    264 }
    265 
    266 
    267 void
    268 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   if (NULL == cmd->name)
    275     return;
    276   GNUNET_CRYPTO_hash (cmd->name,
    277                       strlen (cmd->name),
    278                       &h_name);
    279   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    280               "Storing command %s under variable `%s'\n",
    281               cmd->label,
    282               cmd->name);
    283   (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 interpreter_run (void *cls)
    293 {
    294   struct TALER_TESTING_Interpreter *is = cls;
    295   struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
    296 
    297   is->task = NULL;
    298   if (NULL == cmd->label)
    299   {
    300 
    301     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    302                 "Running command END\n");
    303     is->result = GNUNET_OK;
    304     GNUNET_SCHEDULER_shutdown ();
    305     return;
    306   }
    307 
    308   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    309               "Running command `%s'\n",
    310               cmd->label);
    311   cmd->last_req_time
    312     = GNUNET_TIME_absolute_get ();
    313   if (0 == cmd->num_tries)
    314     cmd->start_time = cmd->last_req_time;
    315   cmd->num_tries++;
    316   TALER_TESTING_update_variables_ (is,
    317                                    cmd);
    318   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 do_shutdown (void *cls)
    332 {
    333   struct TALER_TESTING_Interpreter *is = cls;
    334   struct TALER_TESTING_Command *cmd;
    335   const char *label;
    336 
    337   label = is->commands[is->ip].label;
    338   if (NULL == label)
    339     label = "END";
    340   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    341               "Executing shutdown at `%s'\n",
    342               label);
    343   for (unsigned int j = 0;
    344        NULL != (cmd = &is->commands[j])->label;
    345        j++)
    346     if (NULL != cmd->cleanup)
    347       cmd->cleanup (cmd->cls,
    348                     cmd);
    349   if (NULL != is->task)
    350   {
    351     GNUNET_SCHEDULER_cancel (is->task);
    352     is->task = NULL;
    353   }
    354   if (NULL != is->ctx)
    355   {
    356     GNUNET_CURL_fini (is->ctx);
    357     is->ctx = NULL;
    358   }
    359   if (NULL != is->rc)
    360   {
    361     GNUNET_CURL_gnunet_rc_destroy (is->rc);
    362     is->rc = NULL;
    363   }
    364   if (NULL != is->vars)
    365   {
    366     GNUNET_CONTAINER_multihashmap_destroy (is->vars);
    367     is->vars = NULL;
    368   }
    369   if (NULL != is->timeout_task)
    370   {
    371     GNUNET_SCHEDULER_cancel (is->timeout_task);
    372     is->timeout_task = NULL;
    373   }
    374   if (NULL != is->cwh)
    375   {
    376     GNUNET_wait_child_cancel (is->cwh);
    377     is->cwh = NULL;
    378   }
    379   GNUNET_free (is->commands);
    380 }
    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 do_timeout (void *cls)
    390 {
    391   struct TALER_TESTING_Interpreter *is = cls;
    392 
    393   is->timeout_task = NULL;
    394   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    395               "Terminating test due to timeout\n");
    396   GNUNET_SCHEDULER_shutdown ();
    397 }
    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 maint_child_death (void *cls,
    410                    enum GNUNET_OS_ProcessStatusType type,
    411                    long unsigned int code)
    412 {
    413   struct TALER_TESTING_Interpreter *is = cls;
    414   struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
    415   struct GNUNET_OS_Process **processp;
    416 
    417   is->cwh = NULL;
    418   while (TALER_TESTING_cmd_is_batch (cmd))
    419     cmd = TALER_TESTING_cmd_batch_get_current (cmd);
    420   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    421               "Got SIGCHLD for `%s'.\n",
    422               cmd->label);
    423   if (GNUNET_OK !=
    424       TALER_TESTING_get_trait_process (cmd,
    425                                        &processp))
    426   {
    427     GNUNET_break (0);
    428     TALER_TESTING_interpreter_fail (is);
    429     return;
    430   }
    431   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    432               "Got the dead child process handle, waiting for termination ...\n");
    433   GNUNET_OS_process_destroy (*processp);
    434   *processp = NULL;
    435   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    436               "... definitively terminated\n");
    437   switch (type)
    438   {
    439   case GNUNET_OS_PROCESS_UNKNOWN:
    440     GNUNET_break (0);
    441     TALER_TESTING_interpreter_fail (is);
    442     return;
    443   case GNUNET_OS_PROCESS_RUNNING:
    444     GNUNET_break (0);
    445     TALER_TESTING_interpreter_fail (is);
    446     return;
    447   case GNUNET_OS_PROCESS_STOPPED:
    448     GNUNET_break (0);
    449     TALER_TESTING_interpreter_fail (is);
    450     return;
    451   case GNUNET_OS_PROCESS_EXITED:
    452     if (0 != code)
    453     {
    454       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    455                   "Process exited with unexpected status %u\n",
    456                   (unsigned int) code);
    457       TALER_TESTING_interpreter_fail (is);
    458       return;
    459     }
    460     break;
    461   case GNUNET_OS_PROCESS_SIGNALED:
    462     GNUNET_break (0);
    463     TALER_TESTING_interpreter_fail (is);
    464     return;
    465   }
    466   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    467               "Dead child, go on with next command.\n");
    468   TALER_TESTING_interpreter_next (is);
    469 }
    470 
    471 
    472 void
    473 TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is)
    474 {
    475   struct GNUNET_OS_Process **processp;
    476   struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
    477 
    478   while (TALER_TESTING_cmd_is_batch (cmd))
    479     cmd = TALER_TESTING_cmd_batch_get_current (cmd);
    480   if (GNUNET_OK !=
    481       TALER_TESTING_get_trait_process (cmd,
    482                                        &processp))
    483   {
    484     GNUNET_break (0);
    485     TALER_TESTING_interpreter_fail (is);
    486     return;
    487   }
    488   GNUNET_assert (NULL == is->cwh);
    489   is->cwh
    490     = GNUNET_wait_child (*processp,
    491                          &maint_child_death,
    492                          is);
    493 }
    494 
    495 
    496 void
    497 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   if (NULL != is->timeout_task)
    504   {
    505     GNUNET_SCHEDULER_cancel (is->timeout_task);
    506     is->timeout_task = NULL;
    507   }
    508   /* get the number of commands */
    509   for (i = 0; NULL != commands[i].label; i++)
    510     ;
    511   is->commands = GNUNET_malloc_large ( (i + 1)
    512                                        * sizeof (struct TALER_TESTING_Command));
    513   GNUNET_assert (NULL != is->commands);
    514   GNUNET_memcpy (is->commands,
    515                  commands,
    516                  sizeof (struct TALER_TESTING_Command) * i);
    517   is->timeout_task = GNUNET_SCHEDULER_add_delayed (
    518     timeout,
    519     &do_timeout,
    520     is);
    521   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    522                                  is);
    523   is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
    524                                        is);
    525 }
    526 
    527 
    528 #include "valgrind.h"
    529 
    530 void
    531 TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,
    532                    struct TALER_TESTING_Command *commands)
    533 {
    534   TALER_TESTING_run2 (is,
    535                       commands,
    536                       0 == RUNNING_ON_VALGRIND
    537                       ? GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES,
    538                                                        5)
    539                       : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES,
    540                                                        50));
    541 }
    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 main_wrapper (void *cls)
    582 {
    583   struct MainContext *main_ctx = cls;
    584 
    585   main_ctx->main_cb (main_ctx->main_cb_cls,
    586                      main_ctx->is);
    587 }
    588 
    589 
    590 enum GNUNET_GenericReturnValue
    591 TALER_TESTING_loop (TALER_TESTING_Main main_cb,
    592                     void *main_cb_cls)
    593 {
    594   struct TALER_TESTING_Interpreter is;
    595   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   memset (&is,
    603           0,
    604           sizeof (is));
    605   is.ctx = GNUNET_CURL_init (
    606     &GNUNET_CURL_gnunet_scheduler_reschedule,
    607     &is.rc);
    608   GNUNET_CURL_enable_async_scope_header (is.ctx,
    609                                          "Taler-Correlation-Id");
    610   GNUNET_assert (NULL != is.ctx);
    611   is.rc = GNUNET_CURL_gnunet_rc_create (is.ctx);
    612   is.vars = GNUNET_CONTAINER_multihashmap_create (1024,
    613                                                   false);
    614   /* Blocking */
    615   GNUNET_SCHEDULER_run (&main_wrapper,
    616                         &main_ctx);
    617   return is.result;
    618 }
    619 
    620 
    621 int
    622 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   unsetenv ("XDG_DATA_HOME");
    634   unsetenv ("XDG_CONFIG_HOME");
    635   GNUNET_log_setup (argv[0],
    636                     loglevel,
    637                     NULL);
    638   if (GNUNET_OK !=
    639       TALER_TESTING_get_credentials (cfg_file,
    640                                      exchange_account_section,
    641                                      bs,
    642                                      cred))
    643   {
    644     GNUNET_break (0);
    645     return 77;
    646   }
    647   if (GNUNET_OK !=
    648       TALER_TESTING_cleanup_files_cfg (NULL,
    649                                        cred->cfg))
    650   {
    651     GNUNET_break (0);
    652     return 77;
    653   }
    654   if (GNUNET_OK !=
    655       TALER_extensions_init (cred->cfg))
    656   {
    657     GNUNET_break (0);
    658     return 77;
    659   }
    660   ret = TALER_TESTING_loop (main_cb,
    661                             main_cb_cls);
    662   /* FIXME: should we free 'cred' resources here? */
    663   return (GNUNET_OK == ret) ? 0 : 1;
    664 }
    665 
    666 
    667 /* ************** iterate over commands ********* */
    668 
    669 
    670 void
    671 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   if (asc)
    681   {
    682     inc = 1;
    683     start = 0;
    684     end = is->ip;
    685   }
    686   else
    687   {
    688     inc = -1;
    689     start = is->ip;
    690     end = 0;
    691   }
    692   for (unsigned int off = start; off != end + inc; off += inc)
    693   {
    694     const struct TALER_TESTING_Command *cmd = &is->commands[off];
    695 
    696     cb (cb_cls,
    697         cmd);
    698   }
    699 }
    700 
    701 
    702 /* ************** special commands ********* */
    703 
    704 
    705 struct TALER_TESTING_Command
    706 TALER_TESTING_cmd_end (void)
    707 {
    708   static struct TALER_TESTING_Command cmd;
    709   cmd.label = NULL;
    710 
    711   return cmd;
    712 }
    713 
    714 
    715 struct TALER_TESTING_Command
    716 TALER_TESTING_cmd_set_var (const char *name,
    717                            struct TALER_TESTING_Command cmd)
    718 {
    719   cmd.name = name;
    720   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 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   current = TALER_TESTING_cmd_batch_get_current (cmd);
    765   GNUNET_assert (GNUNET_OK ==
    766                  TALER_TESTING_get_trait_batch_cmds (cmd,
    767                                                      &batch));
    768   match = NULL;
    769   for (new_ip = 0;
    770        NULL != (icmd = &batch[new_ip]);
    771        new_ip++)
    772   {
    773     if (current == target)
    774       current = NULL;
    775     if (icmd == target)
    776     {
    777       match = icmd;
    778       break;
    779     }
    780     if (TALER_TESTING_cmd_is_batch (icmd))
    781     {
    782       int ret = seek_batch (is,
    783                             icmd,
    784                             target);
    785       if (GNUNET_SYSERR == ret)
    786         return GNUNET_SYSERR; /* failure! */
    787       if (GNUNET_OK == ret)
    788       {
    789         match = icmd;
    790         break;
    791       }
    792     }
    793   }
    794   if (NULL == current)
    795   {
    796     /* refuse to jump forward */
    797     GNUNET_break (0);
    798     TALER_TESTING_interpreter_fail (is);
    799     return GNUNET_SYSERR;
    800   }
    801   if (NULL == match)
    802     return GNUNET_NO; /* not found */
    803   TALER_TESTING_cmd_batch_set_current (cmd,
    804                                        new_ip);
    805   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 rewind_ip_run (void *cls,
    818                const struct TALER_TESTING_Command *cmd,
    819                struct TALER_TESTING_Interpreter *is)
    820 {
    821   struct RewindIpState *ris = cls;
    822   const struct TALER_TESTING_Command *target;
    823   unsigned int new_ip;
    824 
    825   (void) cmd;
    826   if (0 == ris->counter)
    827   {
    828     TALER_TESTING_interpreter_next (is);
    829     return;
    830   }
    831   target
    832     = TALER_TESTING_interpreter_lookup_command (is,
    833                                                 ris->target_label);
    834   if (NULL == target)
    835   {
    836     GNUNET_break (0);
    837     TALER_TESTING_interpreter_fail (is);
    838     return;
    839   }
    840   ris->counter--;
    841   for (new_ip = 0;
    842        NULL != is->commands[new_ip].label;
    843        new_ip++)
    844   {
    845     const struct TALER_TESTING_Command *ipcmd
    846       = &is->commands[new_ip];
    847 
    848     if (ipcmd == target)
    849       break;
    850     if (TALER_TESTING_cmd_is_batch (ipcmd))
    851     {
    852       int ret = seek_batch (is,
    853                             ipcmd,
    854                             target);
    855       if (GNUNET_SYSERR == ret)
    856         return;   /* failure! */
    857       if (GNUNET_OK == ret)
    858         break;
    859     }
    860   }
    861   if (new_ip > (unsigned int) is->ip)
    862   {
    863     /* refuse to jump forward */
    864     GNUNET_break (0);
    865     TALER_TESTING_interpreter_fail (is);
    866     return;
    867   }
    868   is->ip = new_ip - 1; /* -1 because the next function will advance by one */
    869   TALER_TESTING_interpreter_next (is);
    870 }
    871 
    872 
    873 struct TALER_TESTING_Command
    874 TALER_TESTING_cmd_rewind_ip (const char *label,
    875                              const char *target_label,
    876                              unsigned int counter)
    877 {
    878   struct RewindIpState *ris;
    879 
    880   ris = GNUNET_new (struct RewindIpState);
    881   ris->target_label = target_label;
    882   ris->counter = counter;
    883   {
    884     struct TALER_TESTING_Command cmd = {
    885       .cls = ris,
    886       .label = label,
    887       .run = &rewind_ip_run
    888     };
    889 
    890     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 authchange_run (void *cls,
    922                 const struct TALER_TESTING_Command *cmd,
    923                 struct TALER_TESTING_Interpreter *is)
    924 {
    925   struct AuthchangeState *ss = cls;
    926 
    927   (void) cmd;
    928   ss->old_ctx = is->ctx;
    929   if (NULL != is->rc)
    930   {
    931     GNUNET_CURL_gnunet_rc_destroy (is->rc);
    932     is->rc = NULL;
    933   }
    934   is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    935                               &is->rc);
    936   GNUNET_CURL_enable_async_scope_header (is->ctx,
    937                                          "Taler-Correlation-Id");
    938   GNUNET_assert (NULL != is->ctx);
    939   is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx);
    940   if (NULL != ss->auth_token)
    941   {
    942     char *authorization;
    943 
    944     GNUNET_asprintf (&authorization,
    945                      "%s: %s",
    946                      MHD_HTTP_HEADER_AUTHORIZATION,
    947                      ss->auth_token);
    948     GNUNET_assert (GNUNET_OK ==
    949                    GNUNET_CURL_append_header (is->ctx,
    950                                               authorization));
    951     GNUNET_free (authorization);
    952   }
    953   TALER_TESTING_interpreter_next (is);
    954 }
    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 deferred_cleanup_cb (void *cls)
    966 {
    967   struct GNUNET_CURL_Context *ctx = cls;
    968 
    969   GNUNET_CURL_fini (ctx);
    970 }
    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 authchange_cleanup (void *cls,
    981                     const struct TALER_TESTING_Command *cmd)
    982 {
    983   struct AuthchangeState *ss = cls;
    984 
    985   (void) cmd;
    986   if (NULL != ss->old_ctx)
    987   {
    988     (void) GNUNET_SCHEDULER_add_now (&deferred_cleanup_cb,
    989                                      ss->old_ctx);
    990     ss->old_ctx = NULL;
    991   }
    992   GNUNET_free (ss);
    993 }
    994 
    995 
    996 struct TALER_TESTING_Command
    997 TALER_TESTING_cmd_set_authorization (const char *label,
    998                                      const char *auth_token)
    999 {
   1000   struct AuthchangeState *ss;
   1001 
   1002   ss = GNUNET_new (struct AuthchangeState);
   1003   ss->auth_token = auth_token;
   1004 
   1005   {
   1006     struct TALER_TESTING_Command cmd = {
   1007       .cls = ss,
   1008       .label = label,
   1009       .run = &authchange_run,
   1010       .cleanup = &authchange_cleanup
   1011     };
   1012 
   1013     return cmd;
   1014   }
   1015 }
   1016 
   1017 
   1018 /* end of testing_api_loop.c */