diff options
Diffstat (limited to 'src/testing/testing_api_loop.c')
-rw-r--r-- | src/testing/testing_api_loop.c | 606 |
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 | */ | ||
37 | struct 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 | |||
90 | const struct GNUNET_TESTING_Command * | ||
91 | get_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 | */ | ||
168 | const struct GNUNET_TESTING_Command * | ||
169 | GNUNET_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 | */ | ||
185 | const struct GNUNET_TESTING_Command * | ||
186 | GNUNET_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 | */ | ||
202 | const struct GNUNET_TESTING_Command * | ||
203 | GNUNET_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 | */ | ||
221 | static void | ||
222 | finish_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 | */ | ||
270 | static void | ||
271 | interpreter_run (void *cls); | ||
272 | |||
273 | |||
274 | /** | ||
275 | * Current command is done, run the next one. | ||
276 | */ | ||
277 | static void | ||
278 | interpreter_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 | |||
307 | void | ||
308 | GNUNET_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 | */ | ||
349 | struct GNUNET_TESTING_Command * | ||
350 | GNUNET_TESTING_interpreter_get_current_command ( | ||
351 | struct GNUNET_TESTING_Interpreter *is) | ||
352 | { | ||
353 | return &is->commands[is->ip]; | ||
354 | } | ||
355 | |||
356 | const char * | ||
357 | GNUNET_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 | */ | ||
371 | static void | ||
372 | interpreter_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 | */ | ||
428 | static void | ||
429 | do_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 | */ | ||
447 | enum GNUNET_GenericReturnValue | ||
448 | GNUNET_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 | */ | ||
461 | enum GNUNET_GenericReturnValue | ||
462 | GNUNET_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 | |||
472 | void | ||
473 | GNUNET_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 | */ | ||
505 | struct 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 | */ | ||
531 | static void | ||
532 | handle_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 | */ | ||
551 | static void | ||
552 | loop_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 | |||
563 | int | ||
564 | GNUNET_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 | |||
579 | void | ||
580 | GNUNET_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 | |||
593 | void | ||
594 | GNUNET_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 */ | ||