-makefile for new test_stream_local (commented)
[oweals/gnunet.git] / src / stream / test_stream_local.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file stream/test_stream_local.c
23  * @brief Stream API testing between local peers
24  * @author Sree Harsha Totakura
25  */
26
27 #include <string.h>
28
29 #include "platform.h"
30 #include "gnunet_util_lib.h"
31 #include "gnunet_mesh_service.h"
32 #include "gnunet_stream_lib.h"
33 #include "gnunet_testing_lib.h"
34
35 #define VERBOSE 1
36
37 /**
38  * Structure for holding peer's sockets and IO Handles
39  */
40 struct PeerData
41 {
42   /**
43    * Peer's stream socket
44    */
45   struct GNUNET_STREAM_Socket *socket;
46
47   /**
48    * Peer's io write handle
49    */
50   struct GNUNET_STREAM_IOWriteHandle *io_write_handle;
51
52   /**
53    * Peer's io read handle
54    */
55   struct GNUNET_STREAM_IOReadHandle *io_read_handle;
56
57   /**
58    * Bytes the peer has written
59    */
60   unsigned int bytes_wrote;
61
62   /**
63    * Byte the peer has read
64    */
65   unsigned int bytes_read;
66 };
67
68 static struct GNUNET_OS_Process *arm_pid;
69 static struct PeerData peer1;
70 static struct PeerData peer2;
71 static struct GNUNET_STREAM_ListenSocket *peer2_listen_socket;
72 static struct GNUNET_CONFIGURATION_Handle *config;
73
74 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
75 static GNUNET_SCHEDULER_TaskIdentifier test_task;
76 static GNUNET_SCHEDULER_TaskIdentifier read_task;
77
78 static char *data = "ABCD";
79 static int result;
80
81 /**
82  * Input processor
83  *
84  * @param cls the closure from GNUNET_STREAM_write/read
85  * @param status the status of the stream at the time this function is called
86  * @param data traffic from the other side
87  * @param size the number of bytes available in data read 
88  * @return number of bytes of processed from 'data' (any data remaining should be
89  *         given to the next time the read processor is called).
90  */
91 static size_t
92 input_processor (void *cls,
93                  enum GNUNET_STREAM_Status status,
94                  const void *input_data,
95                  size_t size);
96
97 /**
98  * Task for calling STREAM_read
99  *
100  * @param cls the peer data entity
101  * @param tc the task context
102  */
103 static void
104 stream_read_task (void *cls,
105                   const struct GNUNET_SCHEDULER_TaskContext *tc)
106 {
107   struct PeerData *peer = cls;
108   
109   peer->io_read_handle = GNUNET_STREAM_read (peer->socket,
110                                              GNUNET_TIME_relative_multiply
111                                              (GNUNET_TIME_UNIT_SECONDS, 5),
112                                              &input_processor,
113                                              peer);
114   GNUNET_assert (NULL != peer->io_read_handle);
115 }
116
117 /**
118  * The write completion function; called upon writing some data to stream or
119  * upon error
120  *
121  * @param cls the closure from GNUNET_STREAM_write/read
122  * @param status the status of the stream at the time this function is called
123  * @param size the number of bytes read or written
124  */
125 static void 
126 write_completion (void *cls,
127                   enum GNUNET_STREAM_Status status,
128                   size_t size);
129
130
131 /**
132  * Task for calling STREAM_write
133  *
134  * @param cls the peer data entity
135  * @param tc the task context
136  */
137 static void
138 stream_write_task (void *cls,
139                    const struct GNUNET_SCHEDULER_TaskContext *tc)
140 {
141   struct PeerData *peer = cls;
142   
143   peer->io_write_handle = 
144     GNUNET_STREAM_write (peer->socket,
145                          (void *) data,
146                          strlen(data) - peer->bytes_wrote,
147                          GNUNET_TIME_relative_multiply
148                          (GNUNET_TIME_UNIT_SECONDS, 5),
149                          &write_completion,
150                          peer);
151  
152   GNUNET_assert (NULL != peer->io_write_handle);
153  }
154
155 /**
156  * Shutdown nicely
157  */
158 static void
159 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
160 {
161   GNUNET_STREAM_close (peer1.socket);
162   if (NULL != peer2.socket)
163     GNUNET_STREAM_close (peer2.socket);
164   if (NULL != peer2_listen_socket)
165     GNUNET_STREAM_listen_close (peer2_listen_socket);
166   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: shutdown\n");
167   if (0 != abort_task)
168   {
169     GNUNET_SCHEDULER_cancel (abort_task);
170   }
171   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: arm\n");
172   if (0 != GNUNET_OS_process_kill (arm_pid, SIGTERM))
173   {
174     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
175   }
176   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Wait\n");
177   /* Free the duplicated configuration */
178   GNUNET_CONFIGURATION_destroy (config);
179   GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (arm_pid));
180   GNUNET_OS_process_close (arm_pid);
181 }
182
183
184 /**
185  * Something went wrong and timed out. Kill everything and set error flag
186  */
187 static void
188 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
189 {
190   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: ABORT\n");
191   if (0 != test_task)
192   {
193     GNUNET_SCHEDULER_cancel (test_task);
194   }
195   if (0 != read_task)
196     {
197       GNUNET_SCHEDULER_cancel (read_task);
198     }
199   result = GNUNET_SYSERR;
200   abort_task = 0;
201   do_shutdown (cls, tc);
202 }
203
204 /**
205  * Signature for input processor 
206  *
207  * @param cls the closure from GNUNET_STREAM_write/read
208  * @param status the status of the stream at the time this function is called
209  * @param data traffic from the other side
210  * @param size the number of bytes available in data read 
211  * @return number of bytes of processed from 'data' (any data remaining should be
212  *         given to the next time the read processor is called).
213  */
214 static size_t
215 input_processor (void *cls,
216                  enum GNUNET_STREAM_Status status,
217                  const void *input_data,
218                  size_t size);
219
220
221 /**
222  * The write completion function; called upon writing some data to stream or
223  * upon error
224  *
225  * @param cls the closure from GNUNET_STREAM_write/read
226  * @param status the status of the stream at the time this function is called
227  * @param size the number of bytes read or written
228  */
229 static void 
230 write_completion (void *cls,
231                   enum GNUNET_STREAM_Status status,
232                   size_t size)
233 {
234   struct PeerData *peer=cls;;
235
236   GNUNET_assert (GNUNET_STREAM_OK == status);
237   GNUNET_assert (size < strlen (data));
238   peer->bytes_wrote += size;
239
240   if (peer->bytes_wrote < strlen(data)) /* Have more data to send */
241     {
242       GNUNET_SCHEDULER_add_now (&stream_write_task, peer);
243     }
244   else
245     {
246       if (&peer1 == peer)   /* Peer1 has finished writing; should read now */
247         {
248           peer->bytes_read = 0;
249           GNUNET_SCHEDULER_add_now (&stream_read_task, peer);
250         }
251     }
252 }
253
254
255 /**
256  * Function executed after stream has been established
257  *
258  * @param cls the closure from GNUNET_STREAM_open
259  * @param socket socket to use to communicate with the other side (read/write)
260  */
261 static void 
262 stream_open_cb (void *cls,
263                 struct GNUNET_STREAM_Socket *socket)
264 {
265   struct PeerData *peer=cls;
266
267   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stream established from peer1\n");
268   peer->bytes_wrote = 0;
269   GNUNET_assert (socket == peer1.socket);
270   GNUNET_assert (socket == peer->socket);
271   GNUNET_SCHEDULER_add_now (&stream_write_task, peer);
272 }
273
274
275 /**
276  * Input processor
277  *
278  * @param cls the closure from GNUNET_STREAM_write/read
279  * @param status the status of the stream at the time this function is called
280  * @param data traffic from the other side
281  * @param size the number of bytes available in data read 
282  * @return number of bytes of processed from 'data' (any data remaining should be
283  *         given to the next time the read processor is called).
284  */
285 static size_t
286 input_processor (void *cls,
287                  enum GNUNET_STREAM_Status status,
288                  const void *input_data,
289                  size_t size)
290 {
291   struct PeerData *peer = cls;
292
293   GNUNET_assert (GNUNET_STREAM_OK == status);
294   GNUNET_assert (size < strlen (data));
295   GNUNET_assert (strncmp ((const char *) data + peer->bytes_read, 
296                           (const char *) input_data,
297                           size));
298   peer->bytes_read += size;
299   
300   if (peer->bytes_read < strlen (data))
301     {
302       GNUNET_SCHEDULER_add_now (&stream_read_task, peer);
303     }
304   else 
305     {
306       if (&peer2 == peer)    /* Peer2 has completed reading; should write */
307         {
308           peer->bytes_wrote = 0;
309           GNUNET_SCHEDULER_add_now (&stream_write_task, peer);
310         }
311       else                      /* Peer1 has completed reading. End of tests */
312         {
313           GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
314         }
315     } 
316   return size;
317 }
318
319   
320 /**
321  * Scheduler call back; to be executed when a new stream is connected
322  * Called from listen connect for peer2
323  */
324 static void
325 stream_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
326 {
327   read_task = GNUNET_SCHEDULER_NO_TASK;
328   GNUNET_assert (NULL != cls);
329   peer2.bytes_read = 0;
330   GNUNET_SCHEDULER_add_now (&stream_read_task, &peer2);
331 }
332
333
334 /**
335  * Functions of this type are called upon new stream connection from other peers
336  *
337  * @param cls the closure from GNUNET_STREAM_listen
338  * @param socket the socket representing the stream
339  * @param initiator the identity of the peer who wants to establish a stream
340  *            with us
341  * @return GNUNET_OK to keep the socket open, GNUNET_SYSERR to close the
342  *             stream (the socket will be invalid after the call)
343  */
344 static int
345 stream_listen_cb (void *cls,
346            struct GNUNET_STREAM_Socket *socket,
347            const struct GNUNET_PeerIdentity *initiator)
348 {
349   struct GNUNET_PeerIdentity self;
350
351   GNUNET_assert (NULL != socket);
352   GNUNET_assert (socket != peer1.socket);
353
354   /* Get our identity */
355   GNUNET_assert (GNUNET_OK == GNUNET_TESTING_get_peer_identity (config,
356                                                                 &self));
357   GNUNET_assert (0 == memcmp (&self,
358                               initiator,
359                               sizeof (struct GNUNET_PeerIdentity)));
360   
361   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
362               "Peer connected: %s\n", GNUNET_i2s(initiator));
363
364   peer2.socket = socket;
365   read_task = GNUNET_SCHEDULER_add_now (&stream_read, (void *) socket);
366   return GNUNET_OK;
367 }
368
369
370 /**
371  * Testing function
372  *
373  * @param cls NULL
374  * @param tc the task context
375  */
376 static void
377 test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
378 {
379   struct GNUNET_PeerIdentity self;
380
381   test_task = GNUNET_SCHEDULER_NO_TASK;
382   /* Get our identity */
383   GNUNET_assert (GNUNET_OK == GNUNET_TESTING_get_peer_identity (config,
384                                                                 &self));
385
386   peer2_listen_socket = GNUNET_STREAM_listen (config,
387                                               10, /* App port */
388                                               &stream_listen_cb,
389                                               NULL);
390   GNUNET_assert (NULL != peer2_listen_socket);
391
392   /* Connect to stream library */
393   peer1.socket = GNUNET_STREAM_open (config,
394                                      &self,         /* Null for local peer? */
395                                      10,           /* App port */
396                                      &stream_open_cb,
397                                      (void *) &peer1);
398   GNUNET_assert (NULL != peer1.socket);                  
399 }
400
401 /**
402  * Initialize framework and start test
403  */
404 static void
405 run (void *cls, char *const *args, const char *cfgfile,
406      const struct GNUNET_CONFIGURATION_Handle *cfg)
407 {
408    GNUNET_log_setup ("test_stream_local",
409 #if VERBOSE
410                     "DEBUG",
411 #else
412                     "WARNING",
413 #endif
414                     NULL);
415    /* Duplicate the configuration */
416    config = GNUNET_CONFIGURATION_dup (cfg);
417    arm_pid =
418      GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm",
419                               "gnunet-service-arm",
420 #if VERBOSE_ARM
421                               "-L", "DEBUG",
422 #endif
423                               "-c", "test_stream_local.conf", NULL);
424
425    abort_task =
426      GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
427                                    (GNUNET_TIME_UNIT_SECONDS, 60), &do_abort,
428                                     NULL);
429    
430    test_task = GNUNET_SCHEDULER_add_now (&test, NULL);
431
432 }
433
434 /**
435  * Main function
436  */
437 int main (int argc, char **argv)
438 {
439   int ret;
440
441   char *const argv2[] = { "test-stream-local",
442                           "-c", "test_stream_local.conf",
443 #if VERBOSE
444                           "-L", "DEBUG",
445 #endif
446                           NULL
447   };
448   
449   struct GNUNET_GETOPT_CommandLineOption options[] = {
450     GNUNET_GETOPT_OPTION_END
451   };
452   
453   ret =
454       GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2,
455                           "test-stream-local", "nohelp", options, &run, NULL);
456
457   if (GNUNET_OK != ret)
458   {
459     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "run failed with error code %d\n",
460                 ret);
461     return 1;
462   }
463   if (GNUNET_SYSERR == result)
464   {
465     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test failed\n");
466     return 1;
467   }
468   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test ok\n");
469   return 0;
470 }