error handling
[oweals/gnunet.git] / src / util / test_mq.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012, 2018 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 util/test_mq.c
23  * @brief tests for mq
24  * @author Florian Dold
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29
30 #define NUM_TRANSMISSIONS 500
31
32 /**
33  * How long does the receiver take per message?
34  */
35 #define RECEIVER_THROTTLE GNUNET_TIME_relative_multiply ( \
36     GNUNET_TIME_UNIT_MILLISECONDS, 1)
37
38 static unsigned int received_cnt;
39
40
41 GNUNET_NETWORK_STRUCT_BEGIN
42
43 struct MyMessage
44 {
45   struct GNUNET_MessageHeader header;
46   uint32_t x GNUNET_PACKED;
47 };
48
49 GNUNET_NETWORK_STRUCT_END
50
51 static int global_ret;
52
53 static struct GNUNET_SCHEDULER_Task *tt;
54
55 static struct GNUNET_SCHEDULER_Task *dt;
56
57 static struct GNUNET_MQ_Handle *cmq;
58
59
60 static void
61 do_shutdown (void *cls)
62 {
63   (void) cls;
64   if (NULL != tt)
65   {
66     GNUNET_SCHEDULER_cancel (tt);
67     tt = NULL;
68   }
69   if (NULL != cmq)
70   {
71     GNUNET_MQ_destroy (cmq);
72     cmq = NULL;
73   }
74 }
75
76
77 static void
78 do_timeout (void *cls)
79 {
80   (void) cls;
81   tt = NULL;
82   GNUNET_SCHEDULER_shutdown ();
83   global_ret = 1;
84 }
85
86
87 /**
88  * Generic error handler, called with the appropriate
89  * error code and the same closure specified at the creation of
90  * the message queue.
91  * Not every message queue implementation supports an error handler.
92  *
93  * @param cls closure
94  * @param error error code
95  */
96 static void
97 error_cb (void *cls,
98           enum GNUNET_MQ_Error error)
99 {
100   GNUNET_break (0);
101   global_ret = 3;
102   GNUNET_SCHEDULER_shutdown ();
103 }
104
105
106 static void
107 client_continue (void *cls)
108 {
109   struct GNUNET_SERVICE_Client *c = cls;
110
111   dt = NULL;
112   GNUNET_SERVICE_client_continue (c);
113 }
114
115
116 static void
117 handle_dummy (void *cls,
118               const struct MyMessage *msg)
119 {
120   struct GNUNET_SERVICE_Client *c = cls;
121
122   GNUNET_assert (NULL == dt);
123   /* artificially make receiver slower than sender */
124   dt = GNUNET_SCHEDULER_add_delayed (RECEIVER_THROTTLE,
125                                      &client_continue,
126                                      c);
127   if (received_cnt != ntohl (msg->x))
128   {
129     GNUNET_break (0);
130     global_ret = 4;
131     GNUNET_SCHEDULER_shutdown ();
132   }
133   received_cnt++;
134 }
135
136
137 static void
138 handle_dummy2 (void *cls,
139                const struct MyMessage *msg)
140 {
141   struct GNUNET_SERVICE_Client *c = cls;
142
143   GNUNET_SERVICE_client_continue (c);
144   if (NUM_TRANSMISSIONS != received_cnt)
145   {
146     GNUNET_break (0);
147     global_ret = 5;
148   }
149   GNUNET_SCHEDULER_shutdown ();
150 }
151
152
153 /**
154  * Function called whenever MQ has sent a message.
155  */
156 static void
157 notify_sent_cb (void *cls)
158 {
159   static unsigned int seen;
160   unsigned int *cnt = cls;
161
162   if (seen != *cnt)
163   {
164     GNUNET_break (0);
165     global_ret = 6;
166     GNUNET_SCHEDULER_shutdown ();
167   }
168   seen++;
169   GNUNET_free (cnt);
170 }
171
172
173 /**
174  * Start running the actual test.
175  *
176  * @param cls closure passed to #GNUNET_SERVICE_MAIN
177  * @param cfg configuration to use for this service
178  * @param sh handle to the newly create service
179  */
180 static void
181 run (void *cls,
182      const struct GNUNET_CONFIGURATION_Handle *cfg,
183      struct GNUNET_SERVICE_Handle *sh)
184 {
185   struct GNUNET_MQ_MessageHandler ch[] = {
186     GNUNET_MQ_handler_end ()
187   };
188   struct GNUNET_MQ_Envelope *env;
189   struct MyMessage *m;
190
191   (void) cls;
192   (void) sh;
193   cmq = GNUNET_CLIENT_connect (cfg,
194                                "test_client",
195                                ch,
196                                &error_cb,
197                                NULL);
198   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
199                                  NULL);
200   tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
201                                      &do_timeout,
202                                      NULL);
203   for (unsigned int i = 0; i < NUM_TRANSMISSIONS; i++)
204   {
205     unsigned int *cnt;
206
207     cnt = GNUNET_new (unsigned int);
208     *cnt = i;
209     env = GNUNET_MQ_msg (m,
210                          GNUNET_MESSAGE_TYPE_DUMMY);
211     GNUNET_MQ_notify_sent (env,
212                            &notify_sent_cb,
213                            cnt);
214     m->x = htonl (i);
215     GNUNET_MQ_send (cmq,
216                     env);
217   }
218   env = GNUNET_MQ_msg (m,
219                        GNUNET_MESSAGE_TYPE_DUMMY2);
220   GNUNET_MQ_send (cmq,
221                   env);
222 }
223
224
225 /**
226  * Callback to be called when a client connects to the service.
227  *
228  * @param cls closure for the service
229  * @param c the new client that connected to the service
230  * @param mq the message queue used to send messages to the client
231  * @return the client-specific (`internal') closure
232  */
233 static void *
234 connect_cb (void *cls,
235             struct GNUNET_SERVICE_Client *c,
236             struct GNUNET_MQ_Handle *mq)
237 {
238   (void) cls;
239   (void) mq;
240   return c;
241 }
242
243
244 /**
245  * Callback to be called when a client disconnected from the service
246  *
247  * @param cls closure for the service
248  * @param c the client that disconnected
249  * @param internal_cls the client-specific (`internal') closure
250  */
251 static void
252 disconnect_cb (void *cls,
253                struct GNUNET_SERVICE_Client *c,
254                void *internal_cls)
255 {
256   (void) cls;
257   (void) c;
258   (void) internal_cls;
259 }
260
261
262 static void
263 test1 ()
264 {
265   struct GNUNET_MQ_Envelope *mqm;
266   struct MyMessage *mm;
267
268   mm = NULL;
269   mqm = NULL;
270
271   mqm = GNUNET_MQ_msg (mm,
272                        GNUNET_MESSAGE_TYPE_DUMMY);
273   GNUNET_assert (NULL != mqm);
274   GNUNET_assert (NULL != mm);
275   GNUNET_assert (GNUNET_MESSAGE_TYPE_DUMMY == ntohs (mm->header.type));
276   GNUNET_assert (sizeof(struct MyMessage) == ntohs (mm->header.size));
277   GNUNET_MQ_discard (mqm);
278 }
279
280
281 static void
282 test2 ()
283 {
284   struct GNUNET_MQ_Envelope *mqm;
285   struct GNUNET_MessageHeader *mh;
286
287   mqm = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_DUMMY);
288   /* how could the above be checked? */
289
290   GNUNET_MQ_discard (mqm);
291
292   mqm = GNUNET_MQ_msg_header_extra (mh,
293                                     20,
294                                     GNUNET_MESSAGE_TYPE_DUMMY);
295   GNUNET_assert (GNUNET_MESSAGE_TYPE_DUMMY == ntohs (mh->type));
296   GNUNET_assert (sizeof(struct GNUNET_MessageHeader) + 20 == ntohs (mh->size));
297   GNUNET_MQ_discard (mqm);
298 }
299
300
301 int
302 main (int argc, char **argv)
303 {
304   char *test_argv[] = {
305     (char *) "test_client",
306     "-c",
307     "test_client_data.conf",
308     NULL
309   };
310   struct GNUNET_MQ_MessageHandler mh[] = {
311     GNUNET_MQ_hd_fixed_size (dummy,
312                              GNUNET_MESSAGE_TYPE_DUMMY,
313                              struct MyMessage,
314                              NULL),
315     GNUNET_MQ_hd_fixed_size (dummy2,
316                              GNUNET_MESSAGE_TYPE_DUMMY2,
317                              struct MyMessage,
318                              NULL),
319     GNUNET_MQ_handler_end ()
320   };
321
322   (void) argc;
323   (void) argv;
324   GNUNET_log_setup ("test-mq",
325                     "INFO",
326                     NULL);
327   test1 ();
328   test2 ();
329   if (0 !=
330       GNUNET_SERVICE_run_ (3,
331                            test_argv,
332                            "test_client",
333                            GNUNET_SERVICE_OPTION_NONE,
334                            &run,
335                            &connect_cb,
336                            &disconnect_cb,
337                            NULL,
338                            mh))
339     return 1;
340   return global_ret;
341 }