aboutsummaryrefslogtreecommitdiff
path: root/src/testing/testing_api_loop.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testing/testing_api_loop.c')
-rw-r--r--src/testing/testing_api_loop.c599
1 files changed, 0 insertions, 599 deletions
diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c
deleted file mode 100644
index 0c24c0e26..000000000
--- a/src/testing/testing_api_loop.c
+++ /dev/null
@@ -1,599 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file testing/testing_api_loop.c
23 * @brief main interpreter loop for testcases
24 * @author Christian Grothoff (GNU Taler testing)
25 * @author Marcello Stanisci (GNU Taler testing)
26 * @author t3sserakt
27*/
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_testing_ng_lib.h"
31#include "testing.h"
32
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
80/**
81 * Lookup command by label.
82 *
83 * @param label label to look for
84 * @return NULL if command was not found
85 */
86const struct GNUNET_TESTING_Command *
87GNUNET_TESTING_interpreter_lookup_command (const char *label)
88{
89 if (NULL == label)
90 {
91 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
92 "Attempt to lookup command for empty label\n");
93 return NULL;
94 }
95 /* Search backwards as we most likely reference recent commands */
96 for (int i = is->ip; i >= 0; i--)
97 {
98 const struct GNUNET_TESTING_Command *cmd = &is->commands[i];
99
100 /* Give precedence to top-level commands. */
101 if ( (NULL != cmd->label) &&
102 (0 == strcmp (cmd->label,
103 label)) )
104 return cmd;
105
106 if (GNUNET_TESTING_cmd_is_batch (cmd))
107 {
108#define BATCH_INDEX 1
109 struct GNUNET_TESTING_Command *batch;
110 struct GNUNET_TESTING_Command *current;
111 struct GNUNET_TESTING_Command *icmd;
112 const struct GNUNET_TESTING_Command *match;
113
114 current = GNUNET_TESTING_cmd_batch_get_current (cmd);
115 GNUNET_assert (GNUNET_OK ==
116 GNUNET_TESTING_get_trait_cmd (cmd,
117 BATCH_INDEX,
118 &batch));
119 /* We must do the loop forward, but we can find the last match */
120 match = NULL;
121 for (unsigned int j = 0;
122 NULL != (icmd = &batch[j])->label;
123 j++)
124 {
125 if (current == icmd)
126 break; /* do not go past current command */
127 if ( (NULL != icmd->label) &&
128 (0 == strcmp (icmd->label,
129 label)) )
130 match = icmd;
131 }
132 if (NULL != match)
133 return match;
134 }
135 }
136 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
137 "Command not found: %s\n",
138 label);
139 return NULL;
140
141}
142
143
144/**
145 * Run the main interpreter loop that performs exchange operations.
146 *
147 * @param cls contains the `struct InterpreterState`
148 */
149static void
150interpreter_run (void *cls);
151
152
153/**
154 * Current command is done, run the next one.
155 */
156static void
157interpreter_next (void *cls)
158{
159 struct GNUNET_TESTING_Interpreter *is = cls;
160 static unsigned long long ipc;
161 static struct GNUNET_TIME_Absolute last_report;
162 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
163
164 if (GNUNET_SYSERR == is->result)
165 return; /* ignore, we already failed! */
166 if (GNUNET_TESTING_cmd_is_batch (cmd))
167 {
168 GNUNET_TESTING_cmd_batch_next (is);
169 }
170 else
171 {
172 cmd->finish_time = GNUNET_TIME_absolute_get ();
173 is->ip++;
174 }
175 if (0 == (ipc % 1000))
176 {
177 if (0 != ipc)
178 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
179 "Interpreter executed 1000 instructions in %s\n",
180 GNUNET_STRINGS_relative_time_to_string (
181 GNUNET_TIME_absolute_get_duration (last_report),
182 GNUNET_YES));
183 last_report = GNUNET_TIME_absolute_get ();
184 }
185 ipc++;
186 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
187 is);
188}
189
190
191/**
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.
352 *
353 * @param is interpreter of the test
354 */
355void
356GNUNET_TESTING_interpreter_fail ()
357{
358 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
359
360 if (GNUNET_SYSERR == is->result)
361 return; /* ignore, we already failed! */
362
363 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364 "interpreter_fail!\n");
365
366 if (NULL != cmd)
367 {
368 while (GNUNET_TESTING_cmd_is_batch (cmd))
369 {
370 cmd = GNUNET_TESTING_cmd_batch_get_current (cmd);
371 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
372 "Batch is at command `%s'\n",
373 cmd->label);
374 }
375
376 }
377 else
378 {
379 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
380 "cmd is 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 }
395
396 is->result = GNUNET_SYSERR;
397 GNUNET_SCHEDULER_shutdown ();
398}
399
400
401/**
402 * Create command array terminator.
403 *
404 * @return a end-command.
405 */
406struct GNUNET_TESTING_Command
407GNUNET_TESTING_cmd_end (void)
408{
409 static struct GNUNET_TESTING_Command cmd;
410 cmd.label = NULL;
411
412 return cmd;
413}
414
415
416/**
417 * Obtain current label.
418 */
419const char *
420GNUNET_TESTING_interpreter_get_current_label (struct
421 GNUNET_TESTING_Interpreter *is)
422{
423 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
424
425 return cmd->label;
426}
427
428
429/**
430 * Run the main interpreter loop.
431 *
432 * @param cls contains the `struct GNUNET_TESTING_Interpreter`
433 */
434static void
435interpreter_run (void *cls)
436{
437 struct FinishTaskClosure *ftc;
438 struct GNUNET_TESTING_Interpreter *is = cls;
439 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
440
441 is->task = NULL;
442
443 if (NULL == cmd->label)
444 {
445
446 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
447 "Running command END %p\n",
448 is);
449 is->result = GNUNET_OK;
450 GNUNET_SCHEDULER_shutdown ();
451 return;
452 }
453 else if (NULL != cmd)
454 {
455 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
456 "Running command `%s' %p\n",
457 cmd->label,
458 is);
459 }
460 cmd->start_time
461 = cmd->last_req_time
462 = GNUNET_TIME_absolute_get ();
463 cmd->num_tries = 1;
464 cmd->run (cmd->cls,
465 cmd,
466 is);
467 if ((NULL != cmd->finish) && (GNUNET_NO == cmd->asynchronous_finish))
468 {
469 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
470 "Next task will not be called directly!\n");
471 ftc = GNUNET_new (struct FinishTaskClosure);
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 }
478 else
479 {
480 interpreter_next (is);
481 }
482}
483
484
485/**
486 * Function run when the test terminates (good or bad).
487 * Cleans up our state.
488 *
489 * @param cls the interpreter state.
490 */
491static void
492do_shutdown (void *cls)
493{
494 (void) cls;
495 struct GNUNET_TESTING_Command *cmd;
496 const char *label;
497
498 label = is->commands[is->ip].label;
499 if (NULL == label)
500 label = "END";
501
502 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
503 "Executing shutdown at `%s'\n",
504 label);
505
506 for (unsigned int j = 0;
507 NULL != (cmd = &is->commands[j])->label;
508 j++) {
509 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
510 "Cleaning up cmd %s\n",
511 cmd->label);
512 cmd->cleanup (cmd->cls,
513 cmd);
514 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
515 "Cleaned up cmd %s\n",
516 cmd->label);
517 }
518
519 if (NULL != is->finish_task)
520 {
521 GNUNET_SCHEDULER_cancel (is->finish_task);
522 cmd->finish_task = NULL;
523 }
524
525 if (NULL != is->task)
526 {
527 GNUNET_SCHEDULER_cancel (is->task);
528 is->task = NULL;
529 }
530 if (NULL != is->timeout_task)
531 {
532 GNUNET_SCHEDULER_cancel (is->timeout_task);
533 is->timeout_task = NULL;
534 }
535 GNUNET_free (is->commands);
536}
537
538
539/**
540 * Function run when the test terminates (good or bad) with timeout.
541 *
542 * @param cls NULL
543 */
544static void
545do_timeout (void *cls)
546{
547 (void) cls;
548
549 is->timeout_task = NULL;
550 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
551 "Terminating test due to timeout\n");
552 GNUNET_SCHEDULER_shutdown ();
553}
554
555
556/**
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,
568 struct GNUNET_TESTING_Command *commands,
569 struct GNUNET_TIME_Relative timeout)
570{
571 unsigned int i;
572
573 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 */
581 for (i = 0; NULL != commands[i].label; i++)
582 ;
583 is->commands = GNUNET_new_array (i + 1,
584 struct GNUNET_TESTING_Command);
585 memcpy (is->commands,
586 commands,
587 sizeof (struct GNUNET_TESTING_Command) * i);
588
589 is->timeout_task = GNUNET_SCHEDULER_add_delayed
590 (timeout,
591 &do_timeout,
592 is);
593 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is);
594 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, is);
595 return GNUNET_OK;
596}
597
598
599/* end of testing_api_loop.c */