From 925c210d978cd0e3dfc718d9e4c65ad713a817b9 Mon Sep 17 00:00:00 2001 From: t3sserakt Date: Thu, 15 Apr 2021 15:35:28 +0200 Subject: - added handling of asynchronous task to testing ng. added testbed commands for setting up test invironment (atm wihtout the use of the ne async handling) --- src/testing/testing.h | 74 ++++++++++ src/testing/testing_api_cmd_batch.c | 3 +- src/testing/testing_api_cmd_hello_world.c | 1 - src/testing/testing_api_cmd_hello_world_birth.c | 1 - src/testing/testing_api_loop.c | 186 +++++++++++++++++++++++- 5 files changed, 256 insertions(+), 9 deletions(-) create mode 100644 src/testing/testing.h (limited to 'src/testing') 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 . + + 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,9 +28,49 @@ #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. * @@ -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) { -- cgit v1.2.3