LCOV - code coverage report
Current view: top level - testing - testing_api_loop.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 176 345 51.0 %
Date: 2025-06-05 21:03:14 Functions: 17 29 58.6 %

          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 */

Generated by: LCOV version 1.16