aboutsummaryrefslogtreecommitdiff
path: root/src/testing/testing_api_loop.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-10-04 12:15:43 +0200
committerChristian Grothoff <christian@grothoff.org>2021-10-04 12:15:43 +0200
commitf146e80752e73247acb9d6c7463188a82d26a774 (patch)
tree13ed03d817ce04daa133507778ac6a1b71bf147f /src/testing/testing_api_loop.c
parent7ecc3a03a0670a1620c603502c9958b95e6dc1d0 (diff)
downloadgnunet-f146e80752e73247acb9d6c7463188a82d26a774.tar.gz
gnunet-f146e80752e73247acb9d6c7463188a82d26a774.zip
-taking a first stab at cleaning up the testing mess
Diffstat (limited to 'src/testing/testing_api_loop.c')
-rw-r--r--src/testing/testing_api_loop.c388
1 files changed, 100 insertions, 288 deletions
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 @@
24 * @author Christian Grothoff (GNU Taler testing) 24 * @author Christian Grothoff (GNU Taler testing)
25 * @author Marcello Stanisci (GNU Taler testing) 25 * @author Marcello Stanisci (GNU Taler testing)
26 * @author t3sserakt 26 * @author t3sserakt
27*/ 27 *
28 * FIXME:
29 * - interpreter failure is NOT returned properly yet!
30 * - abuse of shutdown logic for interpreter termination
31 * => API design flaw to be fixed!
32 */
28#include "platform.h" 33#include "platform.h"
29#include "gnunet_util_lib.h" 34#include "gnunet_util_lib.h"
30#include "gnunet_testing_ng_lib.h" 35#include "gnunet_testing_ng_lib.h"
31#include "testing.h" 36#include "testing.h"
32 37
33#define CHECK_FINISHED_PERIOD \
34 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
35
36struct GNUNET_TESTING_Interpreter *is;
37
38
39/**
40 * Closure used to sync an asynchronous with an synchronous command.
41 */
42struct SyncTaskClosure
43{
44
45 /**
46 * The asynchronous command the synchronous command waits for.
47 */
48 const struct GNUNET_TESTING_Command *async_cmd;
49
50 /**
51 * The synchronous command that waits for the asynchronous command.
52 */
53 const struct GNUNET_TESTING_Command *sync_cmd;
54
55 /**
56 * The interpreter of the test.
57 */
58 struct GNUNET_TESTING_Interpreter *is;
59};
60
61
62/**
63* Closure used to run the finish task.
64*/
65struct FinishTaskClosure
66{
67
68 /**
69 * The asynchronous command the synchronous command waits for.
70 */
71 const struct GNUNET_TESTING_Command *cmd;
72
73 /**
74 * The interpreter of the test.
75 */
76 struct GNUNET_TESTING_Interpreter *is;
77};
78
79 38
80/** 39/**
81 * Lookup command by label. 40 * Lookup command by label.
82 * 41 *
42 * @param is interpreter to lookup command in
83 * @param label label to look for 43 * @param label label to look for
84 * @return NULL if command was not found 44 * @return NULL if command was not found
85 */ 45 */
86const struct GNUNET_TESTING_Command * 46const struct GNUNET_TESTING_Command *
87GNUNET_TESTING_interpreter_lookup_command (const char *label) 47GNUNET_TESTING_interpreter_lookup_command (
48 struct GNUNET_TESTING_Interpreter *is,
49 const char *label)
88{ 50{
89 if (NULL == label) 51 if (NULL == label)
90 { 52 {
@@ -189,210 +151,35 @@ interpreter_next (void *cls)
189 151
190 152
191/** 153/**
192 * 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.
193 *
194 */
195static void
196run_finish_task_next (void *cls)
197{
198 struct FinishTaskClosure *ftc = cls;
199 const struct GNUNET_TESTING_Command *cmd = ftc->cmd;
200 struct GNUNET_TESTING_Interpreter *is = ftc->is;
201 unsigned int finished = cmd->finish (cmd->cls, &interpreter_next, is);
202
203 if (GNUNET_YES == finished)
204 {
205 is->finish_task = NULL;
206 }
207 else if (GNUNET_NO == finished)
208 {
209 is->finish_task = GNUNET_SCHEDULER_add_delayed (CHECK_FINISHED_PERIOD,
210 &run_finish_task_next, ftc);
211 }
212 else
213 {
214 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
215 "Next task finished with an error.\n");
216 GNUNET_TESTING_interpreter_fail ();
217 }
218
219}
220
221
222/**
223 * 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.
224 *
225 * //TODO run_finish_task_next and this function can be merged.
226 *
227 */
228static void
229run_finish_task_sync (void *cls)
230{
231 struct SyncTaskClosure *stc = cls;
232 const struct GNUNET_TESTING_Command *cmd = stc->async_cmd;
233 const struct GNUNET_TESTING_Command *sync_cmd = stc->sync_cmd;
234 struct FinishTaskClosure *ftc;
235 struct SyncState *sync_state = sync_cmd->cls;
236 struct GNUNET_SCHEDULER_Task *finish_task = sync_state->finish_task;
237 unsigned int finished = cmd->finish (cmd->cls, &interpreter_next, is);
238
239 GNUNET_assert (NULL != finish_task);
240 ftc = GNUNET_new (struct FinishTaskClosure);
241 ftc->cmd = stc->sync_cmd;
242 ftc->is = stc->is;
243 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
244 if (cmd->default_timeout.rel_value_us < now.abs_value_us
245 - sync_state->start_finish_time.abs_value_us)
246 {
247 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
248 "The command with label %s did not finish its asynchronous task in time.\n",
249 cmd->label);
250 GNUNET_TESTING_interpreter_fail ();
251 }
252
253 if (GNUNET_YES == finished)
254 {
255 finish_task = NULL;
256 }
257 else if (GNUNET_NO == finished)
258 {
259 finish_task = GNUNET_SCHEDULER_add_delayed (CHECK_FINISHED_PERIOD,
260 &run_finish_task_sync, stc);
261 }
262 else
263 {
264 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
265 "Sync task finished with an error.\n");
266 GNUNET_TESTING_interpreter_fail ();
267 }
268}
269
270
271/**
272 * run method of the command created by the interpreter to wait for another command to finish.
273 *
274 */
275static void
276start_finish_on_ref (void *cls,
277 const struct GNUNET_TESTING_Command *cmd,
278 struct GNUNET_TESTING_Interpreter *is)
279{
280 struct SyncState *sync_state = cls;
281 struct SyncTaskClosure *stc;
282 const struct GNUNET_TESTING_Command *async_cmd;
283
284 async_cmd = sync_state->async_cmd;
285 stc = GNUNET_new (struct SyncTaskClosure);
286 stc->async_cmd = async_cmd;
287 stc->sync_cmd = cmd;
288 stc->is = is;
289 sync_state->start_finish_time = GNUNET_TIME_absolute_get ();
290 sync_state->finish_task = GNUNET_SCHEDULER_add_delayed (
291 CHECK_FINISHED_PERIOD,
292 &run_finish_task_sync,
293 stc);
294}
295
296
297/**
298 * Create (synchronous) command that waits for another command to finish.
299 * If @a cmd_ref did not finish after @a timeout, this command will fail
300 * the test case.
301 *
302 * @param finish_label label for this command
303 * @param cmd_ref reference to a previous command which we should
304 * wait for (call `finish()` on)
305 * @param timeout how long to wait at most for @a cmd_ref to finish
306 * @return a finish-command.
307 */
308const struct GNUNET_TESTING_Command
309GNUNET_TESTING_cmd_finish (const char *finish_label,
310 const char *cmd_ref,
311 struct GNUNET_TIME_Relative timeout)
312{
313 const struct GNUNET_TESTING_Command *async_cmd;
314 struct SyncState *sync_state;
315
316 async_cmd = GNUNET_TESTING_interpreter_lookup_command (cmd_ref);
317 sync_state = GNUNET_new (struct SyncState);
318 sync_state->async_cmd = async_cmd;
319
320 struct GNUNET_TESTING_Command cmd = {
321 .cls = sync_state,
322 .label = finish_label,
323 .run = &start_finish_on_ref,
324 .asynchronous_finish = GNUNET_NO
325 };
326
327 return cmd;
328}
329
330
331const struct GNUNET_TESTING_Command
332GNUNET_TESTING_cmd_make_unblocking (const struct GNUNET_TESTING_Command cmd)
333{
334
335 GNUNET_assert (NULL != cmd.finish);
336 const struct GNUNET_TESTING_Command async_cmd = {
337 .cls = cmd.cls,
338 .label = cmd.label,
339 .run = cmd.run,
340 .cleanup = cmd.cleanup,
341 .traits = cmd.traits,
342 .finish = cmd.finish,
343 .asynchronous_finish = GNUNET_YES
344 };
345
346 return async_cmd;
347}
348
349
350/**
351 * Current command failed, clean up and fail the test case. 154 * Current command failed, clean up and fail the test case.
352 * 155 *
353 * @param is interpreter of the test 156 * @param is interpreter of the test
354 */ 157 */
355void 158void
356GNUNET_TESTING_interpreter_fail () 159GNUNET_TESTING_interpreter_fail (struct GNUNET_TESTING_Interpreter *is)
357{ 160{
358 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip]; 161 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
359 162
360 if (GNUNET_SYSERR == is->result) 163 if (GNUNET_SYSERR == is->result)
361 return; /* ignore, we already failed! */ 164 return; /* ignore, we already failed! */
362
363 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364 "interpreter_fail!\n");
365
366 if (NULL != cmd) 165 if (NULL != cmd)
367 { 166 {
167 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
168 "Failed at command `%s'\n",
169 cmd->label);
368 while (GNUNET_TESTING_cmd_is_batch (cmd)) 170 while (GNUNET_TESTING_cmd_is_batch (cmd))
369 { 171 {
370 cmd = GNUNET_TESTING_cmd_batch_get_current (cmd); 172 cmd = GNUNET_TESTING_cmd_batch_get_current (cmd);
371 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 173 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
372 "Batch is at command `%s'\n", 174 "Failed in batch at command `%s'\n",
373 cmd->label); 175 cmd->label);
374 } 176 }
375
376 } 177 }
377 else 178 else
378 { 179 {
379 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 180 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
380 "cmd is NULL.\n"); 181 "Failed with CMD being NULL!\n");
381 }
382
383 if (NULL == cmd->label)
384 {
385 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
386 "Failed at command `%s'\n",
387 cmd->label);
388
389 }
390 else
391 {
392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
393 "cmd->label is NULL.\n");
394 } 182 }
395
396 is->result = GNUNET_SYSERR; 183 is->result = GNUNET_SYSERR;
397 GNUNET_SCHEDULER_shutdown (); 184 GNUNET_SCHEDULER_shutdown ();
398} 185}
@@ -406,8 +193,9 @@ GNUNET_TESTING_interpreter_fail ()
406struct GNUNET_TESTING_Command 193struct GNUNET_TESTING_Command
407GNUNET_TESTING_cmd_end (void) 194GNUNET_TESTING_cmd_end (void)
408{ 195{
409 static struct GNUNET_TESTING_Command cmd; 196 static struct GNUNET_TESTING_Command cmd = {
410 cmd.label = NULL; 197 .label = NULL
198 };
411 199
412 return cmd; 200 return cmd;
413} 201}
@@ -417,8 +205,8 @@ GNUNET_TESTING_cmd_end (void)
417 * Obtain current label. 205 * Obtain current label.
418 */ 206 */
419const char * 207const char *
420GNUNET_TESTING_interpreter_get_current_label (struct 208GNUNET_TESTING_interpreter_get_current_label (
421 GNUNET_TESTING_Interpreter *is) 209 struct GNUNET_TESTING_Interpreter *is)
422{ 210{
423 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip]; 211 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
424 212
@@ -434,46 +222,36 @@ GNUNET_TESTING_interpreter_get_current_label (struct
434static void 222static void
435interpreter_run (void *cls) 223interpreter_run (void *cls)
436{ 224{
437 struct FinishTaskClosure *ftc;
438 struct GNUNET_TESTING_Interpreter *is = cls; 225 struct GNUNET_TESTING_Interpreter *is = cls;
439 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip]; 226 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
440 227
441 is->task = NULL; 228 is->task = NULL;
442
443 if (NULL == cmd->label) 229 if (NULL == cmd->label)
444 { 230 {
445
446 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
447 "Running command END %p\n", 232 "Running command END\n");
448 is);
449 is->result = GNUNET_OK; 233 is->result = GNUNET_OK;
450 GNUNET_SCHEDULER_shutdown (); 234 GNUNET_SCHEDULER_shutdown ();
451 return; 235 return;
452 } 236 }
453 else if (NULL != cmd) 237 if (NULL != cmd)
454 { 238 {
455 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
456 "Running command `%s' %p\n", 240 "Running command `%s'\n",
457 cmd->label, 241 cmd->label);
458 is);
459 } 242 }
460 cmd->start_time 243 cmd->start_time
461 = cmd->last_req_time 244 = cmd->last_req_time
462 = GNUNET_TIME_absolute_get (); 245 = GNUNET_TIME_absolute_get ();
463 cmd->num_tries = 1; 246 cmd->num_tries = 1;
464 cmd->run (cmd->cls, 247 cmd->run (cmd->cls,
465 cmd,
466 is); 248 is);
467 if ((NULL != cmd->finish) && (GNUNET_NO == cmd->asynchronous_finish)) 249 if ( (NULL != cmd->finish) &&
250 (! cmd->asynchronous_finish) )
468 { 251 {
469 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 252 cmd->finish (cmd->cls,
470 "Next task will not be called directly!\n"); 253 &interpreter_next,
471 ftc = GNUNET_new (struct FinishTaskClosure); 254 is);
472 ftc->cmd = cmd;
473 ftc->is = is;
474 is->finish_task = GNUNET_SCHEDULER_add_delayed (CHECK_FINISHED_PERIOD,
475 &run_finish_task_next,
476 ftc);
477 } 255 }
478 else 256 else
479 { 257 {
@@ -491,37 +269,33 @@ interpreter_run (void *cls)
491static void 269static void
492do_shutdown (void *cls) 270do_shutdown (void *cls)
493{ 271{
494 (void) cls; 272 struct GNUNET_TESTING_Interpreter *is = cls;
495 struct GNUNET_TESTING_Command *cmd; 273 struct GNUNET_TESTING_Command *cmd;
496 const char *label; 274 const char *label;
497 275
498 label = is->commands[is->ip].label; 276 label = is->commands[is->ip].label;
499 if (NULL == label) 277 if (NULL == label)
500 label = "END"; 278 label = "END";
501
502 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 279 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
503 "Executing shutdown at `%s'\n", 280 "Executing shutdown at `%s'\n",
504 label); 281 label);
505
506 for (unsigned int j = 0; 282 for (unsigned int j = 0;
507 NULL != (cmd = &is->commands[j])->label; 283 NULL != (cmd = &is->commands[j])->label;
508 j++) { 284 j++)
285 {
509 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 286 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
510 "Cleaning up cmd %s\n", 287 "Cleaning up cmd %s\n",
511 cmd->label); 288 cmd->label);
512 cmd->cleanup (cmd->cls, 289 cmd->cleanup (cmd->cls);
513 cmd);
514 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 290 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
515 "Cleaned up cmd %s\n", 291 "Cleaned up cmd %s\n",
516 cmd->label); 292 cmd->label);
517 } 293 }
518
519 if (NULL != is->finish_task) 294 if (NULL != is->finish_task)
520 { 295 {
521 GNUNET_SCHEDULER_cancel (is->finish_task); 296 GNUNET_SCHEDULER_cancel (is->finish_task);
522 cmd->finish_task = NULL; 297 is->finish_task = NULL;
523 } 298 }
524
525 if (NULL != is->task) 299 if (NULL != is->task)
526 { 300 {
527 GNUNET_SCHEDULER_cancel (is->task); 301 GNUNET_SCHEDULER_cancel (is->task);
@@ -533,18 +307,19 @@ do_shutdown (void *cls)
533 is->timeout_task = NULL; 307 is->timeout_task = NULL;
534 } 308 }
535 GNUNET_free (is->commands); 309 GNUNET_free (is->commands);
310 GNUNET_free (is);
536} 311}
537 312
538 313
539/** 314/**
540 * Function run when the test terminates (good or bad) with timeout. 315 * Function run when the test terminates (good or bad) with timeout.
541 * 316 *
542 * @param cls NULL 317 * @param cls the interpreter state
543 */ 318 */
544static void 319static void
545do_timeout (void *cls) 320do_timeout (void *cls)
546{ 321{
547 (void) cls; 322 struct GNUNET_TESTING_Interpreter *is = cls;
548 323
549 is->timeout_task = NULL; 324 is->timeout_task = NULL;
550 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 325 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -553,30 +328,15 @@ do_timeout (void *cls)
553} 328}
554 329
555 330
556/** 331enum GNUNET_GenericReturnValue
557 * Run the testsuite. Note, CMDs are copied into
558 * the interpreter state because they are _usually_
559 * defined into the "run" method that returns after
560 * having scheduled the test interpreter.
561 *
562 * @param is the interpreter state
563 * @param commands the list of command to execute
564 * @param timeout how long to wait
565 */
566int
567GNUNET_TESTING_run (const char *cfg_filename, 332GNUNET_TESTING_run (const char *cfg_filename,
568 struct GNUNET_TESTING_Command *commands, 333 struct GNUNET_TESTING_Command *commands,
569 struct GNUNET_TIME_Relative timeout) 334 struct GNUNET_TIME_Relative timeout)
570{ 335{
336 struct GNUNET_TESTING_Interpreter *is;
571 unsigned int i; 337 unsigned int i;
572 338
573 is = GNUNET_new (struct GNUNET_TESTING_Interpreter); 339 is = GNUNET_new (struct GNUNET_TESTING_Interpreter);
574
575 if (NULL != is->timeout_task)
576 {
577 GNUNET_SCHEDULER_cancel (is->timeout_task);
578 is->timeout_task = NULL;
579 }
580 /* get the number of commands */ 340 /* get the number of commands */
581 for (i = 0; NULL != commands[i].label; i++) 341 for (i = 0; NULL != commands[i].label; i++)
582 ; 342 ;
@@ -585,15 +345,67 @@ GNUNET_TESTING_run (const char *cfg_filename,
585 memcpy (is->commands, 345 memcpy (is->commands,
586 commands, 346 commands,
587 sizeof (struct GNUNET_TESTING_Command) * i); 347 sizeof (struct GNUNET_TESTING_Command) * i);
588 348 is->timeout_task
589 is->timeout_task = GNUNET_SCHEDULER_add_delayed 349 = GNUNET_SCHEDULER_add_delayed (timeout,
590 (timeout, 350 &do_timeout,
591 &do_timeout, 351 is);
592 is); 352 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
593 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is); 353 is);
594 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, is); 354 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
355 is);
595 return GNUNET_OK; 356 return GNUNET_OK;
596} 357}
597 358
598 359
360/**
361 * Closure for #loop_run().
362 */
363struct MainParams
364{
365 const char *cfg_filename;
366 struct GNUNET_TESTING_Command *commands;
367 struct GNUNET_TIME_Relative timeout;
368 int rv;
369};
370
371
372/**
373 * Main function to run the test cases.
374 *
375 * @param cls a `struct MainParams *`
376 */
377static void
378loop_run (void *cls)
379{
380 struct MainParams *mp = cls;
381
382 if (GNUNET_OK !=
383 GNUNET_TESTING_run (mp->cfg_filename,
384 mp->commands,
385 mp->timeout))
386 {
387 GNUNET_break (0);
388 mp->rv = EXIT_FAILURE;
389 }
390}
391
392
393int
394GNUNET_TESTING_main (const char *cfg_filename,
395 struct GNUNET_TESTING_Command *commands,
396 struct GNUNET_TIME_Relative timeout)
397{
398 struct MainParams mp = {
399 .cfg_filename = cfg_filename,
400 .commands = commands,
401 .timeout = timeout,
402 .rv = EXIT_SUCCESS
403 };
404
405 GNUNET_SCHEDULER_run (&loop_run,
406 &mp);
407 return mp.rv;
408}
409
410
599/* end of testing_api_loop.c */ 411/* end of testing_api_loop.c */