diff options
Diffstat (limited to 'src/testing/testing_api_cmd_finish.c')
-rw-r--r-- | src/testing/testing_api_cmd_finish.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/src/testing/testing_api_cmd_finish.c b/src/testing/testing_api_cmd_finish.c new file mode 100644 index 000000000..2bcefd803 --- /dev/null +++ b/src/testing/testing_api_cmd_finish.c | |||
@@ -0,0 +1,240 @@ | |||
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 | * @file testing/testing_api_cmd_finish.c | ||
22 | * @brief command to wait for completion of async command | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_util_lib.h" | ||
27 | #include "gnunet_testing_ng_lib.h" | ||
28 | |||
29 | /** | ||
30 | * Struct to use for command-specific context information closure of a command waiting | ||
31 | * for another command. | ||
32 | */ | ||
33 | struct FinishState | ||
34 | { | ||
35 | /** | ||
36 | * Closure for all commands with command-specific context information. | ||
37 | */ | ||
38 | void *cls; | ||
39 | |||
40 | /** | ||
41 | * Label of the asynchronous command the synchronous command of this closure waits for. | ||
42 | */ | ||
43 | const char *async_label; | ||
44 | |||
45 | /** | ||
46 | * Task for running the finish method of the asynchronous task the command is waiting for. | ||
47 | */ | ||
48 | struct GNUNET_SCHEDULER_Task *finish_task; | ||
49 | |||
50 | /** | ||
51 | * Interpreter we are part of. | ||
52 | */ | ||
53 | struct GNUNET_TESTING_Interpreter *is; | ||
54 | |||
55 | /** | ||
56 | * Function to call when done. | ||
57 | */ | ||
58 | GNUNET_SCHEDULER_TaskCallback cont; | ||
59 | |||
60 | /** | ||
61 | * Closure for @e cont. | ||
62 | */ | ||
63 | void *cont_cls; | ||
64 | |||
65 | /** | ||
66 | * How long to wait until finish fails hard? | ||
67 | */ | ||
68 | struct GNUNET_TIME_Relative timeout; | ||
69 | |||
70 | /** | ||
71 | * Set to #GNUNET_OK if the @a async_label command finished on time | ||
72 | */ | ||
73 | enum GNUNET_GenericReturnValue finished; | ||
74 | |||
75 | }; | ||
76 | |||
77 | |||
78 | /** | ||
79 | */ | ||
80 | static void | ||
81 | done_finish (void *cls) | ||
82 | { | ||
83 | struct FinishState *finish_state = cls; | ||
84 | |||
85 | GNUNET_SCHEDULER_cancel (finish_state->finish_task); | ||
86 | finish_state->finish_task = NULL; | ||
87 | finish_state->finished = GNUNET_YES; | ||
88 | if (NULL != finish_state->cont) | ||
89 | { | ||
90 | finish_state->cont (finish_state->cont_cls); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | |||
95 | /** | ||
96 | */ | ||
97 | static void | ||
98 | timeout_finish (void *cls) | ||
99 | { | ||
100 | struct FinishState *finish_state = cls; | ||
101 | |||
102 | finish_state->finish_task = NULL; | ||
103 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
104 | "Timeout waiting for command `%s' to finish\n", | ||
105 | finish_state->async_label); | ||
106 | finish_state->finished = GNUNET_SYSERR; | ||
107 | GNUNET_TESTING_interpreter_fail (finish_state->is); | ||
108 | } | ||
109 | |||
110 | |||
111 | /** | ||
112 | * Run method of the command created by the interpreter to wait for another | ||
113 | * command to finish. | ||
114 | * | ||
115 | */ | ||
116 | static void | ||
117 | run_finish_on_ref (void *cls, | ||
118 | struct GNUNET_TESTING_Interpreter *is) | ||
119 | { | ||
120 | struct FinishState *finish_state = cls; | ||
121 | const struct GNUNET_TESTING_Command *async_cmd; | ||
122 | |||
123 | finish_state->is = is; | ||
124 | async_cmd | ||
125 | = GNUNET_TESTING_interpreter_lookup_command (is, | ||
126 | finish_state->async_label); | ||
127 | if (NULL == async_cmd) | ||
128 | { | ||
129 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
130 | "Did not find command `%s'\n", | ||
131 | finish_state->async_label); | ||
132 | GNUNET_TESTING_interpreter_fail (is); | ||
133 | return; | ||
134 | } | ||
135 | if ( (NULL == async_cmd->finish) || | ||
136 | (! async_cmd->asynchronous_finish) ) | ||
137 | { | ||
138 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
139 | "Cannot finish `%s': not asynchronous\n", | ||
140 | finish_state->async_label); | ||
141 | GNUNET_TESTING_interpreter_fail (is); | ||
142 | return; | ||
143 | } | ||
144 | finish_state->finish_task | ||
145 | = GNUNET_SCHEDULER_add_delayed (finish_state->timeout, | ||
146 | &timeout_finish, | ||
147 | finish_state); | ||
148 | async_cmd->finish (async_cmd->cls, | ||
149 | &done_finish, | ||
150 | finish_state); | ||
151 | } | ||
152 | |||
153 | |||
154 | /** | ||
155 | * Wait for any asynchronous execution of @e run to conclude, | ||
156 | * then call finish_cont. Finish may only be called once per command. | ||
157 | * | ||
158 | * This member may be NULL if this command is a synchronous command, | ||
159 | * and also should be set to NULL once the command has finished. | ||
160 | * | ||
161 | * @param cls closure | ||
162 | * @param cont function to call upon completion, can be NULL | ||
163 | * @param cont_cls closure for @a cont | ||
164 | * @return | ||
165 | * #GNUNET_NO if the command is still running and @a cont will be called later | ||
166 | * #GNUNET_OK if the command completed successfully and @a cont was called | ||
167 | * #GNUNET_SYSERR if the operation @a cont was NOT called | ||
168 | */ | ||
169 | static enum GNUNET_GenericReturnValue | ||
170 | finish_finish_on_ref (void *cls, | ||
171 | GNUNET_SCHEDULER_TaskCallback cont, | ||
172 | void *cont_cls) | ||
173 | { | ||
174 | struct FinishState *finish_state = cls; | ||
175 | |||
176 | switch (finish_state->finished) | ||
177 | { | ||
178 | case GNUNET_OK: | ||
179 | cont (cont_cls); | ||
180 | break; | ||
181 | case GNUNET_SYSERR: | ||
182 | GNUNET_break (0); | ||
183 | break; | ||
184 | case GNUNET_NO: | ||
185 | if (NULL != finish_state->cont) | ||
186 | { | ||
187 | GNUNET_break (0); | ||
188 | return GNUNET_SYSERR; | ||
189 | } | ||
190 | finish_state->cont = cont; | ||
191 | finish_state->cont_cls = cont_cls; | ||
192 | break; | ||
193 | } | ||
194 | return finish_state->finished; | ||
195 | } | ||
196 | |||
197 | |||
198 | /** | ||
199 | * Create (synchronous) command that waits for another command to finish. | ||
200 | * If @a cmd_ref did not finish after @a timeout, this command will fail | ||
201 | * the test case. | ||
202 | * | ||
203 | * @param finish_label label for this command | ||
204 | * @param cmd_ref reference to a previous command which we should | ||
205 | * wait for (call `finish()` on) | ||
206 | * @param timeout how long to wait at most for @a cmd_ref to finish | ||
207 | * @return a finish-command. | ||
208 | */ | ||
209 | const struct GNUNET_TESTING_Command | ||
210 | GNUNET_TESTING_cmd_finish (const char *finish_label, | ||
211 | const char *cmd_ref, | ||
212 | struct GNUNET_TIME_Relative timeout) | ||
213 | { | ||
214 | struct FinishState *finish_state; | ||
215 | |||
216 | finish_state = GNUNET_new (struct FinishState); | ||
217 | finish_state->async_label = cmd_ref; | ||
218 | finish_state->timeout = timeout; | ||
219 | { | ||
220 | struct GNUNET_TESTING_Command cmd = { | ||
221 | .cls = finish_state, | ||
222 | .label = finish_label, | ||
223 | .run = &run_finish_on_ref, | ||
224 | .finish = &finish_finish_on_ref | ||
225 | }; | ||
226 | |||
227 | return cmd; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | |||
232 | struct GNUNET_TESTING_Command | ||
233 | GNUNET_TESTING_cmd_make_unblocking (struct GNUNET_TESTING_Command cmd) | ||
234 | { | ||
235 | /* do not permit this function to be used on | ||
236 | a finish command! */ | ||
237 | GNUNET_assert (cmd.run != &run_finish_on_ref); | ||
238 | cmd.asynchronous_finish = true; | ||
239 | return cmd; | ||
240 | } | ||