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

Generated by: LCOV version 2.0-1