1 /* Tests of TpFileTransferChannel
2 *
3 * Copyright (C) 2010-2011 Morten Mjelva <morten.mjelva@gmail.com>
4 * Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
5 *
6 * Copying and distribution of this file, with or without modification,
7 * are permitted in any medium without royalty provided the copyright
8 * notice and this notice are preserved.
9 */
10
11 #include "config.h"
12
13 #include <glib.h>
14 #include <string.h>
15
16 #include <telepathy-glib/file-transfer-channel.h>
17 #include <telepathy-glib/debug.h>
18 #include <telepathy-glib/defs.h>
19 #include <telepathy-glib/dbus.h>
20
21 #include "tests/lib/util.h"
22 #include "tests/lib/debug.h"
23 #include "tests/lib/simple-conn.h"
24 #include "tests/lib/file-transfer-chan.h"
25 #include "tests/lib/stream-tube-chan.h"
26
27 typedef struct {
28 TpSocketAddressType address_type;
29 TpSocketAccessControl access_control;
30 } TestContext;
31
32 TestContext contexts[] = {
33 { TP_SOCKET_ADDRESS_TYPE_UNIX, TP_SOCKET_ACCESS_CONTROL_LOCALHOST },
34 // { TP_SOCKET_ADDRESS_TYPE_UNIX, TP_SOCKET_ACCESS_CONTROL_CREDENTIALS },
35 // { TP_SOCKET_ADDRESS_TYPE_IPV4, TP_SOCKET_ACCESS_CONTROL_LOCALHOST },
36 // { TP_SOCKET_ADDRESS_TYPE_IPV4, TP_SOCKET_ACCESS_CONTROL_PORT },
37 // { TP_SOCKET_ADDRESS_TYPE_IPV6, TP_SOCKET_ACCESS_CONTROL_LOCALHOST },
38 // { TP_SOCKET_ADDRESS_TYPE_IPV6, TP_SOCKET_ACCESS_CONTROL_PORT },
39
40 { TP_NUM_SOCKET_ADDRESS_TYPES, TP_NUM_SOCKET_ACCESS_CONTROLS }
41 };
42
43 typedef struct {
44 GMainLoop *mainloop;
45 TpDBusDaemon *dbus;
46
47 /* Service side objects */
48 TpBaseConnection *base_connection;
49 TpTestsFileTransferChannel *chan_service;
50 TpHandleRepoIface *contact_repo;
51 TpHandleRepoIface *room_repo;
52
53 /* Client side objects */
54 TpConnection *connection;
55 TpFileTransferChannel *channel;
56 GIOStream *cm_stream;
57
58 GError *error /* initialized where needed */;
59 gint wait;
60 } Test;
61
62
63 /* Callbacks */
64
65 static void
state_notify_cb(GObject * source,GParamSpec * pspec,Test * test)66 state_notify_cb (GObject *source,
67 GParamSpec *pspec,
68 Test *test)
69 {
70 DEBUG ("state_notify_cb was triggered");
71
72 test->wait--;
73 if (test->wait <= 0)
74 g_main_loop_quit (test->mainloop);
75 }
76
77 static void
channel_prepared_cb(GObject * source,GAsyncResult * result,gpointer user_data)78 channel_prepared_cb (GObject *source,
79 GAsyncResult *result,
80 gpointer user_data)
81 {
82 Test *test = user_data;
83
84 tp_proxy_prepare_finish (source, result, &test->error);
85
86 test->wait--;
87 if (test->wait <= 0)
88 g_main_loop_quit (test->mainloop);
89 }
90
91 static void
file_provide_cb(GObject * source,GAsyncResult * result,gpointer user_data)92 file_provide_cb (GObject *source,
93 GAsyncResult *result,
94 gpointer user_data)
95 {
96 Test *test = user_data;
97 DEBUG ("file_provide_cb reached");
98
99 tp_file_transfer_channel_provide_file_finish (
100 TP_FILE_TRANSFER_CHANNEL (source), result, &test->error);
101
102 test->wait--;
103 if (test->wait <= 0)
104 g_main_loop_quit (test->mainloop);
105 }
106
107 static void
file_accept_cb(GObject * source,GAsyncResult * result,gpointer user_data)108 file_accept_cb (GObject *source,
109 GAsyncResult *result,
110 gpointer user_data)
111 {
112 Test *test = user_data;
113 DEBUG ("file_accept_cb reached");
114
115 tp_file_transfer_channel_accept_file_finish (
116 TP_FILE_TRANSFER_CHANNEL (source), result, &test->error);
117
118 test->wait--;
119 if (test->wait <= 0)
120 g_main_loop_quit (test->mainloop);
121 }
122
123
124 /* Internal functions */
125
126 static void
destroy_socket_control_list(gpointer data)127 destroy_socket_control_list (gpointer data)
128 {
129 GArray *tab = data;
130 g_array_unref (tab);
131 }
132
133 static GHashTable *
create_available_socket_types_hash(TpSocketAddressType address_type,TpSocketAccessControl access_control)134 create_available_socket_types_hash (TpSocketAddressType address_type,
135 TpSocketAccessControl access_control)
136 {
137 GHashTable *ret;
138 GArray *tab;
139
140 ret = g_hash_table_new_full (NULL, NULL, NULL, destroy_socket_control_list);
141
142 tab = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl),
143 1);
144 g_array_append_val (tab, access_control);
145
146 g_hash_table_insert (ret, GUINT_TO_POINTER (address_type), tab);
147
148 return ret;
149 }
150
151 static void
create_file_transfer_channel(Test * test,gboolean requested,TpSocketAddressType address_type,TpSocketAccessControl access_control)152 create_file_transfer_channel (Test *test,
153 gboolean requested,
154 TpSocketAddressType address_type,
155 TpSocketAccessControl access_control)
156 {
157 gchar *chan_path;
158 TpHandle handle, alf_handle;
159 GHashTable *props;
160 GHashTable *sockets;
161 GHashTable *metadata;
162 GQuark features[] = { TP_FILE_TRANSFER_CHANNEL_FEATURE_CORE, 0};
163 const gchar * const metadata_values[] = { "cheese", NULL };
164
165 /* Create service-side file transfer channel object */
166 tp_proxy_get_object_path (test->connection);
167 chan_path = g_strdup_printf ("%s/Channel",
168 tp_proxy_get_object_path (test->connection));
169
170 test->contact_repo = tp_base_connection_get_handles (test->base_connection,
171 TP_HANDLE_TYPE_CONTACT);
172 g_assert (test->contact_repo != NULL);
173
174 handle = tp_handle_ensure (test->contact_repo, "bob", NULL, &test->error);
175 g_assert_no_error (test->error);
176
177 alf_handle = tp_handle_ensure (test->contact_repo, "alf", NULL, &test->error);
178 g_assert_no_error (test->error);
179
180 sockets = create_available_socket_types_hash (address_type, access_control);
181
182 metadata = g_hash_table_new (g_str_hash, g_str_equal);
183 g_hash_table_insert (metadata, "banana", (gpointer) metadata_values);
184
185 test->chan_service = g_object_new (
186 TP_TESTS_TYPE_FILE_TRANSFER_CHANNEL,
187 /* TpProxy properties */
188 "object-path", chan_path,
189
190 /* TpChannel properties */
191 "connection", test->base_connection,
192 "handle", handle,
193 "initiator-handle", alf_handle,
194 "requested", requested,
195
196 /* TpFileTransferChannel properties */
197 "available-socket-types", sockets,
198 "content-type", "text/plain",
199 "date", (guint64) 271828,
200 "description", "badger",
201 "filename", "snake.txt",
202 "initial-offset", (guint64) 0,
203 "size", (guint64) 9001,
204 "state", TP_FILE_TRANSFER_STATE_PENDING,
205 "transferred-bytes", (guint64) 42,
206 /* Metadata properties */
207 "service-name", "fit.service.name",
208 "metadata", metadata,
209 NULL);
210
211 /* Create client-side file transfer channel object */
212 g_object_get (test->chan_service,
213 "channel-properties", &props,
214 NULL);
215
216 test->channel = tp_file_transfer_channel_new (test->connection, chan_path,
217 props, &test->error);
218 g_assert_no_error (test->error);
219
220 /* Prepare core feature */
221 tp_proxy_prepare_async (test->channel, features, channel_prepared_cb, test);
222
223 test->wait = 1;
224 g_main_loop_run (test->mainloop);
225 g_assert_no_error (test->error);
226
227 g_free (chan_path);
228 g_hash_table_unref (metadata);
229 g_hash_table_unref (props);
230 g_hash_table_unref (sockets);
231 tp_handle_unref (test->contact_repo, handle);
232 }
233
234 static void
setup(Test * test,gconstpointer data)235 setup (Test *test,
236 gconstpointer data)
237 {
238 test->mainloop = g_main_loop_new (NULL, FALSE);
239 test->dbus = tp_tests_dbus_daemon_dup_or_die ();
240
241 test->error = NULL;
242
243 /* Create (service and client sides) connection objects */
244 tp_tests_create_and_connect_conn (TP_TESTS_TYPE_SIMPLE_CONNECTION,
245 "me@test.com", &test->base_connection, &test->connection);
246 }
247
248 static void
teardown(Test * test,gconstpointer data)249 teardown (Test *test,
250 gconstpointer data)
251 {
252 g_clear_error (&test->error);
253
254 tp_clear_object (&test->dbus);
255 g_main_loop_unref (test->mainloop);
256 test->mainloop = NULL;
257
258 tp_clear_object (&test->chan_service);
259 tp_clear_object (&test->cm_stream);
260
261 tp_tests_connection_assert_disconnect_succeeds (test->connection);
262 g_object_unref (test->connection);
263 g_object_unref (test->base_connection);
264
265 tp_clear_object (&test->channel);
266 }
267
268 typedef void (*TestFunc) (Test *, gconstpointer);
269
270 static gchar *
test_context_to_str(TestContext * ctx,const gchar * base)271 test_context_to_str (TestContext *ctx,
272 const gchar *base)
273 {
274 const gchar *socket, *access_control;
275
276 switch (ctx->address_type)
277 {
278 case TP_SOCKET_ADDRESS_TYPE_UNIX:
279 socket = "unix";
280 break;
281 case TP_SOCKET_ADDRESS_TYPE_IPV4:
282 socket = "ipv4";
283 break;
284 case TP_SOCKET_ADDRESS_TYPE_IPV6:
285 socket = "ipv6";
286 break;
287 default:
288 g_assert_not_reached ();
289 }
290
291 switch (ctx->access_control)
292 {
293 case TP_SOCKET_ACCESS_CONTROL_LOCALHOST:
294 access_control = "localhost";
295 break;
296 case TP_SOCKET_ACCESS_CONTROL_PORT:
297 access_control = "port";
298 break;
299 case TP_SOCKET_ACCESS_CONTROL_CREDENTIALS:
300 access_control = "credentials";
301 break;
302 default:
303 g_assert_not_reached ();
304 }
305
306 return g_strdup_printf ("%s/%s/%s", base, socket, access_control);
307 }
308
309 static void
socket_connected(GObject * source,GAsyncResult * result,gpointer user_data)310 socket_connected (GObject *source,
311 GAsyncResult *result,
312 gpointer user_data)
313 {
314 Test *test = user_data;
315
316 tp_clear_object (&test->cm_stream);
317
318 test->cm_stream = G_IO_STREAM (g_socket_client_connect_finish (
319 G_SOCKET_CLIENT (source), result, &test->error));
320
321 test->wait--;
322 if (test->wait <= 0)
323 g_main_loop_quit (test->mainloop);
324 }
325
326 static void
run_file_transfer_test(const char * test_path,TestFunc ftest)327 run_file_transfer_test (const char *test_path,
328 TestFunc ftest)
329 {
330 guint i;
331
332 for (i = 0; contexts[i].address_type != TP_NUM_SOCKET_ADDRESS_TYPES; i++)
333 {
334 gchar *path = test_context_to_str (&contexts[i], test_path);
335
336 g_test_add (path, Test, GUINT_TO_POINTER (i), setup, ftest, teardown);
337
338 g_free (path);
339 }
340 }
341
342
343 /* Tests */
344
345 /* Test channel creation */
346 static void
test_create_requested(Test * test,gconstpointer data G_GNUC_UNUSED)347 test_create_requested (Test *test,
348 gconstpointer data G_GNUC_UNUSED)
349 {
350 const GError *error = NULL;
351
352 create_file_transfer_channel (test, TRUE, TP_SOCKET_ADDRESS_TYPE_UNIX,
353 TP_SOCKET_ACCESS_CONTROL_LOCALHOST);
354
355 g_assert (TP_IS_FILE_TRANSFER_CHANNEL (test->channel));
356 g_assert (G_IS_OBJECT (test->channel));
357
358 error = tp_proxy_get_invalidated (test->channel);
359 g_assert_no_error (error);
360 }
361
362 static void
test_create_unrequested(Test * test,gconstpointer data G_GNUC_UNUSED)363 test_create_unrequested (Test *test,
364 gconstpointer data G_GNUC_UNUSED)
365 {
366 const GError *error = NULL;
367
368 create_file_transfer_channel (test, FALSE, TP_SOCKET_ADDRESS_TYPE_UNIX,
369 TP_SOCKET_ACCESS_CONTROL_LOCALHOST);
370
371 g_assert (TP_IS_FILE_TRANSFER_CHANNEL (test->channel));
372 g_assert (G_IS_OBJECT (test->channel));
373
374 error = tp_proxy_get_invalidated (test->channel);
375 g_assert_no_error (error);
376 }
377
378 /* Test setting and getting properties */
379 static void
test_properties(Test * test,gconstpointer data G_GNUC_UNUSED)380 test_properties (Test *test,
381 gconstpointer data G_GNUC_UNUSED)
382 {
383 GDateTime *date1, *date2;
384 TpFileTransferStateChangeReason reason;
385 const GError *error = NULL;
386 const GHashTable *metadata;
387 const gchar * const *metadata_values;
388
389 create_file_transfer_channel (test, FALSE, TP_SOCKET_ADDRESS_TYPE_UNIX,
390 TP_SOCKET_ACCESS_CONTROL_LOCALHOST);
391
392 g_assert_cmpstr (tp_file_transfer_channel_get_mime_type (test->channel),
393 ==, "text/plain");
394
395 date1 = tp_file_transfer_channel_get_date (test->channel);
396 date2 = g_date_time_new_from_unix_utc (271828);
397 g_assert (g_date_time_equal (date1, date2));
398 g_date_time_unref (date2);
399
400 g_assert_cmpstr (tp_file_transfer_channel_get_description (test->channel),
401 ==, "badger");
402
403 g_assert_cmpstr (tp_file_transfer_channel_get_filename (test->channel),
404 ==, "snake.txt");
405
406 g_assert_cmpuint (tp_file_transfer_channel_get_size (test->channel),
407 ==, 9001);
408
409 g_assert_cmpuint (tp_file_transfer_channel_get_state (test->channel, &reason),
410 ==, TP_FILE_TRANSFER_STATE_PENDING);
411
412 g_assert_cmpuint (tp_file_transfer_channel_get_transferred_bytes
413 (test->channel), ==, 42);
414
415 g_assert_cmpstr (tp_file_transfer_channel_get_service_name (test->channel),
416 ==, "fit.service.name");
417
418 metadata = tp_file_transfer_channel_get_metadata (test->channel);
419 g_assert_cmpuint (g_hash_table_size ((GHashTable *) metadata), ==, 1);
420 metadata_values = g_hash_table_lookup ((GHashTable *) metadata, "banana");
421 g_assert_cmpuint (g_strv_length ((GStrv) metadata_values), ==, 1);
422 g_assert_cmpstr (metadata_values[0], ==, "cheese");
423
424 error = tp_proxy_get_invalidated (test->channel);
425 g_assert_no_error (error);
426 }
427
428 /* Test sending files */
429 static void
test_provide_success(Test * test,gconstpointer data G_GNUC_UNUSED)430 test_provide_success (Test *test,
431 gconstpointer data G_GNUC_UNUSED)
432 {
433 GSocketAddress *address;
434 GSocketClient *client;
435 GFile *file;
436 TpFileTransferStateChangeReason reason;
437 guint i = GPOINTER_TO_UINT (data);
438
439 create_file_transfer_channel (test, TRUE, contexts[i].address_type,
440 contexts[i].access_control);
441
442 g_assert_cmpuint (tp_file_transfer_channel_get_state (test->channel, &reason),
443 ==, TP_FILE_TRANSFER_STATE_PENDING);
444
445 /* not very pretty */
446 g_file_set_contents ("/tmp/file-transfer", "test", -1, NULL);
447
448 file = g_file_new_for_uri ("file:///tmp/file-transfer");
449 tp_file_transfer_channel_provide_file_async (test->channel,
450 file, file_provide_cb, test);
451 g_object_unref (file);
452
453 test->wait = 1;
454 g_main_loop_run (test->mainloop);
455 g_assert_no_error (test->error);
456
457 g_assert_cmpuint (tp_file_transfer_channel_get_state (test->channel, &reason),
458 ==, TP_FILE_TRANSFER_STATE_PENDING);
459
460 g_signal_connect (test->channel, "notify::state",
461 G_CALLBACK (state_notify_cb), test);
462
463 test->wait = 1;
464 g_main_loop_run (test->mainloop);
465 g_assert_no_error (test->error);
466
467 /* File transfer should be in the open state by now */
468 g_assert_cmpuint (tp_file_transfer_channel_get_state (test->channel, &reason),
469 ==, TP_FILE_TRANSFER_STATE_OPEN);
470
471 /* A wild CLIENT appears */
472 address = tp_tests_file_transfer_channel_get_server_address
473 (test->chan_service);
474 g_assert (address != NULL);
475 client = g_socket_client_new ();
476 g_socket_client_connect_async (client, G_SOCKET_CONNECTABLE (address),
477 NULL, socket_connected, test);
478
479 g_object_unref (client);
480 g_object_unref (address);
481
482 test->wait = 1;
483 g_main_loop_run (test->mainloop);
484 g_assert_no_error (test->error);
485 g_assert (test->cm_stream != NULL);
486 }
487
488 static void
test_cancel_transfer(Test * test,gconstpointer data G_GNUC_UNUSED)489 test_cancel_transfer (Test *test,
490 gconstpointer data G_GNUC_UNUSED)
491 {
492 TpFileTransferStateChangeReason reason;
493
494 create_file_transfer_channel (test, FALSE, TP_SOCKET_ADDRESS_TYPE_UNIX,
495 TP_SOCKET_ACCESS_CONTROL_LOCALHOST);
496
497 g_assert_cmpuint (tp_file_transfer_channel_get_state (test->channel, &reason),
498 ==, TP_FILE_TRANSFER_STATE_PENDING);
499 }
500
501 /* Test receiving files */
502 static void
test_accept_success(Test * test,gconstpointer data G_GNUC_UNUSED)503 test_accept_success (Test *test, gconstpointer data G_GNUC_UNUSED)
504 {
505 GFile *file;
506 TpFileTransferStateChangeReason reason;
507 guint i = GPOINTER_TO_UINT (data);
508
509 create_file_transfer_channel (test, FALSE, contexts[i].address_type,
510 contexts[i].access_control);
511
512 g_assert_cmpuint (tp_file_transfer_channel_get_state (test->channel, &reason),
513 ==, TP_FILE_TRANSFER_STATE_PENDING);
514
515 file = g_file_new_for_uri ("file:///tmp/file-transfer");
516 tp_file_transfer_channel_accept_file_async (test->channel,
517 file, 0, file_accept_cb, test);
518
519 test->wait = 1;
520 g_main_loop_run (test->mainloop);
521 g_assert_no_error (test->error);
522
523 g_assert_cmpuint (tp_file_transfer_channel_get_state (test->channel, &reason),
524 ==, TP_FILE_TRANSFER_STATE_ACCEPTED);
525
526 g_signal_connect (test->channel, "notify::state",
527 G_CALLBACK (state_notify_cb), test);
528
529 test->wait = 1;
530 g_main_loop_run (test->mainloop);
531 g_assert_no_error (test->error);
532
533 /* File transfer should be in the open state by now */
534 g_assert_cmpuint (tp_file_transfer_channel_get_state (test->channel, &reason),
535 ==, TP_FILE_TRANSFER_STATE_OPEN);
536
537 g_object_unref (file);
538 }
539
540 static void
test_accept_twice(Test * test,gconstpointer data G_GNUC_UNUSED)541 test_accept_twice (Test *test, gconstpointer data G_GNUC_UNUSED)
542 {
543 GFile *file;
544
545 create_file_transfer_channel (test, FALSE, TP_SOCKET_ADDRESS_TYPE_UNIX,
546 TP_SOCKET_ACCESS_CONTROL_LOCALHOST);
547
548 file = g_file_new_for_uri ("file:///tmp/file-transfer");
549
550 tp_file_transfer_channel_accept_file_async (test->channel,
551 file, 0, file_accept_cb, test);
552 test->wait = 1;
553 g_main_loop_run (test->mainloop);
554 g_assert_no_error (test->error);
555
556 /* Try to re-accept the transfer */
557 tp_file_transfer_channel_accept_file_async (test->channel,
558 file, 0, file_accept_cb, test);
559 test->wait = 1;
560 g_main_loop_run (test->mainloop);
561 g_assert_error (test->error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT);
562
563 g_object_unref (file);
564 }
565
566 static void
test_accept_outgoing(Test * test,gconstpointer data G_GNUC_UNUSED)567 test_accept_outgoing (Test *test, gconstpointer data G_GNUC_UNUSED)
568 {
569 GFile *file;
570
571 create_file_transfer_channel (test, TRUE, TP_SOCKET_ADDRESS_TYPE_UNIX,
572 TP_SOCKET_ACCESS_CONTROL_LOCALHOST);
573
574 file = g_file_new_for_uri ("file:///tmp/file-transfer");
575
576 tp_file_transfer_channel_accept_file_async (test->channel,
577 file, 0, file_accept_cb, test);
578 test->wait = 1;
579 g_main_loop_run (test->mainloop);
580 g_assert_error (test->error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT);
581 }
582
583 int
main(int argc,char ** argv)584 main (int argc,
585 char **argv)
586 {
587 tp_tests_abort_after (10);
588 tp_debug_set_flags ("all");
589
590 g_test_init (&argc, &argv, NULL);
591 g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id=");
592
593 /* Test basic object creation etc */
594 g_test_add ("/file-transfer-channel/create/requested", Test, NULL, setup,
595 test_create_requested, teardown);
596 g_test_add ("/file-transfer-channel/create/unrequested", Test, NULL, setup,
597 test_create_unrequested, teardown);
598 g_test_add ("/file-transfer-channel/properties", Test, NULL, setup,
599 test_properties, teardown);
600
601 /* Run provide and accept in different contexts */
602 run_file_transfer_test ("/file-transfer-channel/accept/success",
603 test_accept_success);
604 run_file_transfer_test ("/file-transfer-channel/provide/success",
605 test_provide_success);
606
607 /* Test edge cases */
608 /* FIXME: accept_twice has to be after provide/accept_success */
609 g_test_add ("/file-transfer-channel/accept/twice", Test, NULL, setup,
610 test_accept_twice, teardown);
611 g_test_add ("/file-transfer-channel/accept/outgoing", Test, NULL, setup,
612 test_accept_outgoing, teardown);
613 g_test_add ("/file-transfer-channel/provide/cancel", Test, NULL, setup,
614 test_cancel_transfer, teardown);
615
616 return tp_tests_run_with_bus ();
617 }
618