diff options
Diffstat (limited to 'src/lib/testing/testing_api_loop.c')
-rw-r--r-- | src/lib/testing/testing_api_loop.c | 937 |
1 files changed, 937 insertions, 0 deletions
diff --git a/src/lib/testing/testing_api_loop.c b/src/lib/testing/testing_api_loop.c new file mode 100644 index 000000000..db0a00c22 --- /dev/null +++ b/src/lib/testing/testing_api_loop.c | |||
@@ -0,0 +1,937 @@ | |||
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 "gnunet_testing_plugin.h" | ||
32 | #include "gnunet_testing_barrier.h" | ||
33 | #include "gnunet_testing_netjail_lib.h" | ||
34 | #include "testing.h" | ||
35 | |||
36 | /** | ||
37 | * Global state of the interpreter, used by a command | ||
38 | * to access information about other commands. | ||
39 | */ | ||
40 | struct GNUNET_TESTING_Interpreter | ||
41 | { | ||
42 | /** | ||
43 | * Array with handles of helper processes for communication with netjails. | ||
44 | */ | ||
45 | const struct GNUNET_HELPER_Handle **helper; | ||
46 | |||
47 | /** | ||
48 | * Size of the array helper. | ||
49 | * | ||
50 | */ | ||
51 | unsigned int n_helper; | ||
52 | |||
53 | /** | ||
54 | * Function to call with the test result. | ||
55 | */ | ||
56 | GNUNET_TESTING_ResultCallback rc; | ||
57 | |||
58 | /** | ||
59 | * Closure for @e rc. | ||
60 | */ | ||
61 | void *rc_cls; | ||
62 | |||
63 | /** | ||
64 | * Commands the interpreter will run. | ||
65 | */ | ||
66 | struct GNUNET_TESTING_Command *commands; | ||
67 | |||
68 | /** | ||
69 | * Map with barriers for this loop. | ||
70 | */ | ||
71 | struct GNUNET_CONTAINER_MultiShortmap *barriers; | ||
72 | |||
73 | /** | ||
74 | * Number of GNUNET_TESTING_Command in commands. | ||
75 | */ | ||
76 | unsigned int cmds_n; | ||
77 | |||
78 | /** | ||
79 | * Interpreter task (if one is scheduled). | ||
80 | */ | ||
81 | struct GNUNET_SCHEDULER_Task *task; | ||
82 | |||
83 | /** | ||
84 | * Final task that returns the result. | ||
85 | */ | ||
86 | struct GNUNET_SCHEDULER_Task *final_task; | ||
87 | |||
88 | /** | ||
89 | * Task run on timeout. | ||
90 | */ | ||
91 | struct GNUNET_SCHEDULER_Task *timeout_task; | ||
92 | |||
93 | /** | ||
94 | * Instruction pointer. Tells #interpreter_run() which instruction to run | ||
95 | * next. Need (signed) int because it gets -1 when rewinding the | ||
96 | * interpreter to the first CMD. | ||
97 | */ | ||
98 | int ip; | ||
99 | |||
100 | /** | ||
101 | * Result of the testcases, #GNUNET_OK on success | ||
102 | */ | ||
103 | enum GNUNET_GenericReturnValue result; | ||
104 | |||
105 | /** | ||
106 | * Is the interpreter finishing? | ||
107 | */ | ||
108 | unsigned int finishing; | ||
109 | |||
110 | }; | ||
111 | |||
112 | struct FreeBarrierNodeCbCls | ||
113 | { | ||
114 | /** | ||
115 | * The interpreter. | ||
116 | */ | ||
117 | struct GNUNET_TESTING_Interpreter *is; | ||
118 | |||
119 | /** | ||
120 | * The barrier from which the nodes are freed.. | ||
121 | */ | ||
122 | struct GNUNET_TESTING_Barrier *barrier; | ||
123 | }; | ||
124 | |||
125 | |||
126 | static const struct GNUNET_TESTING_Command * | ||
127 | get_command (struct GNUNET_TESTING_Interpreter *is, | ||
128 | const char *label, | ||
129 | unsigned int future) | ||
130 | { | ||
131 | int start_i = GNUNET_NO == future ? is->ip : is->cmds_n - 1; | ||
132 | int end_i = GNUNET_NO == future ? 0 : is->ip + 1; | ||
133 | |||
134 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
135 | "start_i: %u end_i: %u\n", | ||
136 | start_i, | ||
137 | end_i); | ||
138 | if (NULL == label) | ||
139 | { | ||
140 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
141 | "Attempt to lookup command for empty label\n"); | ||
142 | return NULL; | ||
143 | } | ||
144 | |||
145 | for (int i = start_i; i >= end_i; i--) | ||
146 | { | ||
147 | const struct GNUNET_TESTING_Command *cmd = &is->commands[i]; | ||
148 | |||
149 | if (NULL != cmd->run) | ||
150 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
151 | "label to compare %s\n", | ||
152 | cmd->label); | ||
153 | /* Give precedence to top-level commands. */ | ||
154 | if ( (NULL != cmd->run) && | ||
155 | (0 == strcmp (cmd->label, | ||
156 | label)) ) | ||
157 | return cmd; | ||
158 | |||
159 | if (GNUNET_TESTING_cmd_is_batch_ (cmd)) | ||
160 | { | ||
161 | struct GNUNET_TESTING_Command **batch; | ||
162 | struct GNUNET_TESTING_Command *current; | ||
163 | const struct GNUNET_TESTING_Command *icmd; | ||
164 | const struct GNUNET_TESTING_Command *match; | ||
165 | |||
166 | current = GNUNET_TESTING_cmd_batch_get_current_ (cmd); | ||
167 | GNUNET_assert (GNUNET_OK == | ||
168 | GNUNET_TESTING_get_trait_batch_cmds (cmd, | ||
169 | &batch)); | ||
170 | /* We must do the loop forward, but we can find the last match */ | ||
171 | match = NULL; | ||
172 | for (unsigned int j = 0; | ||
173 | NULL != (icmd = &(*batch)[j])->run; | ||
174 | j++) | ||
175 | { | ||
176 | if (current == icmd) | ||
177 | break; /* do not go past current command */ | ||
178 | if ( (NULL != icmd->run) && | ||
179 | (0 == strcmp (icmd->label, | ||
180 | label)) ) | ||
181 | match = icmd; | ||
182 | } | ||
183 | if (NULL != match) | ||
184 | return match; | ||
185 | } | ||
186 | } | ||
187 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
188 | "Command `%s' not found\n", | ||
189 | label); | ||
190 | return NULL; | ||
191 | } | ||
192 | |||
193 | |||
194 | /** | ||
195 | * Lookup command by label. | ||
196 | * Only future commands 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 | */ | ||
202 | const struct GNUNET_TESTING_Command * | ||
203 | GNUNET_TESTING_interpreter_lookup_future_command ( | ||
204 | struct GNUNET_TESTING_Interpreter *is, | ||
205 | const char *label) | ||
206 | { | ||
207 | return get_command (is, label, GNUNET_YES); | ||
208 | } | ||
209 | |||
210 | |||
211 | /** | ||
212 | * Lookup command by label. | ||
213 | * Only commands from current command to commands in the past are looked up. | ||
214 | * | ||
215 | * @param is interpreter to lookup command in | ||
216 | * @param label label of the command to lookup. | ||
217 | * @return the command, if it is found, or NULL. | ||
218 | */ | ||
219 | const struct GNUNET_TESTING_Command * | ||
220 | GNUNET_TESTING_interpreter_lookup_command ( | ||
221 | struct GNUNET_TESTING_Interpreter *is, | ||
222 | const char *label) | ||
223 | { | ||
224 | return get_command (is, label, GNUNET_NO); | ||
225 | } | ||
226 | |||
227 | |||
228 | const struct GNUNET_TESTING_Command * | ||
229 | GNUNET_TESTING_interpreter_lookup_command_all ( | ||
230 | struct GNUNET_TESTING_Interpreter *is, | ||
231 | const char *label) | ||
232 | { | ||
233 | const struct GNUNET_TESTING_Command *cmd; | ||
234 | |||
235 | cmd = get_command (is, label, GNUNET_NO); | ||
236 | if (NULL == cmd) | ||
237 | cmd = get_command (is, label, GNUNET_YES); | ||
238 | return cmd; | ||
239 | } | ||
240 | |||
241 | |||
242 | int | ||
243 | free_barrier_node_cb (void *cls, | ||
244 | const struct GNUNET_ShortHashCode *key, | ||
245 | void *value) | ||
246 | { | ||
247 | struct FreeBarrierNodeCbCls *free_barrier_node_cb_cls = cls; | ||
248 | struct GNUNET_TESTING_NetjailNode *node = value; | ||
249 | struct GNUNET_TESTING_Barrier *barrier = free_barrier_node_cb_cls->barrier; | ||
250 | struct GNUNET_TESTING_Interpreter *is = free_barrier_node_cb_cls->is; | ||
251 | |||
252 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
253 | "free_barrier_node_cb\n"); | ||
254 | if (GNUNET_NO == is->finishing) | ||
255 | { | ||
256 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
257 | "TST_interpreter_send_barrier_crossable\n"); | ||
258 | TST_interpreter_send_barrier_crossable (is, | ||
259 | barrier->name, | ||
260 | node->node_number); | ||
261 | } | ||
262 | GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multishortmap_remove ( | ||
263 | barrier->nodes, key, node)); | ||
264 | return GNUNET_YES; | ||
265 | } | ||
266 | |||
267 | |||
268 | static int | ||
269 | free_barriers_cb (void *cls, | ||
270 | const struct GNUNET_ShortHashCode *key, | ||
271 | void *value) | ||
272 | { | ||
273 | struct GNUNET_TESTING_Interpreter *is = cls; | ||
274 | struct GNUNET_TESTING_Barrier *barrier = value; | ||
275 | struct CommandListEntry *pos; | ||
276 | struct FreeBarrierNodeCbCls *free_barrier_node_cb_cls; | ||
277 | |||
278 | if (NULL != barrier->nodes) | ||
279 | { | ||
280 | free_barrier_node_cb_cls = GNUNET_new (struct FreeBarrierNodeCbCls); | ||
281 | free_barrier_node_cb_cls->barrier = barrier; | ||
282 | free_barrier_node_cb_cls->is = is; | ||
283 | GNUNET_CONTAINER_multishortmap_iterate (barrier->nodes, | ||
284 | free_barrier_node_cb, | ||
285 | free_barrier_node_cb_cls); | ||
286 | GNUNET_CONTAINER_multishortmap_destroy (barrier->nodes); | ||
287 | barrier->nodes = NULL; | ||
288 | } | ||
289 | |||
290 | while (NULL != (pos = barrier->cmds_head)) | ||
291 | { | ||
292 | GNUNET_CONTAINER_DLL_remove (barrier->cmds_head, | ||
293 | barrier->cmds_tail, | ||
294 | pos); | ||
295 | GNUNET_free (pos); | ||
296 | } | ||
297 | GNUNET_free (barrier); | ||
298 | return GNUNET_YES; | ||
299 | } | ||
300 | |||
301 | |||
302 | /** | ||
303 | * Deleting all barriers create in the context of this interpreter. | ||
304 | * | ||
305 | * @param is The interpreter. | ||
306 | */ | ||
307 | static void | ||
308 | interpreter_delete_barriers (struct GNUNET_TESTING_Interpreter *is) | ||
309 | { | ||
310 | GNUNET_CONTAINER_multishortmap_iterate (is->barriers, | ||
311 | free_barriers_cb, | ||
312 | is); | ||
313 | GNUNET_CONTAINER_multishortmap_destroy (is->barriers); | ||
314 | } | ||
315 | |||
316 | |||
317 | /** | ||
318 | * Finish the test run, return the final result. | ||
319 | * | ||
320 | * @param cls the `struct GNUNET_TESTING_Interpreter` | ||
321 | */ | ||
322 | static void | ||
323 | finish_test (void *cls) | ||
324 | { | ||
325 | struct GNUNET_TESTING_Interpreter *is = cls; | ||
326 | struct GNUNET_TESTING_Command *cmd; | ||
327 | const char *label; | ||
328 | |||
329 | is->finishing = GNUNET_YES; | ||
330 | is->final_task = NULL; | ||
331 | label = is->commands[is->ip].label; | ||
332 | if (NULL == is->commands[is->ip].run) | ||
333 | label = "END"; | ||
334 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
335 | "Interpreter finishes at `%s' with status %d\n", | ||
336 | label, | ||
337 | is->result); | ||
338 | for (unsigned int j = 0; | ||
339 | NULL != (cmd = &is->commands[j])->run; | ||
340 | j++) | ||
341 | { | ||
342 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
343 | "Cleaning up cmd %s\n", | ||
344 | cmd->label); | ||
345 | cmd->cleanup (cmd->cls); | ||
346 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
347 | "Cleaned up cmd %s\n", | ||
348 | cmd->label); | ||
349 | } | ||
350 | if (NULL != is->task) | ||
351 | { | ||
352 | GNUNET_SCHEDULER_cancel (is->task); | ||
353 | is->task = NULL; | ||
354 | } | ||
355 | if (NULL != is->timeout_task) | ||
356 | { | ||
357 | GNUNET_SCHEDULER_cancel (is->timeout_task); | ||
358 | is->timeout_task = NULL; | ||
359 | } | ||
360 | GNUNET_free (is->commands); | ||
361 | is->rc (is->rc_cls, | ||
362 | is->result); | ||
363 | interpreter_delete_barriers (is); | ||
364 | GNUNET_free (is->helper); | ||
365 | GNUNET_free (is); | ||
366 | } | ||
367 | |||
368 | |||
369 | /** | ||
370 | * Run the main interpreter loop that performs exchange operations. | ||
371 | * | ||
372 | * @param cls contains the `struct InterpreterState` | ||
373 | */ | ||
374 | static void | ||
375 | interpreter_run (void *cls); | ||
376 | |||
377 | |||
378 | /** | ||
379 | * Current command is done, run the next one. | ||
380 | */ | ||
381 | static void | ||
382 | interpreter_next (void *cls) | ||
383 | { | ||
384 | struct GNUNET_TESTING_Interpreter *is = cls; | ||
385 | static unsigned long long ipc; | ||
386 | static struct GNUNET_TIME_Absolute last_report; | ||
387 | struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip]; | ||
388 | |||
389 | if (GNUNET_SYSERR == is->result) | ||
390 | return; /* ignore, we already failed! */ | ||
391 | cmd->finish_time = GNUNET_TIME_absolute_get (); | ||
392 | if ( (! GNUNET_TESTING_cmd_is_batch_ (cmd)) || | ||
393 | (! GNUNET_TESTING_cmd_batch_next_ (cmd->cls)) ) | ||
394 | is->ip++; | ||
395 | if (0 == (ipc % 1000)) | ||
396 | { | ||
397 | if (0 != ipc) | ||
398 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
399 | "Interpreter executed 1000 instructions in %s\n", | ||
400 | GNUNET_STRINGS_relative_time_to_string ( | ||
401 | GNUNET_TIME_absolute_get_duration (last_report), | ||
402 | GNUNET_YES)); | ||
403 | last_report = GNUNET_TIME_absolute_get (); | ||
404 | } | ||
405 | ipc++; | ||
406 | is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, | ||
407 | is); | ||
408 | } | ||
409 | |||
410 | |||
411 | void | ||
412 | GNUNET_TESTING_interpreter_fail (struct GNUNET_TESTING_Interpreter *is) | ||
413 | { | ||
414 | struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip]; | ||
415 | |||
416 | if (GNUNET_SYSERR == is->result) | ||
417 | { | ||
418 | GNUNET_break (0); | ||
419 | return; /* ignore, we already failed! */ | ||
420 | } | ||
421 | if (NULL != cmd) | ||
422 | { | ||
423 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
424 | "Failed at command `%s'\n", | ||
425 | cmd->label); | ||
426 | while (GNUNET_TESTING_cmd_is_batch_ (cmd)) | ||
427 | { | ||
428 | cmd = GNUNET_TESTING_cmd_batch_get_current_ (cmd); | ||
429 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
430 | "Failed in batch at command `%s'\n", | ||
431 | cmd->label); | ||
432 | } | ||
433 | } | ||
434 | else | ||
435 | { | ||
436 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
437 | "Failed with CMD being NULL!\n"); | ||
438 | } | ||
439 | is->result = GNUNET_SYSERR; | ||
440 | GNUNET_assert (NULL == is->final_task); | ||
441 | is->final_task = GNUNET_SCHEDULER_add_now (&finish_test, | ||
442 | is); | ||
443 | } | ||
444 | |||
445 | |||
446 | /** | ||
447 | * Returns the actual running command. | ||
448 | * | ||
449 | * @param is Global state of the interpreter, used by a command | ||
450 | * to access information about other commands. | ||
451 | * @return The actual running command. | ||
452 | */ | ||
453 | struct GNUNET_TESTING_Command * | ||
454 | GNUNET_TESTING_interpreter_get_current_command ( | ||
455 | struct GNUNET_TESTING_Interpreter *is) | ||
456 | { | ||
457 | return &is->commands[is->ip]; | ||
458 | } | ||
459 | |||
460 | |||
461 | const char * | ||
462 | GNUNET_TESTING_interpreter_get_current_label ( | ||
463 | struct GNUNET_TESTING_Interpreter *is) | ||
464 | { | ||
465 | struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip]; | ||
466 | |||
467 | return cmd->label; | ||
468 | } | ||
469 | |||
470 | |||
471 | /** | ||
472 | * Run the main interpreter loop. | ||
473 | * | ||
474 | * @param cls contains the `struct GNUNET_TESTING_Interpreter` | ||
475 | */ | ||
476 | static void | ||
477 | interpreter_run (void *cls) | ||
478 | { | ||
479 | struct GNUNET_TESTING_Interpreter *is = cls; | ||
480 | struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip]; | ||
481 | |||
482 | is->task = NULL; | ||
483 | if (NULL == cmd->run) | ||
484 | { | ||
485 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
486 | "Running command END\n"); | ||
487 | is->result = GNUNET_OK; | ||
488 | finish_test (is); | ||
489 | return; | ||
490 | } | ||
491 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
492 | "Running command `%s'\n", | ||
493 | cmd->label); | ||
494 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
495 | "start time of %p expected 0 is `%" PRIu64 "'\n", | ||
496 | cmd, | ||
497 | cmd->start_time.abs_value_us); | ||
498 | cmd->start_time | ||
499 | = cmd->last_req_time | ||
500 | = GNUNET_TIME_absolute_get (); | ||
501 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
502 | "start time of %p expected something is `%" PRIu64 "'\n", | ||
503 | cmd, | ||
504 | cmd->start_time.abs_value_us); | ||
505 | cmd->num_tries = 1; | ||
506 | if (NULL != cmd->ac) | ||
507 | { | ||
508 | cmd->ac->is = is; | ||
509 | cmd->ac->cont = &interpreter_next; | ||
510 | cmd->ac->cont_cls = is; | ||
511 | cmd->ac->finished = GNUNET_NO; | ||
512 | } | ||
513 | cmd->run (cmd->cls, | ||
514 | is); | ||
515 | if (NULL == cmd->ac) | ||
516 | { | ||
517 | interpreter_next (is); | ||
518 | } | ||
519 | else if ( (cmd->asynchronous_finish) && | ||
520 | (NULL != cmd->ac->cont) ) | ||
521 | { | ||
522 | cmd->ac->cont = NULL; | ||
523 | interpreter_next (is); | ||
524 | } | ||
525 | } | ||
526 | |||
527 | |||
528 | /** | ||
529 | * Function run when the test terminates (good or bad) with timeout. | ||
530 | * | ||
531 | * @param cls the interpreter state | ||
532 | */ | ||
533 | static void | ||
534 | do_timeout (void *cls) | ||
535 | { | ||
536 | struct GNUNET_TESTING_Interpreter *is = cls; | ||
537 | |||
538 | is->timeout_task = NULL; | ||
539 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
540 | "Terminating test due to global timeout\n"); | ||
541 | is->result = GNUNET_SYSERR; | ||
542 | finish_test (is); | ||
543 | } | ||
544 | |||
545 | |||
546 | enum GNUNET_GenericReturnValue | ||
547 | GNUNET_TESTING_running (const struct GNUNET_TESTING_Command *command) | ||
548 | { | ||
549 | return 0 != command->start_time.abs_value_us && 0 == | ||
550 | command->finish_time.abs_value_us; | ||
551 | } | ||
552 | |||
553 | |||
554 | enum GNUNET_GenericReturnValue | ||
555 | GNUNET_TESTING_finished (const struct GNUNET_TESTING_Command *command) | ||
556 | { | ||
557 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
558 | struct GNUNET_TIME_Relative diff = GNUNET_TIME_absolute_get_difference ( | ||
559 | command->finish_time, | ||
560 | now); | ||
561 | return 0 < diff.rel_value_us; | ||
562 | } | ||
563 | |||
564 | |||
565 | struct GNUNET_TESTING_Interpreter * | ||
566 | GNUNET_TESTING_run (const struct GNUNET_TESTING_Command *commands, | ||
567 | struct GNUNET_TIME_Relative timeout, | ||
568 | GNUNET_TESTING_ResultCallback rc, | ||
569 | void *rc_cls) | ||
570 | { | ||
571 | struct GNUNET_TESTING_Interpreter *is; | ||
572 | unsigned int i; | ||
573 | |||
574 | is = GNUNET_new (struct GNUNET_TESTING_Interpreter); | ||
575 | is->rc = rc; | ||
576 | is->rc_cls = rc_cls; | ||
577 | is->barriers = GNUNET_CONTAINER_multishortmap_create (1, | ||
578 | false); | ||
579 | /* get the number of commands */ | ||
580 | for (i = 0; NULL != commands[i].run; i++) | ||
581 | ; | ||
582 | is->cmds_n = i + 1; | ||
583 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
584 | "Got %u commands\n", | ||
585 | i); | ||
586 | is->commands = GNUNET_malloc_large ( (i + 1) | ||
587 | * sizeof (struct | ||
588 | GNUNET_TESTING_Command)); | ||
589 | GNUNET_assert (NULL != is->commands); | ||
590 | memcpy (is->commands, | ||
591 | commands, | ||
592 | sizeof (struct GNUNET_TESTING_Command) * i); | ||
593 | is->timeout_task | ||
594 | = GNUNET_SCHEDULER_add_delayed (timeout, | ||
595 | &do_timeout, | ||
596 | is); | ||
597 | is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, | ||
598 | is); | ||
599 | |||
600 | return is; | ||
601 | } | ||
602 | |||
603 | |||
604 | struct GNUNET_TESTING_Command | ||
605 | GNUNET_TESTING_command_new (void *cls, | ||
606 | const char *label, | ||
607 | GNUNET_TESTING_CommandRunRoutine run, | ||
608 | GNUNET_TESTING_CommandCleanupRoutine cleanup, | ||
609 | GNUNET_TESTING_CommandGetTraits traits, | ||
610 | struct GNUNET_TESTING_AsyncContext *ac) | ||
611 | { | ||
612 | struct GNUNET_TESTING_Command cmd = { | ||
613 | .cls = cls, | ||
614 | .run = run, | ||
615 | .ac = ac, | ||
616 | .cleanup = cleanup, | ||
617 | .traits = traits | ||
618 | }; | ||
619 | |||
620 | GNUNET_assert (NULL != run); | ||
621 | if (NULL != label) | ||
622 | { | ||
623 | GNUNET_assert (strlen (label) <= | ||
624 | GNUNET_TESTING_CMD_MAX_LABEL_LENGTH); | ||
625 | strncpy (cmd.label, | ||
626 | label, | ||
627 | GNUNET_TESTING_CMD_MAX_LABEL_LENGTH); | ||
628 | } | ||
629 | return cmd; | ||
630 | } | ||
631 | |||
632 | |||
633 | struct GNUNET_TESTING_Command | ||
634 | GNUNET_TESTING_cmd_end (void) | ||
635 | { | ||
636 | struct GNUNET_TESTING_Command cmd = { | ||
637 | .run = NULL | ||
638 | }; | ||
639 | |||
640 | return cmd; | ||
641 | } | ||
642 | |||
643 | |||
644 | /** | ||
645 | * Closure for #loop_run(). | ||
646 | */ | ||
647 | struct MainParams | ||
648 | { | ||
649 | |||
650 | /** | ||
651 | * NULL-label terminated array of commands. | ||
652 | */ | ||
653 | struct GNUNET_TESTING_Command *commands; | ||
654 | |||
655 | /** | ||
656 | * Global timeout for the test. | ||
657 | */ | ||
658 | struct GNUNET_TIME_Relative timeout; | ||
659 | |||
660 | /** | ||
661 | * Set to #EXIT_FAILURE on error. | ||
662 | */ | ||
663 | int rv; | ||
664 | }; | ||
665 | |||
666 | |||
667 | /** | ||
668 | * Function called with the final result of the test. | ||
669 | * | ||
670 | * @param cls the `struct MainParams` | ||
671 | * @param rv #GNUNET_OK if the test passed | ||
672 | */ | ||
673 | static void | ||
674 | handle_result (void *cls, | ||
675 | enum GNUNET_GenericReturnValue rv) | ||
676 | { | ||
677 | struct MainParams *mp = cls; | ||
678 | |||
679 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
680 | "Test exits with status %d\n", | ||
681 | rv); | ||
682 | if (GNUNET_OK != rv) | ||
683 | mp->rv = EXIT_FAILURE; | ||
684 | GNUNET_SCHEDULER_shutdown (); | ||
685 | } | ||
686 | |||
687 | |||
688 | /** | ||
689 | * Main function to run the test cases. | ||
690 | * | ||
691 | * @param cls a `struct MainParams *` | ||
692 | */ | ||
693 | static void | ||
694 | loop_run (void *cls) | ||
695 | { | ||
696 | struct MainParams *mp = cls; | ||
697 | |||
698 | GNUNET_TESTING_run (mp->commands, | ||
699 | mp->timeout, | ||
700 | &handle_result, | ||
701 | mp); | ||
702 | } | ||
703 | |||
704 | |||
705 | /** | ||
706 | * Continuation function from GNUNET_HELPER_send() | ||
707 | * | ||
708 | * @param cls closure | ||
709 | * @param result #GNUNET_OK on success, | ||
710 | * #GNUNET_NO if helper process died | ||
711 | * #GNUNET_SYSERR during GNUNET_HELPER_stop() | ||
712 | */ | ||
713 | static void | ||
714 | clear_msg (void *cls, int result) | ||
715 | { | ||
716 | GNUNET_assert (GNUNET_YES == result); | ||
717 | } | ||
718 | |||
719 | |||
720 | /** | ||
721 | * Adding a helper handle to the interpreter. | ||
722 | * | ||
723 | * @param is The interpreter. | ||
724 | * @param helper The helper handle. | ||
725 | */ | ||
726 | void | ||
727 | GNUNET_TESTING_add_netjail_helper (struct GNUNET_TESTING_Interpreter *is, | ||
728 | const struct GNUNET_HELPER_Handle *helper) | ||
729 | { | ||
730 | GNUNET_array_append (is->helper, is->n_helper, helper); | ||
731 | } | ||
732 | |||
733 | |||
734 | /** | ||
735 | * Send Message to netjail nodes. | ||
736 | * | ||
737 | * @param is The interpreter. | ||
738 | * @param global_node_number The netjail node to inform. | ||
739 | * @param header The message to send. | ||
740 | */ | ||
741 | void | ||
742 | send_message_to_netjail (struct GNUNET_TESTING_Interpreter *is, | ||
743 | unsigned int global_node_number, | ||
744 | struct GNUNET_MessageHeader *header) | ||
745 | { | ||
746 | const struct GNUNET_HELPER_Handle *helper; | ||
747 | |||
748 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
749 | "send message of type %u to locals\n", | ||
750 | ntohs (header->type)); | ||
751 | helper = is->helper[global_node_number - 1]; | ||
752 | struct GNUNET_HELPER_SendHandle *sh = GNUNET_HELPER_send ( | ||
753 | (struct GNUNET_HELPER_Handle *) helper, | ||
754 | header, | ||
755 | GNUNET_NO, | ||
756 | &clear_msg, | ||
757 | NULL); | ||
758 | } | ||
759 | |||
760 | |||
761 | void | ||
762 | TST_interpreter_send_barrier_crossable (struct GNUNET_TESTING_Interpreter *is, | ||
763 | const char *barrier_name, | ||
764 | unsigned int global_node_number) | ||
765 | { | ||
766 | struct CommandBarrierCrossable *adm; | ||
767 | size_t msg_length; | ||
768 | size_t name_len; | ||
769 | |||
770 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
771 | "send barrier name %s barrier_name\n", | ||
772 | barrier_name); | ||
773 | name_len = strlen (barrier_name); | ||
774 | msg_length = sizeof(struct CommandBarrierCrossable) + name_len + 1; | ||
775 | adm = GNUNET_malloc (msg_length); | ||
776 | adm->header.type = htons (GNUNET_MESSAGE_TYPE_CMDS_HELPER_BARRIER_CROSSABLE); | ||
777 | adm->header.size = htons ((uint16_t) msg_length); | ||
778 | memcpy (&adm[1], barrier_name, name_len); | ||
779 | send_message_to_netjail (is, | ||
780 | global_node_number, | ||
781 | &adm->header); | ||
782 | GNUNET_free (adm); | ||
783 | } | ||
784 | |||
785 | |||
786 | /** | ||
787 | * Getting a barrier from the interpreter. | ||
788 | * | ||
789 | * @param is The interpreter. | ||
790 | * @param barrier_name The name of the barrier. | ||
791 | * @return The barrier. | ||
792 | */ | ||
793 | struct GNUNET_TESTING_Barrier * | ||
794 | TST_interpreter_get_barrier (struct GNUNET_TESTING_Interpreter *is, | ||
795 | const char *barrier_name) | ||
796 | { | ||
797 | struct GNUNET_HashCode hc; | ||
798 | struct GNUNET_ShortHashCode create_key; | ||
799 | struct GNUNET_TESTING_Barrier *barrier; | ||
800 | |||
801 | GNUNET_CRYPTO_hash (barrier_name, strlen (barrier_name), &hc); | ||
802 | memcpy (&create_key, | ||
803 | &hc, | ||
804 | sizeof (create_key)); | ||
805 | barrier = GNUNET_CONTAINER_multishortmap_get (is->barriers, &create_key); | ||
806 | return barrier; | ||
807 | } | ||
808 | |||
809 | |||
810 | /** | ||
811 | * Finish all "barrier reached" comands attached to this barrier. | ||
812 | * | ||
813 | * @param barrier The barrier in question. | ||
814 | */ | ||
815 | void | ||
816 | TST_interpreter_finish_attached_cmds (struct GNUNET_TESTING_Interpreter *is, | ||
817 | const char *barrier_name) | ||
818 | { | ||
819 | struct CommandListEntry *pos; | ||
820 | struct GNUNET_TESTING_Barrier *barrier = TST_interpreter_get_barrier (is, | ||
821 | barrier_name); | ||
822 | |||
823 | while (NULL != barrier && NULL != (pos = barrier->cmds_head)) | ||
824 | { | ||
825 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
826 | "command label %s\n", | ||
827 | pos->command->label); | ||
828 | if (GNUNET_NO == pos->command->ac->finished && | ||
829 | GNUNET_NO == pos->command->asynchronous_finish) | ||
830 | { | ||
831 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
832 | "command label %s finish\n", | ||
833 | pos->command->label); | ||
834 | GNUNET_TESTING_async_finish (pos->command->ac); | ||
835 | } | ||
836 | else if (GNUNET_NO == pos->command->ac->finished) | ||
837 | { | ||
838 | pos->command->asynchronous_finish = GNUNET_YES; | ||
839 | } | ||
840 | GNUNET_CONTAINER_DLL_remove (barrier->cmds_head, | ||
841 | barrier->cmds_tail, | ||
842 | pos); | ||
843 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
844 | "command entry label %s removed\n", | ||
845 | pos->command->label); | ||
846 | GNUNET_free (pos); | ||
847 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
848 | "command entry freed\n"); | ||
849 | } | ||
850 | if (NULL != barrier->nodes) | ||
851 | { | ||
852 | struct FreeBarrierNodeCbCls free_barrier_node_cb_cls = { | ||
853 | .barrier = barrier, | ||
854 | .is = is | ||
855 | }; | ||
856 | |||
857 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
858 | "freeing nodes\n"); | ||
859 | GNUNET_CONTAINER_multishortmap_iterate (barrier->nodes, | ||
860 | &free_barrier_node_cb, | ||
861 | &free_barrier_node_cb_cls); | ||
862 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
863 | "nodes freed\n"); | ||
864 | GNUNET_CONTAINER_multishortmap_destroy (barrier->nodes); | ||
865 | barrier->nodes = NULL; | ||
866 | } | ||
867 | } | ||
868 | |||
869 | |||
870 | /** | ||
871 | * Add a barrier to the loop. | ||
872 | * | ||
873 | * @param is The interpreter. | ||
874 | * @param barrier The barrier to add. | ||
875 | */ | ||
876 | void | ||
877 | TST_interpreter_add_barrier (struct GNUNET_TESTING_Interpreter *is, | ||
878 | struct GNUNET_TESTING_Barrier *barrier) | ||
879 | { | ||
880 | struct GNUNET_HashCode hc; | ||
881 | struct GNUNET_ShortHashCode create_key; | ||
882 | |||
883 | GNUNET_CRYPTO_hash (barrier->name, strlen (barrier->name), &hc); | ||
884 | memcpy (&create_key, | ||
885 | &hc, | ||
886 | sizeof (create_key)); | ||
887 | GNUNET_CONTAINER_multishortmap_put (is->barriers, | ||
888 | &create_key, | ||
889 | barrier, | ||
890 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); | ||
891 | } | ||
892 | |||
893 | |||
894 | int | ||
895 | GNUNET_TESTING_main (struct GNUNET_TESTING_Command *commands, | ||
896 | struct GNUNET_TIME_Relative timeout) | ||
897 | { | ||
898 | struct MainParams mp = { | ||
899 | .commands = commands, | ||
900 | .timeout = timeout, | ||
901 | .rv = EXIT_SUCCESS | ||
902 | }; | ||
903 | |||
904 | GNUNET_SCHEDULER_run (&loop_run, | ||
905 | &mp); | ||
906 | return mp.rv; | ||
907 | } | ||
908 | |||
909 | |||
910 | void | ||
911 | GNUNET_TESTING_async_fail (struct GNUNET_TESTING_AsyncContext *ac) | ||
912 | { | ||
913 | GNUNET_assert (GNUNET_NO == ac->finished); | ||
914 | ac->finished = GNUNET_SYSERR; | ||
915 | GNUNET_TESTING_interpreter_fail (ac->is); | ||
916 | if (NULL != ac->cont) | ||
917 | { | ||
918 | ac->cont (ac->cont_cls); | ||
919 | ac->cont = NULL; | ||
920 | } | ||
921 | } | ||
922 | |||
923 | |||
924 | void | ||
925 | GNUNET_TESTING_async_finish (struct GNUNET_TESTING_AsyncContext *ac) | ||
926 | { | ||
927 | GNUNET_assert (GNUNET_NO == ac->finished); | ||
928 | ac->finished = GNUNET_OK; | ||
929 | if (NULL != ac->cont) | ||
930 | { | ||
931 | ac->cont (ac->cont_cls); | ||
932 | ac->cont = NULL; | ||
933 | } | ||
934 | } | ||
935 | |||
936 | |||
937 | /* end of testing_api_loop.c */ | ||