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.c604
1 files changed, 0 insertions, 604 deletions
diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c
deleted file mode 100644
index 95d6b88e6..000000000
--- a/src/testing/testing_api_loop.c
+++ /dev/null
@@ -1,604 +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/**
34 * Global state of the interpreter, used by a command
35 * to access information about other commands.
36 */
37struct GNUNET_TESTING_Interpreter
38{
39
40 /**
41 * Function to call with the test result.
42 */
43 GNUNET_TESTING_ResultCallback rc;
44
45 /**
46 * Closure for @e rc.
47 */
48 void *rc_cls;
49
50 /**
51 * Commands the interpreter will run.
52 */
53 struct GNUNET_TESTING_Command *commands;
54
55 /**
56 * Number of GNUNET_TESTING_Command in commands.
57 */
58 unsigned int cmds_n;
59
60 /**
61 * Interpreter task (if one is scheduled).
62 */
63 struct GNUNET_SCHEDULER_Task *task;
64
65 /**
66 * Final task that returns the result.
67 */
68 struct GNUNET_SCHEDULER_Task *final_task;
69
70 /**
71 * Task run on timeout.
72 */
73 struct GNUNET_SCHEDULER_Task *timeout_task;
74
75 /**
76 * Instruction pointer. Tells #interpreter_run() which instruction to run
77 * next. Need (signed) int because it gets -1 when rewinding the
78 * interpreter to the first CMD.
79 */
80 int ip;
81
82 /**
83 * Result of the testcases, #GNUNET_OK on success
84 */
85 enum GNUNET_GenericReturnValue result;
86
87};
88
89
90const struct GNUNET_TESTING_Command *
91get_command (struct GNUNET_TESTING_Interpreter *is,
92 const char *label,
93 unsigned int future)
94{
95 int start_i = GNUNET_NO == future ? is->ip : is->cmds_n - 1;
96 int end_i = GNUNET_NO == future ? 0 : is->ip + 1;
97
98 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
99 "start_i: %u end_i: %u\n",
100 start_i,
101 end_i);
102 if (NULL == label)
103 {
104 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
105 "Attempt to lookup command for empty label\n");
106 return NULL;
107 }
108
109 for (int i = start_i; i >= end_i; i--)
110 {
111 const struct GNUNET_TESTING_Command *cmd = &is->commands[i];
112
113 if (NULL != cmd->label)
114 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
115 "label to compare %s\n",
116 cmd->label);
117 /* Give precedence to top-level commands. */
118 if ( (NULL != cmd->label) &&
119 (0 == strcmp (cmd->label,
120 label)) )
121 return cmd;
122
123 if (GNUNET_TESTING_cmd_is_batch_ (cmd))
124 {
125 struct GNUNET_TESTING_Command **batch;
126 struct GNUNET_TESTING_Command *current;
127 const struct GNUNET_TESTING_Command *icmd;
128 const struct GNUNET_TESTING_Command *match;
129
130 current = GNUNET_TESTING_cmd_batch_get_current_ (cmd);
131 GNUNET_assert (GNUNET_OK ==
132 GNUNET_TESTING_get_trait_batch_cmds (cmd,
133 &batch));
134 /* We must do the loop forward, but we can find the last match */
135 match = NULL;
136 for (unsigned int j = 0;
137 NULL != (icmd = &(*batch)[j])->label;
138 j++)
139 {
140 if (current == icmd)
141 break; /* do not go past current command */
142 if ( (NULL != icmd->label) &&
143 (0 == strcmp (icmd->label,
144 label)) )
145 match = icmd;
146 }
147 if (NULL != match)
148 return match;
149 }
150 }
151 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
152 "Command `%s' not found\n",
153 label);
154 return NULL;
155}
156
157
158/**
159 * Lookup command by label.
160 * Only future commands are looked up.
161 *
162 * @param is interpreter to lookup command in
163 * @param label label of the command to lookup.
164 * @return the command, if it is found, or NULL.
165 */
166const struct GNUNET_TESTING_Command *
167GNUNET_TESTING_interpreter_lookup_future_command (
168 struct GNUNET_TESTING_Interpreter *is,
169 const char *label)
170{
171 return get_command (is, label, GNUNET_YES);
172}
173
174
175/**
176 * Lookup command by label.
177 * Only commands from current command to commands in the past are looked up.
178 *
179 * @param is interpreter to lookup command in
180 * @param label label of the command to lookup.
181 * @return the command, if it is found, or NULL.
182 */
183const struct GNUNET_TESTING_Command *
184GNUNET_TESTING_interpreter_lookup_command (
185 struct GNUNET_TESTING_Interpreter *is,
186 const char *label)
187{
188 return get_command (is, label, GNUNET_NO);
189}
190
191
192/**
193 * Lookup command by label.
194 * All commands, first into the past, then into the furture are looked up.
195 *
196 * @param is interpreter to lookup command in
197 * @param label label of the command to lookup.
198 * @return the command, if it is found, or NULL.
199 */
200const struct GNUNET_TESTING_Command *
201GNUNET_TESTING_interpreter_lookup_command_all (
202 struct GNUNET_TESTING_Interpreter *is,
203 const char *label)
204{
205 const struct GNUNET_TESTING_Command *cmd;
206
207 cmd = get_command (is, label, GNUNET_NO);
208 if (NULL == cmd)
209 cmd = get_command (is, label, GNUNET_YES);
210 return cmd;
211}
212
213
214/**
215 * Finish the test run, return the final result.
216 *
217 * @param cls the `struct GNUNET_TESTING_Interpreter`
218 */
219static void
220finish_test (void *cls)
221{
222 struct GNUNET_TESTING_Interpreter *is = cls;
223 struct GNUNET_TESTING_Command *cmd;
224 const char *label;
225
226 is->final_task = NULL;
227 label = is->commands[is->ip].label;
228 if (NULL == label)
229 label = "END";
230 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
231 "Interpreter finishes at `%s' with status %d\n",
232 label,
233 is->result);
234 for (unsigned int j = 0;
235 NULL != (cmd = &is->commands[j])->label;
236 j++)
237 {
238 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
239 "Cleaning up cmd %s\n",
240 cmd->label);
241 cmd->cleanup (cmd->cls);
242 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
243 "Cleaned up cmd %s\n",
244 cmd->label);
245 }
246 if (NULL != is->task)
247 {
248 GNUNET_SCHEDULER_cancel (is->task);
249 is->task = NULL;
250 }
251 if (NULL != is->timeout_task)
252 {
253 GNUNET_SCHEDULER_cancel (is->timeout_task);
254 is->timeout_task = NULL;
255 }
256 GNUNET_free (is->commands);
257 is->rc (is->rc_cls,
258 is->result);
259 GNUNET_free (is);
260}
261
262
263/**
264 * Run the main interpreter loop that performs exchange operations.
265 *
266 * @param cls contains the `struct InterpreterState`
267 */
268static void
269interpreter_run (void *cls);
270
271
272/**
273 * Current command is done, run the next one.
274 */
275static void
276interpreter_next (void *cls)
277{
278 struct GNUNET_TESTING_Interpreter *is = cls;
279 static unsigned long long ipc;
280 static struct GNUNET_TIME_Absolute last_report;
281 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
282
283 if (GNUNET_SYSERR == is->result)
284 return; /* ignore, we already failed! */
285 cmd->finish_time = GNUNET_TIME_absolute_get ();
286 if ( (! GNUNET_TESTING_cmd_is_batch_ (cmd)) ||
287 (! GNUNET_TESTING_cmd_batch_next_ (cmd->cls)) )
288 is->ip++;
289 if (0 == (ipc % 1000))
290 {
291 if (0 != ipc)
292 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
293 "Interpreter executed 1000 instructions in %s\n",
294 GNUNET_STRINGS_relative_time_to_string (
295 GNUNET_TIME_absolute_get_duration (last_report),
296 GNUNET_YES));
297 last_report = GNUNET_TIME_absolute_get ();
298 }
299 ipc++;
300 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
301 is);
302}
303
304
305void
306GNUNET_TESTING_interpreter_fail (struct GNUNET_TESTING_Interpreter *is)
307{
308 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
309
310 if (GNUNET_SYSERR == is->result)
311 {
312 GNUNET_break (0);
313 return; /* ignore, we already failed! */
314 }
315 if (NULL != cmd)
316 {
317 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
318 "Failed at command `%s'\n",
319 cmd->label);
320 while (GNUNET_TESTING_cmd_is_batch_ (cmd))
321 {
322 cmd = GNUNET_TESTING_cmd_batch_get_current_ (cmd);
323 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
324 "Failed in batch at command `%s'\n",
325 cmd->label);
326 }
327 }
328 else
329 {
330 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
331 "Failed with CMD being NULL!\n");
332 }
333 is->result = GNUNET_SYSERR;
334 GNUNET_assert (NULL == is->final_task);
335 is->final_task = GNUNET_SCHEDULER_add_now (&finish_test,
336 is);
337}
338
339
340/**
341 * Returns the actual running command.
342 *
343 * @param is Global state of the interpreter, used by a command
344 * to access information about other commands.
345 * @return The actual running command.
346 */
347struct GNUNET_TESTING_Command *
348GNUNET_TESTING_interpreter_get_current_command (
349 struct GNUNET_TESTING_Interpreter *is)
350{
351 return &is->commands[is->ip];
352}
353
354const char *
355GNUNET_TESTING_interpreter_get_current_label (
356 struct GNUNET_TESTING_Interpreter *is)
357{
358 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
359
360 return cmd->label;
361}
362
363
364/**
365 * Run the main interpreter loop.
366 *
367 * @param cls contains the `struct GNUNET_TESTING_Interpreter`
368 */
369static void
370interpreter_run (void *cls)
371{
372 struct GNUNET_TESTING_Interpreter *is = cls;
373 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
374
375 is->task = NULL;
376 if (NULL == cmd->label)
377 {
378 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
379 "Running command END\n");
380 is->result = GNUNET_OK;
381 finish_test (is);
382 return;
383 }
384 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
385 "Running command `%s'\n",
386 cmd->label);
387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
388 "start time of %p expected 0 is `%lu'\n",
389 cmd,
390 cmd->start_time.abs_value_us);
391 cmd->start_time
392 = cmd->last_req_time
393 = GNUNET_TIME_absolute_get ();
394 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
395 "start time of %p expected something is `%lu'\n",
396 cmd,
397 cmd->start_time.abs_value_us);
398 cmd->num_tries = 1;
399 if (NULL != cmd->ac)
400 {
401 cmd->ac->is = is;
402 cmd->ac->cont = &interpreter_next;
403 cmd->ac->cont_cls = is;
404 cmd->ac->finished = GNUNET_NO;
405 }
406 cmd->run (cmd->cls,
407 is);
408 if (NULL == cmd->ac)
409 {
410 interpreter_next (is);
411 }
412 else if ( (cmd->asynchronous_finish) &&
413 (NULL != cmd->ac->cont) )
414 {
415 cmd->ac->cont = NULL;
416 interpreter_next (is);
417 }
418}
419
420
421/**
422 * Function run when the test terminates (good or bad) with timeout.
423 *
424 * @param cls the interpreter state
425 */
426static void
427do_timeout (void *cls)
428{
429 struct GNUNET_TESTING_Interpreter *is = cls;
430
431 is->timeout_task = NULL;
432 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
433 "Terminating test due to global timeout\n");
434 is->result = GNUNET_SYSERR;
435 finish_test (is);
436}
437
438
439/**
440 * Check if the command is running.
441 *
442 * @param cmd The command to check.
443 * @return GNUNET_NO if the command is not running, GNUNET_YES if it is running.
444 */
445enum GNUNET_GenericReturnValue
446GNUNET_TESTING_running (const struct GNUNET_TESTING_Command *command)
447{
448 return 0 != command->start_time.abs_value_us && 0 ==
449 command->finish_time.abs_value_us;
450}
451
452
453/**
454 * Check if a command is finished.
455 *
456 * @param cmd The command to check.
457 * @return GNUNET_NO if the command is not finished, GNUNET_YES if it is finished.
458 */
459enum GNUNET_GenericReturnValue
460GNUNET_TESTING_finished (struct GNUNET_TESTING_Command *command)
461{
462 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
463 struct GNUNET_TIME_Relative diff = GNUNET_TIME_absolute_get_difference (
464 command->finish_time,
465 now);
466 return 0 < diff.rel_value_us;
467}
468
469
470void
471GNUNET_TESTING_run (struct GNUNET_TESTING_Command *commands,
472 struct GNUNET_TIME_Relative timeout,
473 GNUNET_TESTING_ResultCallback rc,
474 void *rc_cls)
475{
476 struct GNUNET_TESTING_Interpreter *is;
477 unsigned int i;
478
479 is = GNUNET_new (struct GNUNET_TESTING_Interpreter);
480 is->rc = rc;
481 is->rc_cls = rc_cls;
482 /* get the number of commands */
483 for (i = 0; NULL != commands[i].label; i++)
484 ;
485 is->cmds_n = i + 1;
486 is->commands = GNUNET_new_array (is->cmds_n,
487 struct GNUNET_TESTING_Command);
488 memcpy (is->commands,
489 commands,
490 sizeof (struct GNUNET_TESTING_Command) * i);
491 is->timeout_task
492 = GNUNET_SCHEDULER_add_delayed (timeout,
493 &do_timeout,
494 is);
495 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
496 is);
497}
498
499
500/**
501 * Closure for #loop_run().
502 */
503struct MainParams
504{
505
506 /**
507 * NULL-label terminated array of commands.
508 */
509 struct GNUNET_TESTING_Command *commands;
510
511 /**
512 * Global timeout for the test.
513 */
514 struct GNUNET_TIME_Relative timeout;
515
516 /**
517 * Set to #EXIT_FAILURE on error.
518 */
519 int rv;
520};
521
522
523/**
524 * Function called with the final result of the test.
525 *
526 * @param cls the `struct MainParams`
527 * @param rv #GNUNET_OK if the test passed
528 */
529static void
530handle_result (void *cls,
531 enum GNUNET_GenericReturnValue rv)
532{
533 struct MainParams *mp = cls;
534
535 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
536 "Test exits with status %d\n",
537 rv);
538 if (GNUNET_OK != rv)
539 mp->rv = EXIT_FAILURE;
540 GNUNET_SCHEDULER_shutdown ();
541}
542
543
544/**
545 * Main function to run the test cases.
546 *
547 * @param cls a `struct MainParams *`
548 */
549static void
550loop_run (void *cls)
551{
552 struct MainParams *mp = cls;
553
554 GNUNET_TESTING_run (mp->commands,
555 mp->timeout,
556 &handle_result,
557 mp);
558}
559
560
561int
562GNUNET_TESTING_main (struct GNUNET_TESTING_Command *commands,
563 struct GNUNET_TIME_Relative timeout)
564{
565 struct MainParams mp = {
566 .commands = commands,
567 .timeout = timeout,
568 .rv = EXIT_SUCCESS
569 };
570
571 GNUNET_SCHEDULER_run (&loop_run,
572 &mp);
573 return mp.rv;
574}
575
576
577void
578GNUNET_TESTING_async_fail (struct GNUNET_TESTING_AsyncContext *ac)
579{
580 GNUNET_assert (GNUNET_NO == ac->finished);
581 ac->finished = GNUNET_SYSERR;
582 GNUNET_TESTING_interpreter_fail (ac->is);
583 if (NULL != ac->cont)
584 {
585 ac->cont (ac->cont_cls);
586 ac->cont = NULL;
587 }
588}
589
590
591void
592GNUNET_TESTING_async_finish (struct GNUNET_TESTING_AsyncContext *ac)
593{
594 GNUNET_assert (GNUNET_NO == ac->finished);
595 ac->finished = GNUNET_OK;
596 if (NULL != ac->cont)
597 {
598 ac->cont (ac->cont_cls);
599 ac->cont = NULL;
600 }
601}
602
603
604/* end of testing_api_loop.c */