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