summaryrefslogtreecommitdiff
path: root/src/testing
diff options
context:
space:
mode:
Diffstat (limited to 'src/testing')
-rw-r--r--src/testing/testing.h74
-rw-r--r--src/testing/testing_api_cmd_batch.c3
-rw-r--r--src/testing/testing_api_cmd_hello_world.c1
-rw-r--r--src/testing/testing_api_cmd_hello_world_birth.c1
-rw-r--r--src/testing/testing_api_loop.c186
5 files changed, 256 insertions, 9 deletions
diff --git a/src/testing/testing.h b/src/testing/testing.h
new file mode 100644
index 000000000..b12466530
--- /dev/null
+++ b/src/testing/testing.h
@@ -0,0 +1,74 @@
+/*
+ This file is part of GNUnet
+ Copyright (C) 2021 GNUnet e.V.
+
+ GNUnet is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
+
+ GNUnet 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
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author t3sserakt
+ */
+
+#include "gnunet_util_lib.h"
+
+/**
+ * Global state of the interpreter, used by a command
+ * to access information about other commands.
+ */
+// SUGGESTION: consider making this struct opaque (only known inside of libgnunettesting,
+// say main loop and a few select commands, like next/fail/batch); + helper
+// function to access 'cfg'?
+struct GNUNET_TESTING_Interpreter
+{
+
+ /**
+ * Commands the interpreter will run.
+ */
+ struct GNUNET_TESTING_Command *commands;
+
+ /**
+ * Interpreter task (if one is scheduled).
+ */
+ struct GNUNET_SCHEDULER_Task *task;
+
+ /**
+ * Finish task of a blocking call to a commands finish method.
+ */
+ struct GNUNET_SCHEDULER_Task *finish_task;
+
+ /**
+ * Our configuration.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Task run on timeout.
+ */
+ struct GNUNET_SCHEDULER_Task *timeout_task;
+
+ /**
+ * 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;
+
+ /**
+ * Result of the testcases, #GNUNET_OK on success
+ */
+ int result;
+
+};
diff --git a/src/testing/testing_api_cmd_batch.c b/src/testing/testing_api_cmd_batch.c
index 531778129..af260f80d 100644
--- a/src/testing/testing_api_cmd_batch.c
+++ b/src/testing/testing_api_cmd_batch.c
@@ -26,7 +26,7 @@
*/
#include "platform.h"
#include "gnunet_testing_ng_lib.h"
-
+#include "testing.h"
/**
* State for a "batch" CMD.
@@ -70,7 +70,6 @@ batch_run (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Exiting from batch: %s\n",
cmd->label);
- GNUNET_TESTING_interpreter_next (is);
return;
}
bs->batch[bs->batch_ip].start_time
diff --git a/src/testing/testing_api_cmd_hello_world.c b/src/testing/testing_api_cmd_hello_world.c
index 8c1d7353d..4347ac818 100644
--- a/src/testing/testing_api_cmd_hello_world.c
+++ b/src/testing/testing_api_cmd_hello_world.c
@@ -89,7 +89,6 @@ hello_world_run (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Now I am a %s\n",
hs->message);
- GNUNET_TESTING_interpreter_next (is);
}
/**
diff --git a/src/testing/testing_api_cmd_hello_world_birth.c b/src/testing/testing_api_cmd_hello_world_birth.c
index 0faf93cd8..2a5bded92 100644
--- a/src/testing/testing_api_cmd_hello_world_birth.c
+++ b/src/testing/testing_api_cmd_hello_world_birth.c
@@ -112,7 +112,6 @@ hello_world_birth_run (void *cls,
{
hbs->what_am_i = "boy!";
}
- GNUNET_TESTING_interpreter_next (is);
}
/**
diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c
index f29329a60..dbd86ba90 100644
--- a/src/testing/testing_api_loop.c
+++ b/src/testing/testing_api_loop.c
@@ -28,10 +28,50 @@
#include "platform.h"
#include "gnunet_util_lib.h"
#include "gnunet_testing_ng_lib.h"
+#include "testing.h"
struct GNUNET_TESTING_Interpreter *is;
/**
+ * Closure used to sync an asynchronous with an synchronous command.
+ */
+struct SyncTaskClosure
+{
+
+ /**
+ * The asynchronous command the synchronous command waits for.
+ */
+ const struct GNUNET_TESTING_Command *async_cmd;
+
+ /**
+ * The synchronous command that waits for the asynchronous command.
+ */
+ const struct GNUNET_TESTING_Command *sync_cmd;
+
+ /**
+ * The interpreter of the test.
+ */
+ struct GNUNET_TESTING_Interpreter *is;
+};
+
+/**
+* Closure used to run the finish task.
+*/
+struct FinishTaskClosure
+{
+
+ /**
+ * The asynchronous command the synchronous command waits for.
+ */
+ const struct GNUNET_TESTING_Command *cmd;
+
+ /**
+ * The interpreter of the test.
+ */
+ struct GNUNET_TESTING_Interpreter *is;
+};
+
+/**
* Lookup command by label.
*
* @param label label to look for
@@ -107,9 +147,10 @@ interpreter_run (void *cls);
/**
* Current command is done, run the next one.
*/
-void
-GNUNET_TESTING_interpreter_next (struct GNUNET_TESTING_Interpreter *is)
+static void
+interpreter_next (void *cls)
{
+ struct GNUNET_TESTING_Interpreter *is = cls;
static unsigned long long ipc;
static struct GNUNET_TIME_Absolute last_report;
struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
@@ -141,6 +182,123 @@ GNUNET_TESTING_interpreter_next (struct GNUNET_TESTING_Interpreter *is)
}
+static void
+run_finish_task_next (void *cls)
+{
+ struct FinishTaskClosure *ftc = cls;
+ const struct GNUNET_TESTING_Command *cmd = ftc->cmd;
+ struct GNUNET_TESTING_Interpreter *is = ftc->is;
+
+ if (cmd->finish (cmd->cls, &interpreter_next, is))
+ {
+ is->finish_task = GNUNET_SCHEDULER_add_now (&run_finish_task_next, ftc);
+ }
+ else
+ {
+ is->finish_task = NULL;
+ }
+
+}
+
+
+static void
+run_finish_task_sync (void *cls)
+{
+ struct SyncTaskClosure *stc = cls;
+ const struct GNUNET_TESTING_Command *cmd = stc->async_cmd;
+ const struct GNUNET_TESTING_Command *sync_cmd = stc->sync_cmd;
+ struct FinishTaskClosure *ftc;
+ struct SyncState *sync_state = sync_cmd->cls;
+ struct GNUNET_SCHEDULER_Task *finish_task = sync_state->finish_task;
+
+ GNUNET_assert (NULL != finish_task);
+ ftc = GNUNET_new (struct FinishTaskClosure);
+ ftc->cmd = stc->sync_cmd;
+ ftc->is = stc->is;
+ struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+ if (cmd->default_timeout.rel_value_us < now.abs_value_us
+ - sync_state->start_finish_time.abs_value_us)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "The command with label %s did not finish its asyncronous task in time.\n",
+ cmd->label);
+ is->result = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+ }
+
+ if (cmd->finish (cmd->cls, run_finish_task_next, ftc))
+ {
+ finish_task = GNUNET_SCHEDULER_add_now (&run_finish_task_sync, stc);
+ }
+ else
+ {
+ finish_task = NULL;
+ }
+}
+
+
+static void
+start_finish_on_ref (void *cls,
+ const struct GNUNET_TESTING_Command *cmd,
+ struct GNUNET_TESTING_Interpreter *is)
+{
+ struct SyncState *sync_state = cls;
+ struct SyncTaskClosure *stc;
+ const struct GNUNET_TESTING_Command *async_cmd;
+
+ async_cmd = sync_state->async_cmd;
+ stc = GNUNET_new (struct SyncTaskClosure);
+ stc->async_cmd = async_cmd;
+ stc->sync_cmd = cmd;
+ stc->is = is;
+ sync_state->start_finish_time = GNUNET_TIME_absolute_get ();
+ sync_state->finish_task = GNUNET_SCHEDULER_add_now (&run_finish_task_sync,
+ stc);
+}
+
+
+const struct GNUNET_TESTING_Command
+GNUNET_TESTING_cmd_finish (const char *finish_label,
+ const char *cmd_ref,
+ struct GNUNET_TIME_Relative timeout)
+{
+ const struct GNUNET_TESTING_Command *async_cmd;
+ struct SyncState *sync_state;
+
+ async_cmd = GNUNET_TESTING_interpreter_lookup_command (cmd_ref);
+ sync_state = GNUNET_new (struct SyncState);
+ sync_state->async_cmd = async_cmd;
+
+ struct GNUNET_TESTING_Command cmd = {
+ .cls = sync_state,
+ .label = finish_label,
+ .run = &start_finish_on_ref,
+ .asynchronous_finish = GNUNET_NO
+ };
+
+ return cmd;
+}
+
+
+const struct GNUNET_TESTING_Command
+GNUNET_TESTING_cmd_make_asynchronous (const struct GNUNET_TESTING_Command cmd)
+{
+
+ GNUNET_assert (NULL != cmd.finish);
+ const struct GNUNET_TESTING_Command async_cmd = {
+ .cls = cmd.cls,
+ .label = cmd.label,
+ .run = cmd.run,
+ .cleanup = cmd.cleanup,
+ .traits = cmd.traits,
+ .finish = cmd.finish,
+ .asynchronous_finish = GNUNET_YES
+ };
+
+ return async_cmd;
+}
+
+
/**
* Current command failed, clean up and fail the test case.
*
@@ -195,14 +353,15 @@ GNUNET_TESTING_interpreter_get_current_label (struct
/**
- * Run the main interpreter loop that performs exchange operations.
+ * Run the main interpreter loop.
*
* @param cls contains the `struct GNUNET_TESTING_Interpreter`
*/
static void
interpreter_run (void *cls)
{
- (void) cls;
+ struct FinishTaskClosure *ftc;
+ struct GNUNET_TESTING_Interpreter *is = cls;
struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
is->task = NULL;
@@ -227,6 +386,17 @@ interpreter_run (void *cls)
cmd->run (cmd->cls,
cmd,
is);
+ if ((NULL != cmd->finish) && (GNUNET_NO == cmd->asynchronous_finish))
+ {
+ ftc = GNUNET_new (struct FinishTaskClosure);
+ ftc->cmd = cmd;
+ ftc->is = is;
+ cmd->finish_task = GNUNET_SCHEDULER_add_now (run_finish_task_next, ftc);
+ }
+ else
+ {
+ interpreter_next (is);
+ }
}
@@ -253,9 +423,15 @@ do_shutdown (void *cls)
for (unsigned int j = 0;
NULL != (cmd = &is->commands[j])->label;
- j++)
+ j++) {
cmd->cleanup (cmd->cls,
cmd);
+ if (NULL != cmd->finish_task)
+ {
+ GNUNET_SCHEDULER_cancel (cmd->finish_task);
+ cmd->finish_task = NULL;
+ }
+ }
if (NULL != is->task)
{