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