2 This file is part of GNUnet.
3 (C) 2006, 2009 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file transport-testing.c
23 * @brief testing lib for transport service
25 * @author Matthias Wachs
27 #include "transport-testing.h"
30 #define LOG(kind,...) GNUNET_log_from(kind, "transport-testing", __VA_ARGS__)
33 static struct PeerContext *
34 find_peer_context (struct GNUNET_TRANSPORT_TESTING_handle *tth,
35 const struct GNUNET_PeerIdentity *peer)
37 GNUNET_assert (tth != NULL);
38 struct PeerContext *t = tth->p_head;
42 if (0 == memcmp (&t->id, peer, sizeof (struct GNUNET_PeerIdentity)))
51 static struct ConnectingContext *
52 find_connecting_context (struct GNUNET_TRANSPORT_TESTING_handle *tth,
53 struct PeerContext *p1, struct PeerContext *p2)
55 GNUNET_assert (tth != NULL);
56 struct ConnectingContext *cc = tth->cc_head;
60 if ((cc->p1 == p1) && (cc->p2 == p2))
62 if ((cc->p1 == p2) && (cc->p2 == p1))
72 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
74 struct PeerContext *p = cls;
76 struct PeerContext *p2;
78 GNUNET_assert (NULL != p);
79 GNUNET_assert (NULL != p->tth);
80 p2 = find_peer_context (p->tth, peer);
82 p->nc (p->cb_cls, peer);
85 GNUNET_asprintf (&p2_s, "%u (`%s')", p2->no, GNUNET_i2s (&p2->id));
87 GNUNET_asprintf (&p2_s, "`%s'", GNUNET_i2s (peer));
88 LOG (GNUNET_ERROR_TYPE_DEBUG,
89 "Peers %s connected to peer %u (`%s')\n",
95 /* Find ConnectingContext */
96 struct ConnectingContext *cc = find_connecting_context (p->tth, p, p2);
102 cc->p1_c = GNUNET_YES;
105 cc->p2_c = GNUNET_YES;
107 if ((cc->p1_c == GNUNET_YES) && (cc->p2_c == GNUNET_YES))
109 cc->cb (cc->p1, cc->p2, cc->cb_cls);
110 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (p->tth, cc);
116 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
118 struct PeerContext *p = cls;
120 /* Find PeerContext */
122 struct PeerContext *p2 = NULL;
126 GNUNET_assert (p->tth != NULL);
127 p2 = find_peer_context (p->tth, peer);
134 GNUNET_asprintf (&p2_s, "%u (`%s')", p2->no, GNUNET_i2s (&p2->id));
136 GNUNET_asprintf (&p2_s, "`%s'", GNUNET_i2s (peer));
137 LOG (GNUNET_ERROR_TYPE_DEBUG,
138 "Peers %s disconnected from peer %u (`%s')\n",
141 GNUNET_i2s (&p->id));
147 p->nd (p->cb_cls, peer);
152 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
153 const struct GNUNET_MessageHeader *message)
155 struct PeerContext *p = cls;
160 p->rec (p->cb_cls, peer, message);
165 get_hello (void *cb_cls, const struct GNUNET_MessageHeader *message)
167 struct PeerContext *p = cb_cls;
168 struct GNUNET_PeerIdentity hello_id;
170 GNUNET_assert (message != NULL);
171 GNUNET_assert (GNUNET_OK ==
172 GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
173 message, &hello_id));
174 GNUNET_assert (0 == memcmp (&hello_id, &p->id, sizeof (hello_id)));
175 GNUNET_free_non_null (p->hello);
176 p->hello = (struct GNUNET_HELLO_Message *) GNUNET_copy_message (message);
178 if (NULL != p->start_cb)
180 LOG (GNUNET_ERROR_TYPE_DEBUG,
181 "Peer %u (`%s') successfully started\n", p->no,
182 GNUNET_i2s (&p->id));
183 p->start_cb (p, p->cb_cls);
190 try_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
192 struct ConnectingContext *cc = cls;
193 struct PeerContext *p1 = cc->p1;
194 struct PeerContext *p2 = cc->p2;
196 cc->tct = GNUNET_SCHEDULER_NO_TASK;
197 if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
200 GNUNET_assert (cc != NULL);
201 GNUNET_assert (cc->p1 != NULL);
202 GNUNET_assert (cc->p2 != NULL);
204 char *p2_s = GNUNET_strdup (GNUNET_i2s (&p2->id));
206 LOG (GNUNET_ERROR_TYPE_DEBUG,
207 "Asking peer %u (`%s') to connect peer %u (`%s'), providing HELLO with %u bytes\n",
208 p1->no, GNUNET_i2s (&p1->id), p2->no, p2_s,
209 GNUNET_HELLO_size (cc->p2->hello));
212 GNUNET_TRANSPORT_offer_hello (cc->th_p1,
213 (const struct GNUNET_MessageHeader *) cc->
214 p2->hello, NULL, NULL);
215 GNUNET_TRANSPORT_try_connect (cc->th_p1, &p2->id, NULL, NULL); /*FIXME TRY_CONNECT change */
218 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &try_connect, cc);
223 * Start a peer with the given configuration
224 * @param tth the testing handle
225 * @param cfgname configuration file
226 * @param peer_id a unique number to identify the peer
227 * @param rec receive callback
228 * @param nc connect callback
229 * @param nd disconnect callback
230 * @param start_cb start callback
231 * @param cb_cls closure for callback
232 * @return the peer context
235 GNUNET_TRANSPORT_TESTING_start_peer (struct GNUNET_TRANSPORT_TESTING_handle *tth,
236 const char *cfgname, int peer_id,
237 GNUNET_TRANSPORT_ReceiveCallback rec,
238 GNUNET_TRANSPORT_NotifyConnect nc,
239 GNUNET_TRANSPORT_NotifyDisconnect nd,
240 GNUNET_TRANSPORT_TESTING_start_cb start_cb,
244 struct GNUNET_PeerIdentity *dummy;
246 GNUNET_assert (NULL != tth);
247 GNUNET_assert (NULL != tth->tl_system);
249 if (GNUNET_DISK_file_test (cfgname) == GNUNET_NO)
251 LOG (GNUNET_ERROR_TYPE_ERROR,
252 "File not found: `%s'\n",
257 struct PeerContext *p = GNUNET_new (struct PeerContext);
258 GNUNET_CONTAINER_DLL_insert (tth->p_head, tth->p_tail, p);
260 /* Create configuration and call testing lib to modify it */
261 p->cfg = GNUNET_CONFIGURATION_create ();
262 GNUNET_assert (GNUNET_OK ==
263 GNUNET_CONFIGURATION_load (p->cfg, cfgname));
264 if (GNUNET_SYSERR == GNUNET_TESTING_configuration_create (tth->tl_system, p->cfg))
266 LOG (GNUNET_ERROR_TYPE_ERROR,
267 "Testing library failed to create unique configuration based on `%s'\n",
274 /* Configure peer with configuration */
275 p->peer = GNUNET_TESTING_peer_configure (tth->tl_system, p->cfg, p->no, NULL, &emsg);
278 LOG (GNUNET_ERROR_TYPE_ERROR,
279 "Testing library failed to create unique configuration based on `%s': `%s'\n",
282 GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
283 GNUNET_free_non_null (emsg);
286 GNUNET_free_non_null (emsg);
287 if (GNUNET_OK != GNUNET_TESTING_peer_start (p->peer))
289 LOG (GNUNET_ERROR_TYPE_ERROR,
290 "Testing library failed to create unique configuration based on `%s'\n",
292 GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
296 memset(&dummy, '\0', sizeof (dummy));
297 GNUNET_TESTING_peer_get_identity (p->peer, &p->id);
298 if (0 == memcmp (&dummy, &p->id, sizeof (struct GNUNET_PeerIdentity)))
300 LOG (GNUNET_ERROR_TYPE_ERROR,
301 "Testing library failed to obtain peer identity for peer %u\n",
303 GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
308 LOG (GNUNET_ERROR_TYPE_DEBUG,
309 "Peer %u configured with identity `%s'\n",
311 GNUNET_i2s_full (&p->id));
318 p->start_cb = start_cb;
324 p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL, p,
326 ¬ify_connect, ¬ify_disconnect);
329 LOG (GNUNET_ERROR_TYPE_ERROR,
330 "Failed to connect to transport service for peer `%s': `%s'\n",
333 GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
337 p->ghh = GNUNET_TRANSPORT_get_hello (p->th, &get_hello, p);
338 GNUNET_assert (p->ghh != NULL);
344 * Restart the given peer
345 * @param tth testing handle
347 * @param cfgname the cfg file used to restart
348 * @param restart_cb callback to call when restarted
349 * @param cb_cls callback closure
350 * @return GNUNET_OK in success otherwise GNUNET_SYSERR
353 GNUNET_TRANSPORT_TESTING_restart_peer (struct GNUNET_TRANSPORT_TESTING_handle
354 *tth, struct PeerContext *p,
356 GNUNET_TRANSPORT_TESTING_start_cb
357 restart_cb, void *cb_cls)
359 GNUNET_assert (tth != NULL);
360 GNUNET_assert (p != NULL);
361 GNUNET_assert (NULL != p->peer);
363 LOG (GNUNET_ERROR_TYPE_DEBUG,
364 "Restarting peer %u (`%s')\n",
366 GNUNET_i2s (&p->id));
369 LOG (GNUNET_ERROR_TYPE_DEBUG,
370 "Stopping peer %u (`%s')\n",
372 GNUNET_i2s (&p->id));
374 GNUNET_TRANSPORT_get_hello_cancel (p->ghh);
378 GNUNET_TRANSPORT_disconnect (p->th);
380 if (GNUNET_SYSERR == GNUNET_TESTING_peer_stop(p->peer))
382 LOG (GNUNET_ERROR_TYPE_ERROR,
383 "Failed to stop peer %u (`%s')\n",
385 GNUNET_i2s (&p->id));
386 return GNUNET_SYSERR;
392 if (GNUNET_SYSERR == GNUNET_TESTING_peer_start(p->peer))
394 LOG (GNUNET_ERROR_TYPE_ERROR,
395 "Failed to restart peer %u (`%s')\n",
396 p->no, GNUNET_i2s (&p->id));
397 return GNUNET_SYSERR;
400 GNUNET_assert (p->th != NULL);
401 GNUNET_assert (p->start_cb == NULL);
402 p->start_cb = restart_cb;
405 p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL, p,
409 GNUNET_assert (NULL != p->th);
411 p->ghh = GNUNET_TRANSPORT_get_hello (p->th, &get_hello, p);
412 GNUNET_assert (p->ghh != NULL);
418 * shutdown the given peer
419 * @param tth testing handle
423 GNUNET_TRANSPORT_TESTING_stop_peer (struct GNUNET_TRANSPORT_TESTING_handle *tth,
424 struct PeerContext *p)
426 GNUNET_assert (p != NULL);
429 GNUNET_TRANSPORT_get_hello_cancel (p->ghh);
434 GNUNET_TRANSPORT_disconnect (p->th);
440 if (GNUNET_OK != GNUNET_TESTING_peer_stop (p->peer))
442 LOG (GNUNET_ERROR_TYPE_DEBUG,
443 "Testing lib failed to stop peer %u (`%s') \n", p->no,
444 GNUNET_i2s (&p->id));
446 GNUNET_TESTING_peer_destroy (p->peer);
450 if (p->hello != NULL)
452 GNUNET_free (p->hello);
457 GNUNET_CONFIGURATION_destroy (p->cfg);
460 GNUNET_CONTAINER_DLL_remove (tth->p_head, tth->p_tail, p);
461 LOG (GNUNET_ERROR_TYPE_DEBUG,
462 "Peer %u (`%s') stopped \n", p->no,
463 GNUNET_i2s (&p->id));
469 * Initiate a connection from p1 to p2 by offering p1 p2's HELLO message
471 * Remarks: start_peer's notify_connect callback can be called before.
473 * @param tth transport testing handle
476 * @param cb the callback to call when both peers notified that they are connected
477 * @param cls callback cls
478 * @return a connect request handle
480 GNUNET_TRANSPORT_TESTING_ConnectRequest
481 GNUNET_TRANSPORT_TESTING_connect_peers (struct GNUNET_TRANSPORT_TESTING_handle *tth,
482 struct PeerContext *p1,
483 struct PeerContext *p2,
484 GNUNET_TRANSPORT_TESTING_connect_cb cb,
487 GNUNET_assert (tth != NULL);
489 struct ConnectingContext *cc =
490 GNUNET_new (struct ConnectingContext);
492 GNUNET_assert (p1 != NULL);
493 GNUNET_assert (p2 != NULL);
503 GNUNET_assert (cc->th_p1 != NULL);
504 GNUNET_assert (cc->th_p2 != NULL);
505 GNUNET_CONTAINER_DLL_insert (tth->cc_head, tth->cc_tail, cc);
506 cc->tct = GNUNET_SCHEDULER_add_now (&try_connect, cc);
507 LOG (GNUNET_ERROR_TYPE_DEBUG,
508 "New connect request %p\n",
516 * Cancel the request to connect two peers
517 * Tou MUST cancel the request if you stop the peers before the peers connected succesfully
519 * @param tth transport testing handle
520 * @param ccr a connect request handle
523 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (struct
524 GNUNET_TRANSPORT_TESTING_handle
526 GNUNET_TRANSPORT_TESTING_ConnectRequest
529 struct ConnectingContext *cc = ccr;
531 GNUNET_assert (tth != NULL);
533 LOG (GNUNET_ERROR_TYPE_DEBUG,
534 "Canceling connect request %p!\n",
537 if (cc->tct != GNUNET_SCHEDULER_NO_TASK)
538 GNUNET_SCHEDULER_cancel (cc->tct);
539 cc->tct = GNUNET_SCHEDULER_NO_TASK;
541 GNUNET_CONTAINER_DLL_remove (tth->cc_head, tth->cc_tail, cc);
547 * Clean up the transport testing
548 * @param tth transport testing handle
551 GNUNET_TRANSPORT_TESTING_done (struct GNUNET_TRANSPORT_TESTING_handle *tth)
553 struct ConnectingContext *cc = tth->cc_head;
554 struct ConnectingContext *ct = NULL;
555 struct PeerContext *p = tth->p_head;
556 struct PeerContext *t = NULL;
558 GNUNET_assert (tth != NULL);
560 while (cc != tth->cc_tail)
563 LOG (GNUNET_ERROR_TYPE_ERROR,
564 "Developer forgot to cancel connect request %p!\n",
566 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (tth, cc);
573 LOG (GNUNET_ERROR_TYPE_ERROR,
574 "Developer forgot to stop peer!\n");
575 GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
579 GNUNET_TESTING_system_destroy (tth->tl_system, GNUNET_YES);
587 * Initialize the transport testing
588 * @return transport testing handle
590 struct GNUNET_TRANSPORT_TESTING_handle *
591 GNUNET_TRANSPORT_TESTING_init ()
593 struct GNUNET_TRANSPORT_TESTING_handle *tth;
595 /* prepare hostkeys */
596 tth = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_handle);
598 /* Init testing the testing lib */
599 tth->tl_system = GNUNET_TESTING_system_create ("transport-testing", NULL,
601 if (NULL == tth->tl_system)
603 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to initialize testing library!\n"));
613 * Some utility functions
617 * Removes all directory separators from absolute filename
618 * @param file the absolute file name, e.g. as found in argv[0]
619 * @return extracted file name, has to be freed by caller
622 extract_filename (const char *file)
624 char *pch = GNUNET_strdup (file);
626 char *filename = NULL;
629 if ((strlen (pch) >= 3) && pch[1] == ':')
631 if (NULL != strstr (pch, "\\"))
633 pch = strtok (pch, "\\");
636 pch = strtok (NULL, "\\");
642 if (filename != NULL)
643 pch = filename; /* If we miss the next condition, filename = pch will
647 if (NULL != strstr (pch, "/"))
649 pch = strtok (pch, "/");
652 pch = strtok (NULL, "/");
662 res = GNUNET_strdup (filename);
663 GNUNET_free (backup);
669 * Extracts the test filename from an absolute file name and removes the extension
670 * @param file absolute file name
671 * @param dest where to store result
674 GNUNET_TRANSPORT_TESTING_get_test_name (const char *file, char **dest)
676 char *filename = extract_filename (file);
677 char *backup = filename;
680 if (filename == NULL)
684 filename = strstr (filename, "tes");
685 if (filename == NULL)
689 if (NULL != (dotexe = strstr (filename, ".exe")))
699 /* create filename */
700 GNUNET_asprintf (dest, "%s", filename);
701 GNUNET_free (backup);
706 * Extracts the filename from an absolute file name and removes the extension
707 * @param file absolute file name
708 * @param dest where to store result
711 GNUNET_TRANSPORT_TESTING_get_test_source_name (const char *file, char **dest)
713 char *src = extract_filename (file);
716 split = strstr (src, ".");
721 GNUNET_asprintf (dest, "%s", src);
727 * Extracts the plugin name from an absolute file name and the test name
729 * @param file absolute file name
730 * @param test test name
731 * @param dest where to store result
734 GNUNET_TRANSPORT_TESTING_get_test_plugin_name (const char *file,
735 const char *test, char **dest)
739 char *e = extract_filename (file);
740 char *t = extract_filename (test);
745 filename = strstr (e, "tes");
746 if (NULL == filename)
749 if (NULL != (dotexe = strstr (filename, ".exe")))
753 filename = strstr (filename, t);
754 if (NULL == filename)
757 filename += strlen (t);
758 if ('\0' != *filename)
760 GNUNET_asprintf (dest, "%s", filename);
771 * This function takes the filename (e.g. argv[0), removes a "lt-"-prefix and
772 * if existing ".exe"-prefix and adds the peer-number
774 * @param file filename of the test, e.g. argv[0]
775 * @param dest where to write the filename
776 * @param count peer number
779 GNUNET_TRANSPORT_TESTING_get_config_name (const char *file, char **dest,
782 char *filename = extract_filename (file);
783 char *backup = filename;
786 if (NULL == filename)
789 filename = strstr (filename, "tes");
790 if (NULL == filename)
793 if (NULL != (dotexe = strstr (filename, ".exe")))
795 GNUNET_asprintf (dest, "%s_peer%u.conf", filename, count);
796 GNUNET_free (backup);
800 GNUNET_free (backup);
804 /* end of transport-testing.c */