[kdbus] KDBUS_ITEM_PAYLOAD_OFF items are (once again) relative to msg header
[platform/upstream/glib.git] / gio / tests / file.c
1 #include <string.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <gio/gio.h>
5 #include <gio/gfiledescriptorbased.h>
6 #ifdef G_OS_UNIX
7 #include <sys/stat.h>
8 #endif
9
10 static void
11 test_basic (void)
12 {
13   GFile *file;
14   gchar *s;
15
16   file = g_file_new_for_path ("./some/directory/testfile");
17
18   s = g_file_get_basename (file);
19   g_assert_cmpstr (s, ==, "testfile");
20   g_free (s);
21
22   s = g_file_get_uri (file);
23   g_assert (g_str_has_prefix (s, "file://"));
24   g_assert (g_str_has_suffix (s, "/some/directory/testfile"));
25   g_free (s);
26
27   g_assert (g_file_has_uri_scheme (file, "file"));
28   s = g_file_get_uri_scheme (file);
29   g_assert_cmpstr (s, ==, "file");
30   g_free (s);
31
32   g_object_unref (file);
33 }
34
35 static void
36 test_parent (void)
37 {
38   GFile *file;
39   GFile *file2;
40   GFile *parent;
41   GFile *root;
42
43   file = g_file_new_for_path ("./some/directory/testfile");
44   file2 = g_file_new_for_path ("./some/directory");
45   root = g_file_new_for_path ("/");
46
47   g_assert (g_file_has_parent (file, file2));
48
49   parent = g_file_get_parent (file);
50   g_assert (g_file_equal (parent, file2));
51   g_object_unref (parent);
52
53   g_assert (g_file_get_parent (root) == NULL);
54
55   g_object_unref (file);
56   g_object_unref (file2);
57   g_object_unref (root);
58 }
59
60 static void
61 test_child (void)
62 {
63   GFile *file;
64   GFile *child;
65   GFile *child2;
66
67   file = g_file_new_for_path ("./some/directory");
68   child = g_file_get_child (file, "child");
69   g_assert (g_file_has_parent (child, file));
70
71   child2 = g_file_get_child_for_display_name (file, "child2", NULL);
72   g_assert (g_file_has_parent (child2, file));
73
74   g_object_unref (child);
75   g_object_unref (child2);
76   g_object_unref (file);
77 }
78
79 static void
80 test_type (void)
81 {
82   GFile *datapath_f;
83   GFile *file;
84   GFileType type;
85   GError *error = NULL;
86
87   datapath_f = g_file_new_for_path (g_test_get_dir (G_TEST_DIST));
88
89   file = g_file_get_child (datapath_f, "g-icon.c");
90   type = g_file_query_file_type (file, 0, NULL);
91   g_assert_cmpint (type, ==, G_FILE_TYPE_REGULAR);
92   g_object_unref (file);
93
94   file = g_file_get_child (datapath_f, "cert-tests");
95   type = g_file_query_file_type (file, 0, NULL);
96   g_assert_cmpint (type, ==, G_FILE_TYPE_DIRECTORY);
97
98   g_file_read (file, NULL, &error);
99   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY);
100   g_error_free (error);
101   g_object_unref (file);
102
103   g_object_unref (datapath_f);
104 }
105
106 static void
107 test_parse_name (void)
108 {
109   GFile *file;
110   gchar *name;
111
112   file = g_file_new_for_uri ("file://somewhere");
113   name = g_file_get_parse_name (file);
114   g_assert_cmpstr (name, ==, "file://somewhere");
115   g_object_unref (file);
116   g_free (name);
117
118   file = g_file_parse_name ("~foo");
119   name = g_file_get_parse_name (file);
120   g_assert (name != NULL);
121   g_object_unref (file);
122   g_free (name);
123 }
124
125 typedef struct
126 {
127   GFile *file;
128   GFileMonitor *monitor;
129   GOutputStream *ostream;
130   GInputStream *istream;
131   GMainLoop *loop;
132   gint buffersize;
133   gint monitor_created;
134   gint monitor_deleted;
135   gint monitor_changed;
136   gchar *monitor_path;
137   gint pos;
138   gchar *data;
139   gchar *buffer;
140   guint timeout;
141 } CreateDeleteData;
142
143 static void
144 monitor_changed (GFileMonitor      *monitor,
145                  GFile             *file,
146                  GFile             *other_file,
147                  GFileMonitorEvent  event_type,
148                  gpointer           user_data)
149 {
150   CreateDeleteData *data = user_data;
151   gchar *path;
152
153   path = g_file_get_path (file);
154   g_assert_cmpstr (data->monitor_path, ==, path);
155   g_free (path);
156
157   if (event_type == G_FILE_MONITOR_EVENT_CREATED)
158     data->monitor_created++;
159   if (event_type == G_FILE_MONITOR_EVENT_DELETED)
160     data->monitor_deleted++;
161   if (event_type == G_FILE_MONITOR_EVENT_CHANGED)
162     data->monitor_changed++;
163 }
164
165
166 static gboolean
167 quit_idle (gpointer user_data)
168 {
169   CreateDeleteData *data = user_data;
170
171   g_source_remove (data->timeout); 
172   g_main_loop_quit (data->loop);
173
174   return FALSE;
175 }
176
177 static void
178 iclosed_cb (GObject      *source,
179             GAsyncResult *res,
180             gpointer      user_data)
181 {
182   CreateDeleteData *data = user_data;
183   GError *error;
184   gboolean ret;
185
186   error = NULL;
187   ret = g_input_stream_close_finish (data->istream, res, &error);
188   g_assert_no_error (error);
189   g_assert (ret);
190
191   g_assert (g_input_stream_is_closed (data->istream));
192
193   ret = g_file_delete (data->file, NULL, &error);
194   g_assert (ret);
195   g_assert_no_error (error);
196
197   /* work around file monitor bug:
198    * inotify events are only processed every 1000 ms, regardless
199    * of the rate limit set on the file monitor
200    */
201   g_timeout_add (2000, quit_idle, data);
202 }
203
204 static void
205 read_cb (GObject      *source,
206          GAsyncResult *res,
207          gpointer      user_data)
208 {
209   CreateDeleteData *data = user_data;
210   GError *error;
211   gssize size;
212
213   error = NULL;
214   size = g_input_stream_read_finish (data->istream, res, &error);
215   g_assert_no_error (error);
216
217   data->pos += size;
218   if (data->pos < strlen (data->data))
219     {
220       g_input_stream_read_async (data->istream,
221                                  data->buffer + data->pos,
222                                  strlen (data->data) - data->pos,
223                                  0,
224                                  NULL,
225                                  read_cb,
226                                  data);
227     }
228   else
229     {
230       g_assert_cmpstr (data->buffer, ==, data->data);
231       g_assert (!g_input_stream_is_closed (data->istream));
232       g_input_stream_close_async (data->istream, 0, NULL, iclosed_cb, data);
233     }
234 }
235
236 static void
237 ipending_cb (GObject      *source,
238              GAsyncResult *res,
239              gpointer      user_data)
240 {
241   CreateDeleteData *data = user_data;
242   GError *error;
243
244   error = NULL;
245   g_input_stream_read_finish (data->istream, res, &error);
246   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING);
247   g_error_free (error);
248 }
249
250 static void
251 skipped_cb (GObject      *source,
252             GAsyncResult *res,
253             gpointer      user_data)
254 {
255   CreateDeleteData *data = user_data;
256   GError *error;
257   gssize size;
258
259   error = NULL;
260   size = g_input_stream_skip_finish (data->istream, res, &error);
261   g_assert_no_error (error);
262   g_assert_cmpint (size, ==, data->pos);
263
264   g_input_stream_read_async (data->istream,
265                              data->buffer + data->pos,
266                              strlen (data->data) - data->pos,
267                              0,
268                              NULL,
269                              read_cb,
270                              data);
271   /* check that we get a pending error */
272   g_input_stream_read_async (data->istream,
273                              data->buffer + data->pos,
274                              strlen (data->data) - data->pos,
275                              0,
276                              NULL,
277                              ipending_cb,
278                              data);
279 }
280
281 static void
282 opened_cb (GObject      *source,
283            GAsyncResult *res,
284            gpointer      user_data)
285 {
286   GFileInputStream *base;
287   CreateDeleteData *data = user_data;
288   GError *error;
289
290   error = NULL;
291   base = g_file_read_finish (data->file, res, &error);
292   g_assert_no_error (error);
293
294   if (data->buffersize == 0)
295     data->istream = G_INPUT_STREAM (g_object_ref (base));
296   else
297     data->istream = g_buffered_input_stream_new_sized (G_INPUT_STREAM (base), data->buffersize);
298   g_object_unref (base);
299
300   data->buffer = g_new0 (gchar, strlen (data->data) + 1);
301
302   /* copy initial segment directly, then skip */
303   memcpy (data->buffer, data->data, 10);
304   data->pos = 10;
305
306   g_input_stream_skip_async (data->istream,
307                              10,
308                              0,
309                              NULL,
310                              skipped_cb,
311                              data);
312 }
313
314 static void
315 oclosed_cb (GObject      *source,
316             GAsyncResult *res,
317             gpointer      user_data)
318 {
319   CreateDeleteData *data = user_data;
320   GError *error;
321   gboolean ret;
322
323   error = NULL;
324   ret = g_output_stream_close_finish (data->ostream, res, &error);
325   g_assert_no_error (error);
326   g_assert (ret);
327   g_assert (g_output_stream_is_closed (data->ostream));
328
329   g_file_read_async (data->file, 0, NULL, opened_cb, data);
330 }
331
332 static void
333 written_cb (GObject      *source,
334             GAsyncResult *res,
335             gpointer      user_data)
336 {
337   CreateDeleteData *data = user_data;
338   gssize size;
339   GError *error;
340
341   error = NULL;
342   size = g_output_stream_write_finish (data->ostream, res, &error);
343   g_assert_no_error (error);
344
345   data->pos += size;
346   if (data->pos < strlen (data->data))
347     {
348       g_output_stream_write_async (data->ostream,
349                                    data->data + data->pos,
350                                    strlen (data->data) - data->pos,
351                                    0,
352                                    NULL,
353                                    written_cb,
354                                    data);
355     }
356   else
357     {
358       g_assert (!g_output_stream_is_closed (data->ostream));
359       g_output_stream_close_async (data->ostream, 0, NULL, oclosed_cb, data);
360     }
361 }
362
363 static void
364 opending_cb (GObject      *source,
365              GAsyncResult *res,
366              gpointer      user_data)
367 {
368   CreateDeleteData *data = user_data;
369   GError *error;
370
371   error = NULL;
372   g_output_stream_write_finish (data->ostream, res, &error);
373   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING);
374   g_error_free (error);
375 }
376
377 static void
378 created_cb (GObject      *source,
379             GAsyncResult *res,
380             gpointer      user_data)
381 {
382   GFileOutputStream *base;
383   CreateDeleteData *data = user_data;
384   GError *error;
385
386   error = NULL;
387   base = g_file_create_finish (G_FILE (source), res, &error);
388   g_assert_no_error (error);
389   g_assert (g_file_query_exists  (data->file, NULL));
390
391   if (data->buffersize == 0)
392     data->ostream = G_OUTPUT_STREAM (g_object_ref (base));
393   else
394     data->ostream = g_buffered_output_stream_new_sized (G_OUTPUT_STREAM (base), data->buffersize);
395   g_object_unref (base);
396
397   g_output_stream_write_async (data->ostream,
398                                data->data,
399                                strlen (data->data),
400                                0,
401                                NULL,
402                                written_cb,
403                                data);
404   /* check that we get a pending error */
405   g_output_stream_write_async (data->ostream,
406                                data->data,
407                                strlen (data->data),
408                                0,
409                                NULL,
410                                opending_cb,
411                                data);
412 }
413
414 static gboolean
415 stop_timeout (gpointer data)
416 {
417   g_assert_not_reached ();
418
419   return FALSE;
420 }
421
422 /*
423  * This test does a fully async create-write-read-delete.
424  * Callbackistan.
425  */
426 static void
427 test_create_delete (gconstpointer d)
428 {
429   GError *error;
430   CreateDeleteData *data;
431   GFileIOStream *iostream;
432
433   data = g_new0 (CreateDeleteData, 1);
434
435   data->buffersize = GPOINTER_TO_INT (d);
436   data->data = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ0123456789";
437   data->pos = 0;
438
439   data->file = g_file_new_tmp ("g_file_create_delete_XXXXXX",
440                                &iostream, NULL);
441   g_assert (data->file != NULL);
442   g_object_unref (iostream);
443
444   data->monitor_path = g_file_get_path (data->file);
445   remove (data->monitor_path);
446
447   g_assert (!g_file_query_exists  (data->file, NULL));
448
449   error = NULL;
450   data->monitor = g_file_monitor_file (data->file, 0, NULL, &error);
451   g_assert_no_error (error);
452
453   /* This test doesn't work with GPollFileMonitor, because it assumes
454    * that the monitor will notice a create immediately followed by a
455    * delete, rather than coalescing them into nothing.
456    */
457   if (!strcmp (G_OBJECT_TYPE_NAME (data->monitor), "GPollFileMonitor"))
458     {
459       g_print ("skipping test for this GFileMonitor implementation");
460       goto skip;
461     }
462
463   g_file_monitor_set_rate_limit (data->monitor, 100);
464
465   g_signal_connect (data->monitor, "changed", G_CALLBACK (monitor_changed), data);
466
467   data->loop = g_main_loop_new (NULL, FALSE);
468
469   data->timeout = g_timeout_add (5000, stop_timeout, NULL);
470
471   g_file_create_async (data->file, 0, 0, NULL, created_cb, data);
472
473   g_main_loop_run (data->loop);
474
475   g_assert_cmpint (data->monitor_created, ==, 1);
476   g_assert_cmpint (data->monitor_deleted, ==, 1);
477   g_assert_cmpint (data->monitor_changed, >, 0);
478
479   g_assert (!g_file_monitor_is_cancelled (data->monitor));
480   g_file_monitor_cancel (data->monitor);
481   g_assert (g_file_monitor_is_cancelled (data->monitor));
482
483   g_main_loop_unref (data->loop);
484   g_object_unref (data->ostream);
485   g_object_unref (data->istream);
486
487  skip:
488   g_object_unref (data->monitor);
489   g_object_unref (data->file);
490   free (data->monitor_path);
491   g_free (data->buffer);
492   g_free (data);
493 }
494
495 static const gchar *replace_data =
496     "/**\n"
497     " * g_file_replace_contents_async:\n"
498     " * @file: input #GFile.\n"
499     " * @contents: string of contents to replace the file with.\n"
500     " * @length: the length of @contents in bytes.\n"
501     " * @etag: (allow-none): a new <link linkend=\"gfile-etag\">entity tag</link> for the @file, or %NULL\n"
502     " * @make_backup: %TRUE if a backup should be created.\n"
503     " * @flags: a set of #GFileCreateFlags.\n"
504     " * @cancellable: optional #GCancellable object, %NULL to ignore.\n"
505     " * @callback: a #GAsyncReadyCallback to call when the request is satisfied\n"
506     " * @user_data: the data to pass to callback function\n"
507     " * \n"
508     " * Starts an asynchronous replacement of @file with the given \n"
509     " * @contents of @length bytes. @etag will replace the document's\n"
510     " * current entity tag.\n"
511     " * \n"
512     " * When this operation has completed, @callback will be called with\n"
513     " * @user_user data, and the operation can be finalized with \n"
514     " * g_file_replace_contents_finish().\n"
515     " * \n"
516     " * If @cancellable is not %NULL, then the operation can be cancelled by\n"
517     " * triggering the cancellable object from another thread. If the operation\n"
518     " * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. \n"
519     " * \n"
520     " * If @make_backup is %TRUE, this function will attempt to \n"
521     " * make a backup of @file.\n"
522     " **/\n";
523
524 typedef struct
525 {
526   GFile *file;
527   const gchar *data;
528   GMainLoop *loop;
529   gboolean again;
530 } ReplaceLoadData;
531
532 static void replaced_cb (GObject      *source,
533                          GAsyncResult *res,
534                          gpointer      user_data);
535
536 static void
537 loaded_cb (GObject      *source,
538            GAsyncResult *res,
539            gpointer      user_data)
540 {
541   ReplaceLoadData *data = user_data;
542   gboolean ret;
543   GError *error;
544   gchar *contents;
545   gsize length;
546
547   error = NULL;
548   ret = g_file_load_contents_finish (data->file, res, &contents, &length, NULL, &error);
549   g_assert (ret);
550   g_assert_no_error (error);
551   g_assert_cmpint (length, ==, strlen (data->data));
552   g_assert_cmpstr (contents, ==, data->data);
553
554   g_free (contents);
555
556   if (data->again)
557     {
558       data->again = FALSE;
559       data->data = "pi pa po";
560
561       g_file_replace_contents_async (data->file,
562                                      data->data,
563                                      strlen (data->data),
564                                      NULL,
565                                      FALSE,
566                                      0,
567                                      NULL,
568                                      replaced_cb,
569                                      data);
570     }
571   else
572     {
573        error = NULL;
574        ret = g_file_delete (data->file, NULL, &error);
575        g_assert_no_error (error);
576        g_assert (ret);
577        g_assert (!g_file_query_exists (data->file, NULL));
578
579        g_main_loop_quit (data->loop);
580     }
581 }
582
583 static void
584 replaced_cb (GObject      *source,
585              GAsyncResult *res,
586              gpointer      user_data)
587 {
588   ReplaceLoadData *data = user_data;
589   GError *error;
590
591   error = NULL;
592   g_file_replace_contents_finish (data->file, res, NULL, &error);
593   g_assert_no_error (error);
594
595   g_file_load_contents_async (data->file, NULL, loaded_cb, data);
596 }
597
598 static void
599 test_replace_load (void)
600 {
601   ReplaceLoadData *data;
602   gchar *path;
603   GFileIOStream *iostream;
604
605   data = g_new0 (ReplaceLoadData, 1);
606   data->again = TRUE;
607   data->data = replace_data;
608
609   data->file = g_file_new_tmp ("g_file_replace_load_XXXXXX",
610                                &iostream, NULL);
611   g_assert (data->file != NULL);
612   g_object_unref (iostream);
613
614   path = g_file_get_path (data->file);
615   remove (path);
616
617   g_assert (!g_file_query_exists (data->file, NULL));
618
619   data->loop = g_main_loop_new (NULL, FALSE);
620
621   g_file_replace_contents_async (data->file,
622                                  data->data,
623                                  strlen (data->data),
624                                  NULL,
625                                  FALSE,
626                                  0,
627                                  NULL,
628                                  replaced_cb,
629                                  data);
630
631   g_main_loop_run (data->loop);
632
633   g_main_loop_unref (data->loop);
634   g_object_unref (data->file);
635   g_free (data);
636   free (path);
637 }
638
639 static void
640 test_replace_cancel (void)
641 {
642   GFile *tmpdir, *file;
643   GFileOutputStream *ostream;
644   GFileEnumerator *fenum;
645   GFileInfo *info;
646   GCancellable *cancellable;
647   gchar *path;
648   gsize nwrote;
649   GError *error = NULL;
650
651   g_test_bug ("629301");
652
653   path = g_dir_make_tmp ("g_file_replace_cancel_XXXXXX", &error);
654   g_assert_no_error (error);
655   tmpdir = g_file_new_for_path (path);
656   g_free (path);
657
658   file = g_file_get_child (tmpdir, "file");
659   g_file_replace_contents (file,
660                            replace_data,
661                            strlen (replace_data),
662                            NULL, FALSE, 0, NULL,
663                            NULL, &error);
664   g_assert_no_error (error);
665
666   ostream = g_file_replace (file, NULL, TRUE, 0, NULL, &error);
667   g_assert_no_error (error);
668
669   g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
670                              replace_data, strlen (replace_data),
671                              &nwrote, NULL, &error);
672   g_assert_no_error (error);
673   g_assert_cmpint (nwrote, ==, strlen (replace_data));
674
675   /* At this point there should be two files; the original and the
676    * temporary.
677    */
678   fenum = g_file_enumerate_children (tmpdir, NULL, 0, NULL, &error);
679   g_assert_no_error (error);
680
681   info = g_file_enumerator_next_file (fenum, NULL, &error);
682   g_assert_no_error (error);
683   g_assert (info != NULL);
684   g_object_unref (info);
685   info = g_file_enumerator_next_file (fenum, NULL, &error);
686   g_assert_no_error (error);
687   g_assert (info != NULL);
688   g_object_unref (info);
689
690   g_file_enumerator_close (fenum, NULL, &error);
691   g_assert_no_error (error);
692   g_object_unref (fenum);
693
694   /* Make sure the temporary gets deleted even if we cancel. */
695   cancellable = g_cancellable_new ();
696   g_cancellable_cancel (cancellable);
697   g_output_stream_close (G_OUTPUT_STREAM (ostream), cancellable, &error);
698   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
699   g_clear_error (&error);
700
701   g_object_unref (cancellable);
702   g_object_unref (ostream);
703
704   g_file_delete (file, NULL, &error);
705   g_assert_no_error (error);
706   g_object_unref (file);
707
708   /* This will only succeed if the temp file was deleted. */
709   g_file_delete (tmpdir, NULL, &error);
710   g_assert_no_error (error);
711   g_object_unref (tmpdir);
712 }
713
714 static void
715 on_file_deleted (GObject      *object,
716                  GAsyncResult *result,
717                  gpointer      user_data)
718 {
719   GError *local_error = NULL;
720   GMainLoop *loop = user_data;
721
722   (void) g_file_delete_finish ((GFile*)object, result, &local_error);
723   g_assert_no_error (local_error);
724
725   g_main_loop_quit (loop);
726 }
727
728 static void
729 test_async_delete (void)
730 {
731   GFile *file;
732   GFileIOStream *iostream;
733   GError *local_error = NULL;
734   GError **error = &local_error;
735   GMainLoop *loop;
736
737   file = g_file_new_tmp ("g_file_delete_XXXXXX",
738                          &iostream, error);
739   g_assert_no_error (local_error);
740   g_object_unref (iostream);
741
742   g_assert (g_file_query_exists (file, NULL));
743
744   loop = g_main_loop_new (NULL, TRUE);
745
746   g_file_delete_async (file, G_PRIORITY_DEFAULT, NULL, on_file_deleted, loop);
747
748   g_main_loop_run (loop);
749
750   g_assert (!g_file_query_exists (file, NULL));
751
752   g_main_loop_unref (loop);
753   g_object_unref (file);
754 }
755
756 #ifdef G_OS_UNIX
757 static void
758 test_copy_preserve_mode (void)
759 {
760   GFile *tmpfile;
761   GFile *dest_tmpfile;
762   GFileInfo *dest_info;
763   GFileIOStream *iostream;
764   GError *local_error = NULL;
765   GError **error = &local_error;
766   guint32 romode = S_IFREG | 0600;
767   guint32 dest_mode;
768
769   tmpfile = g_file_new_tmp ("tmp-copy-preserve-modeXXXXXX",
770                             &iostream, error);
771   g_assert_no_error (local_error);
772   g_io_stream_close ((GIOStream*)iostream, NULL, error);
773   g_assert_no_error (local_error);
774   g_clear_object (&iostream);
775
776   g_file_set_attribute (tmpfile, G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_ATTRIBUTE_TYPE_UINT32,
777                         &romode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
778                         NULL, error);
779   g_assert_no_error (local_error);
780
781   dest_tmpfile = g_file_new_tmp ("tmp-copy-preserve-modeXXXXXX",
782                                  &iostream, error);
783   g_assert_no_error (local_error);
784   g_io_stream_close ((GIOStream*)iostream, NULL, error);
785   g_assert_no_error (local_error);
786   g_clear_object (&iostream);
787
788   g_file_copy (tmpfile, dest_tmpfile, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
789                NULL, NULL, NULL, error);
790   g_assert_no_error (local_error);
791
792   dest_info = g_file_query_info (dest_tmpfile, G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
793                                  NULL, error);
794   g_assert_no_error (local_error);
795
796   dest_mode = g_file_info_get_attribute_uint32 (dest_info, G_FILE_ATTRIBUTE_UNIX_MODE);
797   
798   g_assert_cmpint (dest_mode, ==, romode);
799
800   (void) g_file_delete (tmpfile, NULL, NULL);
801   (void) g_file_delete (dest_tmpfile, NULL, NULL);
802   
803   g_clear_object (&tmpfile);
804   g_clear_object (&dest_tmpfile);
805   g_clear_object (&dest_info);
806 }
807 #endif
808
809 static gchar *
810 splice_to_string (GInputStream   *stream,
811                   GError        **error)
812 {
813   GMemoryOutputStream *buffer = NULL;
814   char *ret = NULL;
815
816   buffer = (GMemoryOutputStream*)g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
817   if (g_output_stream_splice ((GOutputStream*)buffer, stream, 0, NULL, error) < 0)
818     goto out;
819
820   if (!g_output_stream_write ((GOutputStream*)buffer, "\0", 1, NULL, error))
821     goto out;
822
823   if (!g_output_stream_close ((GOutputStream*)buffer, NULL, error))
824     goto out;
825
826   ret = g_memory_output_stream_steal_data (buffer);
827  out:
828   g_clear_object (&buffer);
829   return ret;
830 }
831
832 static guint64
833 get_size_from_du (const gchar *path)
834 {
835   GSubprocess *du;
836   gchar *result;
837   gchar *endptr;
838   guint64 size;
839   GError *error = NULL;
840
841   du = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE,
842                          &error,
843                          "du", "--bytes", "-s", path, NULL);
844   g_assert_no_error (error);
845
846   result = splice_to_string (g_subprocess_get_stdout_pipe (du), &error);
847   g_assert_no_error (error);
848
849   size = g_ascii_strtoll (result, &endptr, 10);
850
851   g_object_unref (du);
852   g_free (result);
853
854   return size;
855 }
856
857 static void
858 test_measure (void)
859 {
860   GFile *file;
861   guint64 size;
862   guint64 num_bytes;
863   guint64 num_dirs;
864   guint64 num_files;
865   GError *error = NULL;
866   gboolean ok;
867   gchar *path;
868
869   path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL);
870   file = g_file_new_for_path (path);
871
872   if (g_find_program_in_path ("du"))
873     {
874       size = get_size_from_du (path);
875     }
876   else
877     {
878       g_test_message ("du not found, skipping byte measurement");
879       size = 0;
880     }
881
882   ok = g_file_measure_disk_usage (file,
883                                   G_FILE_MEASURE_APPARENT_SIZE,
884                                   NULL,
885                                   NULL,
886                                   NULL,
887                                   &num_bytes,
888                                   &num_dirs,
889                                   &num_files,
890                                   &error);
891   g_assert (ok);
892   g_assert_no_error (error);
893
894   if (size > 0)
895     g_assert_cmpuint (num_bytes, ==, size);
896   g_assert_cmpuint (num_dirs, ==, 6);
897   g_assert_cmpuint (num_files, ==, 30);
898
899   g_object_unref (file);
900   g_free (path);
901 }
902
903 typedef struct {
904   guint64 expected_bytes;
905   guint64 expected_dirs;
906   guint64 expected_files;
907   gint progress_count;
908   guint64 progress_bytes;
909   guint64 progress_dirs;
910   guint64 progress_files;
911 } MeasureData;
912
913 static void
914 measure_progress (gboolean reporting,
915                   guint64  current_size,
916                   guint64  num_dirs,
917                   guint64  num_files,
918                   gpointer user_data)
919 {
920   MeasureData *data = user_data;
921
922   data->progress_count += 1;
923
924   g_assert_cmpuint (current_size, >=, data->progress_bytes);
925   g_assert_cmpuint (num_dirs, >=, data->progress_dirs);
926   g_assert_cmpuint (num_files, >=, data->progress_files);
927
928   data->progress_bytes = current_size;
929   data->progress_dirs = num_dirs;
930   data->progress_files = num_files;
931 }
932
933 static void
934 measure_done (GObject      *source,
935               GAsyncResult *res,
936               gpointer      user_data)
937 {
938   MeasureData *data = user_data;
939   guint64 num_bytes, num_dirs, num_files;
940   GError *error = NULL;
941   gboolean ok;
942
943   ok = g_file_measure_disk_usage_finish (G_FILE (source), res, &num_bytes, &num_dirs, &num_files, &error);
944   g_assert (ok);
945   g_assert_no_error (error);
946
947   if (data->expected_bytes > 0)
948     g_assert_cmpuint (data->expected_bytes, ==, num_bytes);
949   g_assert_cmpuint (data->expected_dirs, ==, num_dirs);
950   g_assert_cmpuint (data->expected_files, ==, num_files);
951
952   g_assert_cmpuint (data->progress_count, >, 0);
953   g_assert_cmpuint (num_bytes, >=, data->progress_bytes);
954   g_assert_cmpuint (num_dirs, >=, data->progress_dirs);
955   g_assert_cmpuint (num_files, >=, data->progress_files);
956
957   g_free (data);
958   g_object_unref (source);
959 }
960
961 static void
962 test_measure_async (void)
963 {
964   gchar *path;
965   GFile *file;
966   MeasureData *data;
967
968   data = g_new (MeasureData, 1);
969
970   data->progress_count = 0;
971   data->progress_bytes = 0;
972   data->progress_files = 0;
973   data->progress_dirs = 0;
974
975   path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL);
976   file = g_file_new_for_path (path);
977
978   if (g_find_program_in_path ("du"))
979     {
980       data->expected_bytes = get_size_from_du (path);
981     }
982   else
983     {
984       g_test_message ("du not found, skipping byte measurement");
985       data->expected_bytes = 0;
986     }
987
988   g_free (path);
989
990   data->expected_dirs = 6;
991   data->expected_files = 30;
992
993   g_file_measure_disk_usage_async (file,
994                                    G_FILE_MEASURE_APPARENT_SIZE,
995                                    0, NULL,
996                                    measure_progress, data,
997                                    measure_done, data);
998 }
999
1000 int
1001 main (int argc, char *argv[])
1002 {
1003   g_test_init (&argc, &argv, NULL);
1004
1005   g_test_bug_base ("http://bugzilla.gnome.org/");
1006
1007   g_test_add_func ("/file/basic", test_basic);
1008   g_test_add_func ("/file/parent", test_parent);
1009   g_test_add_func ("/file/child", test_child);
1010   g_test_add_func ("/file/type", test_type);
1011   g_test_add_func ("/file/parse-name", test_parse_name);
1012   g_test_add_data_func ("/file/async-create-delete/0", GINT_TO_POINTER (0), test_create_delete);
1013   g_test_add_data_func ("/file/async-create-delete/1", GINT_TO_POINTER (1), test_create_delete);
1014   g_test_add_data_func ("/file/async-create-delete/10", GINT_TO_POINTER (10), test_create_delete);
1015   g_test_add_data_func ("/file/async-create-delete/25", GINT_TO_POINTER (25), test_create_delete);
1016   g_test_add_data_func ("/file/async-create-delete/4096", GINT_TO_POINTER (4096), test_create_delete);
1017   g_test_add_func ("/file/replace-load", test_replace_load);
1018   g_test_add_func ("/file/replace-cancel", test_replace_cancel);
1019   g_test_add_func ("/file/async-delete", test_async_delete);
1020 #ifdef G_OS_UNIX
1021   g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
1022 #endif
1023   g_test_add_func ("/file/measure", test_measure);
1024   g_test_add_func ("/file/measure-async", test_measure_async);
1025
1026   return g_test_run ();
1027 }