diff options
Diffstat (limited to 'src/testing/testing_api_cmd_finish.c')
-rw-r--r-- | src/testing/testing_api_cmd_finish.c | 198 |
1 files changed, 198 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..3ac0871a5 --- /dev/null +++ b/src/testing/testing_api_cmd_finish.c | |||
@@ -0,0 +1,198 @@ | |||
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 | * Function to call when done. | ||
52 | */ | ||
53 | struct GNUNET_TESTING_AsyncContext ac; | ||
54 | |||
55 | /** | ||
56 | * How long to wait until finish fails hard? | ||
57 | */ | ||
58 | struct GNUNET_TIME_Relative timeout; | ||
59 | |||
60 | }; | ||
61 | |||
62 | |||
63 | /** | ||
64 | * Function called when the command we are waiting on | ||
65 | * is finished. Hence we are finished, too. | ||
66 | * | ||
67 | * @param cls a `struct FinishState` being notified | ||
68 | */ | ||
69 | static void | ||
70 | done_finish (void *cls) | ||
71 | { | ||
72 | struct FinishState *finish_state = cls; | ||
73 | |||
74 | GNUNET_SCHEDULER_cancel (finish_state->finish_task); | ||
75 | finish_state->finish_task = NULL; | ||
76 | GNUNET_TESTING_async_finish (&finish_state->ac); | ||
77 | } | ||
78 | |||
79 | |||
80 | /** | ||
81 | * Function triggered if the command we are waiting | ||
82 | * for did not complete on time. | ||
83 | * | ||
84 | * @param cls our `struct FinishState` | ||
85 | */ | ||
86 | static void | ||
87 | timeout_finish (void *cls) | ||
88 | { | ||
89 | struct FinishState *finish_state = cls; | ||
90 | |||
91 | finish_state->finish_task = NULL; | ||
92 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
93 | "Timeout waiting for command `%s' to finish\n", | ||
94 | finish_state->async_label); | ||
95 | GNUNET_TESTING_async_fail (&finish_state->ac); | ||
96 | } | ||
97 | |||
98 | |||
99 | /** | ||
100 | * Run method of the command created by the interpreter to wait for another | ||
101 | * command to finish. | ||
102 | * | ||
103 | */ | ||
104 | static void | ||
105 | run_finish (void *cls, | ||
106 | struct GNUNET_TESTING_Interpreter *is) | ||
107 | { | ||
108 | struct FinishState *finish_state = cls; | ||
109 | const struct GNUNET_TESTING_Command *async_cmd; | ||
110 | struct GNUNET_TESTING_AsyncContext *aac; | ||
111 | |||
112 | async_cmd | ||
113 | = GNUNET_TESTING_interpreter_lookup_command (is, | ||
114 | finish_state->async_label); | ||
115 | if (NULL == async_cmd) | ||
116 | { | ||
117 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
118 | "Did not find command `%s'\n", | ||
119 | finish_state->async_label); | ||
120 | GNUNET_TESTING_interpreter_fail (is); | ||
121 | return; | ||
122 | } | ||
123 | if ( (NULL == (aac = async_cmd->ac)) || | ||
124 | (! async_cmd->asynchronous_finish) ) | ||
125 | { | ||
126 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
127 | "Cannot finish `%s': not asynchronous\n", | ||
128 | finish_state->async_label); | ||
129 | GNUNET_TESTING_interpreter_fail (is); | ||
130 | return; | ||
131 | } | ||
132 | if (GNUNET_NO != aac->finished) | ||
133 | { | ||
134 | /* Command is already finished, so are we! */ | ||
135 | GNUNET_TESTING_async_finish (&finish_state->ac); | ||
136 | return; | ||
137 | } | ||
138 | finish_state->finish_task | ||
139 | = GNUNET_SCHEDULER_add_delayed (finish_state->timeout, | ||
140 | &timeout_finish, | ||
141 | finish_state); | ||
142 | aac->cont = &done_finish; | ||
143 | aac->cont_cls = finish_state; | ||
144 | } | ||
145 | |||
146 | |||
147 | /** | ||
148 | * Cleanup state of a finish command. | ||
149 | * | ||
150 | * @param cls a `struct FinishState` to clean up | ||
151 | */ | ||
152 | static void | ||
153 | cleanup_finish (void *cls) | ||
154 | { | ||
155 | struct FinishState *finish_state = cls; | ||
156 | |||
157 | if (NULL != finish_state->finish_task) | ||
158 | { | ||
159 | GNUNET_SCHEDULER_cancel (finish_state->finish_task); | ||
160 | finish_state->finish_task = NULL; | ||
161 | } | ||
162 | GNUNET_free (finish_state); | ||
163 | } | ||
164 | |||
165 | |||
166 | const struct GNUNET_TESTING_Command | ||
167 | GNUNET_TESTING_cmd_finish (const char *finish_label, | ||
168 | const char *cmd_ref, | ||
169 | struct GNUNET_TIME_Relative timeout) | ||
170 | { | ||
171 | struct FinishState *finish_state; | ||
172 | |||
173 | finish_state = GNUNET_new (struct FinishState); | ||
174 | finish_state->async_label = cmd_ref; | ||
175 | finish_state->timeout = timeout; | ||
176 | { | ||
177 | struct GNUNET_TESTING_Command cmd = { | ||
178 | .cls = finish_state, | ||
179 | .label = finish_label, | ||
180 | .run = &run_finish, | ||
181 | .ac = &finish_state->ac, | ||
182 | .cleanup = &cleanup_finish | ||
183 | }; | ||
184 | |||
185 | return cmd; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | |||
190 | struct GNUNET_TESTING_Command | ||
191 | GNUNET_TESTING_cmd_make_unblocking (struct GNUNET_TESTING_Command cmd) | ||
192 | { | ||
193 | /* do not permit this function to be used on | ||
194 | a finish command! */ | ||
195 | GNUNET_assert (cmd.run != &run_finish); | ||
196 | cmd.asynchronous_finish = true; | ||
197 | return cmd; | ||
198 | } | ||