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

            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/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          925 : TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
      92              :                                           const char *label)
      93              : {
      94          925 :   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         2532 :   for (int i = is->ip; i >= 0; i--)
     102              :   {
     103         2532 :     const struct TALER_TESTING_Command *cmd = &is->commands[i];
     104              : 
     105              :     /* Give precedence to top-level commands.  */
     106         2532 :     if ( (NULL != cmd->label) &&
     107         2532 :          (0 == strcmp (cmd->label,
     108              :                        label)) )
     109           12 :       return cmd;
     110              : 
     111         2520 :     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         2457 :       current = TALER_TESTING_cmd_batch_get_current (cmd);
     119         2457 :       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         2457 :       match = NULL;
     124         2457 :       for (unsigned int j = 0;
     125        27830 :            NULL != (icmd = &batch[j])->label;
     126        25373 :            j++)
     127              :       {
     128        26286 :         if (current == icmd)
     129          913 :           break; /* do not go past current command */
     130        25373 :         if ( (NULL != icmd->label) &&
     131        25373 :              (0 == strcmp (icmd->label,
     132              :                            label)) )
     133          930 :           match = icmd;
     134              :       }
     135         2457 :       if (NULL != match)
     136          913 :         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         1298 : 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         1298 :   GNUNET_CRYPTO_hash (name,
     154              :                       strlen (name),
     155              :                       &h_name);
     156         1298 :   cmd = GNUNET_CONTAINER_multihashmap_get (is->vars,
     157              :                                            &h_name);
     158         1298 :   if (NULL == cmd)
     159           32 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     160              :                 "Command not found by name: %s\n",
     161              :                 name);
     162         1298 :   return cmd;
     163              : }
     164              : 
     165              : 
     166              : struct GNUNET_CURL_Context *
     167          587 : TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is)
     168              : {
     169          587 :   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         1057 : 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         1057 :   struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
     203              : 
     204         1057 :   if (GNUNET_SYSERR == is->result)
     205            0 :     return; /* ignore, we already failed! */
     206         1057 :   if (TALER_TESTING_cmd_is_batch (cmd))
     207              :   {
     208          819 :     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         1057 :   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         1057 :   ipc++;
     232         1057 :   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         1810 : 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         1810 :   if (NULL == cmd->name)
     275         1760 :     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         1076 : interpreter_run (void *cls)
     293              : {
     294         1076 :   struct TALER_TESTING_Interpreter *is = cls;
     295         1076 :   struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
     296              : 
     297         1076 :   is->task = NULL;
     298         1076 :   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         1057 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     309              :               "Running command `%s'\n",
     310              :               cmd->label);
     311              :   cmd->last_req_time
     312         1057 :     = GNUNET_TIME_absolute_get ();
     313         1057 :   if (0 == cmd->num_tries)
     314          304 :     cmd->start_time = cmd->last_req_time;
     315         1057 :   cmd->num_tries++;
     316         1057 :   TALER_TESTING_update_variables_ (is,
     317              :                                    cmd);
     318         1057 :   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 2.0-1