cb386ac14901eca3e75a1979580547b67f2e5b7b
[platform/upstream/libnice.git] / tests / test-trickle.c
1 /*
2  * This file is part of the Nice GLib ICE library.
3  *
4  * Unit test for ICE in trickle mode (adding remote candidates while the
5  * machine is running).
6  *
7  * (C) 2007 Nokia Corporation. All rights reserved.
8  *  Contact: Kai Vehmanen
9  *
10  * The contents of this file are subject to the Mozilla Public License Version
11  * 1.1 (the "License"); you may not use this file except in compliance with
12  * the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS" basis,
16  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
17  * for the specific language governing rights and limitations under the
18  * License.
19  *
20  * The Original Code is the Nice GLib ICE library.
21  *
22  * The Initial Developers of the Original Code are Collabora Ltd and Nokia
23  * Corporation. All Rights Reserved.
24  *
25  * Contributors:
26  *   Kai Vehmanen, Nokia
27  *
28  * Alternatively, the contents of this file may be used under the terms of the
29  * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
30  * case the provisions of LGPL are applicable instead of those above. If you
31  * wish to allow use of your version of this file only under the terms of the
32  * LGPL and not to allow others to use your version of this file under the
33  * MPL, indicate your decision by deleting the provisions above and replace
34  * them with the notice and other provisions required by the LGPL. If you do
35  * not delete the provisions above, a recipient may use your version of this
36  * file under either the MPL or the LGPL.
37  */
38 #ifdef HAVE_CONFIG_H
39 # include <config.h>
40 #endif
41
42 #include "agent.h"
43
44 #include <stdlib.h>
45 #include <string.h>
46
47
48 static NiceComponentState global_lagent_state = NICE_COMPONENT_STATE_LAST;
49 static NiceComponentState global_ragent_state = NICE_COMPONENT_STATE_LAST;
50 static guint global_components_ready = 0;
51 static guint global_components_ready_exit = 0;
52 static guint global_components_failed = 0;
53 static guint global_components_failed_exit = 0;
54 static GMainLoop *global_mainloop = NULL;
55 static gboolean global_lagent_gathering_done = FALSE;
56 static gboolean global_ragent_gathering_done = FALSE;
57 static gboolean global_lagent_ibr_received = FALSE;
58 static gboolean global_ragent_ibr_received = FALSE;
59 static int global_lagent_cands = 0;
60 static int global_ragent_cands = 0;
61 static gint global_ragent_read = 0;
62
63 static void priv_print_global_status (void)
64 {
65   g_debug ("\tgathering_done=%d", global_lagent_gathering_done && global_ragent_gathering_done);
66   g_debug ("\tlstate=%d", global_lagent_state);
67   g_debug ("\trstate=%d", global_ragent_state);
68   g_debug ("\tL cands=%d R cands=%d", global_lagent_cands, global_ragent_cands);
69 }
70
71 static gboolean timer_cb (gpointer pointer)
72 {
73   g_debug ("test-trickle:%s: %p", G_STRFUNC, pointer);
74
75   /* signal status via a global variable */
76
77   /* note: should not be reached, abort */
78   g_error ("ERROR: test has got stuck, aborting...");
79
80   return FALSE;
81 }
82
83 static gboolean quit_loop_cb (gpointer pointer)
84 {
85   g_debug ("test-trickle:%s: %p", G_STRFUNC, pointer);
86
87   g_main_loop_quit (global_mainloop);
88   return FALSE;
89 }
90
91 static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data)
92 {
93   g_debug ("test-trickle:%s: %p", G_STRFUNC, user_data);
94
95   /* XXX: dear compiler, these are for you: */
96   (void)agent; (void)stream_id; (void)component_id; (void)buf;
97
98   /*
99    * Lets ignore stun packets that got through
100    */
101   if (len < 8)
102     return;
103   if (strncmp ("12345678", buf, 8))
104     return;
105
106   if (GPOINTER_TO_UINT (user_data) == 2) {
107     global_ragent_read = len;
108     g_main_loop_quit (global_mainloop);
109   }
110 }
111
112 static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data)
113 {
114   g_debug ("test-trickle:%s: %p", G_STRFUNC, data);
115
116   if (GPOINTER_TO_UINT (data) == 1)
117     global_lagent_gathering_done = TRUE;
118   else if (GPOINTER_TO_UINT (data) == 2)
119     global_ragent_gathering_done = TRUE;
120
121   if (global_lagent_gathering_done &&
122       global_ragent_gathering_done)
123     g_main_loop_quit (global_mainloop);
124
125   /* XXX: dear compiler, these are for you: */
126   (void)agent;
127 }
128
129 static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data)
130 {
131   gboolean ready_to_connected = FALSE;
132   g_debug ("test-trickle:%s: %p", G_STRFUNC, data);
133
134   if (GPOINTER_TO_UINT (data) == 1) {
135     if (global_lagent_state == NICE_COMPONENT_STATE_READY &&
136         state == NICE_COMPONENT_STATE_CONNECTED)
137       ready_to_connected = TRUE;
138     global_lagent_state = state;
139   } else if (GPOINTER_TO_UINT (data) == 2) {
140     if (global_ragent_state == NICE_COMPONENT_STATE_READY &&
141         state == NICE_COMPONENT_STATE_CONNECTED)
142       ready_to_connected = TRUE;
143     global_ragent_state = state;
144   }
145
146   if (state == NICE_COMPONENT_STATE_READY)
147     global_components_ready++;
148   else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected)
149     global_components_ready--;
150   if (state == NICE_COMPONENT_STATE_FAILED)
151     global_components_failed++;
152
153   g_debug ("test-trickle: checks READY/EXIT-AT %u/%u.", global_components_ready, global_components_ready_exit);
154   g_debug ("test-trickle: checks FAILED/EXIT-AT %u/%u.", global_components_failed, global_components_failed_exit);
155
156   /* signal status via a global variable */
157   if (global_components_ready == global_components_ready_exit &&
158       global_components_failed == global_components_failed_exit) {
159     g_main_loop_quit (global_mainloop); 
160     return;
161   }
162
163   /* XXX: dear compiler, these are for you: */
164   (void)agent; (void)stream_id; (void)data; (void)component_id;
165 }
166
167 static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id, 
168                                  gchar *lfoundation, gchar* rfoundation, gpointer data)
169 {
170   g_debug ("test-trickle:%s: %p", G_STRFUNC, data);
171
172   if (GPOINTER_TO_UINT (data) == 1)
173     ++global_lagent_cands;
174   else if (GPOINTER_TO_UINT (data) == 2)
175     ++global_ragent_cands;
176
177   /* XXX: dear compiler, these are for you: */
178   (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation;
179 }
180
181 static void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id, 
182                              gchar *foundation, gpointer data)
183 {
184   g_debug ("test-trickle:%s: %p", G_STRFUNC, data);
185
186   /* XXX: dear compiler, these are for you: */
187   (void)agent; (void)stream_id; (void)data; (void)component_id; (void)foundation;
188 }
189
190 static void cb_initial_binding_request_received(NiceAgent *agent, guint stream_id, gpointer data)
191 {
192   g_debug ("test-trickle:%s: %p", G_STRFUNC, data);
193
194   if (GPOINTER_TO_UINT (data) == 1)
195     global_lagent_ibr_received = TRUE;
196   else if (GPOINTER_TO_UINT (data) == 2)
197     global_ragent_ibr_received = TRUE;
198
199   /* XXX: dear compiler, these are for you: */
200   (void)agent; (void)stream_id; (void)data;
201 }
202
203
204 int main (void)
205 {
206   NiceAgent *lagent, *ragent;      /* agent's L and R */
207   NiceAddress baseaddr;
208   guint timer_id;
209   GSList *cands, *i;
210   guint ls_id, rs_id;
211
212 #ifdef G_OS_WIN32
213   WSADATA w;
214
215   WSAStartup(0x0202, &w);
216 #endif
217
218   global_mainloop = g_main_loop_new (NULL, FALSE);
219
220   /* step: create the agents L and R */
221   lagent = nice_agent_new (g_main_loop_get_context (global_mainloop),
222       NICE_COMPATIBILITY_GOOGLE);
223   ragent = nice_agent_new (g_main_loop_get_context (global_mainloop),
224       NICE_COMPATIBILITY_GOOGLE);
225
226   if (!nice_address_set_from_string (&baseaddr, "127.0.0.1"))
227     g_assert_not_reached ();
228   nice_agent_add_local_address (lagent, &baseaddr);
229   nice_agent_add_local_address (ragent, &baseaddr);
230
231   /* step: add a timer to catch state changes triggered by signals */
232   timer_id = g_timeout_add (30000, timer_cb, NULL);
233
234   g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done",
235       G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1));
236   g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done",
237       G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2));
238   g_signal_connect (G_OBJECT (lagent), "component-state-changed",
239       G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1));
240   g_signal_connect (G_OBJECT (ragent), "component-state-changed",
241       G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2));
242   g_signal_connect (G_OBJECT (lagent), "new-selected-pair",
243       G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1));
244   g_signal_connect (G_OBJECT (ragent), "new-selected-pair",
245       G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2));
246   g_signal_connect (G_OBJECT (lagent), "new-candidate",
247       G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (1));
248   g_signal_connect (G_OBJECT (ragent), "new-candidate",
249       G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (2));
250   g_signal_connect (G_OBJECT (lagent), "initial-binding-request-received",
251       G_CALLBACK (cb_initial_binding_request_received),
252       GUINT_TO_POINTER (1));
253   g_signal_connect (G_OBJECT (ragent), "initial-binding-request-received",
254       G_CALLBACK (cb_initial_binding_request_received),
255       GUINT_TO_POINTER (2));
256
257   /* step: run test */
258   g_debug ("test-trickle: running test");
259
260   /* step: initialize variables modified by the callbacks */
261   global_components_ready = 0;
262   global_components_ready_exit = 2;
263   global_components_failed = 0;
264   global_components_failed_exit = 0;
265   global_lagent_gathering_done = FALSE;
266   global_ragent_gathering_done = FALSE;
267   global_lagent_ibr_received =
268     global_ragent_ibr_received = FALSE;
269   global_lagent_cands =
270     global_ragent_cands = 0;
271
272   g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL);
273   g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL);
274
275   g_object_set (G_OBJECT (lagent), "ice-trickle", TRUE, NULL);
276   g_object_set (G_OBJECT (ragent), "ice-trickle", TRUE, NULL);
277
278   /* An application using more than one NiceAgent instance may crash due to
279    * a race in gUPnP.
280    *
281    * UPnP can be re-enabled here and in other libnice tests once gUPnP
282    * 1.1.2 / 1.0.4 is released.
283    *
284    * See https://gitlab.gnome.org/GNOME/gupnp/commit/0123e574595e0a547ce26422633df72d63d3d0e0
285    */
286   g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL);
287   g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL);
288
289   /* step: add one stream, with RTP+RTCP components, to each agent */
290   ls_id = nice_agent_add_stream (lagent, 1);
291
292   rs_id = nice_agent_add_stream (ragent, 1);
293   g_assert_cmpuint (ls_id, >, 0);
294   g_assert_cmpuint (rs_id, >, 0);
295
296
297   nice_agent_gather_candidates (lagent, ls_id);
298   nice_agent_gather_candidates (ragent, rs_id);
299
300   /* step: attach to mainloop (needed to register the fds) */
301   nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP,
302       g_main_loop_get_context (global_mainloop), cb_nice_recv,
303       GUINT_TO_POINTER (1));
304   nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP,
305       g_main_loop_get_context (global_mainloop), cb_nice_recv,
306       GUINT_TO_POINTER (2));
307
308   /* step: run mainloop until local candidates are ready
309    *       (see timer_cb() above) */
310   if (global_lagent_gathering_done != TRUE ||
311       global_ragent_gathering_done != TRUE) {
312     g_debug ("test-trickle: Added streams, running mainloop until 'candidate-gathering-done'...");
313     g_main_loop_run (global_mainloop);
314     g_assert (global_lagent_gathering_done == TRUE);
315     g_assert (global_ragent_gathering_done == TRUE);
316   }
317
318   {
319       gchar *ufrag = NULL, *password = NULL;
320       nice_agent_get_local_credentials(lagent, ls_id, &ufrag, &password);
321       nice_agent_set_remote_credentials (ragent,
322           rs_id, ufrag, password);
323       g_free (ufrag);
324       g_free (password);
325       nice_agent_get_local_credentials(ragent, rs_id, &ufrag, &password);
326       nice_agent_set_remote_credentials (lagent,
327           ls_id, ufrag, password);
328       g_free (ufrag);
329       g_free (password);
330   }
331   cands = nice_agent_get_local_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTP);
332   nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, cands);
333   for (i = cands; i; i = i->next)
334     nice_candidate_free ((NiceCandidate *) i->data);
335   g_slist_free (cands);
336   cands = nice_agent_get_local_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTP);
337   nice_agent_set_remote_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, cands);
338   for (i = cands; i; i = i->next)
339     nice_candidate_free ((NiceCandidate *) i->data);
340   g_slist_free (cands);
341
342   nice_agent_peer_candidate_gathering_done (lagent, ls_id);
343   nice_agent_peer_candidate_gathering_done (ragent, rs_id);
344
345   g_debug ("test-trickle: Set properties, next running mainloop until connectivity checks succeed...");
346
347   /* step: run the mainloop until connectivity checks succeed
348    *       (see timer_cb() above) */
349   g_main_loop_run (global_mainloop);
350
351   /* note: verify that STUN binding requests were sent */
352   g_assert (global_lagent_ibr_received == TRUE);
353   g_assert_cmpint (global_ragent_ibr_received, ==, TRUE);
354
355   g_assert_cmpint (global_lagent_state, ==, NICE_COMPONENT_STATE_READY);
356   g_assert_cmpint (global_ragent_state, ==, NICE_COMPONENT_STATE_READY);
357   /* note: verify that correct number of local candidates were reported */
358   g_assert_cmpint (global_lagent_cands, ==, 1);
359   g_assert_cmpint (global_ragent_cands, ==, 1);
360
361   g_debug ("test-trickle: agents are ready.. now adding new buggy candidate");
362
363   g_timeout_add (500, quit_loop_cb, NULL);
364   g_main_loop_run (global_mainloop);
365
366   //global_components_ready--;
367
368   cands = nice_agent_get_local_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTP);
369   nice_address_set_port(&((NiceCandidate *) cands->data)->addr, 80);
370   nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, cands);
371   for (i = cands; i; i = i->next)
372     nice_candidate_free ((NiceCandidate *) i->data);
373   g_slist_free (cands);
374
375   g_assert_cmpint (global_lagent_state, ==, NICE_COMPONENT_STATE_CONNECTED);
376   g_main_loop_run (global_mainloop);
377   g_assert_cmpint (global_lagent_state, ==, NICE_COMPONENT_STATE_READY);
378
379   /*
380   g_debug ("test-trickle: buggy candidate worked, testing lower priority cand");
381
382   cands = nice_agent_get_local_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTP);
383   nice_address_set_port(&((NiceCandidate *) cands->data)->addr, 80);
384   ((NiceCandidate *) cands->data)->priority -= 100;
385   nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, cands);
386   for (i = cands; i; i = i->next)
387     nice_candidate_free ((NiceCandidate *) i->data);
388   g_slist_free (cands);
389
390   g_assert_cmpint (global_lagent_state, ==, NICE_COMPONENT_STATE_READY);*/
391
392   /* note: test payload send and receive */
393   global_ragent_read = 0;
394   g_assert_cmpint (nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678"), ==, 16);
395   g_main_loop_run (global_mainloop);
396   g_assert_cmpint (global_ragent_read, ==, 16);
397
398   g_debug ("test-trickle: Ran mainloop, removing streams...");
399
400   /* step: clean up resources and exit */
401
402   nice_agent_remove_stream (lagent, ls_id);
403   nice_agent_remove_stream (ragent, rs_id);
404   priv_print_global_status ();
405   g_assert_cmpint (global_lagent_state, ==, NICE_COMPONENT_STATE_READY);
406   g_assert_cmpint (global_ragent_state, >=, NICE_COMPONENT_STATE_CONNECTED);
407   /* note: verify that correct number of local candidates were reported */
408   g_assert_cmpint (global_lagent_cands, ==, 1);
409   g_assert_cmpint (global_ragent_cands, ==, 1);
410
411
412   g_object_unref (lagent);
413   g_object_unref (ragent);
414
415   g_main_loop_unref (global_mainloop);
416   global_mainloop = NULL;
417
418   g_source_remove (timer_id);
419
420 #ifdef G_OS_WIN32
421   WSACleanup();
422 #endif
423   return 0;
424 }