From f146e80752e73247acb9d6c7463188a82d26a774 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 4 Oct 2021 12:15:43 +0200 Subject: -taking a first stab at cleaning up the testing mess --- src/testing/testing_api_loop.c | 388 +++++++++++------------------------------ 1 file changed, 100 insertions(+), 288 deletions(-) (limited to 'src/testing/testing_api_loop.c') diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c index 0c24c0e26..1c8eb1db6 100644 --- a/src/testing/testing_api_loop.c +++ b/src/testing/testing_api_loop.c @@ -24,67 +24,29 @@ * @author Christian Grothoff (GNU Taler testing) * @author Marcello Stanisci (GNU Taler testing) * @author t3sserakt -*/ + * + * FIXME: + * - interpreter failure is NOT returned properly yet! + * - abuse of shutdown logic for interpreter termination + * => API design flaw to be fixed! + */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_testing_ng_lib.h" #include "testing.h" -#define CHECK_FINISHED_PERIOD \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) - -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 is interpreter to lookup command in * @param label label to look for * @return NULL if command was not found */ const struct GNUNET_TESTING_Command * -GNUNET_TESTING_interpreter_lookup_command (const char *label) +GNUNET_TESTING_interpreter_lookup_command ( + struct GNUNET_TESTING_Interpreter *is, + const char *label) { if (NULL == label) { @@ -188,211 +150,36 @@ interpreter_next (void *cls) } -/** - * This function checks if the finish function of a command returns GNUNET_YES, when the command is finished. In this case the finish function might have called interpreter_next. IF GNUNET_NO was returned this function is added to the scheduler again. In case of an error interpreter_fail is called. - * - */ -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; - unsigned int finished = cmd->finish (cmd->cls, &interpreter_next, is); - - if (GNUNET_YES == finished) - { - is->finish_task = NULL; - } - else if (GNUNET_NO == finished) - { - is->finish_task = GNUNET_SCHEDULER_add_delayed (CHECK_FINISHED_PERIOD, - &run_finish_task_next, ftc); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Next task finished with an error.\n"); - GNUNET_TESTING_interpreter_fail (); - } - -} - - -/** - * This function checks if the finish function of an asynchronous command returns GNUNET_YES, when the command is finished. In this case the finish function might have called interpreter_next. IF GNUNET_NO was returned this function is added to the scheduler again. In case of an error interpreter_fail is called. - * - * //TODO run_finish_task_next and this function can be merged. - * - */ -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; - unsigned int finished = cmd->finish (cmd->cls, &interpreter_next, is); - - 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 asynchronous task in time.\n", - cmd->label); - GNUNET_TESTING_interpreter_fail (); - } - - if (GNUNET_YES == finished) - { - finish_task = NULL; - } - else if (GNUNET_NO == finished) - { - finish_task = GNUNET_SCHEDULER_add_delayed (CHECK_FINISHED_PERIOD, - &run_finish_task_sync, stc); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Sync task finished with an error.\n"); - GNUNET_TESTING_interpreter_fail (); - } -} - - -/** - * run method of the command created by the interpreter to wait for another command to finish. - * - */ -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_delayed ( - CHECK_FINISHED_PERIOD, - &run_finish_task_sync, - stc); -} - - -/** - * Create (synchronous) command that waits for another command to finish. - * If @a cmd_ref did not finish after @a timeout, this command will fail - * the test case. - * - * @param finish_label label for this command - * @param cmd_ref reference to a previous command which we should - * wait for (call `finish()` on) - * @param timeout how long to wait at most for @a cmd_ref to finish - * @return a finish-command. - */ -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_unblocking (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. * * @param is interpreter of the test */ void -GNUNET_TESTING_interpreter_fail () +GNUNET_TESTING_interpreter_fail (struct GNUNET_TESTING_Interpreter *is) { struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip]; if (GNUNET_SYSERR == is->result) return; /* ignore, we already failed! */ - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "interpreter_fail!\n"); - if (NULL != cmd) { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed at command `%s'\n", + cmd->label); while (GNUNET_TESTING_cmd_is_batch (cmd)) { cmd = GNUNET_TESTING_cmd_batch_get_current (cmd); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Batch is at command `%s'\n", + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed in batch at command `%s'\n", cmd->label); } - } else { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "cmd is NULL.\n"); - } - - if (NULL == cmd->label) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Failed at command `%s'\n", - cmd->label); - - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "cmd->label is NULL.\n"); + "Failed with CMD being NULL!\n"); } - is->result = GNUNET_SYSERR; GNUNET_SCHEDULER_shutdown (); } @@ -406,8 +193,9 @@ GNUNET_TESTING_interpreter_fail () struct GNUNET_TESTING_Command GNUNET_TESTING_cmd_end (void) { - static struct GNUNET_TESTING_Command cmd; - cmd.label = NULL; + static struct GNUNET_TESTING_Command cmd = { + .label = NULL + }; return cmd; } @@ -417,8 +205,8 @@ GNUNET_TESTING_cmd_end (void) * Obtain current label. */ const char * -GNUNET_TESTING_interpreter_get_current_label (struct - GNUNET_TESTING_Interpreter *is) +GNUNET_TESTING_interpreter_get_current_label ( + struct GNUNET_TESTING_Interpreter *is) { struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip]; @@ -434,46 +222,36 @@ GNUNET_TESTING_interpreter_get_current_label (struct static void interpreter_run (void *cls) { - struct FinishTaskClosure *ftc; struct GNUNET_TESTING_Interpreter *is = cls; struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip]; is->task = NULL; - if (NULL == cmd->label) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Running command END %p\n", - is); + "Running command END\n"); is->result = GNUNET_OK; GNUNET_SCHEDULER_shutdown (); return; } - else if (NULL != cmd) + if (NULL != cmd) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Running command `%s' %p\n", - cmd->label, - is); + "Running command `%s'\n", + cmd->label); } cmd->start_time = cmd->last_req_time = GNUNET_TIME_absolute_get (); cmd->num_tries = 1; cmd->run (cmd->cls, - cmd, is); - if ((NULL != cmd->finish) && (GNUNET_NO == cmd->asynchronous_finish)) + if ( (NULL != cmd->finish) && + (! cmd->asynchronous_finish) ) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Next task will not be called directly!\n"); - ftc = GNUNET_new (struct FinishTaskClosure); - ftc->cmd = cmd; - ftc->is = is; - is->finish_task = GNUNET_SCHEDULER_add_delayed (CHECK_FINISHED_PERIOD, - &run_finish_task_next, - ftc); + cmd->finish (cmd->cls, + &interpreter_next, + is); } else { @@ -491,37 +269,33 @@ interpreter_run (void *cls) static void do_shutdown (void *cls) { - (void) cls; + struct GNUNET_TESTING_Interpreter *is = cls; struct GNUNET_TESTING_Command *cmd; const char *label; label = is->commands[is->ip].label; if (NULL == label) label = "END"; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Executing shutdown at `%s'\n", label); - for (unsigned int j = 0; NULL != (cmd = &is->commands[j])->label; - j++) { + j++) + { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up cmd %s\n", cmd->label); - cmd->cleanup (cmd->cls, - cmd); + cmd->cleanup (cmd->cls); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaned up cmd %s\n", cmd->label); } - if (NULL != is->finish_task) { GNUNET_SCHEDULER_cancel (is->finish_task); - cmd->finish_task = NULL; + is->finish_task = NULL; } - if (NULL != is->task) { GNUNET_SCHEDULER_cancel (is->task); @@ -533,18 +307,19 @@ do_shutdown (void *cls) is->timeout_task = NULL; } GNUNET_free (is->commands); + GNUNET_free (is); } /** * Function run when the test terminates (good or bad) with timeout. * - * @param cls NULL + * @param cls the interpreter state */ static void do_timeout (void *cls) { - (void) cls; + struct GNUNET_TESTING_Interpreter *is = cls; is->timeout_task = NULL; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -553,30 +328,15 @@ do_timeout (void *cls) } -/** - * Run the testsuite. Note, CMDs are copied into - * the interpreter state because they are _usually_ - * defined into the "run" method that returns after - * having scheduled the test interpreter. - * - * @param is the interpreter state - * @param commands the list of command to execute - * @param timeout how long to wait - */ -int +enum GNUNET_GenericReturnValue GNUNET_TESTING_run (const char *cfg_filename, struct GNUNET_TESTING_Command *commands, struct GNUNET_TIME_Relative timeout) { + struct GNUNET_TESTING_Interpreter *is; unsigned int i; is = GNUNET_new (struct GNUNET_TESTING_Interpreter); - - if (NULL != is->timeout_task) - { - GNUNET_SCHEDULER_cancel (is->timeout_task); - is->timeout_task = NULL; - } /* get the number of commands */ for (i = 0; NULL != commands[i].label; i++) ; @@ -585,15 +345,67 @@ GNUNET_TESTING_run (const char *cfg_filename, memcpy (is->commands, commands, sizeof (struct GNUNET_TESTING_Command) * i); - - is->timeout_task = GNUNET_SCHEDULER_add_delayed - (timeout, - &do_timeout, - is); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is); - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, is); + is->timeout_task + = GNUNET_SCHEDULER_add_delayed (timeout, + &do_timeout, + is); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + is); + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); return GNUNET_OK; } +/** + * Closure for #loop_run(). + */ +struct MainParams +{ + const char *cfg_filename; + struct GNUNET_TESTING_Command *commands; + struct GNUNET_TIME_Relative timeout; + int rv; +}; + + +/** + * Main function to run the test cases. + * + * @param cls a `struct MainParams *` + */ +static void +loop_run (void *cls) +{ + struct MainParams *mp = cls; + + if (GNUNET_OK != + GNUNET_TESTING_run (mp->cfg_filename, + mp->commands, + mp->timeout)) + { + GNUNET_break (0); + mp->rv = EXIT_FAILURE; + } +} + + +int +GNUNET_TESTING_main (const char *cfg_filename, + struct GNUNET_TESTING_Command *commands, + struct GNUNET_TIME_Relative timeout) +{ + struct MainParams mp = { + .cfg_filename = cfg_filename, + .commands = commands, + .timeout = timeout, + .rv = EXIT_SUCCESS + }; + + GNUNET_SCHEDULER_run (&loop_run, + &mp); + return mp.rv; +} + + /* end of testing_api_loop.c */ -- cgit v1.2.3