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