Imported Upstream version 2.51.1
[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_test_skip ("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: (nullable): 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   guint count;
650   GError *error = NULL;
651
652   g_test_bug ("629301");
653
654   path = g_dir_make_tmp ("g_file_replace_cancel_XXXXXX", &error);
655   g_assert_no_error (error);
656   tmpdir = g_file_new_for_path (path);
657   g_free (path);
658
659   file = g_file_get_child (tmpdir, "file");
660   g_file_replace_contents (file,
661                            replace_data,
662                            strlen (replace_data),
663                            NULL, FALSE, 0, NULL,
664                            NULL, &error);
665   g_assert_no_error (error);
666
667   ostream = g_file_replace (file, NULL, TRUE, 0, NULL, &error);
668   g_assert_no_error (error);
669
670   g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
671                              replace_data, strlen (replace_data),
672                              &nwrote, NULL, &error);
673   g_assert_no_error (error);
674   g_assert_cmpint (nwrote, ==, strlen (replace_data));
675
676   /* At this point there should be two files; the original and the
677    * temporary.
678    */
679   fenum = g_file_enumerate_children (tmpdir, NULL, 0, NULL, &error);
680   g_assert_no_error (error);
681
682   info = g_file_enumerator_next_file (fenum, NULL, &error);
683   g_assert_no_error (error);
684   g_assert (info != NULL);
685   g_object_unref (info);
686   info = g_file_enumerator_next_file (fenum, NULL, &error);
687   g_assert_no_error (error);
688   g_assert (info != NULL);
689   g_object_unref (info);
690
691   g_file_enumerator_close (fenum, NULL, &error);
692   g_assert_no_error (error);
693   g_object_unref (fenum);
694
695   /* Also test the g_file_enumerator_iterate() API */
696   fenum = g_file_enumerate_children (tmpdir, NULL, 0, NULL, &error);
697   g_assert_no_error (error);
698   count = 0;
699
700   while (TRUE)
701     {
702       gboolean ret = g_file_enumerator_iterate (fenum, &info, NULL, NULL, &error);
703       g_assert (ret);
704       g_assert_no_error (error);
705       if (!info)
706         break;
707       count++;
708     }
709   g_assert_cmpint (count, ==, 2);
710
711   g_file_enumerator_close (fenum, NULL, &error);
712   g_assert_no_error (error);
713   g_object_unref (fenum);
714
715   /* Now test just getting child from the g_file_enumerator_iterate() API */
716   fenum = g_file_enumerate_children (tmpdir, "standard::name", 0, NULL, &error);
717   g_assert_no_error (error);
718   count = 0;
719
720   while (TRUE)
721     {
722       GFile *child;
723       gboolean ret = g_file_enumerator_iterate (fenum, NULL, &child, NULL, &error);
724
725       g_assert (ret);
726       g_assert_no_error (error);
727
728       if (!child)
729         break;
730
731       g_assert (G_IS_FILE (child));
732       count++;
733     }
734   g_assert_cmpint (count, ==, 2);
735
736   g_file_enumerator_close (fenum, NULL, &error);
737   g_assert_no_error (error);
738   g_object_unref (fenum);
739
740   /* Make sure the temporary gets deleted even if we cancel. */
741   cancellable = g_cancellable_new ();
742   g_cancellable_cancel (cancellable);
743   g_output_stream_close (G_OUTPUT_STREAM (ostream), cancellable, &error);
744   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
745   g_clear_error (&error);
746
747   g_object_unref (cancellable);
748   g_object_unref (ostream);
749
750   g_file_delete (file, NULL, &error);
751   g_assert_no_error (error);
752   g_object_unref (file);
753
754   /* This will only succeed if the temp file was deleted. */
755   g_file_delete (tmpdir, NULL, &error);
756   g_assert_no_error (error);
757   g_object_unref (tmpdir);
758 }
759
760 static void
761 on_file_deleted (GObject      *object,
762                  GAsyncResult *result,
763                  gpointer      user_data)
764 {
765   GError *local_error = NULL;
766   GMainLoop *loop = user_data;
767
768   (void) g_file_delete_finish ((GFile*)object, result, &local_error);
769   g_assert_no_error (local_error);
770
771   g_main_loop_quit (loop);
772 }
773
774 static void
775 test_async_delete (void)
776 {
777   GFile *file;
778   GFileIOStream *iostream;
779   GError *local_error = NULL;
780   GError **error = &local_error;
781   GMainLoop *loop;
782
783   file = g_file_new_tmp ("g_file_delete_XXXXXX",
784                          &iostream, error);
785   g_assert_no_error (local_error);
786   g_object_unref (iostream);
787
788   g_assert (g_file_query_exists (file, NULL));
789
790   loop = g_main_loop_new (NULL, TRUE);
791
792   g_file_delete_async (file, G_PRIORITY_DEFAULT, NULL, on_file_deleted, loop);
793
794   g_main_loop_run (loop);
795
796   g_assert (!g_file_query_exists (file, NULL));
797
798   g_main_loop_unref (loop);
799   g_object_unref (file);
800 }
801
802 #ifdef G_OS_UNIX
803 static void
804 test_copy_preserve_mode (void)
805 {
806   GFile *tmpfile;
807   GFile *dest_tmpfile;
808   GFileInfo *dest_info;
809   GFileIOStream *iostream;
810   GError *local_error = NULL;
811   GError **error = &local_error;
812   guint32 romode = S_IFREG | 0600;
813   guint32 dest_mode;
814
815   tmpfile = g_file_new_tmp ("tmp-copy-preserve-modeXXXXXX",
816                             &iostream, error);
817   g_assert_no_error (local_error);
818   g_io_stream_close ((GIOStream*)iostream, NULL, error);
819   g_assert_no_error (local_error);
820   g_clear_object (&iostream);
821
822   g_file_set_attribute (tmpfile, G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_ATTRIBUTE_TYPE_UINT32,
823                         &romode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
824                         NULL, error);
825   g_assert_no_error (local_error);
826
827   dest_tmpfile = g_file_new_tmp ("tmp-copy-preserve-modeXXXXXX",
828                                  &iostream, error);
829   g_assert_no_error (local_error);
830   g_io_stream_close ((GIOStream*)iostream, NULL, error);
831   g_assert_no_error (local_error);
832   g_clear_object (&iostream);
833
834   g_file_copy (tmpfile, dest_tmpfile, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
835                NULL, NULL, NULL, error);
836   g_assert_no_error (local_error);
837
838   dest_info = g_file_query_info (dest_tmpfile, G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
839                                  NULL, error);
840   g_assert_no_error (local_error);
841
842   dest_mode = g_file_info_get_attribute_uint32 (dest_info, G_FILE_ATTRIBUTE_UNIX_MODE);
843   
844   g_assert_cmpint (dest_mode, ==, romode);
845
846   (void) g_file_delete (tmpfile, NULL, NULL);
847   (void) g_file_delete (dest_tmpfile, NULL, NULL);
848   
849   g_clear_object (&tmpfile);
850   g_clear_object (&dest_tmpfile);
851   g_clear_object (&dest_info);
852 }
853 #endif
854
855 static gchar *
856 splice_to_string (GInputStream   *stream,
857                   GError        **error)
858 {
859   GMemoryOutputStream *buffer = NULL;
860   char *ret = NULL;
861
862   buffer = (GMemoryOutputStream*)g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
863   if (g_output_stream_splice ((GOutputStream*)buffer, stream, 0, NULL, error) < 0)
864     goto out;
865
866   if (!g_output_stream_write ((GOutputStream*)buffer, "\0", 1, NULL, error))
867     goto out;
868
869   if (!g_output_stream_close ((GOutputStream*)buffer, NULL, error))
870     goto out;
871
872   ret = g_memory_output_stream_steal_data (buffer);
873  out:
874   g_clear_object (&buffer);
875   return ret;
876 }
877
878 static guint64
879 get_size_from_du (const gchar *path)
880 {
881   GSubprocess *du;
882   gchar *result;
883   gchar *endptr;
884   guint64 size;
885   GError *error = NULL;
886
887   du = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE,
888                          &error,
889                          "du", "--bytes", "-s", path, NULL);
890   g_assert_no_error (error);
891
892   result = splice_to_string (g_subprocess_get_stdout_pipe (du), &error);
893   g_assert_no_error (error);
894
895   size = g_ascii_strtoll (result, &endptr, 10);
896
897   g_object_unref (du);
898   g_free (result);
899
900   return size;
901 }
902
903 static void
904 test_measure (void)
905 {
906   GFile *file;
907   guint64 size;
908   guint64 num_bytes;
909   guint64 num_dirs;
910   guint64 num_files;
911   GError *error = NULL;
912   gboolean ok;
913   gchar *path;
914
915   path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL);
916   file = g_file_new_for_path (path);
917
918   if (g_find_program_in_path ("du"))
919     {
920       size = get_size_from_du (path);
921     }
922   else
923     {
924       g_test_message ("du not found, skipping byte measurement");
925       size = 0;
926     }
927
928   ok = g_file_measure_disk_usage (file,
929                                   G_FILE_MEASURE_APPARENT_SIZE,
930                                   NULL,
931                                   NULL,
932                                   NULL,
933                                   &num_bytes,
934                                   &num_dirs,
935                                   &num_files,
936                                   &error);
937   g_assert (ok);
938   g_assert_no_error (error);
939
940   if (size > 0)
941     g_assert_cmpuint (num_bytes, ==, size);
942   g_assert_cmpuint (num_dirs, ==, 6);
943   g_assert_cmpuint (num_files, ==, 30);
944
945   g_object_unref (file);
946   g_free (path);
947 }
948
949 typedef struct {
950   guint64 expected_bytes;
951   guint64 expected_dirs;
952   guint64 expected_files;
953   gint progress_count;
954   guint64 progress_bytes;
955   guint64 progress_dirs;
956   guint64 progress_files;
957 } MeasureData;
958
959 static void
960 measure_progress (gboolean reporting,
961                   guint64  current_size,
962                   guint64  num_dirs,
963                   guint64  num_files,
964                   gpointer user_data)
965 {
966   MeasureData *data = user_data;
967
968   data->progress_count += 1;
969
970   g_assert_cmpuint (current_size, >=, data->progress_bytes);
971   g_assert_cmpuint (num_dirs, >=, data->progress_dirs);
972   g_assert_cmpuint (num_files, >=, data->progress_files);
973
974   data->progress_bytes = current_size;
975   data->progress_dirs = num_dirs;
976   data->progress_files = num_files;
977 }
978
979 static void
980 measure_done (GObject      *source,
981               GAsyncResult *res,
982               gpointer      user_data)
983 {
984   MeasureData *data = user_data;
985   guint64 num_bytes, num_dirs, num_files;
986   GError *error = NULL;
987   gboolean ok;
988
989   ok = g_file_measure_disk_usage_finish (G_FILE (source), res, &num_bytes, &num_dirs, &num_files, &error);
990   g_assert (ok);
991   g_assert_no_error (error);
992
993   if (data->expected_bytes > 0)
994     g_assert_cmpuint (data->expected_bytes, ==, num_bytes);
995   g_assert_cmpuint (data->expected_dirs, ==, num_dirs);
996   g_assert_cmpuint (data->expected_files, ==, num_files);
997
998   g_assert_cmpuint (data->progress_count, >, 0);
999   g_assert_cmpuint (num_bytes, >=, data->progress_bytes);
1000   g_assert_cmpuint (num_dirs, >=, data->progress_dirs);
1001   g_assert_cmpuint (num_files, >=, data->progress_files);
1002
1003   g_free (data);
1004   g_object_unref (source);
1005 }
1006
1007 static void
1008 test_measure_async (void)
1009 {
1010   gchar *path;
1011   GFile *file;
1012   MeasureData *data;
1013
1014   data = g_new (MeasureData, 1);
1015
1016   data->progress_count = 0;
1017   data->progress_bytes = 0;
1018   data->progress_files = 0;
1019   data->progress_dirs = 0;
1020
1021   path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL);
1022   file = g_file_new_for_path (path);
1023
1024   if (g_find_program_in_path ("du"))
1025     {
1026       data->expected_bytes = get_size_from_du (path);
1027     }
1028   else
1029     {
1030       g_test_message ("du not found, skipping byte measurement");
1031       data->expected_bytes = 0;
1032     }
1033
1034   g_free (path);
1035
1036   data->expected_dirs = 6;
1037   data->expected_files = 30;
1038
1039   g_file_measure_disk_usage_async (file,
1040                                    G_FILE_MEASURE_APPARENT_SIZE,
1041                                    0, NULL,
1042                                    measure_progress, data,
1043                                    measure_done, data);
1044 }
1045
1046 int
1047 main (int argc, char *argv[])
1048 {
1049   g_test_init (&argc, &argv, NULL);
1050
1051   g_test_bug_base ("http://bugzilla.gnome.org/");
1052
1053   g_test_add_func ("/file/basic", test_basic);
1054   g_test_add_func ("/file/parent", test_parent);
1055   g_test_add_func ("/file/child", test_child);
1056   g_test_add_func ("/file/type", test_type);
1057   g_test_add_func ("/file/parse-name", test_parse_name);
1058   g_test_add_data_func ("/file/async-create-delete/0", GINT_TO_POINTER (0), test_create_delete);
1059   g_test_add_data_func ("/file/async-create-delete/1", GINT_TO_POINTER (1), test_create_delete);
1060   g_test_add_data_func ("/file/async-create-delete/10", GINT_TO_POINTER (10), test_create_delete);
1061   g_test_add_data_func ("/file/async-create-delete/25", GINT_TO_POINTER (25), test_create_delete);
1062   g_test_add_data_func ("/file/async-create-delete/4096", GINT_TO_POINTER (4096), test_create_delete);
1063   g_test_add_func ("/file/replace-load", test_replace_load);
1064   g_test_add_func ("/file/replace-cancel", test_replace_cancel);
1065   g_test_add_func ("/file/async-delete", test_async_delete);
1066 #ifdef G_OS_UNIX
1067   g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
1068 #endif
1069   g_test_add_func ("/file/measure", test_measure);
1070   g_test_add_func ("/file/measure-async", test_measure_async);
1071
1072   return g_test_run ();
1073 }