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;