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 2, 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
28 #include "transport-testing.h"
31 static struct PeerContext *
32 find_peer_context (struct GNUNET_TRANSPORT_TESTING_handle *tth,
33 const struct GNUNET_PeerIdentity *peer)
35 GNUNET_assert (tth != NULL);
36 struct PeerContext *t = tth->p_head;
40 if (0 == memcmp (&t->id, peer, sizeof (struct GNUNET_PeerIdentity)))
49 static struct ConnectingContext *
50 find_connecting_context (struct GNUNET_TRANSPORT_TESTING_handle *tth,
51 struct PeerContext *p1, struct PeerContext *p2)
53 GNUNET_assert (tth != NULL);
54 struct ConnectingContext *cc = tth->cc_head;
58 if ((cc->p1 == p1) && (cc->p2 == p2))
60 if ((cc->p1 == p2) && (cc->p2 == p1))
70 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
72 struct PeerContext *p = cls;
74 struct PeerContext *p2;
76 GNUNET_assert (NULL != p);
77 GNUNET_assert (NULL != p->tth);
78 p2 = find_peer_context (p->tth, peer);
80 p->nc (p->cb_cls, peer);
83 GNUNET_asprintf (&p2_s, "%u (`%s')", p2->no, GNUNET_i2s (&p2->id));
85 GNUNET_asprintf (&p2_s, "`%s'", GNUNET_i2s (peer));
86 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
87 "Peers %s connected to peer %u (`%s')\n", p2_s, p->no,
91 /* Find ConnectingContext */
92 struct ConnectingContext *cc = find_connecting_context (p->tth, p, p2);
98 cc->p1_c = GNUNET_YES;
101 cc->p2_c = GNUNET_YES;
103 if ((cc->p1_c == GNUNET_YES) && (cc->p2_c == GNUNET_YES))
105 cc->cb (cc->p1, cc->p2, cc->cb_cls);
106 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (p->tth, cc);
112 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
114 struct PeerContext *p = cls;
116 /* Find PeerContext */
118 struct PeerContext *p2 = NULL;
122 GNUNET_assert (p->tth != NULL);
123 p2 = find_peer_context (p->tth, peer);
130 GNUNET_asprintf (&p2_s, "%u (`%s')", p2->no, GNUNET_i2s (&p2->id));
132 GNUNET_asprintf (&p2_s, "`%s'", GNUNET_i2s (peer));
133 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
134 "Peers %s disconnected from peer %u (`%s')\n", p2_s, no,
135 GNUNET_i2s (&p->id));
141 p->nd (p->cb_cls, peer);
146 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
147 const struct GNUNET_MessageHeader *message)
149 struct PeerContext *p = cls;
154 p->rec (p->cb_cls, peer, message);
159 get_hello (void *cb_cls, const struct GNUNET_MessageHeader *message)
161 struct PeerContext *p = cb_cls;
162 struct GNUNET_PeerIdentity hello_id;
164 GNUNET_assert (message != NULL);
165 GNUNET_assert (GNUNET_OK ==
166 GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
167 message, &hello_id));
168 GNUNET_assert (0 == memcmp (&hello_id, &p->id, sizeof (hello_id)));
169 GNUNET_free_non_null (p->hello);
170 p->hello = (struct GNUNET_HELLO_Message *) GNUNET_copy_message (message);
172 if (NULL != p->start_cb)
174 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
175 "Peer %u (`%s') successfully started\n", p->no,
176 GNUNET_i2s (&p->id));
177 p->start_cb (p, p->cb_cls);
184 try_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
186 struct ConnectingContext *cc = cls;
187 struct PeerContext *p1 = cc->p1;
188 struct PeerContext *p2 = cc->p2;
190 cc->tct = GNUNET_SCHEDULER_NO_TASK;
191 if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
194 GNUNET_assert (cc != NULL);
195 GNUNET_assert (cc->p1 != NULL);
196 GNUNET_assert (cc->p2 != NULL);
198 char *p2_s = GNUNET_strdup (GNUNET_i2s (&p2->id));
200 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
201 "Asking peer %u (`%s') to connect peer %u (`%s'), providing HELLO with %u bytes\n",
202 p1->no, GNUNET_i2s (&p1->id), p2->no, p2_s,
203 GNUNET_HELLO_size (cc->p2->hello));
206 GNUNET_TRANSPORT_offer_hello (cc->th_p1,
207 (const struct GNUNET_MessageHeader *) cc->
208 p2->hello, NULL, NULL);
209 GNUNET_TRANSPORT_try_connect (cc->th_p1, &p2->id, NULL, NULL); /*FIXME TRY_CONNECT change */
212 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &try_connect, cc);
217 * Start a peer with the given configuration
218 * @param tth the testing handle
219 * @param cfgname configuration file
220 * @param peer_id a unique number to identify the peer
221 * @param rec receive callback
222 * @param nc connect callback
223 * @param nd disconnect callback
224 * @param start_cb start callback
225 * @param cb_cls closure for callback
226 * @return the peer context
229 GNUNET_TRANSPORT_TESTING_start_peer (struct GNUNET_TRANSPORT_TESTING_handle *tth,
230 const char *cfgname, int peer_id,
231 GNUNET_TRANSPORT_ReceiveCallback rec,
232 GNUNET_TRANSPORT_NotifyConnect nc,
233 GNUNET_TRANSPORT_NotifyDisconnect nd,
234 GNUNET_TRANSPORT_TESTING_start_cb start_cb,
238 struct GNUNET_PeerIdentity *dummy;
240 GNUNET_assert (NULL != tth);
241 GNUNET_assert (NULL != tth->tl_system);
243 if (GNUNET_DISK_file_test (cfgname) == GNUNET_NO)
245 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
246 "File not found: `%s' \n", cfgname);
250 struct PeerContext *p = GNUNET_malloc (sizeof (struct PeerContext));
251 GNUNET_CONTAINER_DLL_insert (tth->p_head, tth->p_tail, p);
253 /* Create configuration and call testing lib to modify it */
254 p->cfg = GNUNET_CONFIGURATION_create ();
255 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
257 if (GNUNET_SYSERR == GNUNET_TESTING_configuration_create (tth->tl_system, p->cfg))
259 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
260 "Testing library failed to create unique configuration based on `%s'\n",
267 /* Configure peer with configuration */
268 p->peer = GNUNET_TESTING_peer_configure (tth->tl_system, p->cfg, p->no, NULL, &emsg);
271 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
272 "Testing library failed to create unique configuration based on `%s': `%s'\n",
274 GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
275 GNUNET_free_non_null (emsg);
278 GNUNET_free_non_null (emsg);
279 if (GNUNET_OK != GNUNET_TESTING_peer_start (p->peer))
281 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
282 "Testing library failed to create unique configuration based on `%s'\n",
284 GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
288 memset(&dummy, '\0', sizeof (dummy));
289 GNUNET_TESTING_peer_get_identity (p->peer, &p->id);
290 if (0 == memcmp (&dummy, &p->id, sizeof (struct GNUNET_PeerIdentity)))
292 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
293 "Testing library failed to obtain peer identity for peer %u\n",
295 GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
300 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
301 "Peer %u configured with identity `%s'\n",
303 GNUNET_i2s (&p->id));
310 p->start_cb = start_cb;
316 p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL, p,
318 ¬ify_connect, ¬ify_disconnect);
321 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
322 "Failed to connect to transport service for peer `%s': `%s'\n",
324 GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
328 p->ghh = GNUNET_TRANSPORT_get_hello (p->th, &get_hello, p);
329 GNUNET_assert (p->ghh != NULL);
335 * Restart the given peer
336 * @param tth testing handle
338 * @param cfgname the cfg file used to restart
339 * @param restart_cb callback to call when restarted
340 * @param cb_cls callback closure
341 * @return GNUNET_OK in success otherwise GNUNET_SYSERR
344 GNUNET_TRANSPORT_TESTING_restart_peer (struct GNUNET_TRANSPORT_TESTING_handle
345 *tth, struct PeerContext *p,
347 GNUNET_TRANSPORT_TESTING_start_cb
348 restart_cb, void *cb_cls)
350 GNUNET_assert (tth != NULL);
351 GNUNET_assert (p != NULL);
352 GNUNET_assert (NULL != p->peer);
354 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
355 "Restarting peer %u (`%s')\n", p->no, GNUNET_i2s (&p->id));
358 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
359 "Stopping peer %u (`%s')\n", p->no, GNUNET_i2s (&p->id));
361 GNUNET_TRANSPORT_get_hello_cancel (p->ghh);
365 GNUNET_TRANSPORT_disconnect (p->th);
367 if (GNUNET_SYSERR == GNUNET_TESTING_peer_stop(p->peer))
369 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
370 "Failed to stop peer %u (`%s')\n", p->no, GNUNET_i2s (&p->id));
371 return GNUNET_SYSERR;
377 if (GNUNET_SYSERR == GNUNET_TESTING_peer_start(p->peer))
379 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
380 "Failed to restart peer %u (`%s')\n",
381 p->no, GNUNET_i2s (&p->id));
382 return GNUNET_SYSERR;
385 GNUNET_assert (p->th != NULL);
386 GNUNET_assert (p->start_cb == NULL);
387 p->start_cb = restart_cb;
390 p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL, p,
394 GNUNET_assert (NULL != p->th);
396 p->ghh = GNUNET_TRANSPORT_get_hello (p->th, &get_hello, p);
397 GNUNET_assert (p->ghh != NULL);
403 * shutdown the given peer
404 * @param tth testing handle
408 GNUNET_TRANSPORT_TESTING_stop_peer (struct GNUNET_TRANSPORT_TESTING_handle *tth,
409 struct PeerContext *p)
411 GNUNET_assert (p != NULL);
414 GNUNET_TRANSPORT_get_hello_cancel (p->ghh);
419 GNUNET_TRANSPORT_disconnect (p->th);
425 if (GNUNET_OK != GNUNET_TESTING_peer_stop (p->peer))
427 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
428 "Testing lib failed to stop peer %u (`%s') \n", p->no,
429 GNUNET_i2s (&p->id));
431 GNUNET_TESTING_peer_destroy (p->peer);
435 if (p->hello != NULL)
437 GNUNET_free (p->hello);
442 GNUNET_CONFIGURATION_destroy (p->cfg);
445 GNUNET_CONTAINER_DLL_remove (tth->p_head, tth->p_tail, p);
446 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
447 "Peer %u (`%s') stopped \n", p->no,
448 GNUNET_i2s (&p->id));
454 * Initiate a connection from p1 to p2 by offering p1 p2's HELLO message
456 * Remarks: start_peer's notify_connect callback can be called before.
458 * @param tth transport testing handle
461 * @param cb the callback to call when both peers notified that they are connected
462 * @param cls callback cls
463 * @return a connect request handle
465 GNUNET_TRANSPORT_TESTING_ConnectRequest
466 GNUNET_TRANSPORT_TESTING_connect_peers (struct GNUNET_TRANSPORT_TESTING_handle *tth,
467 struct PeerContext *p1,
468 struct PeerContext *p2,
469 GNUNET_TRANSPORT_TESTING_connect_cb cb,
472 GNUNET_assert (tth != NULL);
474 struct ConnectingContext *cc =
475 GNUNET_malloc (sizeof (struct ConnectingContext));
477 GNUNET_assert (p1 != NULL);
478 GNUNET_assert (p2 != NULL);
488 GNUNET_assert (cc->th_p1 != NULL);
489 GNUNET_assert (cc->th_p2 != NULL);
490 GNUNET_CONTAINER_DLL_insert (tth->cc_head, tth->cc_tail, cc);
491 cc->tct = GNUNET_SCHEDULER_add_now (&try_connect, cc);
492 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
493 "New connect request %p\n", cc);
500 * Cancel the request to connect two peers
501 * Tou MUST cancel the request if you stop the peers before the peers connected succesfully
503 * @param tth transport testing handle
504 * @param ccr a connect request handle
507 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (struct
508 GNUNET_TRANSPORT_TESTING_handle
510 GNUNET_TRANSPORT_TESTING_ConnectRequest
513 struct ConnectingContext *cc = ccr;
515 GNUNET_assert (tth != NULL);
517 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
518 "Canceling connect request %p!\n", cc);
520 if (cc->tct != GNUNET_SCHEDULER_NO_TASK)
521 GNUNET_SCHEDULER_cancel (cc->tct);
522 cc->tct = GNUNET_SCHEDULER_NO_TASK;
524 GNUNET_CONTAINER_DLL_remove (tth->cc_head, tth->cc_tail, cc);
530 * Clean up the transport testing
531 * @param tth transport testing handle
534 GNUNET_TRANSPORT_TESTING_done (struct GNUNET_TRANSPORT_TESTING_handle *tth)
536 struct ConnectingContext *cc = tth->cc_head;
537 struct ConnectingContext *ct = NULL;
538 struct PeerContext *p = tth->p_head;
539 struct PeerContext *t = NULL;
541 GNUNET_assert (tth != NULL);
543 while (cc != tth->cc_tail)
546 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
547 "Developer forgot to cancel connect request %p!\n", cc);
548 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (tth, cc);
555 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
556 "Developer forgot to stop peer!\n");
557 GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
561 GNUNET_TESTING_system_destroy (tth->tl_system, GNUNET_YES);
569 * Initialize the transport testing
570 * @return transport testing handle
572 struct GNUNET_TRANSPORT_TESTING_handle *
573 GNUNET_TRANSPORT_TESTING_init ()
575 struct GNUNET_TRANSPORT_TESTING_handle *tth;
577 /* prepare hostkeys */
578 tth = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_TESTING_handle));
580 /* Init testing the testing lib */
581 tth->tl_system = GNUNET_TESTING_system_create ("transport-testing", NULL,
583 if (NULL == tth->tl_system)
585 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to initialize testing library!\n"));
595 * Some utility functions
599 * Removes all directory separators from absolute filename
600 * @param file the absolute file name, e.g. as found in argv[0]
601 * @return extracted file name, has to be freed by caller
604 extract_filename (const char *file)
606 char *pch = GNUNET_strdup (file);
608 char *filename = NULL;
611 if ((strlen (pch) >= 3) && pch[1] == ':')
613 if (NULL != strstr (pch, "\\"))
615 pch = strtok (pch, "\\");
618 pch = strtok (NULL, "\\");
624 if (filename != NULL)
625 pch = filename; /* If we miss the next condition, filename = pch will
629 if (NULL != strstr (pch, "/"))
631 pch = strtok (pch, "/");
634 pch = strtok (NULL, "/");
644 res = GNUNET_strdup (filename);
645 GNUNET_free (backup);
651 * Extracts the test filename from an absolute file name and removes the extension
652 * @param file absolute file name
653 * @param dest where to store result
656 GNUNET_TRANSPORT_TESTING_get_test_name (const char *file, char **dest)
658 char *filename = extract_filename (file);
659 char *backup = filename;
662 if (filename == NULL)
666 filename = strstr (filename, "tes");
667 if (filename == NULL)
671 if (NULL != (dotexe = strstr (filename, ".exe")))
681 /* create filename */
682 GNUNET_asprintf (dest, "%s", filename);
683 GNUNET_free (backup);
688 * Extracts the filename from an absolute file name and removes the extension
689 * @param file absolute file name
690 * @param dest where to store result
693 GNUNET_TRANSPORT_TESTING_get_test_source_name (const char *file, char **dest)
695 char *src = extract_filename (file);
698 split = strstr (src, ".");
703 GNUNET_asprintf (dest, "%s", src);
709 * Extracts the plugin name from an absolute file name and the test name
711 * @param file absolute file name
712 * @param test test name
713 * @param dest where to store result
716 GNUNET_TRANSPORT_TESTING_get_test_plugin_name (const char *file,
717 const char *test, char **dest)
721 char *e = extract_filename (file);
722 char *t = extract_filename (test);
727 filename = strstr (e, "tes");
728 if (NULL == filename)
731 if (NULL != (dotexe = strstr (filename, ".exe")))
735 filename = strstr (filename, t);
736 if (NULL == filename)
739 filename += strlen (t);
740 if ('\0' != *filename)
742 GNUNET_asprintf (dest, "%s", filename);
753 * This function takes the filename (e.g. argv[0), removes a "lt-"-prefix and
754 * if existing ".exe"-prefix and adds the peer-number
756 * @param file filename of the test, e.g. argv[0]
757 * @param dest where to write the filename
758 * @param count peer number
761 GNUNET_TRANSPORT_TESTING_get_config_name (const char *file, char **dest,
764 char *filename = extract_filename (file);
765 char *backup = filename;
768 if (NULL == filename)
771 filename = strstr (filename, "tes");
772 if (NULL == filename)
775 if (NULL != (dotexe = strstr (filename, ".exe")))
777 GNUNET_asprintf (dest, "%s_peer%u.conf", filename, count);
778 GNUNET_free (backup);
782 GNUNET_free (backup);
786 /* end of transport-testing.c */