diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index 7779ca2bc..d4251364f 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -388,10 +388,9 @@ struct TALER_TESTING_Interpreter void *final_cleanup_cb_cls; /** - * Instruction pointer. Tells #interpreter_run() which - * instruction to run next. Need (signed) int because - * it gets -1 when rewinding the interpreter to the first - * CMD. + * Instruction pointer. Tells #interpreter_run() which instruction to run + * next. Need (signed) int because it gets -1 when rewinding the + * interpreter to the first CMD. */ int ip; @@ -599,7 +598,22 @@ TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is); * @return a end-command. */ struct TALER_TESTING_Command -TALER_TESTING_cmd_end (); +TALER_TESTING_cmd_end (void); + + +/** + * Make the instruction pointer point to @a target_label + * only if @a counter is greater than zero. + * + * @param label command label + * @param target_label label of the new instruction pointer's destination after the jump; + * must be before the current instruction + * @param counter counts how many times the rewinding is to happen. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_rewind_ip (const char *label, + const char *target_label, + unsigned int counter); /** @@ -816,7 +830,6 @@ TALER_TESTING_setup_with_auditor_and_exchange (TALER_TESTING_Main main_cb, * @param config_filename configuration filename. * @param bank_url base URL of the bank, used by `wget' to check * that the bank was started right. - * * @return the process, or NULL if the process could not * be started. */ @@ -839,6 +852,7 @@ TALER_TESTING_run_bank (const char *config_filename, struct TALER_TESTING_LibeufinServices TALER_TESTING_run_libeufin (const struct TALER_TESTING_BankConfiguration *bc); + /** * Runs the Fakebank by guessing / extracting the portnumber * from the base URL. @@ -1896,6 +1910,18 @@ TALER_TESTING_cmd_batch_next (struct TALER_TESTING_Interpreter *is); struct TALER_TESTING_Command * TALER_TESTING_cmd_batch_get_current (const struct TALER_TESTING_Command *cmd); + +/** + * Set what command the batch should be at. + * + * @param cmd current batch command + * @param new_ip where to move the IP + */ +void +TALER_TESTING_cmd_batch_set_current (const struct TALER_TESTING_Command *cmd, + unsigned int new_ip); + + /** * Make a serialize-keys CMD. * diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index d73e89b3c..014faa469 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -61,6 +61,7 @@ libtalertesting_la_SOURCES = \ testing_api_cmd_refund.c \ testing_api_cmd_refresh.c \ testing_api_cmd_revoke.c \ + testing_api_cmd_rewind.c \ testing_api_cmd_serialize_keys.c \ testing_api_cmd_signal.c \ testing_api_cmd_sleep.c \ diff --git a/src/testing/testing_api_cmd_batch.c b/src/testing/testing_api_cmd_batch.c index 48ccf55f1..d81a5c38d 100644 --- a/src/testing/testing_api_cmd_batch.c +++ b/src/testing/testing_api_cmd_batch.c @@ -228,5 +228,27 @@ TALER_TESTING_cmd_batch_get_current (const struct TALER_TESTING_Command *cmd) { struct BatchState *bs = cmd->cls; + GNUNET_assert (cmd->run == &batch_run); return &bs->batch[bs->batch_ip]; } + + +/** + * Set what command the batch should be at. + * + * @param cmd current batch command + * @param new_ip where to move the IP + */ +void +TALER_TESTING_cmd_batch_set_current (const struct TALER_TESTING_Command *cmd, + unsigned int new_ip) +{ + struct BatchState *bs = cmd->cls; + + /* sanity checks */ + GNUNET_assert (cmd->run == &batch_run); + for (unsigned int i = 0; i < new_ip; i++) + GNUNET_assert (NULL != bs->batch[i].label); + /* actual logic */ + bs->batch_ip = new_ip; +} diff --git a/src/testing/testing_api_cmd_rewind.c b/src/testing/testing_api_cmd_rewind.c new file mode 100644 index 000000000..cde966d7f --- /dev/null +++ b/src/testing/testing_api_cmd_rewind.c @@ -0,0 +1,222 @@ +/* + This file is part of TALER + Copyright (C) 2014-2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + +*/ +/** + * @file testing/testing_api_cmd_rewind.c + * @brief command to rewind the instruction pointer. + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler_testing_lib.h" + + +/** + * State for a "rewind" CMD. + */ +struct RewindIpState +{ + /** + * Instruction pointer to set into the interpreter. + */ + const char *target_label; + + /** + * How many times this set should take place. However, this value lives at + * the calling process, and this CMD is only in charge of checking and + * decremeting it. + */ + unsigned int counter; +}; + + +/** + * Only defined to respect the API. + */ +static void +rewind_ip_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + (void) cls; + (void) cmd; +} + + +/** + * Seek for the @a target command in @a batch (and rewind to it + * if successful). + * + * @param is the interpreter state (for failures) + * @param cmd batch to search for @a target + * @param target command to search for + * @return #GNUNET_OK on success, #GNUNET_NO if target was not found, + * #GNUNET_SYSERR if target is in the future and we failed + */ +static int +seek_batch (struct TALER_TESTING_Interpreter *is, + const struct TALER_TESTING_Command *cmd, + const struct TALER_TESTING_Command *target) +{ + unsigned int new_ip; +#define BATCH_INDEX 1 + struct TALER_TESTING_Command *batch; + struct TALER_TESTING_Command *current; + struct TALER_TESTING_Command *icmd; + const struct TALER_TESTING_Command *match; + + current = TALER_TESTING_cmd_batch_get_current (cmd); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_cmd (cmd, + BATCH_INDEX, + &batch)); + match = NULL; + for (new_ip = 0; + NULL != (icmd = &batch[new_ip]); + new_ip++) + { + if (current == target) + current = NULL; + if (icmd == target) + { + match = icmd; + break; + } + if (TALER_TESTING_cmd_is_batch (icmd)) + { + int ret = seek_batch (is, + icmd, + target); + if (GNUNET_SYSERR == ret) + return GNUNET_SYSERR; /* failure! */ + if (GNUNET_OK == ret) + { + match = icmd; + break; + } + } + } + if (NULL == current) + { + /* refuse to jump forward */ + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return GNUNET_SYSERR; + } + if (NULL == match) + return GNUNET_NO; /* not found */ + TALER_TESTING_cmd_batch_set_current (cmd, + new_ip); + return GNUNET_OK; +} + + +/** + * Run the "rewind" CMD. + * + * @param cls closure. + * @param cmd command being executed now. + * @param is the interpreter state. + */ +static void +rewind_ip_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct RewindIpState *ris = cls; + const struct TALER_TESTING_Command *target; + unsigned int new_ip; + + (void) cmd; + if (0 == ris->counter) + { + TALER_TESTING_interpreter_next (is); + return; + } + target + = TALER_TESTING_interpreter_lookup_command (is, + ris->target_label); + if (NULL == target) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + ris->counter--; + for (new_ip = 0; + NULL != is->commands[new_ip].label; + new_ip++) + { + const struct TALER_TESTING_Command *cmd = &is->commands[new_ip]; + + if (cmd == target) + break; + if (TALER_TESTING_cmd_is_batch (cmd)) + { + int ret = seek_batch (is, + cmd, + target); + if (GNUNET_SYSERR == ret) + return; /* failure! */ + if (GNUNET_OK == ret) + break; + } + } + if (new_ip > is->ip) + { + /* refuse to jump forward */ + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + is->ip = new_ip - 1; /* -1 because the next function will advance by one */ + TALER_TESTING_interpreter_next (is); +} + + +/** + * Make the instruction pointer point to @a new_ip + * only if @a counter is greater than zero. + * + * @param label command label + * @param target_label label of the new instruction pointer's destination after the jump; + * must be before the current instruction + * @param counter counts how many times the rewinding is to happen. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_rewind_ip (const char *label, + const char *target_label, + unsigned int counter) +{ + struct RewindIpState *ris; + + ris = GNUNET_new (struct RewindIpState); + ris->target_label = target_label; + ris->counter = counter; + { + struct TALER_TESTING_Command cmd = { + .cls = ris, + .label = label, + .run = &rewind_ip_run, + .cleanup = &rewind_ip_cleanup + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_stat.c b/src/testing/testing_api_cmd_stat.c index cf6d0b484..9461a22cf 100644 --- a/src/testing/testing_api_cmd_stat.c +++ b/src/testing/testing_api_cmd_stat.c @@ -40,7 +40,6 @@ stat_cleanup (void *cls, (void) cls; (void) cmd; /* nothing to clean. */ - return; } diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c index 9b494da0c..af4a63c6a 100644 --- a/src/testing/testing_api_loop.c +++ b/src/testing/testing_api_loop.c @@ -69,6 +69,7 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is, #define BATCH_INDEX 1 struct TALER_TESTING_Command *batch; struct TALER_TESTING_Command *current; + struct TALER_TESTING_Command *icmd; const struct TALER_TESTING_Command *match; current = TALER_TESTING_cmd_batch_get_current (cmd); @@ -79,15 +80,15 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is, /* We must do the loop forward, but we can find the last match */ match = NULL; for (unsigned int j = 0; - NULL != (cmd = &batch[j])->label; + NULL != (icmd = &batch[j])->label; j++) { - if (current == cmd) + if (current == icmd) break; /* do not go past current command */ - if ( (NULL != cmd->label) && - (0 == strcmp (cmd->label, + if ( (NULL != icmd->label) && + (0 == strcmp (icmd->label, label)) ) - match = cmd; + match = icmd; } if (NULL != match) return match;